[{"data":1,"prerenderedAt":2522},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe":2488,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe":2497,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe":2502,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe":2503},{"id":4,"title":5,"body":6,"dateModified":2453,"datePublished":2453,"description":2454,"extension":2455,"faq":2456,"keyword":2465,"kind":2466,"meta":2467,"navigation":313,"path":2476,"seo":2477,"slug":2480,"stem":2481,"topics":2482,"type":2486,"__hash__":2487},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe\u002Findex.md","Create a Sheet for Each Group in a DataFrame",{"type":7,"value":8,"toc":2440},"minimark",[9,24,239,244,272,494,501,505,512,630,637,641,648,957,970,1153,1157,1160,1466,1477,1481,1484,1783,1786,1790,1801,2030,2041,2286,2290,2340,2344,2359,2363,2372,2376,2385,2391,2397,2403,2407,2436],[10,11,12,13,17,18,23],"p",{},"A common report shape is \"the same table, once per region\" — or per customer, per sales rep, per month. Readers like it because each tab holds exactly the rows they care about, and it is one ",[14,15,16],"code",{},"groupby"," loop in pandas. The loop itself is short; the details around it decide whether the workbook is usable: sheet names that Excel accepts, tabs in a sensible order, a way to find a tab when there are forty of them, and formatting that is the same on every sheet. This guide is part of ",[19,20,22],"a",{"href":21},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002F","Working with Multiple Excel Sheets in Python",".",[25,26,34,35,34,39,34,43,34,51,34,61,34,72,34,79,34,84,34,92,34,99,34,105,34,110,34,113,34,118,34,123,34,130,34,135,34,139,34,142,34,145,34,148,34,153,34,157,34,161,34,164,34,167,34,170,34,175,34,179,34,183,34,186,34,189,34,192,34,197,34,201,34,205,34,208,34,211,34,214,34,219,34,223,34,227,34,230,34,233],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"4 0 752 670","img",[30,31],"group-fan-t","group-fan-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  ",[36,37,38],"title",{"id":30},"One DataFrame split into one worksheet per region",[40,41,42],"desc",{"id":31},"A single orders DataFrame is grouped by region and each group is written to its own worksheet named after the region, plus a contents sheet listing every tab with its row count.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"4","0","752","670","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":57,"fill":58,"stroke":59,"style":60},"20.0","275.5","220.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[62,63,65],"g",{"transform":64},"translate(118.0,288.5) scale(1.000)",[66,67],"path",{"d":68,"fill":69,"stroke":70,"style":71},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[73,74,78],"text",{"x":75,"y":76,"style":77},"130.0","335.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","orders DataFrame",[73,80,83],{"x":75,"y":81,"style":82},"354.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","400 rows, 5 regions",[44,85],{"x":86,"y":87,"width":88,"height":89,"rx":57,"fill":90,"stroke":91,"style":60},"490.0","16.0","250.0","92.0","#fdefd8","var(--gold,#b4740a)",[62,93,95],{"transform":94},"translate(603.0,29.0) scale(1.000)",[66,96],{"d":97,"fill":69,"stroke":98,"style":71},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--gold-ink,#7a4e06)",[73,100,104],{"x":101,"y":102,"style":103},"615.0","75.0","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Contents",[73,106,109],{"x":101,"y":107,"style":108},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","links and row counts",[66,111],{"d":112,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,62.0 480.0,62.0",[114,115],"polygon",{"points":116,"fill":117},"484.0,62.0 475.0,67.0 475.0,57.0","#5b5cf0",[44,119],{"x":86,"y":120,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"120.0","#d9f4f1","var(--teal,#0f9488)",[62,124,126],{"transform":125},"translate(603.0,133.0) scale(1.000)",[66,127],{"d":128,"fill":69,"stroke":129,"style":71},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--teal-ink,#0b6157)",[73,131,134],{"x":101,"y":132,"style":133},"179.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","North",[73,136,138],{"x":101,"y":137,"style":108},"198.0","90 rows",[66,140],{"d":141,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,166.0 480.0,166.0",[114,143],{"points":144,"fill":117},"484.0,166.0 475.0,171.0 475.0,161.0",[44,146],{"x":86,"y":147,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"224.0",[62,149,151],{"transform":150},"translate(603.0,237.0) scale(1.000)",[66,152],{"d":128,"fill":69,"stroke":129,"style":71},[73,154,156],{"x":101,"y":155,"style":133},"283.0","West",[73,158,160],{"x":101,"y":159,"style":108},"302.0","84 rows",[66,162],{"d":163,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,270.0 480.0,270.0",[114,165],{"points":166,"fill":117},"484.0,270.0 475.0,275.0 475.0,265.0",[44,168],{"x":86,"y":169,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"328.0",[62,171,173],{"transform":172},"translate(603.0,341.0) scale(1.000)",[66,174],{"d":128,"fill":69,"stroke":129,"style":71},[73,176,178],{"x":101,"y":177,"style":133},"387.0","Central-Head Office",[73,180,182],{"x":101,"y":181,"style":108},"406.0","79 rows",[66,184],{"d":185,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,374.0 480.0,374.0",[114,187],{"points":188,"fill":117},"484.0,374.0 475.0,379.0 475.0,369.0",[44,190],{"x":86,"y":191,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"432.0",[62,193,195],{"transform":194},"translate(603.0,445.0) scale(1.000)",[66,196],{"d":128,"fill":69,"stroke":129,"style":71},[73,198,200],{"x":101,"y":199,"style":133},"491.0","East",[73,202,204],{"x":101,"y":203,"style":108},"510.0","76 rows",[66,206],{"d":207,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,478.0 480.0,478.0",[114,209],{"points":210,"fill":117},"484.0,478.0 475.0,483.0 475.0,473.0",[44,212],{"x":86,"y":213,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"536.0",[62,215,217],{"transform":216},"translate(603.0,549.0) scale(1.000)",[66,218],{"d":128,"fill":69,"stroke":129,"style":71},[73,220,222],{"x":101,"y":221,"style":133},"595.0","South",[73,224,226],{"x":101,"y":225,"style":108},"614.0","71 rows",[66,228],{"d":229,"fill":69,"stroke":59,"style":60},"M244.0,322.0 C364.0,322.0 364.0,582.0 480.0,582.0",[114,231],{"points":232,"fill":117},"484.0,582.0 475.0,587.0 475.0,577.0",[73,234,238],{"x":235,"y":236,"style":237},"380.0","650.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","groupby('region') → one to_excel call per group, all through one ExcelWriter",[240,241,243],"h2",{"id":242},"prerequisites","Prerequisites",[245,246,251],"pre",{"className":247,"code":248,"language":249,"meta":250,"style":250},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas xlsxwriter\n","bash","",[14,252,253],{"__ignoreMap":250},[254,255,258,262,266,269],"span",{"class":256,"line":257},"line",1,[254,259,261],{"class":260},"sMTad","pip",[254,263,265],{"class":264},"srMev"," install",[254,267,268],{"class":264}," pandas",[254,270,271],{"class":264}," xlsxwriter\n",[245,273,277],{"className":274,"code":275,"language":276,"meta":250,"style":250},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\nrng = np.random.default_rng(11)\norders = pd.DataFrame({\n    \"region\": rng.choice([\"North\", \"South\", \"East\", \"West\", \"Central\u002FHead Office\"], 400),\n    \"rep\": rng.choice([\"Ana\", \"Ben\", \"Cy\", \"Dee\"], 400),\n    \"order_date\": pd.Timestamp(\"2026-01-01\") + pd.to_timedelta(rng.integers(0, 90, 400), unit=\"D\"),\n    \"amount\": rng.integers(50, 5_000, 400),\n})\n","python",[14,278,279,295,308,315,334,345,387,419,465,488],{"__ignoreMap":250},[254,280,281,285,289,292],{"class":256,"line":257},[254,282,284],{"class":283},"s-kum","import",[254,286,288],{"class":287},"skGVy"," numpy ",[254,290,291],{"class":283},"as",[254,293,294],{"class":287}," np\n",[254,296,298,300,303,305],{"class":256,"line":297},2,[254,299,284],{"class":283},[254,301,302],{"class":287}," pandas ",[254,304,291],{"class":283},[254,306,307],{"class":287}," pd\n",[254,309,311],{"class":256,"line":310},3,[254,312,314],{"emptyLinePlaceholder":313},true,"\n",[254,316,318,321,324,327,331],{"class":256,"line":317},4,[254,319,320],{"class":287},"rng ",[254,322,323],{"class":283},"=",[254,325,326],{"class":287}," np.random.default_rng(",[254,328,330],{"class":329},"sP0c6","11",[254,332,333],{"class":287},")\n",[254,335,337,340,342],{"class":256,"line":336},5,[254,338,339],{"class":287},"orders ",[254,341,323],{"class":283},[254,343,344],{"class":287}," pd.DataFrame({\n",[254,346,348,351,354,357,360,363,365,368,370,373,375,378,381,384],{"class":256,"line":347},6,[254,349,350],{"class":264},"    \"region\"",[254,352,353],{"class":287},": rng.choice([",[254,355,356],{"class":264},"\"North\"",[254,358,359],{"class":287},", ",[254,361,362],{"class":264},"\"South\"",[254,364,359],{"class":287},[254,366,367],{"class":264},"\"East\"",[254,369,359],{"class":287},[254,371,372],{"class":264},"\"West\"",[254,374,359],{"class":287},[254,376,377],{"class":264},"\"Central\u002FHead Office\"",[254,379,380],{"class":287},"], ",[254,382,383],{"class":329},"400",[254,385,386],{"class":287},"),\n",[254,388,390,393,395,398,400,403,405,408,410,413,415,417],{"class":256,"line":389},7,[254,391,392],{"class":264},"    \"rep\"",[254,394,353],{"class":287},[254,396,397],{"class":264},"\"Ana\"",[254,399,359],{"class":287},[254,401,402],{"class":264},"\"Ben\"",[254,404,359],{"class":287},[254,406,407],{"class":264},"\"Cy\"",[254,409,359],{"class":287},[254,411,412],{"class":264},"\"Dee\"",[254,414,380],{"class":287},[254,416,383],{"class":329},[254,418,386],{"class":287},[254,420,422,425,428,431,434,437,440,442,444,447,449,451,454,458,460,463],{"class":256,"line":421},8,[254,423,424],{"class":264},"    \"order_date\"",[254,426,427],{"class":287},": pd.Timestamp(",[254,429,430],{"class":264},"\"2026-01-01\"",[254,432,433],{"class":287},") ",[254,435,436],{"class":283},"+",[254,438,439],{"class":287}," pd.to_timedelta(rng.integers(",[254,441,47],{"class":329},[254,443,359],{"class":287},[254,445,446],{"class":329},"90",[254,448,359],{"class":287},[254,450,383],{"class":329},[254,452,453],{"class":287},"), ",[254,455,457],{"class":456},"sa561","unit",[254,459,323],{"class":283},[254,461,462],{"class":264},"\"D\"",[254,464,386],{"class":287},[254,466,468,471,474,477,479,482,484,486],{"class":256,"line":467},9,[254,469,470],{"class":264},"    \"amount\"",[254,472,473],{"class":287},": rng.integers(",[254,475,476],{"class":329},"50",[254,478,359],{"class":287},[254,480,481],{"class":329},"5_000",[254,483,359],{"class":287},[254,485,383],{"class":329},[254,487,386],{"class":287},[254,489,491],{"class":256,"line":490},10,[254,492,493],{"class":287},"})\n",[10,495,496,497,500],{},"One region name, ",[14,498,499],{},"Central\u002FHead Office",", contains a slash — deliberately, because real group keys do.",[240,502,504],{"id":503},"step-1-the-basic-loop","Step 1 — The basic loop",[10,506,507,508,511],{},"Open one ",[14,509,510],{},"ExcelWriter",", loop over the groups and write each one to a sheet named after its key. Writing through a single writer means the file is opened and saved once, however many sheets there are:",[245,513,515],{"className":274,"code":514,"language":276,"meta":250,"style":250},"with pd.ExcelWriter(\"by_region.xlsx\", engine=\"xlsxwriter\") as writer:\n    for region, group in orders.groupby(\"region\"):\n        try:\n            group.to_excel(writer, sheet_name=region, index=False)\n        except Exception as exc:\n            print(type(exc).__name__, exc)\n",[14,516,517,545,565,573,596,610],{"__ignoreMap":250},[254,518,519,522,525,528,530,533,535,538,540,542],{"class":256,"line":257},[254,520,521],{"class":283},"with",[254,523,524],{"class":287}," pd.ExcelWriter(",[254,526,527],{"class":264},"\"by_region.xlsx\"",[254,529,359],{"class":287},[254,531,532],{"class":456},"engine",[254,534,323],{"class":283},[254,536,537],{"class":264},"\"xlsxwriter\"",[254,539,433],{"class":287},[254,541,291],{"class":283},[254,543,544],{"class":287}," writer:\n",[254,546,547,550,553,556,559,562],{"class":256,"line":297},[254,548,549],{"class":283},"    for",[254,551,552],{"class":287}," region, group ",[254,554,555],{"class":283},"in",[254,557,558],{"class":287}," orders.groupby(",[254,560,561],{"class":264},"\"region\"",[254,563,564],{"class":287},"):\n",[254,566,567,570],{"class":256,"line":310},[254,568,569],{"class":283},"        try",[254,571,572],{"class":287},":\n",[254,574,575,578,581,583,586,589,591,594],{"class":256,"line":317},[254,576,577],{"class":287},"            group.to_excel(writer, ",[254,579,580],{"class":456},"sheet_name",[254,582,323],{"class":283},[254,584,585],{"class":287},"region, ",[254,587,588],{"class":456},"index",[254,590,323],{"class":283},[254,592,593],{"class":329},"False",[254,595,333],{"class":287},[254,597,598,601,604,607],{"class":256,"line":336},[254,599,600],{"class":283},"        except",[254,602,603],{"class":329}," Exception",[254,605,606],{"class":283}," as",[254,608,609],{"class":287}," exc:\n",[254,611,612,615,618,621,624,627],{"class":256,"line":347},[254,613,614],{"class":329},"            print",[254,616,617],{"class":287},"(",[254,619,620],{"class":329},"type",[254,622,623],{"class":287},"(exc).",[254,625,626],{"class":329},"__name__",[254,628,629],{"class":287},", exc)\n",[10,631,632,633,636],{},"The loop fails on the slash: ",[14,634,635],{},"InvalidWorksheetName: Invalid Excel character '[]:*?\u002F\\' in sheetname",". Group keys are data, and data does not follow Excel's naming rules, so every key needs cleaning before it becomes a sheet name.",[240,638,640],{"id":639},"step-2-make-every-key-a-valid-unique-sheet-name","Step 2 — Make every key a valid, unique sheet name",[10,642,643,644,647],{},"Excel's rules are short: at most 31 characters, none of ",[14,645,646],{},"[ ] : * ? \u002F \\",", no leading or trailing apostrophe, and uniqueness compared without case. A helper that cleans, truncates and de-duplicates handles all of them:",[245,649,651],{"className":274,"code":650,"language":276,"meta":250,"style":250},"import re\n\nINVALID = re.compile(r\"[\\[\\]:*?\u002F\\\\]\")\n\ndef sheet_names(keys) -> dict:\n    used, names = set(), {}\n    for key in keys:\n        base = INVALID.sub(\"-\", str(key)).strip(\"'\").strip() or \"Sheet\"\n        name, n = base[:31], 1\n        while name.casefold() in used:\n            n += 1\n            suffix = f\" ({n})\"\n            name = base[:31 - len(suffix)] + suffix\n        used.add(name.casefold())\n        names[key] = name\n    return names\n\nprint(sheet_names([\"Central\u002FHead Office\", \"North\", \"north\", \"x\" * 40]))\n# {'Central\u002FHead Office': 'Central-Head Office', 'North': 'North',\n#  'north': 'north (2)', 'xxxx…': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}\n",[14,652,653,660,664,702,706,723,736,748,784,802,815,827,853,879,885,896,905,910,944,951],{"__ignoreMap":250},[254,654,655,657],{"class":256,"line":257},[254,656,284],{"class":283},[254,658,659],{"class":287}," re\n",[254,661,662],{"class":256,"line":297},[254,663,314],{"emptyLinePlaceholder":313},[254,665,666,669,672,675,678,681,684,688,692,695,698,700],{"class":256,"line":310},[254,667,668],{"class":329},"INVALID",[254,670,671],{"class":283}," =",[254,673,674],{"class":287}," re.compile(",[254,676,677],{"class":283},"r",[254,679,680],{"class":264},"\"",[254,682,683],{"class":329},"[",[254,685,687],{"class":686},"s_b0D","\\[\\]",[254,689,691],{"class":690},"sSjpA",":*?\u002F",[254,693,694],{"class":686},"\\\\",[254,696,697],{"class":329},"]",[254,699,680],{"class":264},[254,701,333],{"class":287},[254,703,704],{"class":256,"line":317},[254,705,314],{"emptyLinePlaceholder":313},[254,707,708,711,715,718,721],{"class":256,"line":336},[254,709,710],{"class":283},"def",[254,712,714],{"class":713},"s_Opv"," sheet_names",[254,716,717],{"class":287},"(keys) -> ",[254,719,720],{"class":329},"dict",[254,722,572],{"class":287},[254,724,725,728,730,733],{"class":256,"line":347},[254,726,727],{"class":287},"    used, names ",[254,729,323],{"class":283},[254,731,732],{"class":329}," set",[254,734,735],{"class":287},"(), {}\n",[254,737,738,740,743,745],{"class":256,"line":389},[254,739,549],{"class":283},[254,741,742],{"class":287}," key ",[254,744,555],{"class":283},[254,746,747],{"class":287}," keys:\n",[254,749,750,753,755,758,761,764,766,769,772,775,778,781],{"class":256,"line":421},[254,751,752],{"class":287},"        base ",[254,754,323],{"class":283},[254,756,757],{"class":329}," INVALID",[254,759,760],{"class":287},".sub(",[254,762,763],{"class":264},"\"-\"",[254,765,359],{"class":287},[254,767,768],{"class":329},"str",[254,770,771],{"class":287},"(key)).strip(",[254,773,774],{"class":264},"\"'\"",[254,776,777],{"class":287},").strip() ",[254,779,780],{"class":283},"or",[254,782,783],{"class":264}," \"Sheet\"\n",[254,785,786,789,791,794,797,799],{"class":256,"line":467},[254,787,788],{"class":287},"        name, n ",[254,790,323],{"class":283},[254,792,793],{"class":287}," base[:",[254,795,796],{"class":329},"31",[254,798,380],{"class":287},[254,800,801],{"class":329},"1\n",[254,803,804,807,810,812],{"class":256,"line":490},[254,805,806],{"class":283},"        while",[254,808,809],{"class":287}," name.casefold() ",[254,811,555],{"class":283},[254,813,814],{"class":287}," used:\n",[254,816,818,821,824],{"class":256,"line":817},11,[254,819,820],{"class":287},"            n ",[254,822,823],{"class":283},"+=",[254,825,826],{"class":329}," 1\n",[254,828,830,833,835,838,841,844,847,850],{"class":256,"line":829},12,[254,831,832],{"class":287},"            suffix ",[254,834,323],{"class":283},[254,836,837],{"class":283}," f",[254,839,840],{"class":264},"\" (",[254,842,843],{"class":690},"{",[254,845,846],{"class":287},"n",[254,848,849],{"class":690},"}",[254,851,852],{"class":264},")\"\n",[254,854,856,859,861,863,865,868,871,874,876],{"class":256,"line":855},13,[254,857,858],{"class":287},"            name ",[254,860,323],{"class":283},[254,862,793],{"class":287},[254,864,796],{"class":329},[254,866,867],{"class":283}," -",[254,869,870],{"class":329}," len",[254,872,873],{"class":287},"(suffix)] ",[254,875,436],{"class":283},[254,877,878],{"class":287}," suffix\n",[254,880,882],{"class":256,"line":881},14,[254,883,884],{"class":287},"        used.add(name.casefold())\n",[254,886,888,891,893],{"class":256,"line":887},15,[254,889,890],{"class":287},"        names[key] ",[254,892,323],{"class":283},[254,894,895],{"class":287}," name\n",[254,897,899,902],{"class":256,"line":898},16,[254,900,901],{"class":283},"    return",[254,903,904],{"class":287}," names\n",[254,906,908],{"class":256,"line":907},17,[254,909,314],{"emptyLinePlaceholder":313},[254,911,913,916,919,921,923,925,927,930,932,935,938,941],{"class":256,"line":912},18,[254,914,915],{"class":329},"print",[254,917,918],{"class":287},"(sheet_names([",[254,920,377],{"class":264},[254,922,359],{"class":287},[254,924,356],{"class":264},[254,926,359],{"class":287},[254,928,929],{"class":264},"\"north\"",[254,931,359],{"class":287},[254,933,934],{"class":264},"\"x\"",[254,936,937],{"class":283}," *",[254,939,940],{"class":329}," 40",[254,942,943],{"class":287},"]))\n",[254,945,947],{"class":256,"line":946},19,[254,948,950],{"class":949},"s-wDw","# {'Central\u002FHead Office': 'Central-Head Office', 'North': 'North',\n",[254,952,954],{"class":256,"line":953},20,[254,955,956],{"class":949},"#  'north': 'north (2)', 'xxxx…': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}\n",[10,958,959,960,962,963,966,967,969],{},"The de-duplication step matters more than it looks. Without it, two keys that clean or truncate to the same name — ",[14,961,134],{}," and ",[14,964,965],{},"north",", or two customer names that share their first 31 characters — both write to the same sheet in one ",[14,968,510],{},", and the second group silently replaces the first. You lose a group with no error at all.",[25,971,34,977,34,980,34,983,34,988,34,996,34,1000,34,1006,34,1010,34,1015,34,1017,34,1021,34,1023,34,1027,34,1032,34,1035,34,1037,34,1040,34,1042,34,1045,34,1048,34,1051,34,1053,34,1055,34,1058,34,1062,34,1064,34,1067,34,1070,34,1074,34,1077,34,1079,34,1082,34,1084,34,1087,34,1089,34,1091,34,1093,34,1096,34,1098,34,1100,34,1102,34,1104,34,1106,34,1108,34,1110,34,1113,34,1115,34,1117,34,1119,34,1122,34,1128,34,1132,34,1137,34,1140,34,1147],{"viewBox":972,"role":28,"ariaLabelledBy":973,"xmlns":32,"style":976},"3 0 753 220",[974,975],"group-names-t","group-names-d","width:100%;max-width:753px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,978,979],{"id":974},"Raw group keys turned into valid, unique sheet names",[40,981,982],{"id":975},"Before, the keys include a slash, a name that differs only in case from another, and a 40-character name. After cleaning, the slash becomes a dash, the case duplicate gets a numbered suffix and the long name is cut to 31 characters.",[44,984],{"x":985,"y":47,"width":986,"height":987,"fill":50},"3","753","220",[44,989],{"x":53,"y":990,"width":991,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[44,997],{"x":998,"y":990,"width":999,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"52.0","257.8",[73,1001,1005],{"x":1002,"y":1003,"style":1004},"180.9","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[44,1007],{"x":53,"y":1008,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"64.0","28.0",[73,1011,1014],{"x":1012,"y":1013,"style":1004},"36.0","83.0","1",[44,1016],{"x":998,"y":1008,"width":999,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,1018,1020],{"x":1002,"y":1013,"style":1019},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","group key",[44,1022],{"x":53,"y":89,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1024,1026],{"x":1012,"y":1025,"style":1004},"111.0","2",[44,1028],{"x":998,"y":89,"width":999,"height":1009,"rx":47,"fill":1029,"stroke":1030,"style":1031},"#fee8f2","var(--accent,#f43f8f)","stroke-width:1.5px",[73,1033,499],{"x":1002,"y":1025,"style":1034},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[44,1036],{"x":53,"y":120,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1038,985],{"x":1012,"y":1039,"style":1004},"139.0",[44,1041],{"x":998,"y":120,"width":999,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,1043,134],{"x":1002,"y":1039,"style":1044},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[44,1046],{"x":53,"y":1047,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"148.0",[73,1049,46],{"x":1012,"y":1050,"style":1004},"167.0",[44,1052],{"x":998,"y":1047,"width":999,"height":1009,"rx":47,"fill":1029,"stroke":1030,"style":1031},[73,1054,965],{"x":1002,"y":1050,"style":1034},[44,1056],{"x":53,"y":1057,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"176.0",[73,1059,1061],{"x":1012,"y":1060,"style":1004},"195.0","5",[44,1063],{"x":998,"y":1057,"width":999,"height":1009,"rx":47,"fill":1029,"stroke":1030,"style":1031},[73,1065,1066],{"x":1002,"y":1060,"style":1034},"Very Long Customer Name Limited Europe",[44,1068],{"x":1069,"y":990,"width":991,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"492.9",[44,1071],{"x":1072,"y":990,"width":1073,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"524.9","215.1",[73,1075,1005],{"x":1076,"y":1003,"style":1004},"632.5",[44,1078],{"x":1069,"y":1008,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1080,1014],{"x":1081,"y":1013,"style":1004},"508.9",[44,1083],{"x":1072,"y":1008,"width":1073,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,1085,1086],{"x":1076,"y":1013,"style":1019},"sheet name",[44,1088],{"x":1069,"y":89,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1090,1026],{"x":1081,"y":1025,"style":1004},[44,1092],{"x":1072,"y":89,"width":1073,"height":1009,"rx":47,"fill":121,"stroke":122,"style":1031},[73,1094,178],{"x":1076,"y":1025,"style":1095},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[44,1097],{"x":1069,"y":120,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1099,985],{"x":1081,"y":1039,"style":1004},[44,1101],{"x":1072,"y":120,"width":1073,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,1103,134],{"x":1076,"y":1039,"style":1044},[44,1105],{"x":1069,"y":1047,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1107,46],{"x":1081,"y":1050,"style":1004},[44,1109],{"x":1072,"y":1047,"width":1073,"height":1009,"rx":47,"fill":121,"stroke":122,"style":1031},[73,1111,1112],{"x":1076,"y":1050,"style":1095},"north (2)",[44,1114],{"x":1069,"y":1057,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},[73,1116,1061],{"x":1081,"y":1060,"style":1004},[44,1118],{"x":1072,"y":1057,"width":1073,"height":1009,"rx":47,"fill":121,"stroke":122,"style":1031},[73,1120,1121],{"x":1076,"y":1060,"style":1095},"Very Long Customer Name Limited",[73,1123,1127],{"x":1124,"y":1125,"style":1126},"164.9","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[73,1129,1131],{"x":1130,"y":1125,"style":1126},"616.5","after",[256,1133],{"x1":1134,"y1":1135,"x2":1136,"y2":1135,"stroke":59,"style":60},"319.8","122.0","478.9",[114,1138],{"points":1139,"fill":117},"482.9,122.0 473.9,127.0 473.9,117.0",[44,1141],{"x":1142,"y":1143,"width":1144,"height":1145,"rx":330,"fill":50,"stroke":994,"style":1146},"344.1","89.0","114.5","22.0","stroke-width:1.2px",[73,1148,1152],{"x":1149,"y":1150,"style":1151},"401.4","104.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","sheet_names()",[240,1154,1156],{"id":1155},"step-3-order-the-tabs-and-add-a-contents-sheet","Step 3 — Order the tabs and add a contents sheet",[10,1158,1159],{},"Tabs appear in the order you create them. Sort the keys deliberately — here, largest region first, which is usually what a reader looks for — and put a contents sheet at the front that links to every tab and states its row count:",[245,1161,1163],{"className":274,"code":1162,"language":276,"meta":250,"style":250},"counts = orders[\"region\"].value_counts()\norder = counts.index.tolist()                  # biggest group first\nnames = sheet_names(order)\n\nwith pd.ExcelWriter(\"by_region.xlsx\", engine=\"xlsxwriter\") as writer:\n    contents = writer.book.add_worksheet(\"Contents\")\n    bold = writer.book.add_format({\"bold\": True})\n    contents.write_row(0, 0, [\"Sheet\", \"Rows\", \"Total amount\"], bold)\n    for i, region in enumerate(order, start=1):\n        group = orders[orders[\"region\"] == region].sort_values(\"order_date\")\n        group.to_excel(writer, sheet_name=names[region], index=False)\n        contents.write_url(i, 0, f\"internal:'{names[region]}'!A1\", string=names[region])\n        contents.write_number(i, 1, len(group))\n        contents.write_number(i, 2, int(group[\"amount\"].sum()))\n    contents.set_column(\"A:A\", 24)\n\nprint(pd.ExcelFile(\"by_region.xlsx\").sheet_names)\n",[14,1164,1165,1180,1193,1203,1207,1229,1244,1265,1295,1319,1345,1365,1400,1415,1435,1450,1454],{"__ignoreMap":250},[254,1166,1167,1170,1172,1175,1177],{"class":256,"line":257},[254,1168,1169],{"class":287},"counts ",[254,1171,323],{"class":283},[254,1173,1174],{"class":287}," orders[",[254,1176,561],{"class":264},[254,1178,1179],{"class":287},"].value_counts()\n",[254,1181,1182,1185,1187,1190],{"class":256,"line":297},[254,1183,1184],{"class":287},"order ",[254,1186,323],{"class":283},[254,1188,1189],{"class":287}," counts.index.tolist()                  ",[254,1191,1192],{"class":949},"# biggest group first\n",[254,1194,1195,1198,1200],{"class":256,"line":310},[254,1196,1197],{"class":287},"names ",[254,1199,323],{"class":283},[254,1201,1202],{"class":287}," sheet_names(order)\n",[254,1204,1205],{"class":256,"line":317},[254,1206,314],{"emptyLinePlaceholder":313},[254,1208,1209,1211,1213,1215,1217,1219,1221,1223,1225,1227],{"class":256,"line":336},[254,1210,521],{"class":283},[254,1212,524],{"class":287},[254,1214,527],{"class":264},[254,1216,359],{"class":287},[254,1218,532],{"class":456},[254,1220,323],{"class":283},[254,1222,537],{"class":264},[254,1224,433],{"class":287},[254,1226,291],{"class":283},[254,1228,544],{"class":287},[254,1230,1231,1234,1236,1239,1242],{"class":256,"line":347},[254,1232,1233],{"class":287},"    contents ",[254,1235,323],{"class":283},[254,1237,1238],{"class":287}," writer.book.add_worksheet(",[254,1240,1241],{"class":264},"\"Contents\"",[254,1243,333],{"class":287},[254,1245,1246,1249,1251,1254,1257,1260,1263],{"class":256,"line":389},[254,1247,1248],{"class":287},"    bold ",[254,1250,323],{"class":283},[254,1252,1253],{"class":287}," writer.book.add_format({",[254,1255,1256],{"class":264},"\"bold\"",[254,1258,1259],{"class":287},": ",[254,1261,1262],{"class":329},"True",[254,1264,493],{"class":287},[254,1266,1267,1270,1272,1274,1276,1279,1282,1284,1287,1289,1292],{"class":256,"line":421},[254,1268,1269],{"class":287},"    contents.write_row(",[254,1271,47],{"class":329},[254,1273,359],{"class":287},[254,1275,47],{"class":329},[254,1277,1278],{"class":287},", [",[254,1280,1281],{"class":264},"\"Sheet\"",[254,1283,359],{"class":287},[254,1285,1286],{"class":264},"\"Rows\"",[254,1288,359],{"class":287},[254,1290,1291],{"class":264},"\"Total amount\"",[254,1293,1294],{"class":287},"], bold)\n",[254,1296,1297,1299,1302,1304,1307,1310,1313,1315,1317],{"class":256,"line":467},[254,1298,549],{"class":283},[254,1300,1301],{"class":287}," i, region ",[254,1303,555],{"class":283},[254,1305,1306],{"class":329}," enumerate",[254,1308,1309],{"class":287},"(order, ",[254,1311,1312],{"class":456},"start",[254,1314,323],{"class":283},[254,1316,1014],{"class":329},[254,1318,564],{"class":287},[254,1320,1321,1324,1326,1329,1331,1334,1337,1340,1343],{"class":256,"line":490},[254,1322,1323],{"class":287},"        group ",[254,1325,323],{"class":283},[254,1327,1328],{"class":287}," orders[orders[",[254,1330,561],{"class":264},[254,1332,1333],{"class":287},"] ",[254,1335,1336],{"class":283},"==",[254,1338,1339],{"class":287}," region].sort_values(",[254,1341,1342],{"class":264},"\"order_date\"",[254,1344,333],{"class":287},[254,1346,1347,1350,1352,1354,1357,1359,1361,1363],{"class":256,"line":817},[254,1348,1349],{"class":287},"        group.to_excel(writer, ",[254,1351,580],{"class":456},[254,1353,323],{"class":283},[254,1355,1356],{"class":287},"names[region], ",[254,1358,588],{"class":456},[254,1360,323],{"class":283},[254,1362,593],{"class":329},[254,1364,333],{"class":287},[254,1366,1367,1370,1372,1374,1377,1380,1382,1385,1387,1390,1392,1395,1397],{"class":256,"line":829},[254,1368,1369],{"class":287},"        contents.write_url(i, ",[254,1371,47],{"class":329},[254,1373,359],{"class":287},[254,1375,1376],{"class":283},"f",[254,1378,1379],{"class":264},"\"internal:'",[254,1381,843],{"class":690},[254,1383,1384],{"class":287},"names[region]",[254,1386,849],{"class":690},[254,1388,1389],{"class":264},"'!A1\"",[254,1391,359],{"class":287},[254,1393,1394],{"class":456},"string",[254,1396,323],{"class":283},[254,1398,1399],{"class":287},"names[region])\n",[254,1401,1402,1405,1407,1409,1412],{"class":256,"line":855},[254,1403,1404],{"class":287},"        contents.write_number(i, ",[254,1406,1014],{"class":329},[254,1408,359],{"class":287},[254,1410,1411],{"class":329},"len",[254,1413,1414],{"class":287},"(group))\n",[254,1416,1417,1419,1421,1423,1426,1429,1432],{"class":256,"line":881},[254,1418,1404],{"class":287},[254,1420,1026],{"class":329},[254,1422,359],{"class":287},[254,1424,1425],{"class":329},"int",[254,1427,1428],{"class":287},"(group[",[254,1430,1431],{"class":264},"\"amount\"",[254,1433,1434],{"class":287},"].sum()))\n",[254,1436,1437,1440,1443,1445,1448],{"class":256,"line":887},[254,1438,1439],{"class":287},"    contents.set_column(",[254,1441,1442],{"class":264},"\"A:A\"",[254,1444,359],{"class":287},[254,1446,1447],{"class":329},"24",[254,1449,333],{"class":287},[254,1451,1452],{"class":256,"line":898},[254,1453,314],{"emptyLinePlaceholder":313},[254,1455,1456,1458,1461,1463],{"class":256,"line":907},[254,1457,915],{"class":329},[254,1459,1460],{"class":287},"(pd.ExcelFile(",[254,1462,527],{"class":264},[254,1464,1465],{"class":287},").sheet_names)\n",[10,1467,1468,1469,1472,1473,23],{},"The quotes in ",[14,1470,1471],{},"internal:'Central-Head Office'!A1"," are required for any sheet name containing spaces; using them always is simpler than deciding case by case. A richer contents sheet, with back-links from every tab, is built in ",[19,1474,1476],{"href":1475},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel\u002F","Add a Table of Contents Sheet with Hyperlinks in Excel",[240,1478,1480],{"id":1479},"step-4-format-every-sheet-the-same-way","Step 4 — Format every sheet the same way",[10,1482,1483],{},"Formatting inside the loop, through the writer's worksheet objects, guarantees identical sheets. Freeze the header, set widths, apply number and date formats, and add an autofilter sized to each group:",[245,1485,1487],{"className":274,"code":1486,"language":276,"meta":250,"style":250},"with pd.ExcelWriter(\"by_region.xlsx\", engine=\"xlsxwriter\", datetime_format=\"yyyy-mm-dd\") as writer:\n    money = writer.book.add_format({\"num_format\": \"#,##0\"})\n    for region in order:\n        group = orders[orders[\"region\"] == region].sort_values(\"order_date\")\n        name = names[region]\n        group.to_excel(writer, sheet_name=name, index=False)\n        ws = writer.sheets[name]\n        ws.freeze_panes(1, 0)\n        ws.set_column(\"A:B\", 20)\n        ws.set_column(\"C:C\", 12)\n        ws.set_column(\"D:D\", 12, money)\n        ws.autofilter(0, 0, len(group), len(group.columns) - 1)\n\ncheck = pd.read_excel(\"by_region.xlsx\", sheet_name=None)\nprint({name: len(frame) for name, frame in check.items()})\nprint(sum(len(f) for f in check.values()) == len(orders))   # True\n",[14,1488,1489,1521,1540,1552,1572,1582,1601,1611,1624,1639,1652,1666,1697,1701,1724,1747],{"__ignoreMap":250},[254,1490,1491,1493,1495,1497,1499,1501,1503,1505,1507,1510,1512,1515,1517,1519],{"class":256,"line":257},[254,1492,521],{"class":283},[254,1494,524],{"class":287},[254,1496,527],{"class":264},[254,1498,359],{"class":287},[254,1500,532],{"class":456},[254,1502,323],{"class":283},[254,1504,537],{"class":264},[254,1506,359],{"class":287},[254,1508,1509],{"class":456},"datetime_format",[254,1511,323],{"class":283},[254,1513,1514],{"class":264},"\"yyyy-mm-dd\"",[254,1516,433],{"class":287},[254,1518,291],{"class":283},[254,1520,544],{"class":287},[254,1522,1523,1526,1528,1530,1533,1535,1538],{"class":256,"line":297},[254,1524,1525],{"class":287},"    money ",[254,1527,323],{"class":283},[254,1529,1253],{"class":287},[254,1531,1532],{"class":264},"\"num_format\"",[254,1534,1259],{"class":287},[254,1536,1537],{"class":264},"\"#,##0\"",[254,1539,493],{"class":287},[254,1541,1542,1544,1547,1549],{"class":256,"line":310},[254,1543,549],{"class":283},[254,1545,1546],{"class":287}," region ",[254,1548,555],{"class":283},[254,1550,1551],{"class":287}," order:\n",[254,1553,1554,1556,1558,1560,1562,1564,1566,1568,1570],{"class":256,"line":317},[254,1555,1323],{"class":287},[254,1557,323],{"class":283},[254,1559,1328],{"class":287},[254,1561,561],{"class":264},[254,1563,1333],{"class":287},[254,1565,1336],{"class":283},[254,1567,1339],{"class":287},[254,1569,1342],{"class":264},[254,1571,333],{"class":287},[254,1573,1574,1577,1579],{"class":256,"line":336},[254,1575,1576],{"class":287},"        name ",[254,1578,323],{"class":283},[254,1580,1581],{"class":287}," names[region]\n",[254,1583,1584,1586,1588,1590,1593,1595,1597,1599],{"class":256,"line":347},[254,1585,1349],{"class":287},[254,1587,580],{"class":456},[254,1589,323],{"class":283},[254,1591,1592],{"class":287},"name, ",[254,1594,588],{"class":456},[254,1596,323],{"class":283},[254,1598,593],{"class":329},[254,1600,333],{"class":287},[254,1602,1603,1606,1608],{"class":256,"line":389},[254,1604,1605],{"class":287},"        ws ",[254,1607,323],{"class":283},[254,1609,1610],{"class":287}," writer.sheets[name]\n",[254,1612,1613,1616,1618,1620,1622],{"class":256,"line":421},[254,1614,1615],{"class":287},"        ws.freeze_panes(",[254,1617,1014],{"class":329},[254,1619,359],{"class":287},[254,1621,47],{"class":329},[254,1623,333],{"class":287},[254,1625,1626,1629,1632,1634,1637],{"class":256,"line":467},[254,1627,1628],{"class":287},"        ws.set_column(",[254,1630,1631],{"class":264},"\"A:B\"",[254,1633,359],{"class":287},[254,1635,1636],{"class":329},"20",[254,1638,333],{"class":287},[254,1640,1641,1643,1646,1648,1650],{"class":256,"line":490},[254,1642,1628],{"class":287},[254,1644,1645],{"class":264},"\"C:C\"",[254,1647,359],{"class":287},[254,1649,57],{"class":329},[254,1651,333],{"class":287},[254,1653,1654,1656,1659,1661,1663],{"class":256,"line":817},[254,1655,1628],{"class":287},[254,1657,1658],{"class":264},"\"D:D\"",[254,1660,359],{"class":287},[254,1662,57],{"class":329},[254,1664,1665],{"class":287},", money)\n",[254,1667,1668,1671,1673,1675,1677,1679,1681,1684,1686,1689,1692,1695],{"class":256,"line":829},[254,1669,1670],{"class":287},"        ws.autofilter(",[254,1672,47],{"class":329},[254,1674,359],{"class":287},[254,1676,47],{"class":329},[254,1678,359],{"class":287},[254,1680,1411],{"class":329},[254,1682,1683],{"class":287},"(group), ",[254,1685,1411],{"class":329},[254,1687,1688],{"class":287},"(group.columns) ",[254,1690,1691],{"class":283},"-",[254,1693,1694],{"class":329}," 1",[254,1696,333],{"class":287},[254,1698,1699],{"class":256,"line":855},[254,1700,314],{"emptyLinePlaceholder":313},[254,1702,1703,1706,1708,1711,1713,1715,1717,1719,1722],{"class":256,"line":881},[254,1704,1705],{"class":287},"check ",[254,1707,323],{"class":283},[254,1709,1710],{"class":287}," pd.read_excel(",[254,1712,527],{"class":264},[254,1714,359],{"class":287},[254,1716,580],{"class":456},[254,1718,323],{"class":283},[254,1720,1721],{"class":329},"None",[254,1723,333],{"class":287},[254,1725,1726,1728,1731,1733,1736,1739,1742,1744],{"class":256,"line":887},[254,1727,915],{"class":329},[254,1729,1730],{"class":287},"({name: ",[254,1732,1411],{"class":329},[254,1734,1735],{"class":287},"(frame) ",[254,1737,1738],{"class":283},"for",[254,1740,1741],{"class":287}," name, frame ",[254,1743,555],{"class":283},[254,1745,1746],{"class":287}," check.items()})\n",[254,1748,1749,1751,1753,1756,1758,1760,1763,1765,1768,1770,1773,1775,1777,1780],{"class":256,"line":898},[254,1750,915],{"class":329},[254,1752,617],{"class":287},[254,1754,1755],{"class":329},"sum",[254,1757,617],{"class":287},[254,1759,1411],{"class":329},[254,1761,1762],{"class":287},"(f) ",[254,1764,1738],{"class":283},[254,1766,1767],{"class":287}," f ",[254,1769,555],{"class":283},[254,1771,1772],{"class":287}," check.values()) ",[254,1774,1336],{"class":283},[254,1776,870],{"class":329},[254,1778,1779],{"class":287},"(orders))   ",[254,1781,1782],{"class":949},"# True\n",[10,1784,1785],{},"The final check is worth keeping in production code, and it costs one extra read of a file you just wrote: the rows across all sheets must add up to the rows you started with. If a group ever goes missing — a name collision the helper did not catch, a filter applied by mistake — the job fails instead of publishing an incomplete report.",[240,1787,1789],{"id":1788},"step-5-group-by-two-keys","Step 5 — Group by two keys",[10,1791,1792,1793,1797,1798,1800],{},"Reports often split on two dimensions at once — a sheet per region ",[1794,1795,1796],"em",{},"and"," month. ",[14,1799,16],{}," with a list of columns yields tuple keys; turn each tuple into a readable name before cleaning it:",[245,1802,1804],{"className":274,"code":1803,"language":276,"meta":250,"style":250},"orders[\"month\"] = orders[\"order_date\"].dt.strftime(\"%Y-%m\")\npairs = sorted(orders.groupby([\"region\", \"month\"]).groups)\npair_names = sheet_names([f\"{region} {month}\" for region, month in pairs])\n\nwith pd.ExcelWriter(\"by_region_month.xlsx\", engine=\"xlsxwriter\") as writer:\n    for (region, month), group in orders.groupby([\"region\", \"month\"]):\n        group.drop(columns=\"month\").to_excel(writer, sheet_name=pair_names[f\"{region} {month}\"], index=False)\n\ntabs = pd.ExcelFile(\"by_region_month.xlsx\").sheet_names\nprint(len(tabs), tabs[:3])   # 15 ['Central-Head Office 2026-01', 'Central-Head Office 2026-02', 'Central-Head Office 2026-03']\n",[14,1805,1806,1830,1852,1894,1898,1921,1942,1992,1996,2011],{"__ignoreMap":250},[254,1807,1808,1811,1814,1816,1818,1820,1822,1825,1828],{"class":256,"line":257},[254,1809,1810],{"class":287},"orders[",[254,1812,1813],{"class":264},"\"month\"",[254,1815,1333],{"class":287},[254,1817,323],{"class":283},[254,1819,1174],{"class":287},[254,1821,1342],{"class":264},[254,1823,1824],{"class":287},"].dt.strftime(",[254,1826,1827],{"class":264},"\"%Y-%m\"",[254,1829,333],{"class":287},[254,1831,1832,1835,1837,1840,1843,1845,1847,1849],{"class":256,"line":297},[254,1833,1834],{"class":287},"pairs ",[254,1836,323],{"class":283},[254,1838,1839],{"class":329}," sorted",[254,1841,1842],{"class":287},"(orders.groupby([",[254,1844,561],{"class":264},[254,1846,359],{"class":287},[254,1848,1813],{"class":264},[254,1850,1851],{"class":287},"]).groups)\n",[254,1853,1854,1857,1859,1862,1864,1866,1868,1871,1873,1876,1879,1881,1883,1886,1889,1891],{"class":256,"line":310},[254,1855,1856],{"class":287},"pair_names ",[254,1858,323],{"class":283},[254,1860,1861],{"class":287}," sheet_names([",[254,1863,1376],{"class":283},[254,1865,680],{"class":264},[254,1867,843],{"class":690},[254,1869,1870],{"class":287},"region",[254,1872,849],{"class":690},[254,1874,1875],{"class":690}," {",[254,1877,1878],{"class":287},"month",[254,1880,849],{"class":690},[254,1882,680],{"class":264},[254,1884,1885],{"class":283}," for",[254,1887,1888],{"class":287}," region, month ",[254,1890,555],{"class":283},[254,1892,1893],{"class":287}," pairs])\n",[254,1895,1896],{"class":256,"line":317},[254,1897,314],{"emptyLinePlaceholder":313},[254,1899,1900,1902,1904,1907,1909,1911,1913,1915,1917,1919],{"class":256,"line":336},[254,1901,521],{"class":283},[254,1903,524],{"class":287},[254,1905,1906],{"class":264},"\"by_region_month.xlsx\"",[254,1908,359],{"class":287},[254,1910,532],{"class":456},[254,1912,323],{"class":283},[254,1914,537],{"class":264},[254,1916,433],{"class":287},[254,1918,291],{"class":283},[254,1920,544],{"class":287},[254,1922,1923,1925,1928,1930,1933,1935,1937,1939],{"class":256,"line":347},[254,1924,549],{"class":283},[254,1926,1927],{"class":287}," (region, month), group ",[254,1929,555],{"class":283},[254,1931,1932],{"class":287}," orders.groupby([",[254,1934,561],{"class":264},[254,1936,359],{"class":287},[254,1938,1813],{"class":264},[254,1940,1941],{"class":287},"]):\n",[254,1943,1944,1947,1950,1952,1954,1957,1959,1961,1964,1966,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990],{"class":256,"line":389},[254,1945,1946],{"class":287},"        group.drop(",[254,1948,1949],{"class":456},"columns",[254,1951,323],{"class":283},[254,1953,1813],{"class":264},[254,1955,1956],{"class":287},").to_excel(writer, ",[254,1958,580],{"class":456},[254,1960,323],{"class":283},[254,1962,1963],{"class":287},"pair_names[",[254,1965,1376],{"class":283},[254,1967,680],{"class":264},[254,1969,843],{"class":690},[254,1971,1870],{"class":287},[254,1973,849],{"class":690},[254,1975,1875],{"class":690},[254,1977,1878],{"class":287},[254,1979,849],{"class":690},[254,1981,680],{"class":264},[254,1983,380],{"class":287},[254,1985,588],{"class":456},[254,1987,323],{"class":283},[254,1989,593],{"class":329},[254,1991,333],{"class":287},[254,1993,1994],{"class":256,"line":421},[254,1995,314],{"emptyLinePlaceholder":313},[254,1997,1998,2001,2003,2006,2008],{"class":256,"line":467},[254,1999,2000],{"class":287},"tabs ",[254,2002,323],{"class":283},[254,2004,2005],{"class":287}," pd.ExcelFile(",[254,2007,1906],{"class":264},[254,2009,2010],{"class":287},").sheet_names\n",[254,2012,2013,2015,2017,2019,2022,2024,2027],{"class":256,"line":490},[254,2014,915],{"class":329},[254,2016,617],{"class":287},[254,2018,1411],{"class":329},[254,2020,2021],{"class":287},"(tabs), tabs[:",[254,2023,985],{"class":329},[254,2025,2026],{"class":287},"])   ",[254,2028,2029],{"class":949},"# 15 ['Central-Head Office 2026-01', 'Central-Head Office 2026-02', 'Central-Head Office 2026-03']\n",[10,2031,2032,2033,2036,2037,2040],{},"Two keys multiply quickly: five regions over three months is fifteen tabs, and a year would be sixty. Put the ",[1794,2034,2035],{},"coarser"," key first in the name so the tabs sort into readable blocks — all of one region's months together —  and keep names short — ",[14,2038,2039],{},"Central-Head Office 2026-01"," is already 27 of the 31 characters Excel allows. When the count climbs past a few dozen, split on one key into sheets and leave the other as a column the reader can filter.",[25,2042,34,2048,34,2051,34,2054,34,2059,34,2065,34,2070,34,2077,34,2082,34,2085,34,2088,34,2092,34,2096,34,2100,34,2104,34,2108,34,2111,34,2114,34,2116,34,2120,34,2122,34,2125,34,2127,34,2130,34,2133,34,2136,34,2138,34,2140,34,2142,34,2144,34,2146,34,2149,34,2152,34,2155,34,2157,34,2159,34,2161,34,2164,34,2166,34,2169,34,2172,34,2175,34,2177,34,2179,34,2181,34,2184,34,2186,34,2189,34,2192,34,2195,34,2197,34,2199,34,2201,34,2204,34,2206,34,2209,34,2212,34,2216,34,2218,34,2220,34,2222,34,2225,34,2227,34,2230,34,2235,34,2241,34,2246,34,2250,34,2254,34,2258,34,2261,34,2265,34,2268,34,2272,34,2275,34,2279,34,2282],{"viewBox":2043,"role":28,"ariaLabelledBy":2044,"xmlns":32,"style":2047},"105 0 570 304",[2045,2046],"group-contents-t","group-contents-d","width:100%;max-width:570px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,2049,2050],{"id":2045},"The contents sheet at the front of the workbook",[40,2052,2053],{"id":2046},"The first sheet lists every group sheet as a clickable link together with its row count and total amount, largest group first, so a reader can jump to any tab without scrolling through the tab bar.",[44,2055],{"x":2056,"y":47,"width":2057,"height":2058,"fill":50},"105","570","304",[44,2060],{"x":2061,"y":87,"width":2062,"height":2063,"rx":2064,"fill":993,"stroke":994,"style":1031},"208.3","343.4","30.0","8",[44,2066],{"x":2061,"y":2067,"width":2062,"height":2068,"rx":47,"fill":993,"stroke":994,"style":2069},"38.0","8.0","stroke-width:0px",[62,2071,2073],{"transform":2072},"translate(218.3,23.0) scale(0.667)",[66,2074],{"d":2075,"fill":69,"stroke":129,"style":2076},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[73,2078,2081],{"x":2079,"y":1012,"style":2080},"240.3","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","by_region.xlsx",[44,2083],{"x":2061,"y":2084,"width":991,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"46.0",[44,2086],{"x":2079,"y":2084,"width":2087,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"148.3",[73,2089,1005],{"x":2090,"y":2091,"style":1004},"314.5","63.0",[44,2093],{"x":2094,"y":2084,"width":2095,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"388.6","57.8",[73,2097,2099],{"x":2098,"y":2091,"style":1004},"417.5","B",[44,2101],{"x":2102,"y":2084,"width":2103,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"446.4","105.3",[73,2105,2107],{"x":2106,"y":2091,"style":1004},"499.0","C",[44,2109],{"x":2061,"y":2110,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"70.0",[73,2112,1014],{"x":2113,"y":1143,"style":1004},"224.3",[44,2115],{"x":2079,"y":2110,"width":2087,"height":1009,"rx":47,"fill":58,"stroke":59,"style":1031},[73,2117,2119],{"x":2090,"y":1143,"style":2118},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Sheet",[44,2121],{"x":2094,"y":2110,"width":2095,"height":1009,"rx":47,"fill":58,"stroke":59,"style":1031},[73,2123,2124],{"x":2098,"y":1143,"style":2118},"Rows",[44,2126],{"x":2102,"y":2110,"width":2103,"height":1009,"rx":47,"fill":58,"stroke":59,"style":1031},[73,2128,2129],{"x":2106,"y":1143,"style":2118},"Total amount",[44,2131],{"x":2061,"y":2132,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"98.0",[73,2134,1026],{"x":2113,"y":2135,"style":1004},"117.0",[44,2137],{"x":2079,"y":2132,"width":2087,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2139,134],{"x":2090,"y":2135,"style":2118},[44,2141],{"x":2094,"y":2132,"width":2095,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2143,446],{"x":2098,"y":2135,"style":1044},[44,2145],{"x":2102,"y":2132,"width":2103,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2147,2148],{"x":2106,"y":2135,"style":1044},"211,889",[44,2150],{"x":2061,"y":2151,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"126.0",[73,2153,985],{"x":2113,"y":2154,"style":1004},"145.0",[44,2156],{"x":2079,"y":2151,"width":2087,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2158,156],{"x":2090,"y":2154,"style":2118},[44,2160],{"x":2094,"y":2151,"width":2095,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2162,2163],{"x":2098,"y":2154,"style":1044},"84",[44,2165],{"x":2102,"y":2151,"width":2103,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2167,2168],{"x":2106,"y":2154,"style":1044},"197,910",[44,2170],{"x":2061,"y":2171,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"154.0",[73,2173,46],{"x":2113,"y":2174,"style":1004},"173.0",[44,2176],{"x":2079,"y":2171,"width":2087,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2178,178],{"x":2090,"y":2174,"style":2118},[44,2180],{"x":2094,"y":2171,"width":2095,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2182,2183],{"x":2098,"y":2174,"style":1044},"79",[44,2185],{"x":2102,"y":2171,"width":2103,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2187,2188],{"x":2106,"y":2174,"style":1044},"194,474",[44,2190],{"x":2061,"y":2191,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"182.0",[73,2193,1061],{"x":2113,"y":2194,"style":1004},"201.0",[44,2196],{"x":2079,"y":2191,"width":2087,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2198,200],{"x":2090,"y":2194,"style":2118},[44,2200],{"x":2094,"y":2191,"width":2095,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2202,2203],{"x":2098,"y":2194,"style":1044},"76",[44,2205],{"x":2102,"y":2191,"width":2103,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2207,2208],{"x":2106,"y":2194,"style":1044},"186,307",[44,2210],{"x":2061,"y":2211,"width":991,"height":1009,"rx":47,"fill":993,"stroke":994,"style":995},"210.0",[73,2213,2215],{"x":2113,"y":2214,"style":1004},"229.0","6",[44,2217],{"x":2079,"y":2211,"width":2087,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2219,222],{"x":2090,"y":2214,"style":2118},[44,2221],{"x":2094,"y":2211,"width":2095,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2223,2224],{"x":2098,"y":2214,"style":1044},"71",[44,2226],{"x":2102,"y":2211,"width":2103,"height":1009,"rx":47,"fill":50,"stroke":994,"style":995},[73,2228,2229],{"x":2106,"y":2214,"style":1044},"169,312",[44,2231],{"x":2232,"y":2233,"width":2234,"height":992,"rx":47,"fill":50,"stroke":994,"style":995},"216.3","238.0","78.2",[256,2236],{"x1":2237,"y1":2238,"x2":2239,"y2":2238,"stroke":122,"style":2240},"218.3","261.0","292.5","stroke-width:3px",[73,2242,104],{"x":2243,"y":2244,"style":2245},"255.4","254.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[44,2247],{"x":2248,"y":2233,"width":2249,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"296.5","56.0",[73,2251,134],{"x":2252,"y":2244,"style":2253},"324.5","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[44,2255],{"x":2256,"y":2233,"width":2257,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"354.4","53.3",[73,2259,156],{"x":2260,"y":2244,"style":2253},"381.1",[44,2262],{"x":2263,"y":2233,"width":2264,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"409.7","139.4",[73,2266,178],{"x":2267,"y":2244,"style":2253},"479.4",[44,2269],{"x":2270,"y":2233,"width":2271,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"551.1","48.4",[73,2273,200],{"x":2274,"y":2244,"style":2253},"575.2",[44,2276],{"x":2277,"y":2233,"width":2278,"height":992,"rx":47,"fill":993,"stroke":994,"style":995},"601.4","57.2",[73,2280,222],{"x":2281,"y":2244,"style":2253},"630.0",[73,2283,2285],{"x":235,"y":2284,"style":237},"284.0","sheet names in column A are internal hyperlinks; the tab bar follows the same order",[240,2287,2289],{"id":2288},"common-pitfalls","Common pitfalls",[2291,2292,2293,2301,2311,2317,2330],"ul",{},[2294,2295,2296,2300],"li",{},[2297,2298,2299],"strong",{},"Name collisions overwrite silently."," Two keys that clean to the same name write into one sheet. De-duplicate names before writing and assert the total row count afterwards.",[2294,2302,2303,2306,2307,2310],{},[2297,2304,2305],{},"openpyxl is more forgiving than Excel."," With the openpyxl engine, a 40-character name produces only a warning and a file Excel may refuse to open, and a case-duplicate is renamed to ",[14,2308,2309],{},"North1"," behind your back. Clean names yourself whichever engine you use.",[2294,2312,2313,2316],{},[2297,2314,2315],{},"Too many tabs."," A sheet per customer for 600 customers produces a workbook nobody can navigate. Above a few dozen groups, prefer one sheet with a filter or one file per group.",[2294,2318,2319,2322,2323,2326,2327,23],{},[2297,2320,2321],{},"Reserved names."," Excel reserves ",[14,2324,2325],{},"History"," for its change-tracking feature and will not accept a sheet with that name, even though both Python writers do. Treat it like an invalid character and append a suffix, for example ",[14,2328,2329],{},"History (data)",[2294,2331,2332,2335,2336,2339],{},[2297,2333,2334],{},"Empty groups."," A categorical column can yield empty groups with ",[14,2337,2338],{},"observed=False",". Skip groups with no rows, or they become empty sheets with just a header.",[240,2341,2343],{"id":2342},"performance-and-scale-notes","Performance and scale notes",[10,2345,2346,2347,2350,2351,2354,2355,23],{},"Filtering the full frame once per group, as in the examples, is fine for a handful of groups; with hundreds, iterate ",[14,2348,2349],{},"orders.groupby(\"region\", sort=False)"," once instead, which splits the frame in a single pass. Writing is dominated by the total number of cells, not the number of sheets, so splitting a frame into sheets costs about the same as writing it once. For groups that should become separate ",[1794,2352,2353],{},"files"," for different recipients, the same loop writes one workbook per key — see ",[19,2356,2358],{"href":2357},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsplit-one-excel-sheet-into-multiple-files-by-value\u002F","Split One Excel Sheet into Multiple Files by Column Value",[240,2360,2362],{"id":2361},"conclusion","Conclusion",[10,2364,2365,2366,2368,2369,2371],{},"One ",[14,2367,16],{}," loop through one ",[14,2370,510],{}," produces a sheet per group. Around it, clean every key into a valid sheet name, de-duplicate case-insensitively before writing so no group overwrites another, sort the tabs on purpose, add a contents sheet with links and counts, and format each sheet inside the loop. Finish by checking that the rows across all sheets add up to the source.",[240,2373,2375],{"id":2374},"frequently-asked-questions","Frequently asked questions",[10,2377,2378,2381,2382,2384],{},[2297,2379,2380],{},"What characters are not allowed in Excel sheet names?","\nThe characters ",[254,2383],{}," : * ? \u002F and backslash are not allowed, a name cannot be longer than 31 characters, and it cannot start or end with an apostrophe. Names are compared without regard to case, so \"North\" and \"north\" count as duplicates.",[10,2386,2387,2390],{},[2297,2388,2389],{},"Why did one of my groups disappear from the workbook?","\nTwo group keys produced the same sheet name after cleaning or truncation, and pandas wrote the second group into the sheet it had already created. Check that the cleaned names are unique before writing, as shown in this guide.",[10,2392,2393,2396],{},[2297,2394,2395],{},"How do I put the sheets in a particular order?","\nSheets appear in the order you write them. Sort the group keys the way you want before the loop — alphabetically, by size, or by a custom list — rather than relying on groupby's default sort.",[10,2398,2399,2402],{},[2297,2400,2401],{},"Should I use one sheet per group or one file per group?","\nOne workbook with a sheet per group suits a single reader who compares groups. One file per group suits distribution, where each recipient should see only their own data.",[240,2404,2406],{"id":2405},"related","Related",[2291,2408,2409,2415,2422,2429],{},[2294,2410,2411,2412,2414],{},"Up one level: ",[19,2413,22],{"href":21}," — reading and writing multi-sheet workbooks.",[2294,2416,2417,2421],{},[19,2418,2420],{"href":2419},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File"," — the writer patterns underneath.",[2294,2423,2424,2428],{},[19,2425,2427],{"href":2426},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-excel-report-per-region-in-a-loop\u002F","Generate One Excel Report per Region in a Loop"," — templated files instead of sheets.",[2294,2430,2431,2435],{},[19,2432,2434],{"href":2433},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Frename-reorder-and-delete-excel-sheets-with-openpyxl\u002F","Rename, Reorder and Delete Excel Sheets with openpyxl"," — tidying tabs after the fact.",[2437,2438,2439],"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 .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":250,"searchDepth":297,"depth":297,"links":2441},[2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452],{"id":242,"depth":297,"text":243},{"id":503,"depth":297,"text":504},{"id":639,"depth":297,"text":640},{"id":1155,"depth":297,"text":1156},{"id":1479,"depth":297,"text":1480},{"id":1788,"depth":297,"text":1789},{"id":2288,"depth":297,"text":2289},{"id":2342,"depth":297,"text":2343},{"id":2361,"depth":297,"text":2362},{"id":2374,"depth":297,"text":2375},{"id":2405,"depth":297,"text":2406},"2026-09-23","Split one DataFrame into one worksheet per region, customer or month with groupby and ExcelWriter — with safe sheet names, a contents sheet, consistent formatting, and the silent overwrite trap when two groups map to the same name.","md",[2457,2459,2461,2463],{"q":2380,"a":2458},"The characters [ ] : * ? \u002F and backslash are not allowed, a name cannot be longer than 31 characters, and it cannot start or end with an apostrophe. Names are compared without regard to case, so \"North\" and \"north\" count as duplicates.",{"q":2389,"a":2460},"Two group keys produced the same sheet name after cleaning or truncation, and pandas wrote the second group into the sheet it had already created. Check that the cleaned names are unique before writing, as shown in this guide.",{"q":2395,"a":2462},"Sheets appear in the order you write them. Sort the group keys the way you want before the loop — alphabetically, by size, or by a custom list — rather than relying on groupby's default sort.",{"q":2401,"a":2464},"One workbook with a sheet per group suits a single reader who compares groups. One file per group suits distribution, where each recipient should see only their own data.","create a sheet for each group in a dataframe",null,{"breadcrumb":2468},[2469,2472,2475],{"name":2470,"item":2471},"Home","\u002F",{"name":2473,"item":2474},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":22,"item":21},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe",{"title":2478,"description":2479},"One Excel Sheet per Group from a pandas DataFrame","Write a separate worksheet for each group of a DataFrame using groupby and pd.ExcelWriter — sanitising and de-duplicating sheet names, ordering tabs, adding a summary sheet, and formatting every sheet.","create-a-sheet-for-each-group-in-a-dataframe","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe\u002Findex",[2483,2484,2485],"sheets-and-files","writing-excel","pandas","how-to","lCpRuDaDvsQ8TeZbVEQUM7W1A19nJzEfIw_BpE6Z2_4",[2489,2493],{"title":2490,"path":2491,"stem":2492,"children":-1},"Copy a Sheet Between Excel Workbooks with Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcopy-a-sheet-between-excel-workbooks-with-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcopy-a-sheet-between-excel-workbooks-with-python\u002Findex",{"title":2494,"path":2495,"stem":2496,"children":-1},"List Every Sheet with Its Size and Visibility","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Flist-every-sheet-with-its-size-and-visibility","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Flist-every-sheet-with-its-size-and-visibility\u002Findex",[2498,2500],{"path":2499,"title":2473},"\u002Fgetting-started-with-python-excel-automation",{"path":2501,"title":22},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python",[],[2504,2507,2510,2513,2516,2519],{"path":2505,"title":2506,"kind":2486},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fconsolidate-timesheets-from-many-workbooks","Consolidate Timesheets from Many Workbooks with Python",{"path":2508,"title":2509,"kind":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fcombine-sheets-with-different-layouts-into-one-table","Combine Sheets with Different Layouts into One Table",{"path":2511,"title":2512,"kind":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fappend-new-rows-to-a-master-excel-file-without-duplicates","Append New Rows to a Master Excel File Without Duplicates",{"path":2514,"title":2515,"kind":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fconcatenate-excel-sheets-with-different-columns","Concatenate Excel Sheets with Different Columns",{"path":2517,"title":2518,"kind":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-excel-files-with-different-column-names","Merge Excel Files with Different Column Names in pandas",{"path":2520,"title":2521,"kind":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-more-than-two-excel-files-on-a-key-column","Merge More Than Two Excel Files on a Key Column",1790972076117]