[{"data":1,"prerenderedAt":1390},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted":1381},{"id":4,"title":5,"body":6,"description":1372,"extension":1373,"meta":1374,"navigation":111,"path":1375,"seo":1376,"stem":1379,"__hash__":1380},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted\u002Findex.md","Export a Pandas Pivot Table to Excel (Formatted)",{"type":7,"value":8,"toc":1360},"minimark",[9,32,37,65,68,72,83,413,436,440,468,593,606,610,669,679,683,686,1052,1065,1069,1194,1205,1209,1220,1224,1244,1262,1277,1291,1300,1304,1322,1326,1356],[10,11,12,13,17,18,21,22,25,26,31],"p",{},"To export a pandas pivot table to a clean, formatted Excel file, build the table with ",[14,15,16],"code",{},"pivot_table()",", flatten any ",[14,19,20],{},"MultiIndex"," on the columns and index so it writes as a flat grid, save it with ",[14,23,24],{},"to_excel()",", then reopen the file with openpyxl to apply a styled header and number formats. The flattening step is what keeps the output from exporting as messy stacked headers. This page is part of the ",[27,28,30],"a",{"href":29},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data"," cluster.",[33,34,36],"h2",{"id":35},"prerequisites","Prerequisites",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-bash shiki shiki-themes github-light github-dark","pip install pandas openpyxl\n","bash","",[14,45,46],{"__ignoreMap":43},[47,48,51,55,59,62],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"sScJk","pip",[47,56,58],{"class":57},"sZZnC"," install",[47,60,61],{"class":57}," pandas",[47,63,64],{"class":57}," openpyxl\n",[10,66,67],{},"Every block below runs in order against the sample data built first.",[33,69,71],{"id":70},"build-the-pivot-table-with-a-grand-total","Build the pivot table with a grand total",[10,73,74,75,78,79,82],{},"Set ",[14,76,77],{},"margins=True"," to append a grand-total row and column. ",[14,80,81],{},"margins_name"," controls the label so it reads cleanly:",[38,84,88],{"className":85,"code":86,"language":87,"meta":43,"style":43},"language-python shiki shiki-themes github-light github-dark","import pandas as pd\n\ndf = pd.DataFrame({\n    \"Region\": [\"East\", \"East\", \"West\", \"West\", \"East\", \"West\"],\n    \"Product\": [\"Widget\", \"Gadget\", \"Widget\", \"Gadget\", \"Widget\", \"Widget\"],\n    \"Quarter\": [\"Q1\", \"Q1\", \"Q1\", \"Q2\", \"Q2\", \"Q2\"],\n    \"Sales\": [1200.5, 800.0, 950.25, 1100.0, 1350.75, 700.0],\n})\n\npivot = pd.pivot_table(\n    df,\n    index=[\"Region\", \"Product\"],\n    columns=\"Quarter\",\n    values=\"Sales\",\n    aggfunc=\"sum\",\n    fill_value=0,\n    margins=True,\n    margins_name=\"Total\",\n)\nprint(pivot)\n","python",[14,89,90,106,113,125,162,196,230,269,275,280,291,297,319,333,346,359,372,385,398,404],{"__ignoreMap":43},[47,91,92,96,100,103],{"class":49,"line":50},[47,93,95],{"class":94},"szBVR","import",[47,97,99],{"class":98},"sVt8B"," pandas ",[47,101,102],{"class":94},"as",[47,104,105],{"class":98}," pd\n",[47,107,109],{"class":49,"line":108},2,[47,110,112],{"emptyLinePlaceholder":111},true,"\n",[47,114,116,119,122],{"class":49,"line":115},3,[47,117,118],{"class":98},"df ",[47,120,121],{"class":94},"=",[47,123,124],{"class":98}," pd.DataFrame({\n",[47,126,128,131,134,137,140,142,144,147,149,151,153,155,157,159],{"class":49,"line":127},4,[47,129,130],{"class":57},"    \"Region\"",[47,132,133],{"class":98},": [",[47,135,136],{"class":57},"\"East\"",[47,138,139],{"class":98},", ",[47,141,136],{"class":57},[47,143,139],{"class":98},[47,145,146],{"class":57},"\"West\"",[47,148,139],{"class":98},[47,150,146],{"class":57},[47,152,139],{"class":98},[47,154,136],{"class":57},[47,156,139],{"class":98},[47,158,146],{"class":57},[47,160,161],{"class":98},"],\n",[47,163,165,168,170,173,175,178,180,182,184,186,188,190,192,194],{"class":49,"line":164},5,[47,166,167],{"class":57},"    \"Product\"",[47,169,133],{"class":98},[47,171,172],{"class":57},"\"Widget\"",[47,174,139],{"class":98},[47,176,177],{"class":57},"\"Gadget\"",[47,179,139],{"class":98},[47,181,172],{"class":57},[47,183,139],{"class":98},[47,185,177],{"class":57},[47,187,139],{"class":98},[47,189,172],{"class":57},[47,191,139],{"class":98},[47,193,172],{"class":57},[47,195,161],{"class":98},[47,197,199,202,204,207,209,211,213,215,217,220,222,224,226,228],{"class":49,"line":198},6,[47,200,201],{"class":57},"    \"Quarter\"",[47,203,133],{"class":98},[47,205,206],{"class":57},"\"Q1\"",[47,208,139],{"class":98},[47,210,206],{"class":57},[47,212,139],{"class":98},[47,214,206],{"class":57},[47,216,139],{"class":98},[47,218,219],{"class":57},"\"Q2\"",[47,221,139],{"class":98},[47,223,219],{"class":57},[47,225,139],{"class":98},[47,227,219],{"class":57},[47,229,161],{"class":98},[47,231,233,236,238,242,244,247,249,252,254,257,259,262,264,267],{"class":49,"line":232},7,[47,234,235],{"class":57},"    \"Sales\"",[47,237,133],{"class":98},[47,239,241],{"class":240},"sj4cs","1200.5",[47,243,139],{"class":98},[47,245,246],{"class":240},"800.0",[47,248,139],{"class":98},[47,250,251],{"class":240},"950.25",[47,253,139],{"class":98},[47,255,256],{"class":240},"1100.0",[47,258,139],{"class":98},[47,260,261],{"class":240},"1350.75",[47,263,139],{"class":98},[47,265,266],{"class":240},"700.0",[47,268,161],{"class":98},[47,270,272],{"class":49,"line":271},8,[47,273,274],{"class":98},"})\n",[47,276,278],{"class":49,"line":277},9,[47,279,112],{"emptyLinePlaceholder":111},[47,281,283,286,288],{"class":49,"line":282},10,[47,284,285],{"class":98},"pivot ",[47,287,121],{"class":94},[47,289,290],{"class":98}," pd.pivot_table(\n",[47,292,294],{"class":49,"line":293},11,[47,295,296],{"class":98},"    df,\n",[47,298,300,304,306,309,312,314,317],{"class":49,"line":299},12,[47,301,303],{"class":302},"s4XuR","    index",[47,305,121],{"class":94},[47,307,308],{"class":98},"[",[47,310,311],{"class":57},"\"Region\"",[47,313,139],{"class":98},[47,315,316],{"class":57},"\"Product\"",[47,318,161],{"class":98},[47,320,322,325,327,330],{"class":49,"line":321},13,[47,323,324],{"class":302},"    columns",[47,326,121],{"class":94},[47,328,329],{"class":57},"\"Quarter\"",[47,331,332],{"class":98},",\n",[47,334,336,339,341,344],{"class":49,"line":335},14,[47,337,338],{"class":302},"    values",[47,340,121],{"class":94},[47,342,343],{"class":57},"\"Sales\"",[47,345,332],{"class":98},[47,347,349,352,354,357],{"class":49,"line":348},15,[47,350,351],{"class":302},"    aggfunc",[47,353,121],{"class":94},[47,355,356],{"class":57},"\"sum\"",[47,358,332],{"class":98},[47,360,362,365,367,370],{"class":49,"line":361},16,[47,363,364],{"class":302},"    fill_value",[47,366,121],{"class":94},[47,368,369],{"class":240},"0",[47,371,332],{"class":98},[47,373,375,378,380,383],{"class":49,"line":374},17,[47,376,377],{"class":302},"    margins",[47,379,121],{"class":94},[47,381,382],{"class":240},"True",[47,384,332],{"class":98},[47,386,388,391,393,396],{"class":49,"line":387},18,[47,389,390],{"class":302},"    margins_name",[47,392,121],{"class":94},[47,394,395],{"class":57},"\"Total\"",[47,397,332],{"class":98},[47,399,401],{"class":49,"line":400},19,[47,402,403],{"class":98},")\n",[47,405,407,410],{"class":49,"line":406},20,[47,408,409],{"class":240},"print",[47,411,412],{"class":98},"(pivot)\n",[10,414,415,416,418,419,139,422,425,426,139,429,139,432,435],{},"This produces a ",[14,417,20],{}," on the rows (",[14,420,421],{},"Region",[14,423,424],{},"Product",") and named columns (",[14,427,428],{},"Q1",[14,430,431],{},"Q2",[14,433,434],{},"Total","). Written as-is, the row index spans two header levels and the column index name leaks into the corner cell.",[33,437,439],{"id":438},"flatten-the-index-and-columns","Flatten the index and columns",[10,441,442,445,446,448,449,453,454,456,457,460,461,463,464,467],{},[14,443,444],{},"reset_index()"," turns the row ",[14,447,20],{}," into regular columns. If the ",[450,451,452],"strong",{},"columns"," are also a ",[14,455,20],{}," (which happens when you pass multiple ",[14,458,459],{},"values"," or stacked ",[14,462,452],{},"), flatten them with ",[14,465,466],{},"to_flat_index()"," and join the tuples into readable strings:",[38,469,471],{"className":85,"code":470,"language":87,"meta":43,"style":43},"flat = pivot.reset_index()\n\n# Flatten MultiIndex columns if present\nif isinstance(flat.columns, pd.MultiIndex):\n    flat.columns = [\"_\".join(str(c) for c in col if c != \"\").strip(\"_\")\n                    for col in flat.columns.to_flat_index()]\n\n# Drop the leftover \"Quarter\" columns-axis name\nflat.columns.name = None\nprint(flat)\n",[14,472,473,483,487,493,504,555,567,571,576,586],{"__ignoreMap":43},[47,474,475,478,480],{"class":49,"line":50},[47,476,477],{"class":98},"flat ",[47,479,121],{"class":94},[47,481,482],{"class":98}," pivot.reset_index()\n",[47,484,485],{"class":49,"line":108},[47,486,112],{"emptyLinePlaceholder":111},[47,488,489],{"class":49,"line":115},[47,490,492],{"class":491},"sJ8bj","# Flatten MultiIndex columns if present\n",[47,494,495,498,501],{"class":49,"line":127},[47,496,497],{"class":94},"if",[47,499,500],{"class":240}," isinstance",[47,502,503],{"class":98},"(flat.columns, pd.MultiIndex):\n",[47,505,506,509,511,514,517,520,523,526,529,532,535,538,540,542,545,548,551,553],{"class":49,"line":164},[47,507,508],{"class":98},"    flat.columns ",[47,510,121],{"class":94},[47,512,513],{"class":98}," [",[47,515,516],{"class":57},"\"_\"",[47,518,519],{"class":98},".join(",[47,521,522],{"class":240},"str",[47,524,525],{"class":98},"(c) ",[47,527,528],{"class":94},"for",[47,530,531],{"class":98}," c ",[47,533,534],{"class":94},"in",[47,536,537],{"class":98}," col ",[47,539,497],{"class":94},[47,541,531],{"class":98},[47,543,544],{"class":94},"!=",[47,546,547],{"class":57}," \"\"",[47,549,550],{"class":98},").strip(",[47,552,516],{"class":57},[47,554,403],{"class":98},[47,556,557,560,562,564],{"class":49,"line":198},[47,558,559],{"class":94},"                    for",[47,561,537],{"class":98},[47,563,534],{"class":94},[47,565,566],{"class":98}," flat.columns.to_flat_index()]\n",[47,568,569],{"class":49,"line":232},[47,570,112],{"emptyLinePlaceholder":111},[47,572,573],{"class":49,"line":271},[47,574,575],{"class":491},"# Drop the leftover \"Quarter\" columns-axis name\n",[47,577,578,581,583],{"class":49,"line":277},[47,579,580],{"class":98},"flat.columns.name ",[47,582,121],{"class":94},[47,584,585],{"class":240}," None\n",[47,587,588,590],{"class":49,"line":282},[47,589,409],{"class":240},[47,591,592],{"class":98},"(flat)\n",[10,594,595,596,598,599,602,603,605],{},"For this example the columns are single-level, so ",[14,597,444],{}," plus clearing ",[14,600,601],{},"columns.name"," is enough. Keep the ",[14,604,20],{}," branch in your pipeline so it stays correct when the shape changes.",[33,607,609],{"id":608},"write-the-flattened-table-to-excel","Write the flattened table to Excel",[38,611,613],{"className":85,"code":612,"language":87,"meta":43,"style":43},"flat.to_excel(\"pivot_report.xlsx\", index=False, sheet_name=\"Summary\",\n              engine=\"openpyxl\")\nprint(\"Wrote pivot_report.xlsx\")\n",[14,614,615,645,657],{"__ignoreMap":43},[47,616,617,620,623,625,628,630,633,635,638,640,643],{"class":49,"line":50},[47,618,619],{"class":98},"flat.to_excel(",[47,621,622],{"class":57},"\"pivot_report.xlsx\"",[47,624,139],{"class":98},[47,626,627],{"class":302},"index",[47,629,121],{"class":94},[47,631,632],{"class":240},"False",[47,634,139],{"class":98},[47,636,637],{"class":302},"sheet_name",[47,639,121],{"class":94},[47,641,642],{"class":57},"\"Summary\"",[47,644,332],{"class":98},[47,646,647,650,652,655],{"class":49,"line":108},[47,648,649],{"class":302},"              engine",[47,651,121],{"class":94},[47,653,654],{"class":57},"\"openpyxl\"",[47,656,403],{"class":98},[47,658,659,661,664,667],{"class":49,"line":115},[47,660,409],{"class":240},[47,662,663],{"class":98},"(",[47,665,666],{"class":57},"\"Wrote pivot_report.xlsx\"",[47,668,403],{"class":98},[10,670,671,672,675,676,678],{},"Pass ",[14,673,674],{},"index=False"," because the index is already a real column after ",[14,677,444],{}," — otherwise you get a duplicate, unnamed first column.",[33,680,682],{"id":681},"style-the-header-and-apply-number-formats","Style the header and apply number formats",[10,684,685],{},"pandas writes values but not formatting. Reopen the saved file with openpyxl to bold the header, fill it, and apply a currency number format to the numeric columns:",[38,687,689],{"className":85,"code":688,"language":87,"meta":43,"style":43},"from openpyxl import load_workbook\nfrom openpyxl.styles import Font, PatternFill, Alignment\n\nwb = load_workbook(\"pivot_report.xlsx\")\nws = wb[\"Summary\"]\n\nheader_fill = PatternFill(\"solid\", fgColor=\"1F4E78\")\nheader_font = Font(bold=True, color=\"FFFFFF\")\n\nfor cell in ws[1]:\n    cell.fill = header_fill\n    cell.font = header_font\n    cell.alignment = Alignment(horizontal=\"center\")\n\n# Currency format on every numeric column (skip the two text key columns)\nfor row in ws.iter_rows(min_row=2, min_col=3):\n    for cell in row:\n        cell.number_format = '#,##0.00'\n\n# Bold the grand-total row (last row)\nfor cell in ws[ws.max_row]:\n    cell.font = Font(bold=True)\n\nws.column_dimensions[\"A\"].width = 12\nws.column_dimensions[\"B\"].width = 12\n\nwb.save(\"pivot_report.xlsx\")\nprint(f\"Formatted {ws.max_row - 1} data rows\")\n",[14,690,691,704,716,720,734,749,753,778,807,811,829,839,849,869,873,878,911,923,933,937,942,954,971,976,993,1007,1012,1022],{"__ignoreMap":43},[47,692,693,696,699,701],{"class":49,"line":50},[47,694,695],{"class":94},"from",[47,697,698],{"class":98}," openpyxl ",[47,700,95],{"class":94},[47,702,703],{"class":98}," load_workbook\n",[47,705,706,708,711,713],{"class":49,"line":108},[47,707,695],{"class":94},[47,709,710],{"class":98}," openpyxl.styles ",[47,712,95],{"class":94},[47,714,715],{"class":98}," Font, PatternFill, Alignment\n",[47,717,718],{"class":49,"line":115},[47,719,112],{"emptyLinePlaceholder":111},[47,721,722,725,727,730,732],{"class":49,"line":127},[47,723,724],{"class":98},"wb ",[47,726,121],{"class":94},[47,728,729],{"class":98}," load_workbook(",[47,731,622],{"class":57},[47,733,403],{"class":98},[47,735,736,739,741,744,746],{"class":49,"line":164},[47,737,738],{"class":98},"ws ",[47,740,121],{"class":94},[47,742,743],{"class":98}," wb[",[47,745,642],{"class":57},[47,747,748],{"class":98},"]\n",[47,750,751],{"class":49,"line":198},[47,752,112],{"emptyLinePlaceholder":111},[47,754,755,758,760,763,766,768,771,773,776],{"class":49,"line":232},[47,756,757],{"class":98},"header_fill ",[47,759,121],{"class":94},[47,761,762],{"class":98}," PatternFill(",[47,764,765],{"class":57},"\"solid\"",[47,767,139],{"class":98},[47,769,770],{"class":302},"fgColor",[47,772,121],{"class":94},[47,774,775],{"class":57},"\"1F4E78\"",[47,777,403],{"class":98},[47,779,780,783,785,788,791,793,795,797,800,802,805],{"class":49,"line":271},[47,781,782],{"class":98},"header_font ",[47,784,121],{"class":94},[47,786,787],{"class":98}," Font(",[47,789,790],{"class":302},"bold",[47,792,121],{"class":94},[47,794,382],{"class":240},[47,796,139],{"class":98},[47,798,799],{"class":302},"color",[47,801,121],{"class":94},[47,803,804],{"class":57},"\"FFFFFF\"",[47,806,403],{"class":98},[47,808,809],{"class":49,"line":277},[47,810,112],{"emptyLinePlaceholder":111},[47,812,813,815,818,820,823,826],{"class":49,"line":282},[47,814,528],{"class":94},[47,816,817],{"class":98}," cell ",[47,819,534],{"class":94},[47,821,822],{"class":98}," ws[",[47,824,825],{"class":240},"1",[47,827,828],{"class":98},"]:\n",[47,830,831,834,836],{"class":49,"line":293},[47,832,833],{"class":98},"    cell.fill ",[47,835,121],{"class":94},[47,837,838],{"class":98}," header_fill\n",[47,840,841,844,846],{"class":49,"line":299},[47,842,843],{"class":98},"    cell.font ",[47,845,121],{"class":94},[47,847,848],{"class":98}," header_font\n",[47,850,851,854,856,859,862,864,867],{"class":49,"line":321},[47,852,853],{"class":98},"    cell.alignment ",[47,855,121],{"class":94},[47,857,858],{"class":98}," Alignment(",[47,860,861],{"class":302},"horizontal",[47,863,121],{"class":94},[47,865,866],{"class":57},"\"center\"",[47,868,403],{"class":98},[47,870,871],{"class":49,"line":335},[47,872,112],{"emptyLinePlaceholder":111},[47,874,875],{"class":49,"line":348},[47,876,877],{"class":491},"# Currency format on every numeric column (skip the two text key columns)\n",[47,879,880,882,885,887,890,893,895,898,900,903,905,908],{"class":49,"line":361},[47,881,528],{"class":94},[47,883,884],{"class":98}," row ",[47,886,534],{"class":94},[47,888,889],{"class":98}," ws.iter_rows(",[47,891,892],{"class":302},"min_row",[47,894,121],{"class":94},[47,896,897],{"class":240},"2",[47,899,139],{"class":98},[47,901,902],{"class":302},"min_col",[47,904,121],{"class":94},[47,906,907],{"class":240},"3",[47,909,910],{"class":98},"):\n",[47,912,913,916,918,920],{"class":49,"line":374},[47,914,915],{"class":94},"    for",[47,917,817],{"class":98},[47,919,534],{"class":94},[47,921,922],{"class":98}," row:\n",[47,924,925,928,930],{"class":49,"line":387},[47,926,927],{"class":98},"        cell.number_format ",[47,929,121],{"class":94},[47,931,932],{"class":57}," '#,##0.00'\n",[47,934,935],{"class":49,"line":400},[47,936,112],{"emptyLinePlaceholder":111},[47,938,939],{"class":49,"line":406},[47,940,941],{"class":491},"# Bold the grand-total row (last row)\n",[47,943,945,947,949,951],{"class":49,"line":944},21,[47,946,528],{"class":94},[47,948,817],{"class":98},[47,950,534],{"class":94},[47,952,953],{"class":98}," ws[ws.max_row]:\n",[47,955,957,959,961,963,965,967,969],{"class":49,"line":956},22,[47,958,843],{"class":98},[47,960,121],{"class":94},[47,962,787],{"class":98},[47,964,790],{"class":302},[47,966,121],{"class":94},[47,968,382],{"class":240},[47,970,403],{"class":98},[47,972,974],{"class":49,"line":973},23,[47,975,112],{"emptyLinePlaceholder":111},[47,977,979,982,985,988,990],{"class":49,"line":978},24,[47,980,981],{"class":98},"ws.column_dimensions[",[47,983,984],{"class":57},"\"A\"",[47,986,987],{"class":98},"].width ",[47,989,121],{"class":94},[47,991,992],{"class":240}," 12\n",[47,994,996,998,1001,1003,1005],{"class":49,"line":995},25,[47,997,981],{"class":98},[47,999,1000],{"class":57},"\"B\"",[47,1002,987],{"class":98},[47,1004,121],{"class":94},[47,1006,992],{"class":240},[47,1008,1010],{"class":49,"line":1009},26,[47,1011,112],{"emptyLinePlaceholder":111},[47,1013,1015,1018,1020],{"class":49,"line":1014},27,[47,1016,1017],{"class":98},"wb.save(",[47,1019,622],{"class":57},[47,1021,403],{"class":98},[47,1023,1025,1027,1029,1032,1035,1038,1041,1044,1047,1050],{"class":49,"line":1024},28,[47,1026,409],{"class":240},[47,1028,663],{"class":98},[47,1030,1031],{"class":94},"f",[47,1033,1034],{"class":57},"\"Formatted ",[47,1036,1037],{"class":240},"{",[47,1039,1040],{"class":98},"ws.max_row ",[47,1042,1043],{"class":94},"-",[47,1045,1046],{"class":240}," 1}",[47,1048,1049],{"class":57}," data rows\"",[47,1051,403],{"class":98},[10,1053,1054,1057,1058,1061,1062,1064],{},[14,1055,1056],{},"number_format"," is a cell-level string; ",[14,1059,1060],{},"'#,##0.00'"," gives thousands separators and two decimals. Adjusting ",[14,1063,902],{}," skips the text key columns so only the numbers get the currency format.",[33,1066,1068],{"id":1067},"common-pitfalls","Common pitfalls",[1070,1071,1072,1088],"table",{},[1073,1074,1075],"thead",{},[1076,1077,1078,1082,1085],"tr",{},[1079,1080,1081],"th",{},"Symptom",[1079,1083,1084],{},"Cause",[1079,1086,1087],{},"Fix",[1089,1090,1091,1108,1127,1144,1162,1177],"tbody",{},[1076,1092,1093,1097,1102],{},[1094,1095,1096],"td",{},"Stacked, half-empty header rows in Excel",[1094,1098,1099,1101],{},[14,1100,20],{}," columns written directly",[1094,1103,1104,1105,1107],{},"Flatten with ",[14,1106,466],{}," and join the tuples",[1076,1109,1110,1113,1119],{},[1094,1111,1112],{},"Empty extra column on the left",[1094,1114,1115,1116,1118],{},"Row ",[14,1117,20],{}," written as the index",[1094,1120,1121,1123,1124],{},[14,1122,444],{},", then ",[14,1125,1126],{},"to_excel(index=False)",[1076,1128,1129,1136,1139],{},[1094,1130,1131,1132,1135],{},"Stray ",[14,1133,1134],{},"Quarter"," label in a corner cell",[1094,1137,1138],{},"Columns-axis name survives the write",[1094,1140,1141],{},[14,1142,1143],{},"flat.columns.name = None",[1076,1145,1146,1152,1157],{},[1094,1147,1148,1149],{},"Grand total labeled ",[14,1150,1151],{},"All",[1094,1153,1154,1155],{},"Default ",[14,1156,81],{},[1094,1158,671,1159],{},[14,1160,1161],{},"margins_name=\"Total\"",[1076,1163,1164,1167,1174],{},[1094,1165,1166],{},"Formatting gone after editing in pandas",[1094,1168,1169,1170,1173],{},"Re-reading with ",[14,1171,1172],{},"read_excel"," strips styles",[1094,1175,1176],{},"Apply styles last, with openpyxl, and don't round-trip",[1076,1178,1179,1182,1188],{},[1094,1180,1181],{},"Index name lost after flatten",[1094,1183,1184,1187],{},[14,1185,1186],{},"reset_index"," consumes index names into columns",[1094,1189,1190,1191],{},"Read them from the new column headers, not ",[14,1192,1193],{},"df.index.name",[10,1195,1196,1197,1200,1201,1204],{},"The last row matters most: any time you read a formatted file back into pandas and re-write it, all openpyxl styling is discarded. Treat the openpyxl pass as the final step and never funnel a styled workbook back through ",[14,1198,1199],{},"pd.read_excel","\u002F",[14,1202,1203],{},"to_excel",".",[33,1206,1208],{"id":1207},"performance-and-scale-note","Performance and scale note",[10,1210,1211,1212,1215,1216,1219],{},"The pivot and flatten are vectorized and trivial even for large source frames; the openpyxl styling loop is the bottleneck because it touches each cell in Python. For pivots up to a few thousand rows this is instant. If you are formatting tens of thousands of rows, prefer the xlsxwriter engine with ",[14,1213,1214],{},"pd.ExcelWriter(path, engine=\"xlsxwriter\")"," and apply a single ",[14,1217,1218],{},"set_column(..., cell_format)"," per column instead of looping cells — column-level formats are far faster than per-cell assignment.",[33,1221,1223],{"id":1222},"frequently-asked-questions","Frequently asked questions",[10,1225,1226,1229,1230,1233,1234,1236,1237,1239,1240,1243],{},[450,1227,1228],{},"Why do my pivot columns export as messy stacked headers?"," Because ",[14,1231,1232],{},"pivot_table"," produced a ",[14,1235,20],{}," on the columns and ",[14,1238,1203],{}," writes each level as its own header row. Flatten with ",[14,1241,1242],{},"df.columns = df.columns.to_flat_index()"," and join the tuples into single strings before writing.",[10,1245,1246,1249,1250,1252,1253,1255,1256,1258,1259,1261],{},[450,1247,1248],{},"How do I add a grand-total row?"," Pass ",[14,1251,77],{}," to ",[14,1254,1232],{},". Use ",[14,1257,1161],{}," to rename the default ",[14,1260,1151],{}," label on both the total row and column.",[10,1263,1264,1267,1268,1270,1271,1273,1274,1204],{},[450,1265,1266],{},"Why is there a blank extra column on the left of my sheet?"," The row ",[14,1269,20],{}," was written as the spreadsheet index. Call ",[14,1272,444],{}," to turn it into real columns, then ",[14,1275,1276],{},"to_excel(..., index=False)",[10,1278,1279,1282,1283,1286,1287,1290],{},[450,1280,1281],{},"Can I format the pivot without reopening the file?"," Yes — write through ",[14,1284,1285],{},"pd.ExcelWriter"," with ",[14,1288,1289],{},"engine=\"xlsxwriter\""," and apply formats in the same context manager. The openpyxl reopen approach is simpler when the file already exists.",[10,1292,1293,1296,1297,1299],{},[450,1294,1295],{},"Why did my formatting disappear?"," You likely read the styled file back with ",[14,1298,1199],{}," and re-wrote it. pandas does not preserve cell styles, so make the openpyxl\u002Fxlsxwriter formatting your final step.",[33,1301,1303],{"id":1302},"conclusion","Conclusion",[10,1305,1306,1307,1309,1310,1286,1312,1314,1315,1318,1319,1321],{},"A clean formatted pivot export is four steps: pivot with ",[14,1308,77],{},", flatten the ",[14,1311,20],{},[14,1313,1186],{}," and ",[14,1316,1317],{},"to_flat_index",", write with ",[14,1320,674],{},", then style once with openpyxl. Keeping the flatten and style steps separate from any re-read is what guarantees a tidy, formatted result.",[33,1323,1325],{"id":1324},"where-to-go-next","Where to go next",[1327,1328,1329,1335,1342,1349],"ul",{},[1330,1331,1332,1334],"li",{},[27,1333,30],{"href":29}," — the parent cluster.",[1330,1336,1337,1341],{},[27,1338,1340],{"href":1339},"\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 fundamentals behind this export.",[1330,1343,1344,1348],{},[27,1345,1347],{"href":1346},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","Applying Number and Date Formats in Excel"," — deeper number-format strings for the styling step.",[1330,1350,1351,1355],{},[27,1352,1354],{"href":1353},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002F","Building Multi-Sheet Excel Dashboards"," — combine several formatted pivots into one workbook.",[1357,1358,1359],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":43,"searchDepth":108,"depth":108,"links":1361},[1362,1363,1364,1365,1366,1367,1368,1369,1370,1371],{"id":35,"depth":108,"text":36},{"id":70,"depth":108,"text":71},{"id":438,"depth":108,"text":439},{"id":608,"depth":108,"text":609},{"id":681,"depth":108,"text":682},{"id":1067,"depth":108,"text":1068},{"id":1207,"depth":108,"text":1208},{"id":1222,"depth":108,"text":1223},{"id":1302,"depth":108,"text":1303},{"id":1324,"depth":108,"text":1325},"Build a pandas pivot_table, flatten its MultiIndex, export with to_excel, then add a styled header, number formats, and a grand-total row with openpyxl.","md",{},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted",{"title":1377,"description":1378},"Export Pandas Pivot Table to Excel (Formatted)","Write a pandas pivot_table to a clean, formatted Excel file: flatten MultiIndex columns, add margins totals, then style headers and number formats with openpyxl.","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted\u002Findex","_C1uF3JO4P3qp4arVsKXvEjAJqHKB6mjff0aLGKXRWQ",[1382,1386],{"title":1383,"path":1384,"stem":1385,"children":-1},"Create a Pivot Table from Excel with Pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcreate-pivot-table-from-excel-with-pandas","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcreate-pivot-table-from-excel-with-pandas\u002Findex",{"title":1387,"path":1388,"stem":1389,"children":-1},"Handling Missing Data in Excel Reports with Pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports","advanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Findex",1781773160991]