[{"data":1,"prerenderedAt":1781},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas":1749,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas":1758,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas":1763,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas":1764},{"id":4,"title":5,"body":6,"dateModified":1714,"datePublished":1714,"description":1715,"extension":1716,"faq":1717,"keyword":1726,"kind":1727,"meta":1728,"navigation":110,"path":1738,"seo":1739,"slug":1742,"stem":1743,"topics":1744,"type":1747,"__hash__":1748},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas\u002Findex.md","Sort Pivot Rows in a Custom Order with pandas",{"type":7,"value":8,"toc":1699},"minimark",[9,39,44,72,78,82,202,214,218,477,508,652,656,659,784,801,805,808,894,913,1015,1019,1022,1144,1152,1156,1163,1287,1302,1441,1445,1448,1527,1530,1534,1549,1553,1614,1618,1627,1631,1640,1650,1656,1662,1666,1695],[10,11,12,13,17,18,21,22,21,25,28,29,32,33,38],"p",{},"A monthly sales pivot comes out as Apr, Aug, Dec, Feb, Jan… A size breakdown reads L, M, S, XL, XS. Ticket priorities appear as High, Low, Medium, Urgent. pandas sorts group labels alphabetically because it has no way to know that months run January to December or that XS comes before S — unless you tell it. The cleanest way to tell it is an ordered ",[14,15,16],"code",{},"Categorical",": a column type that carries its own sort order, which ",[14,19,20],{},"groupby",", ",[14,23,24],{},"pivot_table",[14,26,27],{},"sort_values"," and charts all respect. For one-off reports, ",[14,30,31],{},"reindex"," with an explicit list does the same job. This page shows both, plus the details that trip up real reports: empty categories, an \"Other\" bucket that belongs at the end, and keeping the order when the table is written to Excel. It belongs to ",[34,35,37],"a",{"href":36},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data",".",[40,41,43],"h2",{"id":42},"prerequisites","Prerequisites",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,52,53],{"__ignoreMap":50},[54,55,58,62,66,69],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sMTad","pip",[54,63,65],{"class":64},"srMev"," install",[54,67,68],{"class":64}," pandas",[54,70,71],{"class":64}," openpyxl\n",[10,73,74,77],{},[14,75,76],{},"orders_categories.xlsx"," has 300 order lines with a month abbreviation (January to September), a garment size including a \"One size\" bucket, a priority and a region that includes \"Other\".",[40,79,81],{"id":80},"step-1-the-alphabetical-problem","Step 1 — The alphabetical problem",[45,83,87],{"className":84,"code":85,"language":86,"meta":50,"style":50},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\norders = pd.read_excel(\"orders_categories.xlsx\")\nplain = orders.pivot_table(index=\"Month\", columns=\"Size\", values=\"Units\", aggfunc=\"sum\", fill_value=0)\nprint(plain)\n","python",[14,88,89,105,112,130,193],{"__ignoreMap":50},[54,90,91,95,99,102],{"class":56,"line":57},[54,92,94],{"class":93},"s-kum","import",[54,96,98],{"class":97},"skGVy"," pandas ",[54,100,101],{"class":93},"as",[54,103,104],{"class":97}," pd\n",[54,106,108],{"class":56,"line":107},2,[54,109,111],{"emptyLinePlaceholder":110},true,"\n",[54,113,115,118,121,124,127],{"class":56,"line":114},3,[54,116,117],{"class":97},"orders ",[54,119,120],{"class":93},"=",[54,122,123],{"class":97}," pd.read_excel(",[54,125,126],{"class":64},"\"orders_categories.xlsx\"",[54,128,129],{"class":97},")\n",[54,131,133,136,138,141,145,147,150,152,155,157,160,162,165,167,170,172,175,177,180,182,185,187,191],{"class":56,"line":132},4,[54,134,135],{"class":97},"plain ",[54,137,120],{"class":93},[54,139,140],{"class":97}," orders.pivot_table(",[54,142,144],{"class":143},"sa561","index",[54,146,120],{"class":93},[54,148,149],{"class":64},"\"Month\"",[54,151,21],{"class":97},[54,153,154],{"class":143},"columns",[54,156,120],{"class":93},[54,158,159],{"class":64},"\"Size\"",[54,161,21],{"class":97},[54,163,164],{"class":143},"values",[54,166,120],{"class":93},[54,168,169],{"class":64},"\"Units\"",[54,171,21],{"class":97},[54,173,174],{"class":143},"aggfunc",[54,176,120],{"class":93},[54,178,179],{"class":64},"\"sum\"",[54,181,21],{"class":97},[54,183,184],{"class":143},"fill_value",[54,186,120],{"class":93},[54,188,190],{"class":189},"sP0c6","0",[54,192,129],{"class":97},[54,194,196,199],{"class":56,"line":195},5,[54,197,198],{"class":189},"print",[54,200,201],{"class":97},"(plain)\n",[10,203,204,205,208,209,213],{},"Rows run Apr, Aug, Feb, Jan, Jul, Jun, Mar, May, Sep, and columns L, M, One size, S, XL, XS. Every reader has to reorder the table in their head, and a line chart drawn from it zigzags through the year. Sorting the result with ",[14,206,207],{},"sort_index"," changes nothing, because alphabetical ",[210,211,212],"em",{},"is"," the index's natural order.",[40,215,217],{"id":216},"step-2-ordered-categoricals-carry-the-order-with-the-data","Step 2 — Ordered Categoricals carry the order with the data",[45,219,221],{"className":84,"code":220,"language":86,"meta":50,"style":50},"MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nSIZES = [\"XS\", \"S\", \"M\", \"L\", \"XL\", \"One size\"]\norders[\"Month\"] = pd.Categorical(orders[\"Month\"], categories=MONTHS, ordered=True)\norders[\"Size\"] = pd.Categorical(orders[\"Size\"], categories=SIZES, ordered=True)\nordered = orders.pivot_table(index=\"Month\", columns=\"Size\", values=\"Units\", aggfunc=\"sum\",\n                             fill_value=0, observed=False)\nprint(ordered)\n",[14,222,223,295,334,373,405,447,469],{"__ignoreMap":50},[54,224,225,228,231,234,237,239,242,244,247,249,252,254,257,259,262,264,267,269,272,274,277,279,282,284,287,289,292],{"class":56,"line":57},[54,226,227],{"class":189},"MONTHS",[54,229,230],{"class":93}," =",[54,232,233],{"class":97}," [",[54,235,236],{"class":64},"\"Jan\"",[54,238,21],{"class":97},[54,240,241],{"class":64},"\"Feb\"",[54,243,21],{"class":97},[54,245,246],{"class":64},"\"Mar\"",[54,248,21],{"class":97},[54,250,251],{"class":64},"\"Apr\"",[54,253,21],{"class":97},[54,255,256],{"class":64},"\"May\"",[54,258,21],{"class":97},[54,260,261],{"class":64},"\"Jun\"",[54,263,21],{"class":97},[54,265,266],{"class":64},"\"Jul\"",[54,268,21],{"class":97},[54,270,271],{"class":64},"\"Aug\"",[54,273,21],{"class":97},[54,275,276],{"class":64},"\"Sep\"",[54,278,21],{"class":97},[54,280,281],{"class":64},"\"Oct\"",[54,283,21],{"class":97},[54,285,286],{"class":64},"\"Nov\"",[54,288,21],{"class":97},[54,290,291],{"class":64},"\"Dec\"",[54,293,294],{"class":97},"]\n",[54,296,297,300,302,304,307,309,312,314,317,319,322,324,327,329,332],{"class":56,"line":107},[54,298,299],{"class":189},"SIZES",[54,301,230],{"class":93},[54,303,233],{"class":97},[54,305,306],{"class":64},"\"XS\"",[54,308,21],{"class":97},[54,310,311],{"class":64},"\"S\"",[54,313,21],{"class":97},[54,315,316],{"class":64},"\"M\"",[54,318,21],{"class":97},[54,320,321],{"class":64},"\"L\"",[54,323,21],{"class":97},[54,325,326],{"class":64},"\"XL\"",[54,328,21],{"class":97},[54,330,331],{"class":64},"\"One size\"",[54,333,294],{"class":97},[54,335,336,339,341,344,346,349,351,354,357,359,361,363,366,368,371],{"class":56,"line":114},[54,337,338],{"class":97},"orders[",[54,340,149],{"class":64},[54,342,343],{"class":97},"] ",[54,345,120],{"class":93},[54,347,348],{"class":97}," pd.Categorical(orders[",[54,350,149],{"class":64},[54,352,353],{"class":97},"], ",[54,355,356],{"class":143},"categories",[54,358,120],{"class":93},[54,360,227],{"class":189},[54,362,21],{"class":97},[54,364,365],{"class":143},"ordered",[54,367,120],{"class":93},[54,369,370],{"class":189},"True",[54,372,129],{"class":97},[54,374,375,377,379,381,383,385,387,389,391,393,395,397,399,401,403],{"class":56,"line":132},[54,376,338],{"class":97},[54,378,159],{"class":64},[54,380,343],{"class":97},[54,382,120],{"class":93},[54,384,348],{"class":97},[54,386,159],{"class":64},[54,388,353],{"class":97},[54,390,356],{"class":143},[54,392,120],{"class":93},[54,394,299],{"class":189},[54,396,21],{"class":97},[54,398,365],{"class":143},[54,400,120],{"class":93},[54,402,370],{"class":189},[54,404,129],{"class":97},[54,406,407,410,412,414,416,418,420,422,424,426,428,430,432,434,436,438,440,442,444],{"class":56,"line":195},[54,408,409],{"class":97},"ordered ",[54,411,120],{"class":93},[54,413,140],{"class":97},[54,415,144],{"class":143},[54,417,120],{"class":93},[54,419,149],{"class":64},[54,421,21],{"class":97},[54,423,154],{"class":143},[54,425,120],{"class":93},[54,427,159],{"class":64},[54,429,21],{"class":97},[54,431,164],{"class":143},[54,433,120],{"class":93},[54,435,169],{"class":64},[54,437,21],{"class":97},[54,439,174],{"class":143},[54,441,120],{"class":93},[54,443,179],{"class":64},[54,445,446],{"class":97},",\n",[54,448,450,453,455,457,459,462,464,467],{"class":56,"line":449},6,[54,451,452],{"class":143},"                             fill_value",[54,454,120],{"class":93},[54,456,190],{"class":189},[54,458,21],{"class":97},[54,460,461],{"class":143},"observed",[54,463,120],{"class":93},[54,465,466],{"class":189},"False",[54,468,129],{"class":97},[54,470,472,474],{"class":56,"line":471},7,[54,473,198],{"class":189},[54,475,476],{"class":97},"(ordered)\n",[10,478,479,480,21,482,21,484,487,488,491,492,495,496,499,500,503,504,507],{},"Rows now run January to December and columns XS to One size. The order is part of the column's type, so every later operation — ",[14,481,20],{},[14,483,27],{},[14,485,486],{},"value_counts(sort=False)",", a chart — follows it without being told again. Two side effects matter. Values not in the category list become ",[14,489,490],{},"NaN",", so a typo such as ",[14,493,494],{},"\"Sept\""," silently disappears; check with ",[14,497,498],{},"orders[\"Month\"].isna().sum()"," after converting. And ",[14,501,502],{},"observed=False"," keeps categories with no data: October to December appear as rows of zeros. That is right for a year-to-date table that should show the empty months, and wrong for a table that should only show months so far — pass ",[14,505,506],{},"observed=True"," then.",[509,510,518,519,518,523,518,527,518,534,518,543,518,550,518,555,518,560,518,563,518,567,518,570,518,574,518,577,518,581,518,584,518,588,518,591,518,594,518,598,518,601,518,603,518,605,518,607,518,610,518,612,518,614,518,616,518,619,518,624,518,627,518,633,518,638,518,646],"svg",{"viewBox":511,"role":512,"ariaLabelledBy":513,"xmlns":516,"style":517},"121 0 526 224","img",[514,515],"cat-months-t","cat-months-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:526px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[520,521,522],"title",{"id":514},"The same pivot before and after an ordered Categorical",[524,525,526],"desc",{"id":515},"On the left the pivot rows in alphabetical order — Apr, Aug, Feb, Jan, Jul. On the right the same rows in calendar order — Jan, Feb, Mar, Apr, May — after converting Month to an ordered Categorical.",[528,529],"rect",{"x":530,"y":190,"width":531,"height":532,"fill":533},"121","526","224","#ffffff",[528,535],{"x":536,"y":537,"width":538,"height":539,"rx":190,"fill":540,"stroke":541,"style":542},"146.2","40.0","60.5","28.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1.5px",[544,545,549],"text",{"x":546,"y":547,"style":548},"176.4","59.0","font-size:11px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Month",[528,551],{"x":536,"y":552,"width":538,"height":539,"rx":190,"fill":553,"stroke":554,"style":542},"68.0","#fdefd8","var(--gold,#b4740a)",[544,556,559],{"x":546,"y":557,"style":558},"87.0","font-size:11px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Apr",[528,561],{"x":536,"y":562,"width":538,"height":539,"rx":190,"fill":553,"stroke":554,"style":542},"96.0",[544,564,566],{"x":546,"y":565,"style":558},"115.0","Aug",[528,568],{"x":536,"y":569,"width":538,"height":539,"rx":190,"fill":553,"stroke":554,"style":542},"124.0",[544,571,573],{"x":546,"y":572,"style":558},"143.0","Feb",[528,575],{"x":536,"y":576,"width":538,"height":539,"rx":190,"fill":553,"stroke":554,"style":542},"152.0",[544,578,580],{"x":546,"y":579,"style":558},"171.0","Jan",[528,582],{"x":536,"y":583,"width":538,"height":539,"rx":190,"fill":553,"stroke":554,"style":542},"180.0",[544,585,587],{"x":546,"y":586,"style":558},"199.0","Jul",[528,589],{"x":590,"y":537,"width":538,"height":539,"rx":190,"fill":540,"stroke":541,"style":542},"553.3",[544,592,549],{"x":593,"y":547,"style":548},"583.6",[528,595],{"x":590,"y":552,"width":538,"height":539,"rx":190,"fill":596,"stroke":597,"style":542},"#d9f4f1","var(--teal,#0f9488)",[544,599,580],{"x":593,"y":557,"style":600},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[528,602],{"x":590,"y":562,"width":538,"height":539,"rx":190,"fill":596,"stroke":597,"style":542},[544,604,573],{"x":593,"y":565,"style":600},[528,606],{"x":590,"y":569,"width":538,"height":539,"rx":190,"fill":596,"stroke":597,"style":542},[544,608,609],{"x":593,"y":572,"style":600},"Mar",[528,611],{"x":590,"y":576,"width":538,"height":539,"rx":190,"fill":596,"stroke":597,"style":542},[544,613,559],{"x":593,"y":579,"style":600},[528,615],{"x":590,"y":583,"width":538,"height":539,"rx":190,"fill":596,"stroke":597,"style":542},[544,617,618],{"x":593,"y":586,"style":600},"May",[544,620,623],{"x":546,"y":621,"style":622},"29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","alphabetical",[544,625,626],{"x":593,"y":621,"style":622},"calendar order",[56,628],{"x1":629,"y1":569,"x2":630,"y2":569,"stroke":631,"style":632},"216.7","539.3","var(--brand,#5b5cf0)","stroke-width:2px",[634,635],"polygon",{"points":636,"fill":637},"543.3,124.0 534.3,129.0 534.3,119.0","#5b5cf0",[528,639],{"x":640,"y":641,"width":642,"height":643,"rx":644,"fill":533,"stroke":541,"style":645},"257.3","91.0","245.3","22.0","11","stroke-width:1.2px",[544,647,651],{"x":648,"y":649,"style":650},"380.0","106.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","pd.Categorical(…, ordered=True)",[40,653,655],{"id":654},"step-3-reindex-for-a-one-off-order","Step 3 — reindex for a one-off order",[10,657,658],{},"When the order only matters for one output, reorder the finished table instead of converting columns:",[45,660,662],{"className":84,"code":661,"language":86,"meta":50,"style":50},"PRIORITY = [\"Urgent\", \"High\", \"Medium\", \"Low\"]\nby_priority = orders.groupby(\"Priority\")[\"Units\"].agg([\"count\", \"sum\"]).reindex(PRIORITY)\nprint(by_priority)\ncols_first = ordered.reindex(columns=[\"One size\", \"XS\", \"S\", \"M\", \"L\", \"XL\"])\nprint(cols_first.columns.tolist())\n",[14,663,664,693,728,735,777],{"__ignoreMap":50},[54,665,666,669,671,673,676,678,681,683,686,688,691],{"class":56,"line":57},[54,667,668],{"class":189},"PRIORITY",[54,670,230],{"class":93},[54,672,233],{"class":97},[54,674,675],{"class":64},"\"Urgent\"",[54,677,21],{"class":97},[54,679,680],{"class":64},"\"High\"",[54,682,21],{"class":97},[54,684,685],{"class":64},"\"Medium\"",[54,687,21],{"class":97},[54,689,690],{"class":64},"\"Low\"",[54,692,294],{"class":97},[54,694,695,698,700,703,706,709,711,714,717,719,721,724,726],{"class":56,"line":107},[54,696,697],{"class":97},"by_priority ",[54,699,120],{"class":93},[54,701,702],{"class":97}," orders.groupby(",[54,704,705],{"class":64},"\"Priority\"",[54,707,708],{"class":97},")[",[54,710,169],{"class":64},[54,712,713],{"class":97},"].agg([",[54,715,716],{"class":64},"\"count\"",[54,718,21],{"class":97},[54,720,179],{"class":64},[54,722,723],{"class":97},"]).reindex(",[54,725,668],{"class":189},[54,727,129],{"class":97},[54,729,730,732],{"class":56,"line":114},[54,731,198],{"class":189},[54,733,734],{"class":97},"(by_priority)\n",[54,736,737,740,742,745,747,749,752,754,756,758,760,762,764,766,768,770,772,774],{"class":56,"line":132},[54,738,739],{"class":97},"cols_first ",[54,741,120],{"class":93},[54,743,744],{"class":97}," ordered.reindex(",[54,746,154],{"class":143},[54,748,120],{"class":93},[54,750,751],{"class":97},"[",[54,753,331],{"class":64},[54,755,21],{"class":97},[54,757,306],{"class":64},[54,759,21],{"class":97},[54,761,311],{"class":64},[54,763,21],{"class":97},[54,765,316],{"class":64},[54,767,21],{"class":97},[54,769,321],{"class":64},[54,771,21],{"class":97},[54,773,326],{"class":64},[54,775,776],{"class":97},"])\n",[54,778,779,781],{"class":56,"line":195},[54,780,198],{"class":189},[54,782,783],{"class":97},"(cols_first.columns.tolist())\n",[10,785,786,789,790,792,793,796,797,800],{},[14,787,788],{},"reindex(PRIORITY)"," returns the rows in the listed order: Urgent first, Low last — the order a support manager reads in. Labels missing from the data appear as ",[14,791,490],{}," rows and labels missing from the list are dropped, so compare the list with ",[14,794,795],{},"set(df.index)"," when the data can contain unexpected values. ",[14,798,799],{},"reindex(columns=...)"," does the same for columns; here \"One size\" is moved to the front for a report that treats it as the default. Reindexing is simpler for presentation; Categoricals are better when the order is a property of the data that many steps rely on.",[40,802,804],{"id":803},"step-4-sort-by-value-but-keep-other-last","Step 4 — Sort by value, but keep \"Other\" last",[10,806,807],{},"A common report sorts regions by total, biggest first — with \"Other\" at the bottom regardless of its size, and the total row after that:",[45,809,811],{"className":84,"code":810,"language":86,"meta":50,"style":50},"by_region = orders.groupby(\"Region\")[\"Units\"].sum()\nnamed = by_region.drop(\"Other\").sort_values(ascending=False)\nranked = pd.concat([named, by_region[[\"Other\"]]])\nranked[\"Total\"] = by_region.sum()\nprint(ranked)\n",[14,812,813,832,857,872,887],{"__ignoreMap":50},[54,814,815,818,820,822,825,827,829],{"class":56,"line":57},[54,816,817],{"class":97},"by_region ",[54,819,120],{"class":93},[54,821,702],{"class":97},[54,823,824],{"class":64},"\"Region\"",[54,826,708],{"class":97},[54,828,169],{"class":64},[54,830,831],{"class":97},"].sum()\n",[54,833,834,837,839,842,845,848,851,853,855],{"class":56,"line":107},[54,835,836],{"class":97},"named ",[54,838,120],{"class":93},[54,840,841],{"class":97}," by_region.drop(",[54,843,844],{"class":64},"\"Other\"",[54,846,847],{"class":97},").sort_values(",[54,849,850],{"class":143},"ascending",[54,852,120],{"class":93},[54,854,466],{"class":189},[54,856,129],{"class":97},[54,858,859,862,864,867,869],{"class":56,"line":114},[54,860,861],{"class":97},"ranked ",[54,863,120],{"class":93},[54,865,866],{"class":97}," pd.concat([named, by_region[[",[54,868,844],{"class":64},[54,870,871],{"class":97},"]]])\n",[54,873,874,877,880,882,884],{"class":56,"line":132},[54,875,876],{"class":97},"ranked[",[54,878,879],{"class":64},"\"Total\"",[54,881,343],{"class":97},[54,883,120],{"class":93},[54,885,886],{"class":97}," by_region.sum()\n",[54,888,889,891],{"class":56,"line":195},[54,890,198],{"class":189},[54,892,893],{"class":97},"(ranked)\n",[10,895,896,897,900,901,904,905,908,909,38],{},"Dropping \"Other\", sorting the rest, and appending it again is clearer than a clever sort key. The total is computed from the original series and added last. If the totals come from ",[14,898,899],{},"pivot_table(margins=True)",", the margins row is named ",[14,902,903],{},"All"," and sorts like any other label, so move it to the end explicitly with ",[14,906,907],{},"table.reindex([*sorted_labels, \"All\"])",". Subtotal layouts are covered in ",[34,910,912],{"href":911},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-subtotals-and-grand-totals-to-a-pandas-pivot-table\u002F","Add Subtotals and Grand Totals to a pandas Pivot Table",[509,914,518,920,518,923,518,926,518,931,518,938,518,944,518,949,518,954,518,957,518,961,518,964,518,968,518,970,518,974,518,977,518,981,518,984,518,988,518,991,518,995,518,998,518,1002,518,1005,518,1010],{"viewBox":915,"role":512,"ariaLabelledBy":916,"xmlns":516,"style":919},"4 0 697 246",[917,918],"cat-rank-t","cat-rank-d","width:100%;max-width:697px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[520,921,922],{"id":917},"Regions ranked by units with Other kept last",[524,924,925],{"id":918},"Units per region sorted from largest to smallest — East 738, South 647, West 600, North 578 — with the Other bucket, 519, placed after them regardless of its size.",[528,927],{"x":928,"y":190,"width":929,"height":930,"fill":533},"4","697","246",[528,932],{"x":933,"y":934,"width":935,"height":539,"rx":936,"fill":540,"stroke":541,"style":937},"20.0","16.0","65.8","7","stroke-width:1px",[544,939,943],{"x":940,"y":941,"style":942},"52.9","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","East",[528,945],{"x":946,"y":934,"width":947,"height":539,"rx":936,"fill":948,"stroke":631,"style":542},"95.8","527.8","#f0f4ff",[544,950,953],{"x":951,"y":941,"style":952},"631.6","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","738",[528,955],{"x":933,"y":956,"width":935,"height":539,"rx":936,"fill":540,"stroke":541,"style":937},"56.0",[544,958,960],{"x":940,"y":959,"style":942},"74.5","South",[528,962],{"x":946,"y":956,"width":963,"height":539,"rx":936,"fill":948,"stroke":631,"style":542},"462.7",[544,965,967],{"x":966,"y":959,"style":952},"566.5","647",[528,969],{"x":933,"y":562,"width":935,"height":539,"rx":936,"fill":540,"stroke":541,"style":937},[544,971,973],{"x":940,"y":972,"style":942},"114.5","West",[528,975],{"x":946,"y":562,"width":976,"height":539,"rx":936,"fill":948,"stroke":631,"style":542},"429.1",[544,978,980],{"x":979,"y":972,"style":952},"532.9","600",[528,982],{"x":933,"y":983,"width":935,"height":539,"rx":936,"fill":540,"stroke":541,"style":937},"136.0",[544,985,987],{"x":940,"y":986,"style":942},"154.5","North",[528,989],{"x":946,"y":983,"width":990,"height":539,"rx":936,"fill":948,"stroke":631,"style":542},"413.4",[544,992,994],{"x":993,"y":986,"style":952},"517.2","578",[528,996],{"x":933,"y":997,"width":935,"height":539,"rx":936,"fill":540,"stroke":541,"style":937},"176.0",[544,999,1001],{"x":940,"y":1000,"style":942},"194.5","Other",[528,1003],{"x":946,"y":997,"width":1004,"height":539,"rx":936,"fill":540,"stroke":541,"style":542},"371.2",[544,1006,1009],{"x":1007,"y":1000,"style":1008},"475.0","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","519 · always last",[544,1011,1014],{"x":648,"y":1012,"style":1013},"226.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","Here Other also happens to be the smallest; the code would place it last even if it were the largest.",[40,1016,1018],{"id":1017},"step-5-fixed-group-order-value-order-inside-each-group","Step 5 — Fixed group order, value order inside each group",[10,1020,1021],{},"Two-level layouts often need both: categories in a fixed order (priority), and rows within each sorted by value (regions by units). Sort on both columns, with the Categorical providing the first key:",[45,1023,1025],{"className":84,"code":1024,"language":86,"meta":50,"style":50},"orders[\"Priority\"] = pd.Categorical(orders[\"Priority\"], categories=PRIORITY, ordered=True)\ntwo_level = (orders.groupby([\"Priority\", \"Region\"], observed=True)[\"Units\"].sum()\n                   .reset_index()\n                   .sort_values([\"Priority\", \"Units\"], ascending=[True, False]))\nprint(two_level.head(10).to_string(index=False))\n",[14,1026,1027,1059,1089,1094,1122],{"__ignoreMap":50},[54,1028,1029,1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057],{"class":56,"line":57},[54,1030,338],{"class":97},[54,1032,705],{"class":64},[54,1034,343],{"class":97},[54,1036,120],{"class":93},[54,1038,348],{"class":97},[54,1040,705],{"class":64},[54,1042,353],{"class":97},[54,1044,356],{"class":143},[54,1046,120],{"class":93},[54,1048,668],{"class":189},[54,1050,21],{"class":97},[54,1052,365],{"class":143},[54,1054,120],{"class":93},[54,1056,370],{"class":189},[54,1058,129],{"class":97},[54,1060,1061,1064,1066,1069,1071,1073,1075,1077,1079,1081,1083,1085,1087],{"class":56,"line":107},[54,1062,1063],{"class":97},"two_level ",[54,1065,120],{"class":93},[54,1067,1068],{"class":97}," (orders.groupby([",[54,1070,705],{"class":64},[54,1072,21],{"class":97},[54,1074,824],{"class":64},[54,1076,353],{"class":97},[54,1078,461],{"class":143},[54,1080,120],{"class":93},[54,1082,370],{"class":189},[54,1084,708],{"class":97},[54,1086,169],{"class":64},[54,1088,831],{"class":97},[54,1090,1091],{"class":56,"line":114},[54,1092,1093],{"class":97},"                   .reset_index()\n",[54,1095,1096,1099,1101,1103,1105,1107,1109,1111,1113,1115,1117,1119],{"class":56,"line":132},[54,1097,1098],{"class":97},"                   .sort_values([",[54,1100,705],{"class":64},[54,1102,21],{"class":97},[54,1104,169],{"class":64},[54,1106,353],{"class":97},[54,1108,850],{"class":143},[54,1110,120],{"class":93},[54,1112,751],{"class":97},[54,1114,370],{"class":189},[54,1116,21],{"class":97},[54,1118,466],{"class":189},[54,1120,1121],{"class":97},"]))\n",[54,1123,1124,1126,1129,1132,1135,1137,1139,1141],{"class":56,"line":195},[54,1125,198],{"class":189},[54,1127,1128],{"class":97},"(two_level.head(",[54,1130,1131],{"class":189},"10",[54,1133,1134],{"class":97},").to_string(",[54,1136,144],{"class":143},[54,1138,120],{"class":93},[54,1140,466],{"class":189},[54,1142,1143],{"class":97},"))\n",[10,1145,1146,1148,1149,1151],{},[14,1147,27],{}," sorts the Priority column by its categorical order (Urgent first) and Units descending within each priority — a sort that is awkward with plain strings and natural with Categoricals. ",[14,1150,506],{}," drops priority-region combinations that never occurred.",[40,1153,1155],{"id":1154},"step-6-keep-the-order-in-excel","Step 6 — Keep the order in Excel",[10,1157,1158,1159,1162],{},"The order is preserved when the table is written, because ",[14,1160,1161],{},"to_excel"," writes rows in the DataFrame's order:",[45,1164,1166],{"className":84,"code":1165,"language":86,"meta":50,"style":50},"with pd.ExcelWriter(\"pivot_ordered.xlsx\", engine=\"xlsxwriter\") as xw:\n    ordered.to_excel(xw, sheet_name=\"Month x size\")\n    by_priority.to_excel(xw, sheet_name=\"By priority\")\n    ranked.rename(\"Units\").to_frame().to_excel(xw, sheet_name=\"By region\")\ncheck = pd.read_excel(\"pivot_ordered.xlsx\", sheet_name=\"Month x size\", index_col=0)\nprint(check.index[:4].tolist(), check.columns.tolist())\n",[14,1167,1168,1197,1212,1226,1245,1275],{"__ignoreMap":50},[54,1169,1170,1173,1176,1179,1181,1184,1186,1189,1192,1194],{"class":56,"line":57},[54,1171,1172],{"class":93},"with",[54,1174,1175],{"class":97}," pd.ExcelWriter(",[54,1177,1178],{"class":64},"\"pivot_ordered.xlsx\"",[54,1180,21],{"class":97},[54,1182,1183],{"class":143},"engine",[54,1185,120],{"class":93},[54,1187,1188],{"class":64},"\"xlsxwriter\"",[54,1190,1191],{"class":97},") ",[54,1193,101],{"class":93},[54,1195,1196],{"class":97}," xw:\n",[54,1198,1199,1202,1205,1207,1210],{"class":56,"line":107},[54,1200,1201],{"class":97},"    ordered.to_excel(xw, ",[54,1203,1204],{"class":143},"sheet_name",[54,1206,120],{"class":93},[54,1208,1209],{"class":64},"\"Month x size\"",[54,1211,129],{"class":97},[54,1213,1214,1217,1219,1221,1224],{"class":56,"line":114},[54,1215,1216],{"class":97},"    by_priority.to_excel(xw, ",[54,1218,1204],{"class":143},[54,1220,120],{"class":93},[54,1222,1223],{"class":64},"\"By priority\"",[54,1225,129],{"class":97},[54,1227,1228,1231,1233,1236,1238,1240,1243],{"class":56,"line":132},[54,1229,1230],{"class":97},"    ranked.rename(",[54,1232,169],{"class":64},[54,1234,1235],{"class":97},").to_frame().to_excel(xw, ",[54,1237,1204],{"class":143},[54,1239,120],{"class":93},[54,1241,1242],{"class":64},"\"By region\"",[54,1244,129],{"class":97},[54,1246,1247,1250,1252,1254,1256,1258,1260,1262,1264,1266,1269,1271,1273],{"class":56,"line":195},[54,1248,1249],{"class":97},"check ",[54,1251,120],{"class":93},[54,1253,123],{"class":97},[54,1255,1178],{"class":64},[54,1257,21],{"class":97},[54,1259,1204],{"class":143},[54,1261,120],{"class":93},[54,1263,1209],{"class":64},[54,1265,21],{"class":97},[54,1267,1268],{"class":143},"index_col",[54,1270,120],{"class":93},[54,1272,190],{"class":189},[54,1274,129],{"class":97},[54,1276,1277,1279,1282,1284],{"class":56,"line":449},[54,1278,198],{"class":189},[54,1280,1281],{"class":97},"(check.index[:",[54,1283,928],{"class":189},[54,1285,1286],{"class":97},"].tolist(), check.columns.tolist())\n",[10,1288,1289,1290,1293,1294,1297,1298,38],{},"The file opens with months in calendar order and sizes from XS up, and reading it back confirms it. One caution: if the recipient sorts the sheet in Excel, Excel sorts text alphabetically again; for a sheet people will re-sort, add a numeric ",[14,1291,1292],{},"MonthNumber"," or ",[14,1295,1296],{},"SortKey"," column they can sort by. A native Excel pivot table built from the data has its own custom-list sorting, covered in ",[34,1299,1301],{"href":1300},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-a-native-excel-pivot-table-with-python\u002F","Add a Native Excel Pivot Table with Python",[509,1303,518,1309,518,1312,518,1315,518,1319,518,1325,518,1336,518,1340,518,1344,518,1348,518,1351,518,1354,518,1359,518,1363,518,1368,518,1375,518,1380,518,1385,518,1387,518,1390,518,1394,518,1397,518,1400,518,1406,518,1409,518,1412,518,1414,518,1417,518,1421,518,1424,518,1427,518,1434,518,1438],{"viewBox":1304,"role":512,"ariaLabelledBy":1305,"xmlns":516,"style":1308},"4 0 752 272",[1306,1307],"cat-choose-t","cat-choose-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[520,1310,1311],{"id":1306},"Categorical or reindex?",[524,1313,1314],{"id":1307},"If the order is a property of the data used in many steps, convert the column to an ordered Categorical. If it matters only for one output table, reindex the result. If readers will re-sort the sheet in Excel, also write a numeric sort key column.",[528,1316],{"x":928,"y":190,"width":1317,"height":1318,"fill":533},"752","272",[528,1320],{"x":1321,"y":934,"width":1322,"height":1323,"rx":1324,"fill":948,"stroke":631,"style":632},"240.0","280.0","75.5","12",[1326,1327,1329],"g",{"transform":1328},"translate(368.0,29.0) scale(1.000)",[1330,1331],"path",{"d":1332,"fill":1333,"stroke":1334,"style":1335},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[544,1337,1339],{"x":648,"y":1323,"style":1338},"font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Where does the order matter?",[56,1341],{"x1":648,"y1":1342,"x2":648,"y2":1343,"stroke":631,"style":632},"91.5","111.5",[56,1345],{"x1":1346,"y1":1343,"x2":1347,"y2":1343,"stroke":631,"style":632},"134.7","625.3",[56,1349],{"x1":1346,"y1":1343,"x2":1346,"y2":1350,"stroke":631,"style":632},"157.5",[634,1352],{"points":1353,"fill":637},"134.7,161.5 129.7,152.5 139.7,152.5",[528,1355],{"x":1356,"y":1357,"width":576,"height":1358,"rx":1131,"fill":533,"stroke":541,"style":645},"58.6","121.5","21.0",[544,1360,1362],{"x":1346,"y":983,"style":1361},"font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","many steps and charts",[528,1364],{"x":933,"y":1365,"width":1366,"height":1367,"rx":1324,"fill":596,"stroke":597,"style":632},"163.5","229.3","93.0",[1326,1369,1371],{"transform":1370},"translate(122.7,176.5) scale(1.000)",[1330,1372],{"d":1373,"fill":1333,"stroke":1374,"style":1335},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--teal-ink,#0b6157)",[544,1376,1379],{"x":1346,"y":1377,"style":1378},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Ordered Categorical",[544,1381,1384],{"x":1346,"y":1382,"style":1383},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","order travels with the data",[56,1386],{"x1":648,"y1":1343,"x2":648,"y2":1350,"stroke":631,"style":632},[634,1388],{"points":1389,"fill":637},"380.0,161.5 375.0,152.5 385.0,152.5",[528,1391],{"x":1392,"y":1357,"width":1393,"height":1358,"rx":1131,"fill":533,"stroke":541,"style":645},"322.9","114.2",[544,1395,1396],{"x":648,"y":983,"style":1361},"one output table",[528,1398],{"x":1399,"y":1365,"width":1366,"height":1367,"rx":1324,"fill":948,"stroke":631,"style":632},"265.3",[1326,1401,1403],{"transform":1402},"translate(368.0,176.5) scale(1.000)",[1330,1404],{"d":1405,"fill":1333,"stroke":1334,"style":1335},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[544,1407,1408],{"x":648,"y":1377,"style":1338},"reindex the result",[544,1410,1411],{"x":648,"y":1382,"style":1383},"simple and explicit",[56,1413],{"x1":1347,"y1":1343,"x2":1347,"y2":1350,"stroke":631,"style":632},[634,1415],{"points":1416,"fill":637},"625.3,161.5 620.3,152.5 630.3,152.5",[528,1418],{"x":1419,"y":1357,"width":1420,"height":1358,"rx":1131,"fill":533,"stroke":541,"style":645},"547.3","156.2",[544,1422,1423],{"x":1347,"y":983,"style":1361},"readers re-sort in Excel",[528,1425],{"x":1426,"y":1365,"width":1366,"height":1367,"rx":1324,"fill":553,"stroke":554,"style":632},"510.7",[1326,1428,1430],{"transform":1429},"translate(613.3,176.5) scale(1.000)",[1330,1431],{"d":1432,"fill":1333,"stroke":1433,"style":1335},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--gold-ink,#7a4e06)",[544,1435,1437],{"x":1347,"y":1377,"style":1436},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Add a sort key column",[544,1439,1440],{"x":1347,"y":1382,"style":1383},"numeric order survives",[40,1442,1444],{"id":1443},"step-7-order-columns-by-their-total","Step 7 — Order columns by their total",[10,1446,1447],{},"Sometimes the order should come from the data: a size or product matrix with its biggest columns on the left. Sort the column labels by their totals and reindex — keeping a deliberate catch-all at the end:",[45,1449,1451],{"className":84,"code":1450,"language":86,"meta":50,"style":50},"totals = ordered.sum().drop(\"One size\").sort_values(ascending=False)\nby_volume = ordered.reindex(columns=[*totals.index, \"One size\"])\nprint(by_volume.columns.tolist())\nprint(by_volume.loc[[\"Jan\", \"Feb\", \"Mar\"]])\n",[14,1452,1453,1475,1500,1507],{"__ignoreMap":50},[54,1454,1455,1458,1460,1463,1465,1467,1469,1471,1473],{"class":56,"line":57},[54,1456,1457],{"class":97},"totals ",[54,1459,120],{"class":93},[54,1461,1462],{"class":97}," ordered.sum().drop(",[54,1464,331],{"class":64},[54,1466,847],{"class":97},[54,1468,850],{"class":143},[54,1470,120],{"class":93},[54,1472,466],{"class":189},[54,1474,129],{"class":97},[54,1476,1477,1480,1482,1484,1486,1488,1490,1493,1496,1498],{"class":56,"line":107},[54,1478,1479],{"class":97},"by_volume ",[54,1481,120],{"class":93},[54,1483,744],{"class":97},[54,1485,154],{"class":143},[54,1487,120],{"class":93},[54,1489,751],{"class":97},[54,1491,1492],{"class":93},"*",[54,1494,1495],{"class":97},"totals.index, ",[54,1497,331],{"class":64},[54,1499,776],{"class":97},[54,1501,1502,1504],{"class":56,"line":114},[54,1503,198],{"class":189},[54,1505,1506],{"class":97},"(by_volume.columns.tolist())\n",[54,1508,1509,1511,1514,1516,1518,1520,1522,1524],{"class":56,"line":132},[54,1510,198],{"class":189},[54,1512,1513],{"class":97},"(by_volume.loc[[",[54,1515,236],{"class":64},[54,1517,21],{"class":97},[54,1519,241],{"class":64},[54,1521,21],{"class":97},[54,1523,246],{"class":64},[54,1525,1526],{"class":97},"]])\n",[10,1528,1529],{},"Medium is the best-selling size, so it moves to the left, followed by Large and Small; \"One size\" stays at the end because it is not comparable with the graded sizes. The rows keep their calendar order from the Categorical. This kind of data-driven order changes from month to month, so it belongs in the report step, not in the column type.",[40,1531,1533],{"id":1532},"performance-notes","Performance notes",[10,1535,1536,1537,1540,1541,1543,1544,1546,1547,38],{},"Converting a column to a Categorical is cheap and usually ",[210,1538,1539],{},"saves"," memory, because each distinct label is stored once and rows hold small integer codes; group-bys on categorical columns are also faster than on strings. The one cost is ",[14,1542,502],{}," on several categorical keys at once: pandas builds the full product of all categories, which for three columns of a few hundred categories each is millions of mostly empty groups. Use ",[14,1545,506],{}," for multi-key group-bys and add back only the empty categories you need with ",[14,1548,31],{},[40,1550,1552],{"id":1551},"common-pitfalls","Common pitfalls",[1554,1555,1556,1567,1581,1594,1608],"ul",{},[1557,1558,1559,1563,1564,1566],"li",{},[1560,1561,1562],"strong",{},"Values outside the category list."," They become ",[14,1565,490],{}," without warning; check the count after converting.",[1557,1568,1569,1574,1575,1577,1578,1580],{},[1560,1570,1571,1572,38],{},"Forgetting ",[14,1573,461],{}," ",[14,1576,502],{}," shows empty categories; ",[14,1579,506],{}," hides them — choose deliberately.",[1557,1582,1583,1586,1587,1589,1590,1593],{},[1560,1584,1585],{},"Sorting the margins row."," The ",[14,1588,903],{}," row of ",[14,1591,1592],{},"margins=True"," is just another label; move it last explicitly.",[1557,1595,1596,1599,1600,1603,1604,1607],{},[1560,1597,1598],{},"Unordered Categoricals."," Without ",[14,1601,1602],{},"ordered=True",", comparisons like ",[14,1605,1606],{},"\u003C \"Jun\""," raise and some sorts fall back to category codes inconsistently; set the flag.",[1557,1609,1610,1613],{},[1560,1611,1612],{},"Re-sorting in Excel."," Alphabetical again; provide a numeric sort column.",[40,1615,1617],{"id":1616},"conclusion","Conclusion",[10,1619,1620,1621,1623,1624,1626],{},"Alphabetical order is pandas' default for labels, and it is rarely the business order. Convert columns with a natural order — months, sizes, priorities — to ordered Categoricals so every group-by and pivot follows it, decide with ",[14,1622,461],{}," whether empty categories appear, use ",[14,1625,31],{}," for one-off orders, drop and re-append \"Other\" and totals to keep them last, sort by a categorical and a value together for two-level layouts, and write a numeric sort key when readers will re-sort the sheet in Excel.",[40,1628,1630],{"id":1629},"frequently-asked-questions","Frequently asked questions",[10,1632,1633,1636,1637,1639],{},[1560,1634,1635],{},"How do I sort pandas pivot rows by month instead of alphabetically?","\nConvert the month column to an ordered Categorical with categories in calendar order, pd.Categorical(df",[54,1638,149],{},", categories=MONTHS, ordered=True), before building the pivot table.",[10,1641,1642,1645,1646,1649],{},[1560,1643,1644],{},"How do I put rows in a specific order in a pandas pivot table?","\nCall reindex on the finished table with a list of labels in the order you want, for example table.reindex(",[54,1647,1648],{},"\"Urgent\", \"High\", \"Medium\", \"Low\"",").",[10,1651,1652,1655],{},[1560,1653,1654],{},"Why do empty categories show up as zero rows in my pivot?","\nWith Categorical columns and observed=False, pandas includes every category, even those with no data. Pass observed=True to show only categories that occur.",[10,1657,1658,1661],{},[1560,1659,1660],{},"How do I keep an \"Other\" category at the bottom when sorting by value?","\nDrop \"Other\" from the series, sort the remaining rows by value, then concatenate the \"Other\" row back at the end, followed by any total row.",[40,1663,1665],{"id":1664},"related","Related",[1554,1667,1668,1674,1681,1688],{},[1557,1669,1670,1671,1673],{},"Up one level: ",[34,1672,37],{"href":36}," — summarising data with pandas.",[1557,1675,1676,1680],{},[34,1677,1679],{"href":1678},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcreate-pivot-table-from-excel-with-pandas\u002F","Create a Pivot Table from Excel with pandas"," — the pivot_table basics.",[1557,1682,1683,1687],{},[34,1684,1686],{"href":1685},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fsort-excel-rows-with-python-before-writing\u002F","Sort Excel Rows with Python Before Writing"," — sorting detail tables for output.",[1557,1689,1690,1694],{},[34,1691,1693],{"href":1692},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fgroup-excel-rows-by-month-and-quarter-with-pandas\u002F","Group Excel Rows by Month and Quarter with pandas"," — grouping real dates, which sort correctly by themselves.",[1696,1697,1698],"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":50,"searchDepth":107,"depth":107,"links":1700},[1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713],{"id":42,"depth":107,"text":43},{"id":80,"depth":107,"text":81},{"id":216,"depth":107,"text":217},{"id":654,"depth":107,"text":655},{"id":803,"depth":107,"text":804},{"id":1017,"depth":107,"text":1018},{"id":1154,"depth":107,"text":1155},{"id":1443,"depth":107,"text":1444},{"id":1532,"depth":107,"text":1533},{"id":1551,"depth":107,"text":1552},{"id":1616,"depth":107,"text":1617},{"id":1629,"depth":107,"text":1630},{"id":1664,"depth":107,"text":1665},"2026-10-02","Pivot tables sort labels alphabetically — Apr, Aug, Dec, Feb… and L, M, S, XL — which is never what a report wants. Impose a business order with ordered Categoricals or reindex, keep empty categories visible, push \"Other\" and totals to the end, sort by value within a fixed group order, and make sure the order survives the trip to Excel.","md",[1718,1720,1722,1724],{"q":1635,"a":1719},"Convert the month column to an ordered Categorical with categories in calendar order, pd.Categorical(df[\"Month\"], categories=MONTHS, ordered=True), before building the pivot table.",{"q":1644,"a":1721},"Call reindex on the finished table with a list of labels in the order you want, for example table.reindex([\"Urgent\", \"High\", \"Medium\", \"Low\"]).",{"q":1654,"a":1723},"With Categorical columns and observed=False, pandas includes every category, even those with no data. Pass observed=True to show only categories that occur.",{"q":1660,"a":1725},"Drop \"Other\" from the series, sort the remaining rows by value, then concatenate the \"Other\" row back at the end, followed by any total row.","sort pivot rows in a custom order with pandas",null,{"breadcrumb":1729},[1730,1733,1736],{"name":1731,"item":1732},"Home","\u002F",{"name":1734,"item":1735},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":1737,"item":36},"Creating Pivot Tables from Excel Data with Pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fsort-pivot-rows-in-a-custom-order-with-pandas",{"title":1740,"description":1741},"Sort pandas Pivot Table Rows in a Custom Order","Custom row and column order for pandas pivot tables: ordered Categorical for months, sizes and priorities, reindex for one-off orders, observed=False for empty categories, Other and Total last, sorting by value within groups, and Excel output.","sort-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",[1745,1746],"pivots-and-summaries","pandas","how-to","zGyK8dHnX4jJSlFMshAgtwUesDUkmQ1HH-s_RymDN1I",[1750,1754],{"title":1751,"path":1752,"stem":1753,"children":-1},"Run an ABC-XYZ Inventory Classification","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification\u002Findex",{"title":1755,"path":1756,"stem":1757,"children":-1},"Turn a Pivot Table into a Heatmap in Excel","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fturn-a-pivot-table-into-a-heatmap-in-excel","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fturn-a-pivot-table-into-a-heatmap-in-excel\u002Findex",[1759,1761],{"path":1760,"title":1737},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data",{"path":1762,"title":1734},"\u002Fadvanced-data-transformation-and-cleaning",[],[1765,1768,1771,1773,1775,1778],{"path":1766,"title":1767,"kind":1747},"\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":1769,"title":1770,"kind":1747},"\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":1772,"title":1301,"kind":1747},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-a-native-excel-pivot-table-with-python",{"path":1774,"title":912,"kind":1747},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-subtotals-and-grand-totals-to-a-pandas-pivot-table",{"path":1776,"title":1777,"kind":1747},"\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":1779,"title":1780,"kind":1747},"\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",1790972086645]