[{"data":1,"prerenderedAt":2174},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification":2141,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification":2149,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification":2154,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification":2155},{"id":4,"title":5,"body":6,"dateModified":2108,"datePublished":2108,"description":2109,"extension":2110,"faq":2111,"keyword":2120,"kind":2121,"meta":2122,"navigation":104,"path":2131,"seo":2132,"slug":2135,"stem":2136,"topics":2137,"type":2139,"__hash__":2140},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification\u002Findex.md","Run an ABC-XYZ Inventory Classification",{"type":7,"value":8,"toc":2096},"minimark",[9,29,34,63,69,73,76,426,433,437,440,748,755,858,862,865,965,968,972,975,1391,1398,1515,1616,1620,1623,1984,1987,1991,2026,2030,2033,2037,2043,2049,2055,2061,2065,2092],[10,11,12,13,18,19,23,24,28],"p",{},"A warehouse with two thousand stock items cannot manage every one with the same care. ABC analysis sorts items by value — the few A items that make up most of the money deserve the closest attention — and is covered on its own in ",[14,15,17],"a",{"href":16},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-a-pareto-analysis-on-excel-data-with-pandas\u002F","Run a Pareto Analysis on Excel Data with pandas",". But value is only half the story. An A item that sells a steady 200 units every month is easy to plan; an A item that sells 50 one month and 600 the next is where stock-outs and excess stock come from. XYZ analysis adds that second dimension — how ",[20,21,22],"em",{},"variable"," demand is — and the combined ABC-XYZ matrix tells a planner which items can be replenished automatically and which need judgement. This page builds both classifications from monthly demand data with pandas, puts them in the nine-cell matrix, attaches a stocking policy to each cell, and writes the result to Excel. It belongs to ",[14,25,27],{"href":26},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data",".",[30,31,33],"h2",{"id":32},"prerequisites","Prerequisites",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[42,43,44],"code",{"__ignoreMap":40},[45,46,49,53,57,60],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"sMTad","pip",[45,54,56],{"class":55},"srMev"," install",[45,58,59],{"class":55}," pandas",[45,61,62],{"class":55}," openpyxl\n",[10,64,65,68],{},[42,66,67],{},"demand.xlsx"," has twelve months of demand, October 2025 to September 2026, for 240 stock items: SKU, month, units issued and unit cost. Some items sell steadily, some seasonally, some in occasional large lumps, and a handful were introduced only in the last three months.",[30,70,72],{"id":71},"step-1-abc-by-consumption-value","Step 1 — ABC by consumption value",[10,74,75],{},"ABC classes rank items by annual consumption value — units times cost — and cut the ranking where the cumulative share of total value reaches set thresholds, commonly 80% and 95%:",[35,77,81],{"className":78,"code":79,"language":80,"meta":40,"style":40},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndemand = pd.read_excel(\"demand.xlsx\")\ndemand[\"Value\"] = demand[\"Units\"] * demand[\"Unit cost\"]\nitems = demand.groupby(\"SKU\").agg(units=(\"Units\", \"sum\"), value=(\"Value\", \"sum\"), cost=(\"Unit cost\", \"first\"))\nitems = items.sort_values(\"value\", ascending=False)\nitems[\"cum_share\"] = items[\"value\"].cumsum() \u002F items[\"value\"].sum()\nitems[\"ABC\"] = pd.cut(items[\"cum_share\"], [0, 0.80, 0.95, 1.0], labels=[\"A\", \"B\", \"C\"], include_lowest=True)\nprint(items.groupby(\"ABC\", observed=True).agg(items=(\"value\", \"size\"), value_share=(\"value\", lambda v: v.sum() \u002F items[\"value\"].sum())).round(3))\n","python",[42,82,83,99,106,124,157,226,252,283,357],{"__ignoreMap":40},[45,84,85,89,93,96],{"class":47,"line":48},[45,86,88],{"class":87},"s-kum","import",[45,90,92],{"class":91},"skGVy"," pandas ",[45,94,95],{"class":87},"as",[45,97,98],{"class":91}," pd\n",[45,100,102],{"class":47,"line":101},2,[45,103,105],{"emptyLinePlaceholder":104},true,"\n",[45,107,109,112,115,118,121],{"class":47,"line":108},3,[45,110,111],{"class":91},"demand ",[45,113,114],{"class":87},"=",[45,116,117],{"class":91}," pd.read_excel(",[45,119,120],{"class":55},"\"demand.xlsx\"",[45,122,123],{"class":91},")\n",[45,125,127,130,133,136,138,141,144,146,149,151,154],{"class":47,"line":126},4,[45,128,129],{"class":91},"demand[",[45,131,132],{"class":55},"\"Value\"",[45,134,135],{"class":91},"] ",[45,137,114],{"class":87},[45,139,140],{"class":91}," demand[",[45,142,143],{"class":55},"\"Units\"",[45,145,135],{"class":91},[45,147,148],{"class":87},"*",[45,150,140],{"class":91},[45,152,153],{"class":55},"\"Unit cost\"",[45,155,156],{"class":91},"]\n",[45,158,160,163,165,168,171,174,178,180,183,185,188,191,194,197,199,201,203,205,207,209,212,214,216,218,220,223],{"class":47,"line":159},5,[45,161,162],{"class":91},"items ",[45,164,114],{"class":87},[45,166,167],{"class":91}," demand.groupby(",[45,169,170],{"class":55},"\"SKU\"",[45,172,173],{"class":91},").agg(",[45,175,177],{"class":176},"sa561","units",[45,179,114],{"class":87},[45,181,182],{"class":91},"(",[45,184,143],{"class":55},[45,186,187],{"class":91},", ",[45,189,190],{"class":55},"\"sum\"",[45,192,193],{"class":91},"), ",[45,195,196],{"class":176},"value",[45,198,114],{"class":87},[45,200,182],{"class":91},[45,202,132],{"class":55},[45,204,187],{"class":91},[45,206,190],{"class":55},[45,208,193],{"class":91},[45,210,211],{"class":176},"cost",[45,213,114],{"class":87},[45,215,182],{"class":91},[45,217,153],{"class":55},[45,219,187],{"class":91},[45,221,222],{"class":55},"\"first\"",[45,224,225],{"class":91},"))\n",[45,227,229,231,233,236,239,241,244,246,250],{"class":47,"line":228},6,[45,230,162],{"class":91},[45,232,114],{"class":87},[45,234,235],{"class":91}," items.sort_values(",[45,237,238],{"class":55},"\"value\"",[45,240,187],{"class":91},[45,242,243],{"class":176},"ascending",[45,245,114],{"class":87},[45,247,249],{"class":248},"sP0c6","False",[45,251,123],{"class":91},[45,253,255,258,261,263,265,268,270,273,276,278,280],{"class":47,"line":254},7,[45,256,257],{"class":91},"items[",[45,259,260],{"class":55},"\"cum_share\"",[45,262,135],{"class":91},[45,264,114],{"class":87},[45,266,267],{"class":91}," items[",[45,269,238],{"class":55},[45,271,272],{"class":91},"].cumsum() ",[45,274,275],{"class":87},"\u002F",[45,277,267],{"class":91},[45,279,238],{"class":55},[45,281,282],{"class":91},"].sum()\n",[45,284,286,288,291,293,295,298,300,303,306,308,311,313,316,318,321,324,327,329,332,335,337,340,342,345,347,350,352,355],{"class":47,"line":285},8,[45,287,257],{"class":91},[45,289,290],{"class":55},"\"ABC\"",[45,292,135],{"class":91},[45,294,114],{"class":87},[45,296,297],{"class":91}," pd.cut(items[",[45,299,260],{"class":55},[45,301,302],{"class":91},"], [",[45,304,305],{"class":248},"0",[45,307,187],{"class":91},[45,309,310],{"class":248},"0.80",[45,312,187],{"class":91},[45,314,315],{"class":248},"0.95",[45,317,187],{"class":91},[45,319,320],{"class":248},"1.0",[45,322,323],{"class":91},"], ",[45,325,326],{"class":176},"labels",[45,328,114],{"class":87},[45,330,331],{"class":91},"[",[45,333,334],{"class":55},"\"A\"",[45,336,187],{"class":91},[45,338,339],{"class":55},"\"B\"",[45,341,187],{"class":91},[45,343,344],{"class":55},"\"C\"",[45,346,323],{"class":91},[45,348,349],{"class":176},"include_lowest",[45,351,114],{"class":87},[45,353,354],{"class":248},"True",[45,356,123],{"class":91},[45,358,360,363,366,368,370,373,375,377,379,382,384,386,388,390,393,395,398,400,402,404,406,409,412,414,416,418,421,424],{"class":47,"line":359},9,[45,361,362],{"class":248},"print",[45,364,365],{"class":91},"(items.groupby(",[45,367,290],{"class":55},[45,369,187],{"class":91},[45,371,372],{"class":176},"observed",[45,374,114],{"class":87},[45,376,354],{"class":248},[45,378,173],{"class":91},[45,380,381],{"class":176},"items",[45,383,114],{"class":87},[45,385,182],{"class":91},[45,387,238],{"class":55},[45,389,187],{"class":91},[45,391,392],{"class":55},"\"size\"",[45,394,193],{"class":91},[45,396,397],{"class":176},"value_share",[45,399,114],{"class":87},[45,401,182],{"class":91},[45,403,238],{"class":55},[45,405,187],{"class":91},[45,407,408],{"class":87},"lambda",[45,410,411],{"class":91}," v: v.sum() ",[45,413,275],{"class":87},[45,415,267],{"class":91},[45,417,238],{"class":55},[45,419,420],{"class":91},"].sum())).round(",[45,422,423],{"class":248},"3",[45,425,225],{"class":91},[10,427,428,429,432],{},"Sorting by value and taking the cumulative share puts the most valuable items first; ",[42,430,431],{},"pd.cut"," assigns A to items within the first 80% of value, B to the next 15% and C to the rest. The usual Pareto shape appears: 59 of the 240 items are A and account for 80% of the money, while 109 C items — almost half the range — account for 5%. The thresholds are conventions, not laws — some businesses use 70\u002F90, or classify by margin or by number of picks instead of value — so keep them as named settings.",[30,434,436],{"id":435},"step-2-xyz-by-demand-variability","Step 2 — XYZ by demand variability",[10,438,439],{},"XYZ classes measure how predictable demand is, using the coefficient of variation (CV) of monthly demand — the standard deviation divided by the mean:",[35,441,443],{"className":78,"code":442,"language":80,"meta":40,"style":40},"monthly = demand.pivot_table(index=\"SKU\", columns=\"Month\", values=\"Units\", aggfunc=\"sum\", fill_value=0)\nstats = pd.DataFrame({\"mean\": monthly.mean(axis=1), \"std\": monthly.std(axis=1, ddof=0),\n                      \"months_with_demand\": (monthly > 0).sum(axis=1)})\nstats[\"cv\"] = (stats[\"std\"] \u002F stats[\"mean\"]).where(stats[\"mean\"] > 0)\nstats[\"XYZ\"] = pd.cut(stats[\"cv\"], [0, 0.5, 1.0, float(\"inf\")], labels=[\"X\", \"Y\", \"Z\"], include_lowest=True)\nitems = items.join(stats)\nprint(items[\"XYZ\"].value_counts().sort_index().to_dict())\nprint(items.groupby(\"XYZ\", observed=True)[\"cv\"].agg([\"min\", \"max\"]).round(2))\n",[42,444,445,501,551,577,616,687,696,708],{"__ignoreMap":40},[45,446,447,450,452,455,458,460,462,464,467,469,472,474,477,479,481,483,486,488,490,492,495,497,499],{"class":47,"line":48},[45,448,449],{"class":91},"monthly ",[45,451,114],{"class":87},[45,453,454],{"class":91}," demand.pivot_table(",[45,456,457],{"class":176},"index",[45,459,114],{"class":87},[45,461,170],{"class":55},[45,463,187],{"class":91},[45,465,466],{"class":176},"columns",[45,468,114],{"class":87},[45,470,471],{"class":55},"\"Month\"",[45,473,187],{"class":91},[45,475,476],{"class":176},"values",[45,478,114],{"class":87},[45,480,143],{"class":55},[45,482,187],{"class":91},[45,484,485],{"class":176},"aggfunc",[45,487,114],{"class":87},[45,489,190],{"class":55},[45,491,187],{"class":91},[45,493,494],{"class":176},"fill_value",[45,496,114],{"class":87},[45,498,305],{"class":248},[45,500,123],{"class":91},[45,502,503,506,508,511,514,517,520,522,525,527,530,533,535,537,539,541,544,546,548],{"class":47,"line":101},[45,504,505],{"class":91},"stats ",[45,507,114],{"class":87},[45,509,510],{"class":91}," pd.DataFrame({",[45,512,513],{"class":55},"\"mean\"",[45,515,516],{"class":91},": monthly.mean(",[45,518,519],{"class":176},"axis",[45,521,114],{"class":87},[45,523,524],{"class":248},"1",[45,526,193],{"class":91},[45,528,529],{"class":55},"\"std\"",[45,531,532],{"class":91},": monthly.std(",[45,534,519],{"class":176},[45,536,114],{"class":87},[45,538,524],{"class":248},[45,540,187],{"class":91},[45,542,543],{"class":176},"ddof",[45,545,114],{"class":87},[45,547,305],{"class":248},[45,549,550],{"class":91},"),\n",[45,552,553,556,559,562,565,568,570,572,574],{"class":47,"line":108},[45,554,555],{"class":55},"                      \"months_with_demand\"",[45,557,558],{"class":91},": (monthly ",[45,560,561],{"class":87},">",[45,563,564],{"class":248}," 0",[45,566,567],{"class":91},").sum(",[45,569,519],{"class":176},[45,571,114],{"class":87},[45,573,524],{"class":248},[45,575,576],{"class":91},")})\n",[45,578,579,582,585,587,589,592,594,596,598,601,603,606,608,610,612,614],{"class":47,"line":126},[45,580,581],{"class":91},"stats[",[45,583,584],{"class":55},"\"cv\"",[45,586,135],{"class":91},[45,588,114],{"class":87},[45,590,591],{"class":91}," (stats[",[45,593,529],{"class":55},[45,595,135],{"class":91},[45,597,275],{"class":87},[45,599,600],{"class":91}," stats[",[45,602,513],{"class":55},[45,604,605],{"class":91},"]).where(stats[",[45,607,513],{"class":55},[45,609,135],{"class":91},[45,611,561],{"class":87},[45,613,564],{"class":248},[45,615,123],{"class":91},[45,617,618,620,623,625,627,630,632,634,636,638,641,643,645,647,650,652,655,658,660,662,664,667,669,672,674,677,679,681,683,685],{"class":47,"line":159},[45,619,581],{"class":91},[45,621,622],{"class":55},"\"XYZ\"",[45,624,135],{"class":91},[45,626,114],{"class":87},[45,628,629],{"class":91}," pd.cut(stats[",[45,631,584],{"class":55},[45,633,302],{"class":91},[45,635,305],{"class":248},[45,637,187],{"class":91},[45,639,640],{"class":248},"0.5",[45,642,187],{"class":91},[45,644,320],{"class":248},[45,646,187],{"class":91},[45,648,649],{"class":248},"float",[45,651,182],{"class":91},[45,653,654],{"class":55},"\"inf\"",[45,656,657],{"class":91},")], ",[45,659,326],{"class":176},[45,661,114],{"class":87},[45,663,331],{"class":91},[45,665,666],{"class":55},"\"X\"",[45,668,187],{"class":91},[45,670,671],{"class":55},"\"Y\"",[45,673,187],{"class":91},[45,675,676],{"class":55},"\"Z\"",[45,678,323],{"class":91},[45,680,349],{"class":176},[45,682,114],{"class":87},[45,684,354],{"class":248},[45,686,123],{"class":91},[45,688,689,691,693],{"class":47,"line":228},[45,690,162],{"class":91},[45,692,114],{"class":87},[45,694,695],{"class":91}," items.join(stats)\n",[45,697,698,700,703,705],{"class":47,"line":254},[45,699,362],{"class":248},[45,701,702],{"class":91},"(items[",[45,704,622],{"class":55},[45,706,707],{"class":91},"].value_counts().sort_index().to_dict())\n",[45,709,710,712,714,716,718,720,722,724,727,729,732,735,737,740,743,746],{"class":47,"line":285},[45,711,362],{"class":248},[45,713,365],{"class":91},[45,715,622],{"class":55},[45,717,187],{"class":91},[45,719,372],{"class":176},[45,721,114],{"class":87},[45,723,354],{"class":248},[45,725,726],{"class":91},")[",[45,728,584],{"class":55},[45,730,731],{"class":91},"].agg([",[45,733,734],{"class":55},"\"min\"",[45,736,187],{"class":91},[45,738,739],{"class":55},"\"max\"",[45,741,742],{"class":91},"]).round(",[45,744,745],{"class":248},"2",[45,747,225],{"class":91},[10,749,750,751,754],{},"A CV below 0.5 means monthly demand stays within about half its average — class X, predictable. Between 0.5 and 1.0 is Y — variable, often seasonal. Above 1.0 is Z — erratic, typically items that sell nothing most months and a lot occasionally. The pivot uses ",[42,752,753],{},"fill_value=0",", because a month with no issues is real zero demand, which matters for the CV: leaving such months out would make lumpy items look steady. As with ABC, the cut-offs are conventions to adjust to your business.",[756,757,765,766,765,770,765,774,765,781,765,791,765,802,765,809,765,815,765,820,765,824,765,828,765,833,765,840,765,845,765,849,765,852,765,855],"svg",{"viewBox":758,"role":759,"ariaLabelledBy":760,"xmlns":763,"style":764},"4 0 752 207","img",[761,762],"abc-dims-t","abc-dims-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[767,768,769],"title",{"id":761},"The two dimensions of the classification",[771,772,773],"desc",{"id":762},"ABC ranks items by annual consumption value, with A items making up the first 80 percent of value, B the next 15 and C the rest. XYZ ranks items by the coefficient of variation of monthly demand, with X below 0.5, Y between 0.5 and 1 and Z above 1.",[775,776],"rect",{"x":777,"y":305,"width":778,"height":779,"fill":780},"4","752","207","#ffffff",[775,782],{"x":783,"y":784,"width":785,"height":786,"rx":787,"fill":788,"stroke":789,"style":790},"20.0","16.0","351.0","175.0","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[792,793,795],"g",{"transform":794},"translate(182.5,29.0) scale(1.083)",[796,797],"path",{"d":798,"fill":799,"stroke":800,"style":801},"M4 20h16 M7 17v-5 M12 17V7 M17 17v-8","none","var(--brand-strong,#4338ca)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[803,804,808],"text",{"x":805,"y":806,"style":807},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","ABC — how much it matters",[47,810],{"x1":811,"y1":812,"x2":813,"y2":812,"stroke":789,"style":814},"36.0","91.0","355.0","stroke-width:1px",[803,816,819],{"x":805,"y":817,"style":818},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","A: first 80% of value",[803,821,823],{"x":805,"y":822,"style":818},"137.0","B: next 15%",[803,825,827],{"x":805,"y":826,"style":818},"163.0","C: last 5% (long tail)",[775,829],{"x":830,"y":784,"width":785,"height":786,"rx":787,"fill":831,"stroke":832,"style":790},"389.0","#d9f4f1","var(--teal,#0f9488)",[792,834,836],{"transform":835},"translate(551.5,29.0) scale(1.083)",[796,837],{"d":838,"fill":799,"stroke":839,"style":801},"M4 20h16 M4 20V4 M6 16l4-5 4 3 6-7","var(--teal-ink,#0b6157)",[803,841,844],{"x":842,"y":806,"style":843},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","XYZ — how predictable",[47,846],{"x1":847,"y1":812,"x2":848,"y2":812,"stroke":832,"style":814},"405.0","724.0",[803,850,851],{"x":842,"y":817,"style":818},"X: CV \u003C 0.5, steady",[803,853,854],{"x":842,"y":822,"style":818},"Y: CV 0.5–1, variable\u002Fseasonal",[803,856,857],{"x":842,"y":826,"style":818},"Z: CV > 1, erratic",[30,859,861],{"id":860},"step-3-items-with-too-little-history","Step 3 — Items with too little history",[10,863,864],{},"A CV from three months of data is meaningless, and items introduced recently would otherwise land in whatever class their few months happen to suggest. Flag them separately before building the matrix:",[35,866,868],{"className":78,"code":867,"language":80,"meta":40,"style":40},"first_month = monthly.gt(0).idxmax(axis=1)\nitems[\"first_month\"] = first_month\nitems[\"new_item\"] = items[\"first_month\"] >= \"2026-06\"\nitems.loc[items[\"new_item\"], \"XYZ\"] = None\nprint(\"new items held out of XYZ:\", items.index[items[\"new_item\"]].tolist())\n",[42,869,870,893,907,930,948],{"__ignoreMap":40},[45,871,872,875,877,880,882,885,887,889,891],{"class":47,"line":48},[45,873,874],{"class":91},"first_month ",[45,876,114],{"class":87},[45,878,879],{"class":91}," monthly.gt(",[45,881,305],{"class":248},[45,883,884],{"class":91},").idxmax(",[45,886,519],{"class":176},[45,888,114],{"class":87},[45,890,524],{"class":248},[45,892,123],{"class":91},[45,894,895,897,900,902,904],{"class":47,"line":101},[45,896,257],{"class":91},[45,898,899],{"class":55},"\"first_month\"",[45,901,135],{"class":91},[45,903,114],{"class":87},[45,905,906],{"class":91}," first_month\n",[45,908,909,911,914,916,918,920,922,924,927],{"class":47,"line":108},[45,910,257],{"class":91},[45,912,913],{"class":55},"\"new_item\"",[45,915,135],{"class":91},[45,917,114],{"class":87},[45,919,267],{"class":91},[45,921,899],{"class":55},[45,923,135],{"class":91},[45,925,926],{"class":87},">=",[45,928,929],{"class":55}," \"2026-06\"\n",[45,931,932,935,937,939,941,943,945],{"class":47,"line":126},[45,933,934],{"class":91},"items.loc[items[",[45,936,913],{"class":55},[45,938,323],{"class":91},[45,940,622],{"class":55},[45,942,135],{"class":91},[45,944,114],{"class":87},[45,946,947],{"class":248}," None\n",[45,949,950,952,954,957,960,962],{"class":47,"line":159},[45,951,362],{"class":248},[45,953,182],{"class":91},[45,955,956],{"class":55},"\"new items held out of XYZ:\"",[45,958,959],{"class":91},", items.index[items[",[45,961,913],{"class":55},[45,963,964],{"class":91},"]].tolist())\n",[10,966,967],{},"Items whose first demand was in the last four months are marked as new and their XYZ class is cleared. They still have an ABC class — their value so far is real — but planning them by variability waits until there is enough history, typically six to twelve months. Listing them explicitly also stops them dragging down the X\u002FY\u002FZ counts in a way nobody can explain. Look at the list rather than trusting it blindly: besides the eight genuinely new items, it catches SKU-0018, an established lumpy item whose first lump of the year happened to fall in June. A first-demand date cannot tell a late starter from an item that sells rarely, so where the item master holds a launch date, use that instead.",[30,969,971],{"id":970},"step-4-the-abc-xyz-matrix-and-a-policy-per-cell","Step 4 — The ABC-XYZ matrix and a policy per cell",[10,973,974],{},"Cross-tabulate the two classes to get the nine-cell matrix, with item counts and each cell's share of total value:",[35,976,978],{"className":78,"code":977,"language":80,"meta":40,"style":40},"established = items[~items[\"new_item\"]]\ncounts = pd.crosstab(established[\"ABC\"], established[\"XYZ\"])\nvalue_share = (pd.crosstab(established[\"ABC\"], established[\"XYZ\"], values=established[\"value\"], aggfunc=\"sum\")\n               .div(items[\"value\"].sum()).mul(100).round(1))\nprint(counts.to_string())\nprint(value_share.to_string())\n\nPOLICY = {\n    (\"A\", \"X\"): \"automatic replenishment, low safety stock\",\n    (\"A\", \"Y\"): \"forecast-driven, review monthly\",\n    (\"A\", \"Z\"): \"manual planning, order to demand\",\n    (\"B\", \"X\"): \"automatic replenishment\",\n    (\"B\", \"Y\"): \"forecast-driven, moderate safety stock\",\n    (\"B\", \"Z\"): \"review quarterly, consider make-to-order\",\n    (\"C\", \"X\"): \"automatic, larger order quantities\",\n    (\"C\", \"Y\"): \"simple reorder point\",\n    (\"C\", \"Z\"): \"stock on demand or discontinue\",\n}\nitems[\"Class\"] = items[\"ABC\"].astype(str) + items[\"XYZ\"].astype(str).replace(\"nan\", \"?\")\nitems[\"Policy\"] = [POLICY.get((a, x), \"new item: review after 6 months\") for a, x in zip(items[\"ABC\"], items[\"XYZ\"])]\nprint(items[\"Class\"].value_counts().head(5).to_dict())\n",[42,979,980,999,1019,1056,1076,1083,1090,1094,1105,1125,1143,1161,1179,1197,1215,1233,1251,1269,1275,1324,1373],{"__ignoreMap":40},[45,981,982,985,987,989,992,994,996],{"class":47,"line":48},[45,983,984],{"class":91},"established ",[45,986,114],{"class":87},[45,988,267],{"class":91},[45,990,991],{"class":87},"~",[45,993,257],{"class":91},[45,995,913],{"class":55},[45,997,998],{"class":91},"]]\n",[45,1000,1001,1004,1006,1009,1011,1014,1016],{"class":47,"line":101},[45,1002,1003],{"class":91},"counts ",[45,1005,114],{"class":87},[45,1007,1008],{"class":91}," pd.crosstab(established[",[45,1010,290],{"class":55},[45,1012,1013],{"class":91},"], established[",[45,1015,622],{"class":55},[45,1017,1018],{"class":91},"])\n",[45,1020,1021,1024,1026,1029,1031,1033,1035,1037,1039,1041,1044,1046,1048,1050,1052,1054],{"class":47,"line":108},[45,1022,1023],{"class":91},"value_share ",[45,1025,114],{"class":87},[45,1027,1028],{"class":91}," (pd.crosstab(established[",[45,1030,290],{"class":55},[45,1032,1013],{"class":91},[45,1034,622],{"class":55},[45,1036,323],{"class":91},[45,1038,476],{"class":176},[45,1040,114],{"class":87},[45,1042,1043],{"class":91},"established[",[45,1045,238],{"class":55},[45,1047,323],{"class":91},[45,1049,485],{"class":176},[45,1051,114],{"class":87},[45,1053,190],{"class":55},[45,1055,123],{"class":91},[45,1057,1058,1061,1063,1066,1069,1072,1074],{"class":47,"line":126},[45,1059,1060],{"class":91},"               .div(items[",[45,1062,238],{"class":55},[45,1064,1065],{"class":91},"].sum()).mul(",[45,1067,1068],{"class":248},"100",[45,1070,1071],{"class":91},").round(",[45,1073,524],{"class":248},[45,1075,225],{"class":91},[45,1077,1078,1080],{"class":47,"line":159},[45,1079,362],{"class":248},[45,1081,1082],{"class":91},"(counts.to_string())\n",[45,1084,1085,1087],{"class":47,"line":228},[45,1086,362],{"class":248},[45,1088,1089],{"class":91},"(value_share.to_string())\n",[45,1091,1092],{"class":47,"line":254},[45,1093,105],{"emptyLinePlaceholder":104},[45,1095,1096,1099,1102],{"class":47,"line":285},[45,1097,1098],{"class":248},"POLICY",[45,1100,1101],{"class":87}," =",[45,1103,1104],{"class":91}," {\n",[45,1106,1107,1110,1112,1114,1116,1119,1122],{"class":47,"line":359},[45,1108,1109],{"class":91},"    (",[45,1111,334],{"class":55},[45,1113,187],{"class":91},[45,1115,666],{"class":55},[45,1117,1118],{"class":91},"): ",[45,1120,1121],{"class":55},"\"automatic replenishment, low safety stock\"",[45,1123,1124],{"class":91},",\n",[45,1126,1128,1130,1132,1134,1136,1138,1141],{"class":47,"line":1127},10,[45,1129,1109],{"class":91},[45,1131,334],{"class":55},[45,1133,187],{"class":91},[45,1135,671],{"class":55},[45,1137,1118],{"class":91},[45,1139,1140],{"class":55},"\"forecast-driven, review monthly\"",[45,1142,1124],{"class":91},[45,1144,1146,1148,1150,1152,1154,1156,1159],{"class":47,"line":1145},11,[45,1147,1109],{"class":91},[45,1149,334],{"class":55},[45,1151,187],{"class":91},[45,1153,676],{"class":55},[45,1155,1118],{"class":91},[45,1157,1158],{"class":55},"\"manual planning, order to demand\"",[45,1160,1124],{"class":91},[45,1162,1164,1166,1168,1170,1172,1174,1177],{"class":47,"line":1163},12,[45,1165,1109],{"class":91},[45,1167,339],{"class":55},[45,1169,187],{"class":91},[45,1171,666],{"class":55},[45,1173,1118],{"class":91},[45,1175,1176],{"class":55},"\"automatic replenishment\"",[45,1178,1124],{"class":91},[45,1180,1182,1184,1186,1188,1190,1192,1195],{"class":47,"line":1181},13,[45,1183,1109],{"class":91},[45,1185,339],{"class":55},[45,1187,187],{"class":91},[45,1189,671],{"class":55},[45,1191,1118],{"class":91},[45,1193,1194],{"class":55},"\"forecast-driven, moderate safety stock\"",[45,1196,1124],{"class":91},[45,1198,1200,1202,1204,1206,1208,1210,1213],{"class":47,"line":1199},14,[45,1201,1109],{"class":91},[45,1203,339],{"class":55},[45,1205,187],{"class":91},[45,1207,676],{"class":55},[45,1209,1118],{"class":91},[45,1211,1212],{"class":55},"\"review quarterly, consider make-to-order\"",[45,1214,1124],{"class":91},[45,1216,1218,1220,1222,1224,1226,1228,1231],{"class":47,"line":1217},15,[45,1219,1109],{"class":91},[45,1221,344],{"class":55},[45,1223,187],{"class":91},[45,1225,666],{"class":55},[45,1227,1118],{"class":91},[45,1229,1230],{"class":55},"\"automatic, larger order quantities\"",[45,1232,1124],{"class":91},[45,1234,1236,1238,1240,1242,1244,1246,1249],{"class":47,"line":1235},16,[45,1237,1109],{"class":91},[45,1239,344],{"class":55},[45,1241,187],{"class":91},[45,1243,671],{"class":55},[45,1245,1118],{"class":91},[45,1247,1248],{"class":55},"\"simple reorder point\"",[45,1250,1124],{"class":91},[45,1252,1254,1256,1258,1260,1262,1264,1267],{"class":47,"line":1253},17,[45,1255,1109],{"class":91},[45,1257,344],{"class":55},[45,1259,187],{"class":91},[45,1261,676],{"class":55},[45,1263,1118],{"class":91},[45,1265,1266],{"class":55},"\"stock on demand or discontinue\"",[45,1268,1124],{"class":91},[45,1270,1272],{"class":47,"line":1271},18,[45,1273,1274],{"class":91},"}\n",[45,1276,1278,1280,1283,1285,1287,1289,1291,1294,1297,1300,1303,1305,1307,1309,1311,1314,1317,1319,1322],{"class":47,"line":1277},19,[45,1279,257],{"class":91},[45,1281,1282],{"class":55},"\"Class\"",[45,1284,135],{"class":91},[45,1286,114],{"class":87},[45,1288,267],{"class":91},[45,1290,290],{"class":55},[45,1292,1293],{"class":91},"].astype(",[45,1295,1296],{"class":248},"str",[45,1298,1299],{"class":91},") ",[45,1301,1302],{"class":87},"+",[45,1304,267],{"class":91},[45,1306,622],{"class":55},[45,1308,1293],{"class":91},[45,1310,1296],{"class":248},[45,1312,1313],{"class":91},").replace(",[45,1315,1316],{"class":55},"\"nan\"",[45,1318,187],{"class":91},[45,1320,1321],{"class":55},"\"?\"",[45,1323,123],{"class":91},[45,1325,1327,1329,1332,1334,1336,1339,1341,1344,1347,1349,1352,1355,1358,1361,1363,1365,1368,1370],{"class":47,"line":1326},20,[45,1328,257],{"class":91},[45,1330,1331],{"class":55},"\"Policy\"",[45,1333,135],{"class":91},[45,1335,114],{"class":87},[45,1337,1338],{"class":91}," [",[45,1340,1098],{"class":248},[45,1342,1343],{"class":91},".get((a, x), ",[45,1345,1346],{"class":55},"\"new item: review after 6 months\"",[45,1348,1299],{"class":91},[45,1350,1351],{"class":87},"for",[45,1353,1354],{"class":91}," a, x ",[45,1356,1357],{"class":87},"in",[45,1359,1360],{"class":248}," zip",[45,1362,702],{"class":91},[45,1364,290],{"class":55},[45,1366,1367],{"class":91},"], items[",[45,1369,622],{"class":55},[45,1371,1372],{"class":91},"])]\n",[45,1374,1376,1378,1380,1382,1385,1388],{"class":47,"line":1375},21,[45,1377,362],{"class":248},[45,1379,702],{"class":91},[45,1381,1282],{"class":55},[45,1383,1384],{"class":91},"].value_counts().head(",[45,1386,1387],{"class":248},"5",[45,1389,1390],{"class":91},").to_dict())\n",[10,1392,1393,1394,28],{},"The count matrix shows how many items sit in each cell; the value-share matrix shows where the money is. Here 47 AX items carry 64% of all value, and 10 AZ items another 14.6%. Together the two matrices guide effort: AX items are valuable and predictable, so they can be replenished automatically with modest safety stock; AZ items are valuable and erratic — the cell that needs a planner's attention; CZ items tie up little money and are hard to forecast, so they are candidates to stock only on demand, or to discontinue. The policy table is the deliverable a warehouse team actually uses, and keeping it as data makes it easy to adapt. The crosstab technique itself is covered in ",[14,1395,1397],{"href":1396},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbuild-a-crosstab-from-excel-data-with-pandas\u002F","Build a Crosstab from Excel Data with pandas",[756,1399,765,1404,765,1407,765,1410,765,1413,765,1418,765,1423,765,1429,765,1432,765,1436,765,1439,765,1443,765,1448,765,1453,765,1455,765,1460,765,1462,765,1465,765,1467,765,1470,765,1473,765,1477,765,1479,765,1482,765,1484,765,1487,765,1489,765,1492,765,1495,765,1499,765,1501,765,1505,765,1507,765,1510,765,1512],{"viewBox":1400,"role":759,"ariaLabelledBy":1401,"xmlns":763,"style":764},"4 0 752 174",[1402,1403],"abc-matrix-t","abc-matrix-d",[767,1405,1406],{"id":1402},"The ABC-XYZ matrix and a policy per cell",[771,1408,1409],{"id":1403},"Rows A, B and C by value and columns X, Y and Z by predictability. AX items are replenished automatically, AY items forecast and reviewed monthly, AZ items planned manually. CX items get larger order quantities and CZ items are stocked on demand or discontinued.",[775,1411],{"x":777,"y":305,"width":778,"height":1412,"fill":780},"174",[775,1414],{"x":783,"y":784,"width":1415,"height":1416,"rx":305,"fill":780,"stroke":1417,"style":814},"107.3","40.0","var(--line,#cdd5e6)",[775,1419],{"x":1420,"y":784,"width":1421,"height":1416,"rx":305,"fill":788,"stroke":789,"style":1422},"127.3","204.2","stroke-width:1.2px",[803,1424,1428],{"x":1425,"y":1426,"style":1427},"229.4","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","X steady",[775,1430],{"x":1431,"y":784,"width":1421,"height":1416,"rx":305,"fill":788,"stroke":789,"style":1422},"331.5",[803,1433,1435],{"x":1434,"y":1426,"style":1427},"433.6","Y variable",[775,1437],{"x":1438,"y":784,"width":1421,"height":1416,"rx":305,"fill":788,"stroke":789,"style":1422},"535.8",[803,1440,1442],{"x":1441,"y":1426,"style":1427},"637.9","Z erratic",[775,1444],{"x":783,"y":1445,"width":1415,"height":1446,"rx":305,"fill":1447,"stroke":1417,"style":814},"56.0","34.0","#e7ebef",[803,1449,1452],{"x":1450,"y":806,"style":1451},"32.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","A high value",[775,1454],{"x":1420,"y":1445,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1456,1459],{"x":1425,"y":1457,"style":1458},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","auto, low safety stock",[775,1461],{"x":1431,"y":1445,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1463,1464],{"x":1434,"y":1457,"style":1458},"forecast, monthly review",[775,1466],{"x":1438,"y":1445,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1468,1469],{"x":1441,"y":1457,"style":1458},"manual planning",[775,1471],{"x":783,"y":1472,"width":1415,"height":1446,"rx":305,"fill":1447,"stroke":1417,"style":814},"90.0",[803,1474,1476],{"x":1450,"y":1475,"style":1451},"112.0","B medium",[775,1478],{"x":1420,"y":1472,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1480,1481],{"x":1425,"y":817,"style":1458},"auto",[775,1483],{"x":1431,"y":1472,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1485,1486],{"x":1434,"y":817,"style":1458},"forecast, moderate stock",[775,1488],{"x":1438,"y":1472,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1490,1491],{"x":1441,"y":817,"style":1458},"quarterly review",[775,1493],{"x":783,"y":1494,"width":1415,"height":1446,"rx":305,"fill":1447,"stroke":1417,"style":814},"124.0",[803,1496,1498],{"x":1450,"y":1497,"style":1451},"146.0","C low value",[775,1500],{"x":1420,"y":1494,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1502,1504],{"x":1425,"y":1503,"style":1458},"145.0","auto, big batches",[775,1506],{"x":1431,"y":1494,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1508,1509],{"x":1434,"y":1503,"style":1458},"reorder point",[775,1511],{"x":1438,"y":1494,"width":1421,"height":1446,"rx":305,"fill":780,"stroke":1417,"style":814},[803,1513,1514],{"x":1441,"y":1503,"style":1458},"on demand \u002F discontinue",[756,1516,765,1522,765,1525,765,1528,765,1532,765,1537,765,1543,765,1548,765,1553,765,1555,765,1559,765,1564,765,1569,765,1572,765,1576,765,1579,765,1584,765,1587,765,1591,765,1594,765,1599,765,1602,765,1606,765,1611],{"viewBox":1517,"role":759,"ariaLabelledBy":1518,"xmlns":763,"style":1521},"4 0 744 220",[1519,1520],"abc-share-t","abc-share-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[767,1523,1524],{"id":1519},"Where the value sits in the matrix",[771,1526,1527],{"id":1520},"AX items hold 64 percent of total consumption value, AZ items 14.6 percent and BX items 11.1 percent. All other cells together hold the remaining tenth or so.",[775,1529],{"x":777,"y":305,"width":1530,"height":1531,"fill":780},"744","220",[775,1533],{"x":783,"y":784,"width":1534,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":814},"115.6","28.0","7",[803,1538,1542],{"x":1539,"y":1540,"style":1541},"77.8","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","AX (47 items)",[775,1544],{"x":1545,"y":784,"width":1546,"height":1535,"rx":1536,"fill":831,"stroke":832,"style":1547},"145.6","536.2","stroke-width:1.5px",[803,1549,1552],{"x":1550,"y":1540,"style":1551},"689.8","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","64.0%",[775,1554],{"x":783,"y":1445,"width":1534,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":814},[803,1556,1558],{"x":1539,"y":1557,"style":1541},"74.5","AZ (10 items)",[775,1560],{"x":1545,"y":1445,"width":1561,"height":1535,"rx":1536,"fill":1562,"stroke":1563,"style":1547},"122.3","#fce9e9","var(--danger,#dc2626)",[803,1565,1568],{"x":1566,"y":1557,"style":1567},"275.9","font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:start","14.6%",[775,1570],{"x":783,"y":1571,"width":1534,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":814},"96.0",[803,1573,1575],{"x":1539,"y":1574,"style":1541},"114.5","BX (53 items)",[775,1577],{"x":1545,"y":1571,"width":1578,"height":1535,"rx":1536,"fill":788,"stroke":789,"style":1547},"93.0",[803,1580,1583],{"x":1581,"y":1574,"style":1582},"246.6","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","11.1%",[775,1585],{"x":783,"y":1586,"width":1534,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":814},"136.0",[803,1588,1590],{"x":1539,"y":1589,"style":1541},"154.5","CX (70 items)",[775,1592],{"x":1545,"y":1586,"width":1593,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":1547},"28.5",[803,1595,1598],{"x":1596,"y":1589,"style":1597},"182.1","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","3.4%",[775,1600],{"x":783,"y":1601,"width":1534,"height":1535,"rx":1536,"fill":1447,"stroke":1417,"style":814},"176.0",[803,1603,1605],{"x":1539,"y":1604,"style":1541},"194.5","BZ (14 items)",[775,1607],{"x":1545,"y":1601,"width":1608,"height":1535,"rx":1536,"fill":1609,"stroke":1610,"style":1547},"22.6","#fdefd8","var(--gold,#b4740a)",[803,1612,1615],{"x":1613,"y":1604,"style":1614},"176.3","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","2.7%",[30,1617,1619],{"id":1618},"step-5-write-the-matrix-and-the-item-list-to-excel","Step 5 — Write the matrix and the item list to Excel",[10,1621,1622],{},"The planner's workbook has the matrix with counts and value shares, and the full item list with class and policy:",[35,1624,1626],{"className":78,"code":1625,"language":80,"meta":40,"style":40},"from openpyxl.formatting.rule import ColorScaleRule\nfrom openpyxl.styles import Font\n\nwith pd.ExcelWriter(\"abc_xyz.xlsx\", engine=\"openpyxl\") as xw:\n    counts.to_excel(xw, sheet_name=\"Matrix\", startrow=1)\n    value_share.to_excel(xw, sheet_name=\"Matrix\", startrow=7)\n    ws = xw.sheets[\"Matrix\"]\n    ws[\"A1\"], ws[\"A7\"] = \"Items per cell\", \"Share of total value (%)\"\n    ws[\"A1\"].font = ws[\"A7\"].font = Font(bold=True)\n    ws.conditional_formatting.add(\"B9:D11\", ColorScaleRule(start_type=\"min\", start_color=\"FFFFFF\", end_type=\"max\", end_color=\"4338CA\"))\n    out = items.reset_index()[[\"SKU\", \"ABC\", \"XYZ\", \"Class\", \"value\", \"cv\", \"months_with_demand\", \"new_item\", \"Policy\"]]\n    out.round({\"value\": 2, \"cv\": 2}).to_excel(xw, sheet_name=\"Items\", index=False)\n    xw.sheets[\"Items\"].column_dimensions[\"I\"].width = 44\nprint(\"value share by cell (%):\", value_share.stack().sort_values(ascending=False).head(3).to_dict())\n",[42,1627,1628,1641,1653,1657,1685,1709,1730,1744,1770,1802,1851,1898,1938,1959],{"__ignoreMap":40},[45,1629,1630,1633,1636,1638],{"class":47,"line":48},[45,1631,1632],{"class":87},"from",[45,1634,1635],{"class":91}," openpyxl.formatting.rule ",[45,1637,88],{"class":87},[45,1639,1640],{"class":91}," ColorScaleRule\n",[45,1642,1643,1645,1648,1650],{"class":47,"line":101},[45,1644,1632],{"class":87},[45,1646,1647],{"class":91}," openpyxl.styles ",[45,1649,88],{"class":87},[45,1651,1652],{"class":91}," Font\n",[45,1654,1655],{"class":47,"line":108},[45,1656,105],{"emptyLinePlaceholder":104},[45,1658,1659,1662,1665,1668,1670,1673,1675,1678,1680,1682],{"class":47,"line":126},[45,1660,1661],{"class":87},"with",[45,1663,1664],{"class":91}," pd.ExcelWriter(",[45,1666,1667],{"class":55},"\"abc_xyz.xlsx\"",[45,1669,187],{"class":91},[45,1671,1672],{"class":176},"engine",[45,1674,114],{"class":87},[45,1676,1677],{"class":55},"\"openpyxl\"",[45,1679,1299],{"class":91},[45,1681,95],{"class":87},[45,1683,1684],{"class":91}," xw:\n",[45,1686,1687,1690,1693,1695,1698,1700,1703,1705,1707],{"class":47,"line":159},[45,1688,1689],{"class":91},"    counts.to_excel(xw, ",[45,1691,1692],{"class":176},"sheet_name",[45,1694,114],{"class":87},[45,1696,1697],{"class":55},"\"Matrix\"",[45,1699,187],{"class":91},[45,1701,1702],{"class":176},"startrow",[45,1704,114],{"class":87},[45,1706,524],{"class":248},[45,1708,123],{"class":91},[45,1710,1711,1714,1716,1718,1720,1722,1724,1726,1728],{"class":47,"line":228},[45,1712,1713],{"class":91},"    value_share.to_excel(xw, ",[45,1715,1692],{"class":176},[45,1717,114],{"class":87},[45,1719,1697],{"class":55},[45,1721,187],{"class":91},[45,1723,1702],{"class":176},[45,1725,114],{"class":87},[45,1727,1536],{"class":248},[45,1729,123],{"class":91},[45,1731,1732,1735,1737,1740,1742],{"class":47,"line":254},[45,1733,1734],{"class":91},"    ws ",[45,1736,114],{"class":87},[45,1738,1739],{"class":91}," xw.sheets[",[45,1741,1697],{"class":55},[45,1743,156],{"class":91},[45,1745,1746,1749,1752,1755,1758,1760,1762,1765,1767],{"class":47,"line":285},[45,1747,1748],{"class":91},"    ws[",[45,1750,1751],{"class":55},"\"A1\"",[45,1753,1754],{"class":91},"], ws[",[45,1756,1757],{"class":55},"\"A7\"",[45,1759,135],{"class":91},[45,1761,114],{"class":87},[45,1763,1764],{"class":55}," \"Items per cell\"",[45,1766,187],{"class":91},[45,1768,1769],{"class":55},"\"Share of total value (%)\"\n",[45,1771,1772,1774,1776,1779,1781,1784,1786,1788,1790,1793,1796,1798,1800],{"class":47,"line":359},[45,1773,1748],{"class":91},[45,1775,1751],{"class":55},[45,1777,1778],{"class":91},"].font ",[45,1780,114],{"class":87},[45,1782,1783],{"class":91}," ws[",[45,1785,1757],{"class":55},[45,1787,1778],{"class":91},[45,1789,114],{"class":87},[45,1791,1792],{"class":91}," Font(",[45,1794,1795],{"class":176},"bold",[45,1797,114],{"class":87},[45,1799,354],{"class":248},[45,1801,123],{"class":91},[45,1803,1804,1807,1810,1813,1816,1818,1820,1822,1825,1827,1830,1832,1835,1837,1839,1841,1844,1846,1849],{"class":47,"line":1127},[45,1805,1806],{"class":91},"    ws.conditional_formatting.add(",[45,1808,1809],{"class":55},"\"B9:D11\"",[45,1811,1812],{"class":91},", ColorScaleRule(",[45,1814,1815],{"class":176},"start_type",[45,1817,114],{"class":87},[45,1819,734],{"class":55},[45,1821,187],{"class":91},[45,1823,1824],{"class":176},"start_color",[45,1826,114],{"class":87},[45,1828,1829],{"class":55},"\"FFFFFF\"",[45,1831,187],{"class":91},[45,1833,1834],{"class":176},"end_type",[45,1836,114],{"class":87},[45,1838,739],{"class":55},[45,1840,187],{"class":91},[45,1842,1843],{"class":176},"end_color",[45,1845,114],{"class":87},[45,1847,1848],{"class":55},"\"4338CA\"",[45,1850,225],{"class":91},[45,1852,1853,1856,1858,1861,1863,1865,1867,1869,1871,1873,1875,1877,1879,1881,1883,1885,1888,1890,1892,1894,1896],{"class":47,"line":1145},[45,1854,1855],{"class":91},"    out ",[45,1857,114],{"class":87},[45,1859,1860],{"class":91}," items.reset_index()[[",[45,1862,170],{"class":55},[45,1864,187],{"class":91},[45,1866,290],{"class":55},[45,1868,187],{"class":91},[45,1870,622],{"class":55},[45,1872,187],{"class":91},[45,1874,1282],{"class":55},[45,1876,187],{"class":91},[45,1878,238],{"class":55},[45,1880,187],{"class":91},[45,1882,584],{"class":55},[45,1884,187],{"class":91},[45,1886,1887],{"class":55},"\"months_with_demand\"",[45,1889,187],{"class":91},[45,1891,913],{"class":55},[45,1893,187],{"class":91},[45,1895,1331],{"class":55},[45,1897,998],{"class":91},[45,1899,1900,1903,1905,1908,1910,1912,1914,1916,1918,1921,1923,1925,1928,1930,1932,1934,1936],{"class":47,"line":1163},[45,1901,1902],{"class":91},"    out.round({",[45,1904,238],{"class":55},[45,1906,1907],{"class":91},": ",[45,1909,745],{"class":248},[45,1911,187],{"class":91},[45,1913,584],{"class":55},[45,1915,1907],{"class":91},[45,1917,745],{"class":248},[45,1919,1920],{"class":91},"}).to_excel(xw, ",[45,1922,1692],{"class":176},[45,1924,114],{"class":87},[45,1926,1927],{"class":55},"\"Items\"",[45,1929,187],{"class":91},[45,1931,457],{"class":176},[45,1933,114],{"class":87},[45,1935,249],{"class":248},[45,1937,123],{"class":91},[45,1939,1940,1943,1945,1948,1951,1954,1956],{"class":47,"line":1181},[45,1941,1942],{"class":91},"    xw.sheets[",[45,1944,1927],{"class":55},[45,1946,1947],{"class":91},"].column_dimensions[",[45,1949,1950],{"class":55},"\"I\"",[45,1952,1953],{"class":91},"].width ",[45,1955,114],{"class":87},[45,1957,1958],{"class":248}," 44\n",[45,1960,1961,1963,1965,1968,1971,1973,1975,1977,1980,1982],{"class":47,"line":1199},[45,1962,362],{"class":248},[45,1964,182],{"class":91},[45,1966,1967],{"class":55},"\"value share by cell (%):\"",[45,1969,1970],{"class":91},", value_share.stack().sort_values(",[45,1972,243],{"class":176},[45,1974,114],{"class":87},[45,1976,249],{"class":248},[45,1978,1979],{"class":91},").head(",[45,1981,423],{"class":248},[45,1983,1390],{"class":91},[10,1985,1986],{},"The value-share matrix gets a colour scale so the cells holding most of the money stand out; the Items sheet lists every SKU with its classes, the figures behind them and the recommended policy, ready to filter by class. Run the classification monthly or quarterly on a rolling twelve months: items move between classes as demand changes, and an item drifting from AX to AZ is an early warning worth reacting to.",[30,1988,1990],{"id":1989},"common-pitfalls","Common pitfalls",[1992,1993,1994,2002,2008,2014,2020],"ul",{},[1995,1996,1997,2001],"li",{},[1998,1999,2000],"strong",{},"ABC alone."," Value without variability treats steady and erratic items the same.",[1995,2003,2004,2007],{},[1998,2005,2006],{},"Leaving zero-demand months out of the CV."," Lumpy items then look steady; fill them with 0.",[1995,2009,2010,2013],{},[1998,2011,2012],{},"Classifying new items on a few months."," Hold them out until there is enough history.",[1995,2015,2016,2019],{},[1998,2017,2018],{},"Thresholds treated as fixed rules."," 80\u002F95 and 0.5\u002F1.0 are conventions; tune them.",[1995,2021,2022,2025],{},[1998,2023,2024],{},"A matrix with no policy."," The point is what to do with each cell.",[30,2027,2029],{"id":2028},"conclusion","Conclusion",[10,2031,2032],{},"An ABC-XYZ classification combines value and predictability. ABC classes come from each item's cumulative share of annual consumption value; XYZ classes from the coefficient of variation of monthly demand, with zero-demand months included. Hold items with too little history out of XYZ, cross-tabulate the classes into a nine-cell matrix of counts and value shares, attach a stocking policy to each cell, and write the matrix and item list to Excel so planners can act on it — then rerun it regularly to catch items changing class.",[30,2034,2036],{"id":2035},"frequently-asked-questions","Frequently asked questions",[10,2038,2039,2042],{},[1998,2040,2041],{},"What is ABC-XYZ analysis?","\nA classification of stock items by consumption value (ABC) and by demand variability (XYZ). Combining them in a 3×3 matrix shows which items are valuable and predictable and which are valuable but erratic.",[10,2044,2045,2048],{},[1998,2046,2047],{},"How do I calculate XYZ classes in pandas?","\nPivot monthly demand per item with zeros for months without demand, compute the coefficient of variation as the standard deviation divided by the mean, and cut it at thresholds such as 0.5 and 1.0.",[10,2050,2051,2054],{},[1998,2052,2053],{},"Why include months with zero demand in the CV?","\nThey are real demand of zero. Leaving them out makes items that sell only occasionally look steady.",[10,2056,2057,2060],{},[1998,2058,2059],{},"How should new items be classified?","\nGive them an ABC class from their value so far but hold back the XYZ class until they have six to twelve months of history.",[30,2062,2064],{"id":2063},"related","Related",[1992,2066,2067,2073,2078,2085],{},[1995,2068,2069,2070,2072],{},"Up one level: ",[14,2071,27],{"href":26}," — summaries and crosstabs.",[1995,2074,2075,2077],{},[14,2076,17],{"href":16}," — the ABC half on its own.",[1995,2079,2080,2084],{},[14,2081,2083],{"href":2082},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report\u002F","Build an Inventory Reorder Report"," — turning the policies into orders.",[1995,2086,2087,2091],{},[14,2088,2090],{"href":2089},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fcalculate-weighted-average-cost-inventory-valuation\u002F","Calculate Weighted Average Cost Inventory Valuation"," — valuing the same stock.",[2093,2094,2095],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}",{"title":40,"searchDepth":101,"depth":101,"links":2097},[2098,2099,2100,2101,2102,2103,2104,2105,2106,2107],{"id":32,"depth":101,"text":33},{"id":71,"depth":101,"text":72},{"id":435,"depth":101,"text":436},{"id":860,"depth":101,"text":861},{"id":970,"depth":101,"text":971},{"id":1618,"depth":101,"text":1619},{"id":1989,"depth":101,"text":1990},{"id":2028,"depth":101,"text":2029},{"id":2035,"depth":101,"text":2036},{"id":2063,"depth":101,"text":2064},"2026-10-02","Classify stock items by how much they matter and how predictable they are with pandas: ABC classes from cumulative share of consumption value, XYZ classes from the coefficient of variation of monthly demand, the nine-cell ABC-XYZ matrix with item counts and value shares, a stocking policy per cell, items with too little history, and an Excel matrix sheet a planner can use.","md",[2112,2114,2116,2118],{"q":2041,"a":2113},"A classification of stock items by consumption value (ABC) and by demand variability (XYZ). Combining them in a 3×3 matrix shows which items are valuable and predictable and which are valuable but erratic.",{"q":2047,"a":2115},"Pivot monthly demand per item with zeros for months without demand, compute the coefficient of variation as the standard deviation divided by the mean, and cut it at thresholds such as 0.5 and 1.0.",{"q":2053,"a":2117},"They are real demand of zero. Leaving them out makes items that sell only occasionally look steady.",{"q":2059,"a":2119},"Give them an ABC class from their value so far but hold back the XYZ class until they have six to twelve months of history.","run an abc-xyz inventory classification",null,{"breadcrumb":2123},[2124,2126,2129],{"name":2125,"item":275},"Home",{"name":2127,"item":2128},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":2130,"item":26},"Creating Pivot Tables from Excel Data with Pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification",{"title":2133,"description":2134},"Run an ABC-XYZ Inventory Classification with pandas","ABC-XYZ analysis from Excel demand data: ABC by cumulative consumption value, XYZ by coefficient of variation, a 3×3 matrix of counts and value share, policies per class, sparse history handling and an Excel report.","run-an-abc-xyz-inventory-classification","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification\u002Findex",[2138],"pivots-and-summaries","how-to","wPyob3s0JKI1M-jPt7sK6qb3_TtGzqCE6HG5ft92dwE",[2142,2145],{"title":17,"path":2143,"stem":2144,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-a-pareto-analysis-on-excel-data-with-pandas","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-a-pareto-analysis-on-excel-data-with-pandas\u002Findex",{"title":2146,"path":2147,"stem":2148,"children":-1},"Sort Pivot Rows in a Custom Order with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas\u002Findex",[2150,2152],{"path":2151,"title":2130},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data",{"path":2153,"title":2127},"\u002Fadvanced-data-transformation-and-cleaning",[],[2156,2159,2162,2165,2168,2171],{"path":2157,"title":2158,"kind":2139},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-churn-report-from-subscription-data","Build a Customer Churn Report from Subscription Data",{"path":2160,"title":2161,"kind":2139},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frefresh-pivot-tables-and-queries-in-excel-with-python","Refresh Excel Pivot Tables and Queries with Python",{"path":2163,"title":2164,"kind":2139},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-a-native-excel-pivot-table-with-python","Add a Native Excel Pivot Table with Python",{"path":2166,"title":2167,"kind":2139},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-subtotals-and-grand-totals-to-a-pandas-pivot-table","Add Subtotals and Grand Totals to a pandas Pivot Table",{"path":2169,"title":2170,"kind":2139},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":2172,"title":2173,"kind":2139},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbuild-a-cohort-retention-table-from-excel-data","Build a Cohort Retention Table from Excel Data",1790972086619]