[{"data":1,"prerenderedAt":2779},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Funpivot-a-wide-excel-sheet-with-pandas-melt":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Funpivot-a-wide-excel-sheet-with-pandas-melt":2770},{"id":4,"title":5,"body":6,"dateModified":2747,"datePublished":2747,"description":2748,"extension":2749,"faq":2750,"meta":2762,"navigation":290,"path":2763,"seo":2764,"slug":2766,"stem":2767,"type":2768,"__hash__":2769},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Funpivot-a-wide-excel-sheet-with-pandas-melt\u002Findex.md","Unpivot a Wide Excel Sheet with pandas melt",{"type":7,"value":8,"toc":2734},"minimark",[9,24,224,229,259,262,452,456,461,600,625,729,732,736,742,863,880,1079,1087,1091,1112,1262,1406,1431,1435,1502,1509,1679,1686,1690,1693,2021,2028,2035,2085,2089,2231,2235,2240,2247,2341,2347,2392,2398,2401,2611,2619,2623,2628,2632,2654,2663,2673,2685,2691,2695,2730],[10,11,12,13,17,18,23],"p",{},"Spreadsheets grow sideways. A report starts with one column per month, and two years later it has twenty-four value columns, a new one added every reporting cycle, and every formula and chart has to be rewritten each time. Analysis wants the opposite shape: one row per observation, with the period as a value rather than a column name. ",[14,15,16],"code",{},"pd.melt"," performs that reshape in one call — and the details that matter are which columns you name as identifiers, and turning the header text back into real dates. This guide covers both. It extends ",[19,20,22],"a",{"href":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data",".",[25,26,35,36,35,40,35,44,35,51,35,58,35,68,35,74,35,80,35,85,35,88,35,92,35,95,35,99,35,103,35,108,35,112,35,115,35,117,35,120,35,122,35,125,35,127,35,131,35,133,35,136,35,138,35,141,35,143,35,146,35,151,35,157,35,162,35,168,35,172,35,176,35,179,35,182,35,186,35,189,35,194,35,198,35,202,35,205,35,209,35,212,35,216,35,219],"svg",{"viewBox":27,"role":28,"ariaLabel":29,"ariaLabelledBy":30,"xmlns":33,"style":34},"0 0 800 254","img","A wide sheet with one column per month becoming a long frame with region, month and revenue columns, where each cell of the wide grid becomes one row.",[31,32],"melt-t","melt-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:800px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[37,38,39],"title",{"id":31},"Wide to long: every value cell becomes a row",[41,42,43],"desc",{"id":32},"On the left a wide sheet with a region column and three month columns, so two regions and three months occupy six value cells in a two by three grid. On the right the same data in long form: six rows, each carrying a region, a month and a revenue value. The month, which was a column name, is now a value in its own column, so adding a fourth month adds rows rather than changing the table's shape.",[45,46],"rect",{"x":47,"y":47,"width":48,"height":49,"fill":50},"0","800","254","#ffffff",[52,53,57],"text",{"x":54,"y":55,"style":56},"180","30","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","wide — one column per month",[45,59],{"x":60,"y":61,"width":62,"height":63,"rx":64,"fill":65,"stroke":66,"style":67},"20","44","82","28","5","#ebebfd","var(--brand,#5b5cf0)","stroke-width:2px",[52,69,73],{"x":70,"y":71,"style":72},"61","63","font-size:10px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","region",[45,75],{"x":76,"y":61,"width":77,"height":63,"rx":64,"fill":78,"stroke":79,"style":67},"106","76","#fdefd8","var(--gold,#b4740a)",[52,81,84],{"x":82,"y":71,"style":83},"144","font-size:10px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","2026-06",[45,86],{"x":87,"y":61,"width":77,"height":63,"rx":64,"fill":78,"stroke":79,"style":67},"186",[52,89,91],{"x":90,"y":71,"style":83},"224","2026-07",[45,93],{"x":94,"y":61,"width":77,"height":63,"rx":64,"fill":78,"stroke":79,"style":67},"266",[52,96,98],{"x":97,"y":71,"style":83},"304","2026-08",[45,100],{"x":60,"y":77,"width":62,"height":101,"rx":64,"fill":50,"stroke":102},"26","var(--line,#cdd5e6)",[52,104,107],{"x":70,"y":105,"style":106},"94","font-size:10px;fill:var(--text,#172033);text-anchor:middle","North",[45,109],{"x":76,"y":77,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},"#d9f4f1","var(--teal,#0f9488)",[52,113,114],{"x":82,"y":105,"style":106},"5150",[45,116],{"x":87,"y":77,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},[52,118,119],{"x":90,"y":105,"style":106},"4820",[45,121],{"x":94,"y":77,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},[52,123,124],{"x":97,"y":105,"style":106},"5402",[45,126],{"x":60,"y":76,"width":62,"height":101,"rx":64,"fill":50,"stroke":102},[52,128,130],{"x":70,"y":129,"style":106},"124","South",[45,132],{"x":76,"y":76,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},[52,134,135],{"x":82,"y":129,"style":106},"4268",[45,137],{"x":87,"y":76,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},[52,139,140],{"x":90,"y":129,"style":106},"3980",[45,142],{"x":94,"y":76,"width":77,"height":101,"rx":64,"fill":110,"stroke":111},[52,144,145],{"x":97,"y":129,"style":106},"3140",[52,147,150],{"x":54,"y":148,"style":149},"164","font-size:10.5px;fill:var(--muted,#5b6780);text-anchor:middle","a new month changes the table's shape",[152,153],"line",{"x1":154,"y1":155,"x2":156,"y2":155,"stroke":66,"style":67},"360","100","404",[158,159],"polygon",{"points":160,"fill":161},"412,100 400,94 400,106","#5b5cf0",[52,163,167],{"x":164,"y":165,"style":166},"386","84","font-size:10px;fill:var(--muted,#5b6780);text-anchor:middle","melt",[52,169,171],{"x":170,"y":55,"style":56},"602","long — one row per observation",[45,173],{"x":174,"y":61,"width":175,"height":63,"rx":64,"fill":65,"stroke":66,"style":67},"428","112",[52,177,73],{"x":178,"y":71,"style":72},"484",[45,180],{"x":181,"y":61,"width":175,"height":63,"rx":64,"fill":78,"stroke":79,"style":67},"544",[52,183,185],{"x":184,"y":71,"style":83},"600","month",[45,187],{"x":188,"y":61,"width":175,"height":63,"rx":64,"fill":110,"stroke":111,"style":67},"660",[52,190,193],{"x":191,"y":71,"style":192},"716","font-size:10px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","revenue",[45,195],{"x":174,"y":77,"width":196,"height":197,"rx":64,"fill":50,"stroke":102},"344","24",[52,199,201],{"x":184,"y":200,"style":106},"93","North · 2026-06 · 5150",[45,203],{"x":174,"y":204,"width":196,"height":197,"rx":64,"fill":50,"stroke":102},"104",[52,206,208],{"x":184,"y":207,"style":106},"121","South · 2026-06 · 4268",[45,210],{"x":174,"y":211,"width":196,"height":197,"rx":64,"fill":50,"stroke":102},"132",[52,213,215],{"x":184,"y":214,"style":106},"149","North · 2026-07 · 4820",[52,217,218],{"x":184,"y":54,"style":166},"… six rows in total",[52,220,223],{"x":184,"y":221,"style":222},"206","font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","a new month adds rows, not columns",[225,226,228],"h2",{"id":227},"prerequisites","Prerequisites",[230,231,236],"pre",{"className":232,"code":233,"language":234,"meta":235,"style":235},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[14,237,238],{"__ignoreMap":235},[239,240,242,246,250,253,256],"span",{"class":152,"line":241},1,[239,243,245],{"class":244},"sMTad","pip",[239,247,249],{"class":248},"srMev"," install",[239,251,252],{"class":248}," pandas",[239,254,255],{"class":248}," openpyxl",[239,257,258],{"class":248}," xlsxwriter\n",[10,260,261],{},"A wide sheet of the kind that accumulates:",[230,263,267],{"className":264,"code":265,"language":266,"meta":235,"style":235},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nwide = pd.DataFrame({\n    \"region\": [\"North\", \"South\", \"West\"],\n    \"owner\": [\"A. Chen\", \"B. Ortiz\", \"C. Novak\"],\n    \"2026-06\": [5150.00, 4268.50, 3511.25],\n    \"2026-07\": [4820.50, 3980.25, 2711.50],\n    \"2026-08\": [5402.75, 3140.75, 1820.00],\n})\nwide.to_excel(\"wide_report.xlsx\", index=False)\n","python",[14,268,269,285,292,304,330,353,377,400,423,429],{"__ignoreMap":235},[239,270,271,275,279,282],{"class":152,"line":241},[239,272,274],{"class":273},"s-kum","import",[239,276,278],{"class":277},"skGVy"," pandas ",[239,280,281],{"class":273},"as",[239,283,284],{"class":277}," pd\n",[239,286,288],{"class":152,"line":287},2,[239,289,291],{"emptyLinePlaceholder":290},true,"\n",[239,293,295,298,301],{"class":152,"line":294},3,[239,296,297],{"class":277},"wide ",[239,299,300],{"class":273},"=",[239,302,303],{"class":277}," pd.DataFrame({\n",[239,305,307,310,313,316,319,322,324,327],{"class":152,"line":306},4,[239,308,309],{"class":248},"    \"region\"",[239,311,312],{"class":277},": [",[239,314,315],{"class":248},"\"North\"",[239,317,318],{"class":277},", ",[239,320,321],{"class":248},"\"South\"",[239,323,318],{"class":277},[239,325,326],{"class":248},"\"West\"",[239,328,329],{"class":277},"],\n",[239,331,333,336,338,341,343,346,348,351],{"class":152,"line":332},5,[239,334,335],{"class":248},"    \"owner\"",[239,337,312],{"class":277},[239,339,340],{"class":248},"\"A. Chen\"",[239,342,318],{"class":277},[239,344,345],{"class":248},"\"B. Ortiz\"",[239,347,318],{"class":277},[239,349,350],{"class":248},"\"C. Novak\"",[239,352,329],{"class":277},[239,354,356,359,361,365,367,370,372,375],{"class":152,"line":355},6,[239,357,358],{"class":248},"    \"2026-06\"",[239,360,312],{"class":277},[239,362,364],{"class":363},"sP0c6","5150.00",[239,366,318],{"class":277},[239,368,369],{"class":363},"4268.50",[239,371,318],{"class":277},[239,373,374],{"class":363},"3511.25",[239,376,329],{"class":277},[239,378,380,383,385,388,390,393,395,398],{"class":152,"line":379},7,[239,381,382],{"class":248},"    \"2026-07\"",[239,384,312],{"class":277},[239,386,387],{"class":363},"4820.50",[239,389,318],{"class":277},[239,391,392],{"class":363},"3980.25",[239,394,318],{"class":277},[239,396,397],{"class":363},"2711.50",[239,399,329],{"class":277},[239,401,403,406,408,411,413,416,418,421],{"class":152,"line":402},8,[239,404,405],{"class":248},"    \"2026-08\"",[239,407,312],{"class":277},[239,409,410],{"class":363},"5402.75",[239,412,318],{"class":277},[239,414,415],{"class":363},"3140.75",[239,417,318],{"class":277},[239,419,420],{"class":363},"1820.00",[239,422,329],{"class":277},[239,424,426],{"class":152,"line":425},9,[239,427,428],{"class":277},"})\n",[239,430,432,435,438,440,444,446,449],{"class":152,"line":431},10,[239,433,434],{"class":277},"wide.to_excel(",[239,436,437],{"class":248},"\"wide_report.xlsx\"",[239,439,318],{"class":277},[239,441,443],{"class":442},"sa561","index",[239,445,300],{"class":273},[239,447,448],{"class":363},"False",[239,450,451],{"class":277},")\n",[225,453,455],{"id":454},"step-1-melt-naming-the-identifiers","Step 1 — Melt, naming the identifiers",[10,457,458,460],{},[14,459,167],{}," splits the columns into two groups: the identifiers that stay as columns, and everything else, which collapses into a name column and a value column.",[230,462,464],{"className":264,"code":463,"language":266,"meta":235,"style":235},"import pandas as pd\n\nwide = pd.read_excel(\"wide_report.xlsx\")\n\nlong = wide.melt(\n    id_vars=[\"region\", \"owner\"],     # stay as columns\n    var_name=\"month\",                # the old column names land here\n    value_name=\"revenue\",            # the old cell values land here\n)\nprint(long.head())\n#   region     owner    month  revenue\n# 0  North   A. Chen  2026-06  5150.00\n# 1  South  B. Ortiz  2026-06  4268.50\n",[14,465,466,476,480,493,497,508,533,549,565,569,582,588,594],{"__ignoreMap":235},[239,467,468,470,472,474],{"class":152,"line":241},[239,469,274],{"class":273},[239,471,278],{"class":277},[239,473,281],{"class":273},[239,475,284],{"class":277},[239,477,478],{"class":152,"line":287},[239,479,291],{"emptyLinePlaceholder":290},[239,481,482,484,486,489,491],{"class":152,"line":294},[239,483,297],{"class":277},[239,485,300],{"class":273},[239,487,488],{"class":277}," pd.read_excel(",[239,490,437],{"class":248},[239,492,451],{"class":277},[239,494,495],{"class":152,"line":306},[239,496,291],{"emptyLinePlaceholder":290},[239,498,499,502,505],{"class":152,"line":332},[239,500,501],{"class":442},"long",[239,503,504],{"class":273}," =",[239,506,507],{"class":277}," wide.melt(\n",[239,509,510,513,515,518,521,523,526,529],{"class":152,"line":355},[239,511,512],{"class":442},"    id_vars",[239,514,300],{"class":273},[239,516,517],{"class":277},"[",[239,519,520],{"class":248},"\"region\"",[239,522,318],{"class":277},[239,524,525],{"class":248},"\"owner\"",[239,527,528],{"class":277},"],     ",[239,530,532],{"class":531},"s-wDw","# stay as columns\n",[239,534,535,538,540,543,546],{"class":152,"line":379},[239,536,537],{"class":442},"    var_name",[239,539,300],{"class":273},[239,541,542],{"class":248},"\"month\"",[239,544,545],{"class":277},",                ",[239,547,548],{"class":531},"# the old column names land here\n",[239,550,551,554,556,559,562],{"class":152,"line":402},[239,552,553],{"class":442},"    value_name",[239,555,300],{"class":273},[239,557,558],{"class":248},"\"revenue\"",[239,560,561],{"class":277},",            ",[239,563,564],{"class":531},"# the old cell values land here\n",[239,566,567],{"class":152,"line":425},[239,568,451],{"class":277},[239,570,571,574,577,579],{"class":152,"line":431},[239,572,573],{"class":363},"print",[239,575,576],{"class":277},"(",[239,578,501],{"class":442},[239,580,581],{"class":277},".head())\n",[239,583,585],{"class":152,"line":584},11,[239,586,587],{"class":531},"#   region     owner    month  revenue\n",[239,589,591],{"class":152,"line":590},12,[239,592,593],{"class":531},"# 0  North   A. Chen  2026-06  5150.00\n",[239,595,597],{"class":152,"line":596},13,[239,598,599],{"class":531},"# 1  South  B. Ortiz  2026-06  4268.50\n",[10,601,602,606,607,609,610,613,614,617,618,620,621,624],{},[603,604,605],"strong",{},"Name the identifiers, not the values."," ",[14,608,167],{}," also accepts ",[14,611,612],{},"value_vars",", and using it looks equivalent — but it is not. Next month a ",[14,615,616],{},"2026-09"," column appears, and a ",[14,619,612],{}," list silently omits it while an ",[14,622,623],{},"id_vars"," list picks it up automatically:",[230,626,628],{"className":264,"code":627,"language":266,"meta":235,"style":235},"# Fragile: needs editing every month.\nlong = wide.melt(id_vars=\"region\",\n                 value_vars=[\"2026-06\", \"2026-07\", \"2026-08\"])\n\n# Robust: any new period column is included without a change.\nlong = wide.melt(id_vars=[\"region\", \"owner\"],\n                 var_name=\"month\", value_name=\"revenue\")\n",[14,629,630,635,653,678,682,687,709],{"__ignoreMap":235},[239,631,632],{"class":152,"line":241},[239,633,634],{"class":531},"# Fragile: needs editing every month.\n",[239,636,637,639,641,644,646,648,650],{"class":152,"line":287},[239,638,501],{"class":442},[239,640,504],{"class":273},[239,642,643],{"class":277}," wide.melt(",[239,645,623],{"class":442},[239,647,300],{"class":273},[239,649,520],{"class":248},[239,651,652],{"class":277},",\n",[239,654,655,658,660,662,665,667,670,672,675],{"class":152,"line":294},[239,656,657],{"class":442},"                 value_vars",[239,659,300],{"class":273},[239,661,517],{"class":277},[239,663,664],{"class":248},"\"2026-06\"",[239,666,318],{"class":277},[239,668,669],{"class":248},"\"2026-07\"",[239,671,318],{"class":277},[239,673,674],{"class":248},"\"2026-08\"",[239,676,677],{"class":277},"])\n",[239,679,680],{"class":152,"line":306},[239,681,291],{"emptyLinePlaceholder":290},[239,683,684],{"class":152,"line":332},[239,685,686],{"class":531},"# Robust: any new period column is included without a change.\n",[239,688,689,691,693,695,697,699,701,703,705,707],{"class":152,"line":355},[239,690,501],{"class":442},[239,692,504],{"class":273},[239,694,643],{"class":277},[239,696,623],{"class":442},[239,698,300],{"class":273},[239,700,517],{"class":277},[239,702,520],{"class":248},[239,704,318],{"class":277},[239,706,525],{"class":248},[239,708,329],{"class":277},[239,710,711,714,716,718,720,723,725,727],{"class":152,"line":379},[239,712,713],{"class":442},"                 var_name",[239,715,300],{"class":273},[239,717,542],{"class":248},[239,719,318],{"class":277},[239,721,722],{"class":442},"value_name",[239,724,300],{"class":273},[239,726,558],{"class":248},[239,728,451],{"class":277},[10,730,731],{},"That single choice is the difference between a script that keeps working and one that quietly under-reports from the month somebody adds a column.",[225,733,735],{"id":734},"step-2-turn-the-header-text-into-real-dates","Step 2 — Turn the header text into real dates",[10,737,738,739,741],{},"The ",[14,740,185],{}," column is text, so it sorts alphabetically and cannot be grouped by quarter. Parse it once, after melting — which is one conversion over a column rather than one per header:",[230,743,745],{"className":264,"code":744,"language":266,"meta":235,"style":235},"import pandas as pd\n\nlong[\"month\"] = pd.to_datetime(long[\"month\"], format=\"%Y-%m\", errors=\"coerce\")\n\nunparsed = long[\"month\"].isna().sum()\nif unparsed:\n    print(f\"warning: {unparsed} row(s) had an unparseable period label\")\n",[14,746,747,757,761,806,810,827,835],{"__ignoreMap":235},[239,748,749,751,753,755],{"class":152,"line":241},[239,750,274],{"class":273},[239,752,278],{"class":277},[239,754,281],{"class":273},[239,756,284],{"class":277},[239,758,759],{"class":152,"line":287},[239,760,291],{"emptyLinePlaceholder":290},[239,762,763,765,767,769,772,774,777,779,781,783,786,789,791,794,796,799,801,804],{"class":152,"line":294},[239,764,501],{"class":442},[239,766,517],{"class":277},[239,768,542],{"class":248},[239,770,771],{"class":277},"] ",[239,773,300],{"class":273},[239,775,776],{"class":277}," pd.to_datetime(",[239,778,501],{"class":442},[239,780,517],{"class":277},[239,782,542],{"class":248},[239,784,785],{"class":277},"], ",[239,787,788],{"class":442},"format",[239,790,300],{"class":273},[239,792,793],{"class":248},"\"%Y-%m\"",[239,795,318],{"class":277},[239,797,798],{"class":442},"errors",[239,800,300],{"class":273},[239,802,803],{"class":248},"\"coerce\"",[239,805,451],{"class":277},[239,807,808],{"class":152,"line":306},[239,809,291],{"emptyLinePlaceholder":290},[239,811,812,815,817,820,822,824],{"class":152,"line":332},[239,813,814],{"class":277},"unparsed ",[239,816,300],{"class":273},[239,818,819],{"class":442}," long",[239,821,517],{"class":277},[239,823,542],{"class":248},[239,825,826],{"class":277},"].isna().sum()\n",[239,828,829,832],{"class":152,"line":355},[239,830,831],{"class":273},"if",[239,833,834],{"class":277}," unparsed:\n",[239,836,837,840,842,845,848,852,855,858,861],{"class":152,"line":379},[239,838,839],{"class":363},"    print",[239,841,576],{"class":277},[239,843,844],{"class":273},"f",[239,846,847],{"class":248},"\"warning: ",[239,849,851],{"class":850},"sSjpA","{",[239,853,854],{"class":277},"unparsed",[239,856,857],{"class":850},"}",[239,859,860],{"class":248}," row(s) had an unparseable period label\"",[239,862,451],{"class":277},[10,864,865,866,868,869,318,872,875,876,879],{},"Headers are rarely as tidy as ",[14,867,84],{},". Real ones look like ",[14,870,871],{},"Jun-26",[14,873,874],{},"Q3 2026"," or ",[14,877,878],{},"Aug Actual",", so extract the part that is a date before parsing:",[230,881,883],{"className":264,"code":882,"language":266,"meta":235,"style":235},"import pandas as pd\n\ndef parse_period(labels, fmt=\"%b-%y\"):\n    \"\"\"Pull a period out of a messy column heading and parse it.\"\"\"\n    text = labels.astype(\"string\").str.strip()\n    extracted = text.str.extract(\n        r\"([A-Za-z]{3}[- ]?\\d{2,4}|\\d{4}[-\u002F]\\d{2})\", expand=False\n    )\n    cleaned = extracted.str.replace(\" \", \"-\", regex=False)\n    return pd.to_datetime(cleaned, format=fmt, errors=\"coerce\")\n",[14,884,885,895,899,925,930,946,956,1022,1027,1056],{"__ignoreMap":235},[239,886,887,889,891,893],{"class":152,"line":241},[239,888,274],{"class":273},[239,890,278],{"class":277},[239,892,281],{"class":273},[239,894,284],{"class":277},[239,896,897],{"class":152,"line":287},[239,898,291],{"emptyLinePlaceholder":290},[239,900,901,904,908,911,913,916,919,922],{"class":152,"line":294},[239,902,903],{"class":273},"def",[239,905,907],{"class":906},"s_Opv"," parse_period",[239,909,910],{"class":277},"(labels, fmt",[239,912,300],{"class":273},[239,914,915],{"class":248},"\"",[239,917,918],{"class":850},"%b",[239,920,921],{"class":248},"-%y\"",[239,923,924],{"class":277},"):\n",[239,926,927],{"class":152,"line":306},[239,928,929],{"class":248},"    \"\"\"Pull a period out of a messy column heading and parse it.\"\"\"\n",[239,931,932,935,937,940,943],{"class":152,"line":332},[239,933,934],{"class":277},"    text ",[239,936,300],{"class":273},[239,938,939],{"class":277}," labels.astype(",[239,941,942],{"class":248},"\"string\"",[239,944,945],{"class":277},").str.strip()\n",[239,947,948,951,953],{"class":152,"line":355},[239,949,950],{"class":277},"    extracted ",[239,952,300],{"class":273},[239,954,955],{"class":277}," text.str.extract(\n",[239,957,958,961,963,966,969,972,975,977,980,982,985,988,991,993,996,998,1001,1004,1007,1010,1012,1014,1017,1019],{"class":152,"line":379},[239,959,960],{"class":273},"        r",[239,962,915],{"class":248},[239,964,965],{"class":363},"([",[239,967,968],{"class":850},"A-Za-z",[239,970,971],{"class":363},"]",[239,973,974],{"class":273},"{3}",[239,976,517],{"class":363},[239,978,979],{"class":850},"- ",[239,981,971],{"class":363},[239,983,984],{"class":273},"?",[239,986,987],{"class":363},"\\d",[239,989,990],{"class":273},"{2,4}|",[239,992,987],{"class":363},[239,994,995],{"class":273},"{4}",[239,997,517],{"class":363},[239,999,1000],{"class":850},"-\u002F",[239,1002,1003],{"class":363},"]\\d",[239,1005,1006],{"class":273},"{2}",[239,1008,1009],{"class":363},")",[239,1011,915],{"class":248},[239,1013,318],{"class":277},[239,1015,1016],{"class":442},"expand",[239,1018,300],{"class":273},[239,1020,1021],{"class":363},"False\n",[239,1023,1024],{"class":152,"line":402},[239,1025,1026],{"class":277},"    )\n",[239,1028,1029,1032,1034,1037,1040,1042,1045,1047,1050,1052,1054],{"class":152,"line":425},[239,1030,1031],{"class":277},"    cleaned ",[239,1033,300],{"class":273},[239,1035,1036],{"class":277}," extracted.str.replace(",[239,1038,1039],{"class":248},"\" \"",[239,1041,318],{"class":277},[239,1043,1044],{"class":248},"\"-\"",[239,1046,318],{"class":277},[239,1048,1049],{"class":442},"regex",[239,1051,300],{"class":273},[239,1053,448],{"class":363},[239,1055,451],{"class":277},[239,1057,1058,1061,1064,1066,1068,1071,1073,1075,1077],{"class":152,"line":431},[239,1059,1060],{"class":273},"    return",[239,1062,1063],{"class":277}," pd.to_datetime(cleaned, ",[239,1065,788],{"class":442},[239,1067,300],{"class":273},[239,1069,1070],{"class":277},"fmt, ",[239,1072,798],{"class":442},[239,1074,300],{"class":273},[239,1076,803],{"class":248},[239,1078,451],{"class":277},[10,1080,1081,1082,1086],{},"Once the column is real dates, everything in ",[19,1083,1085],{"href":1084},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fgroup-excel-rows-by-month-and-quarter-with-pandas\u002F","grouping Excel rows by month and quarter"," becomes available — quarterly rollups, fiscal periods, gap filling.",[225,1088,1090],{"id":1089},"step-3-handle-a-two-row-header","Step 3 — Handle a two-row header",[10,1092,1093,1094,1097,1098,318,1101,318,1104,1107,1108,1111],{},"Wide sheets often stack a period row above a measure row: ",[14,1095,1096],{},"Q1"," spanning ",[14,1099,1100],{},"Units",[14,1102,1103],{},"Revenue",[14,1105,1106],{},"Margin",", then ",[14,1109,1110],{},"Q2"," doing the same. Read both header rows and melt on the levels.",[25,1113,35,1120,35,1123,35,1126,35,1132,35,1137,35,1140,35,1146,35,1149,35,1152,35,1155,35,1158,35,1161,35,1165,35,1168,35,1172,35,1175,35,1179,35,1181,35,1184,35,1187,35,1190,35,1193,35,1198,35,1203,35,1206,35,1209,35,1213,35,1216,35,1219,35,1223,35,1226,35,1230,35,1234,35,1238,35,1240,35,1245,35,1248,35,1252,35,1255,35,1259],{"viewBox":1114,"role":28,"ariaLabel":1115,"ariaLabelledBy":1116,"xmlns":33,"style":1119},"8 3 784 196","A two-level header melting into two separate columns: the upper level becomes a period column and the lower level becomes a measure column.",[1117,1118],"two-t","two-d","width:100%;max-width:784px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[37,1121,1122],{"id":1117},"A two-level header becomes two columns",[41,1124,1125],{"id":1118},"A wide sheet whose upper header row holds Q1 and Q2 spanning groups, and whose lower row holds Units and Revenue within each. Melting with two var_names produces a long frame with a period column carrying Q1 or Q2, a measure column carrying Units or Revenue, and a single value column. Each of the four original value columns per row therefore becomes four rows.",[45,1127],{"x":1128,"y":1129,"width":1130,"height":1131,"fill":50},"8","3","784","196",[52,1133,1136],{"x":1134,"y":55,"style":1135},"184","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","two header rows",[45,1138],{"x":197,"y":61,"width":62,"height":101,"rx":64,"fill":1139,"stroke":102},"#f0f2f5",[52,1141,1145],{"x":1142,"y":1143,"style":1144},"65","62","font-size:9.5px;fill:var(--muted,#5b6780);text-anchor:middle","—",[45,1147],{"x":1148,"y":61,"width":175,"height":101,"rx":64,"fill":65,"stroke":66,"style":67},"110",[52,1150,1096],{"x":1151,"y":1143,"style":72},"166",[45,1153],{"x":1154,"y":61,"width":175,"height":101,"rx":64,"fill":65,"stroke":66,"style":67},"226",[52,1156,1110],{"x":1157,"y":1143,"style":72},"282",[45,1159],{"x":197,"y":1160,"width":62,"height":101,"rx":64,"fill":78,"stroke":79,"style":67},"74",[52,1162,73],{"x":1142,"y":1163,"style":1164},"92","font-size:9.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[45,1166],{"x":1148,"y":1160,"width":1167,"height":101,"rx":64,"fill":78,"stroke":79},"54",[52,1169,1100],{"x":1170,"y":1163,"style":1171},"137","font-size:9px;fill:var(--text,#172033);text-anchor:middle",[45,1173],{"x":1174,"y":1160,"width":1167,"height":101,"rx":64,"fill":78,"stroke":79},"168",[52,1176,1178],{"x":1177,"y":1163,"style":1171},"195","Rev",[45,1180],{"x":1154,"y":1160,"width":1167,"height":101,"rx":64,"fill":78,"stroke":79},[52,1182,1100],{"x":1183,"y":1163,"style":1171},"253",[45,1185],{"x":1186,"y":1160,"width":1167,"height":101,"rx":64,"fill":78,"stroke":79},"284",[52,1188,1178],{"x":1189,"y":1163,"style":1171},"311",[45,1191],{"x":197,"y":204,"width":1192,"height":197,"rx":64,"fill":50,"stroke":102},"314",[52,1194,1197],{"x":1195,"y":207,"style":1196},"181","font-size:9.5px;fill:var(--text,#172033);text-anchor:middle","North · 412 · 5150 · 388 · 4820",[152,1199],{"x1":1200,"y1":1201,"x2":1202,"y2":1201,"stroke":66,"style":67},"356","90","396",[158,1204],{"points":1205,"fill":161},"404,90 392,84 392,96",[52,1207,1208],{"x":184,"y":55,"style":1135},"four columns, four rows per region",[45,1210],{"x":1211,"y":61,"width":1212,"height":101,"rx":64,"fill":78,"stroke":79,"style":67},"420","88",[52,1214,73],{"x":1215,"y":1143,"style":83},"464",[45,1217],{"x":1218,"y":61,"width":1212,"height":101,"rx":64,"fill":65,"stroke":66,"style":67},"512",[52,1220,1222],{"x":1221,"y":1143,"style":72},"556","period",[45,1224],{"x":1225,"y":61,"width":1212,"height":101,"rx":64,"fill":65,"stroke":66,"style":67},"604",[52,1227,1229],{"x":1228,"y":1143,"style":72},"648","measure",[45,1231],{"x":1232,"y":61,"width":1233,"height":101,"rx":64,"fill":110,"stroke":111,"style":67},"696","80",[52,1235,1237],{"x":1236,"y":1143,"style":192},"736","value",[45,1239],{"x":1211,"y":1160,"width":1200,"height":197,"rx":64,"fill":50,"stroke":102},[52,1241,1244],{"x":1242,"y":1243,"style":1196},"598","91","North · Q1 · Units · 412",[45,1246],{"x":1211,"y":1247,"width":1200,"height":197,"rx":64,"fill":50,"stroke":102},"102",[52,1249,1251],{"x":1242,"y":1250,"style":1196},"119","North · Q1 · Rev · 5150",[45,1253],{"x":1211,"y":1254,"width":1200,"height":197,"rx":64,"fill":50,"stroke":102},"130",[52,1256,1258],{"x":1242,"y":1257,"style":1196},"147","North · Q2 · Units · 388",[52,1260,1261],{"x":1242,"y":54,"style":166},"… and North · Q2 · Rev · 4820",[230,1263,1265],{"className":264,"code":1264,"language":266,"meta":235,"style":235},"import pandas as pd\n\nstacked = pd.read_excel(\"quarterly_wide.xlsx\", header=[0, 1], index_col=0)\nstacked.columns.names = [\"period\", \"measure\"]\n\nlong = (\n    stacked.stack([\"period\", \"measure\"], future_stack=True)\n           .rename(\"value\")\n           .reset_index()\n)\nprint(long.head())\n",[14,1266,1267,1277,1281,1320,1341,1345,1354,1377,1387,1392,1396],{"__ignoreMap":235},[239,1268,1269,1271,1273,1275],{"class":152,"line":241},[239,1270,274],{"class":273},[239,1272,278],{"class":277},[239,1274,281],{"class":273},[239,1276,284],{"class":277},[239,1278,1279],{"class":152,"line":287},[239,1280,291],{"emptyLinePlaceholder":290},[239,1282,1283,1286,1288,1290,1293,1295,1298,1300,1302,1304,1306,1309,1311,1314,1316,1318],{"class":152,"line":294},[239,1284,1285],{"class":277},"stacked ",[239,1287,300],{"class":273},[239,1289,488],{"class":277},[239,1291,1292],{"class":248},"\"quarterly_wide.xlsx\"",[239,1294,318],{"class":277},[239,1296,1297],{"class":442},"header",[239,1299,300],{"class":273},[239,1301,517],{"class":277},[239,1303,47],{"class":363},[239,1305,318],{"class":277},[239,1307,1308],{"class":363},"1",[239,1310,785],{"class":277},[239,1312,1313],{"class":442},"index_col",[239,1315,300],{"class":273},[239,1317,47],{"class":363},[239,1319,451],{"class":277},[239,1321,1322,1325,1327,1330,1333,1335,1338],{"class":152,"line":306},[239,1323,1324],{"class":277},"stacked.columns.names ",[239,1326,300],{"class":273},[239,1328,1329],{"class":277}," [",[239,1331,1332],{"class":248},"\"period\"",[239,1334,318],{"class":277},[239,1336,1337],{"class":248},"\"measure\"",[239,1339,1340],{"class":277},"]\n",[239,1342,1343],{"class":152,"line":332},[239,1344,291],{"emptyLinePlaceholder":290},[239,1346,1347,1349,1351],{"class":152,"line":355},[239,1348,501],{"class":442},[239,1350,504],{"class":273},[239,1352,1353],{"class":277}," (\n",[239,1355,1356,1359,1361,1363,1365,1367,1370,1372,1375],{"class":152,"line":379},[239,1357,1358],{"class":277},"    stacked.stack([",[239,1360,1332],{"class":248},[239,1362,318],{"class":277},[239,1364,1337],{"class":248},[239,1366,785],{"class":277},[239,1368,1369],{"class":442},"future_stack",[239,1371,300],{"class":273},[239,1373,1374],{"class":363},"True",[239,1376,451],{"class":277},[239,1378,1379,1382,1385],{"class":152,"line":402},[239,1380,1381],{"class":277},"           .rename(",[239,1383,1384],{"class":248},"\"value\"",[239,1386,451],{"class":277},[239,1388,1389],{"class":152,"line":425},[239,1390,1391],{"class":277},"           .reset_index()\n",[239,1393,1394],{"class":152,"line":431},[239,1395,451],{"class":277},[239,1397,1398,1400,1402,1404],{"class":152,"line":584},[239,1399,573],{"class":363},[239,1401,576],{"class":277},[239,1403,501],{"class":442},[239,1405,581],{"class":277},[10,1407,1408,1409,1412,1413,1415,1416,1418,1419,1422,1423,1426,1427,23],{},"Using ",[14,1410,1411],{},"stack"," here rather than ",[14,1414,167],{}," is the exception to the earlier rule — ",[14,1417,1411],{}," understands index levels natively, and a ",[14,1420,1421],{},"MultiIndex"," column is exactly that. ",[14,1424,1425],{},"future_stack=True"," opts into the newer behaviour, which keeps rows whose value is missing rather than silently dropping them. Reading multi-level headers is covered in ",[19,1428,1430],{"href":1429},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fskip-rows-and-set-header-when-reading-excel-with-pandas\u002F","skipping rows and setting the header",[225,1432,1434],{"id":1433},"step-4-drop-or-keep-the-empty-cells","Step 4 — Drop or keep the empty cells",[25,1436,35,1442,35,1445,35,1448,35,1450,35,1454,35,1460,35,1464,35,1467,35,1472,35,1478,35,1482,35,1485,35,1489,35,1491,35,1493,35,1498],{"viewBox":1437,"role":28,"ariaLabel":1438,"ariaLabelledBy":1439,"xmlns":33,"style":34},"0 0 800 226","Sparse grids inflate after melting: a six by twelve grid two-thirds empty becomes seventy-two rows of which forty-eight carry no value, so dropping them early halves the work downstream.",[1440,1441],"sparse-t","sparse-d",[37,1443,1444],{"id":1440},"A sparse grid melts into mostly empty rows",[41,1446,1447],{"id":1441},"A wide grid of six regions by twelve months holds seventy-two value cells, of which only twenty-four are populated. Melting produces one row per cell, so the long frame has seventy-two rows and forty-eight of them carry a null value. Dropping those immediately after the melt leaves twenty-four real observations, and every subsequent operation works on a third of the data.",[45,1449],{"x":47,"y":47,"width":48,"height":1154,"fill":50},[52,1451,1453],{"x":1452,"y":63,"style":56},"400","6 regions × 12 months = 72 cells, 24 populated",[45,1455],{"x":1456,"y":1457,"width":1458,"height":55,"rx":1459,"fill":1139,"stroke":102},"14","56","140","6",[52,1461,1463],{"x":165,"y":77,"style":1462},"font-size:11px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","after melt",[45,1465],{"x":1174,"y":1457,"width":1466,"height":55,"rx":64,"fill":110,"stroke":111,"style":67},"192",[52,1468,1471],{"x":1469,"y":77,"style":1470},"264","font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","24 real",[45,1473],{"x":1474,"y":1457,"width":1475,"height":55,"rx":64,"fill":1476,"stroke":1477,"style":67},"364","384","#fee8f2","var(--accent,#f43f8f)",[52,1479,1481],{"x":1221,"y":77,"style":1480},"font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","48 rows carrying nothing",[45,1483],{"x":1456,"y":1484,"width":1458,"height":55,"rx":1459,"fill":1139,"stroke":102},"116",[52,1486,1488],{"x":165,"y":1487,"style":1462},"136","after dropna",[45,1490],{"x":1174,"y":1484,"width":1466,"height":55,"rx":64,"fill":110,"stroke":111,"style":67},[52,1492,1471],{"x":1469,"y":1487,"style":1470},[52,1494,1497],{"x":1495,"y":1487,"style":1496},"380","font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157)","every later step works on a third of the rows",[52,1499,1501],{"x":1452,"y":1466,"style":1500},"font-size:11px;fill:var(--muted,#5b6780);text-anchor:middle","drop only when a blank means \"no data\" — when it means zero, fill instead",[10,1503,1504,1505,1508],{},"A wide grid is usually sparse: not every region has a value in every month. Melting turns each empty cell into a row with a ",[14,1506,1507],{},"NaN"," value, which can multiply the row count considerably.",[230,1510,1512],{"className":264,"code":1511,"language":266,"meta":235,"style":235},"import pandas as pd\n\nlong = wide.melt(id_vars=[\"region\", \"owner\"],\n                 var_name=\"month\", value_name=\"revenue\")\n\nprint(f\"{len(long)} rows, {long['revenue'].isna().sum()} of them empty\")\n\n# Keep only real observations — usually right for analysis.\nobserved = long.dropna(subset=[\"revenue\"])\n\n# Or keep them, when an absent month genuinely means zero.\nzeroed = long.fillna({\"revenue\": 0})\n",[14,1513,1514,1524,1528,1550,1568,1572,1617,1621,1626,1649,1653,1658],{"__ignoreMap":235},[239,1515,1516,1518,1520,1522],{"class":152,"line":241},[239,1517,274],{"class":273},[239,1519,278],{"class":277},[239,1521,281],{"class":273},[239,1523,284],{"class":277},[239,1525,1526],{"class":152,"line":287},[239,1527,291],{"emptyLinePlaceholder":290},[239,1529,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548],{"class":152,"line":294},[239,1531,501],{"class":442},[239,1533,504],{"class":273},[239,1535,643],{"class":277},[239,1537,623],{"class":442},[239,1539,300],{"class":273},[239,1541,517],{"class":277},[239,1543,520],{"class":248},[239,1545,318],{"class":277},[239,1547,525],{"class":248},[239,1549,329],{"class":277},[239,1551,1552,1554,1556,1558,1560,1562,1564,1566],{"class":152,"line":306},[239,1553,713],{"class":442},[239,1555,300],{"class":273},[239,1557,542],{"class":248},[239,1559,318],{"class":277},[239,1561,722],{"class":442},[239,1563,300],{"class":273},[239,1565,558],{"class":248},[239,1567,451],{"class":277},[239,1569,1570],{"class":152,"line":332},[239,1571,291],{"emptyLinePlaceholder":290},[239,1573,1574,1576,1578,1580,1582,1584,1587,1589,1591,1593,1595,1598,1600,1602,1604,1607,1610,1612,1615],{"class":152,"line":355},[239,1575,573],{"class":363},[239,1577,576],{"class":277},[239,1579,844],{"class":273},[239,1581,915],{"class":248},[239,1583,851],{"class":850},[239,1585,1586],{"class":363},"len",[239,1588,576],{"class":277},[239,1590,501],{"class":442},[239,1592,1009],{"class":277},[239,1594,857],{"class":850},[239,1596,1597],{"class":248}," rows, ",[239,1599,851],{"class":850},[239,1601,501],{"class":442},[239,1603,517],{"class":277},[239,1605,1606],{"class":248},"'revenue'",[239,1608,1609],{"class":277},"].isna().sum()",[239,1611,857],{"class":850},[239,1613,1614],{"class":248}," of them empty\"",[239,1616,451],{"class":277},[239,1618,1619],{"class":152,"line":379},[239,1620,291],{"emptyLinePlaceholder":290},[239,1622,1623],{"class":152,"line":402},[239,1624,1625],{"class":531},"# Keep only real observations — usually right for analysis.\n",[239,1627,1628,1631,1633,1635,1638,1641,1643,1645,1647],{"class":152,"line":425},[239,1629,1630],{"class":277},"observed ",[239,1632,300],{"class":273},[239,1634,819],{"class":442},[239,1636,1637],{"class":277},".dropna(",[239,1639,1640],{"class":442},"subset",[239,1642,300],{"class":273},[239,1644,517],{"class":277},[239,1646,558],{"class":248},[239,1648,677],{"class":277},[239,1650,1651],{"class":152,"line":431},[239,1652,291],{"emptyLinePlaceholder":290},[239,1654,1655],{"class":152,"line":584},[239,1656,1657],{"class":531},"# Or keep them, when an absent month genuinely means zero.\n",[239,1659,1660,1663,1665,1667,1670,1672,1675,1677],{"class":152,"line":590},[239,1661,1662],{"class":277},"zeroed ",[239,1664,300],{"class":273},[239,1666,819],{"class":442},[239,1668,1669],{"class":277},".fillna({",[239,1671,558],{"class":248},[239,1673,1674],{"class":277},": ",[239,1676,47],{"class":363},[239,1678,428],{"class":277},[10,1680,1681,1682,23],{},"Choose deliberately. Dropping is right when a blank means \"no data recorded\"; filling with zero is right when it means \"nothing happened\". Getting it backwards either understates a total or invents activity — the distinction developed in ",[19,1683,1685],{"href":1684},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffind-and-report-missing-values-in-an-excel-file\u002F","finding and reporting missing values",[225,1687,1689],{"id":1688},"step-5-write-the-long-form-back","Step 5 — Write the long form back",[10,1691,1692],{},"The long shape is what every downstream tool wants — pivot tables, charts, database loads:",[230,1694,1696],{"className":264,"code":1695,"language":266,"meta":235,"style":235},"import pandas as pd\n\ndef write_long(long, path, sheet_name=\"Data\", table_name=\"Observations\"):\n    \"\"\"Write the unpivoted frame as a named table, ready to pivot from.\"\"\"\n    with pd.ExcelWriter(path, engine=\"xlsxwriter\",\n                        date_format=\"yyyy-mm-dd\") as writer:\n        long.to_excel(writer, sheet_name=sheet_name, index=False)\n        sheet = writer.sheets[sheet_name]\n\n        sheet.add_table(0, 0, len(long), len(long.columns) - 1,\n                        {\"name\": table_name,\n                         \"columns\": [{\"header\": str(c)} for c in long.columns],\n                         \"style\": \"Table Style Medium 2\"})\n        money = writer.book.add_format({\"num_format\": \"#,##0.00\"})\n        sheet.set_column(\"A:B\", 16)\n        sheet.set_column(\"C:C\", 13)\n        sheet.set_column(\"D:D\", 14, money)\n        sheet.freeze_panes(1, 0)\n\n    return path\n\nwrite_long(observed, \"long_report.xlsx\")\n",[14,1697,1698,1708,1712,1737,1742,1760,1778,1802,1812,1816,1855,1866,1899,1911,1932,1948,1963,1978,1992,1997,2005,2010],{"__ignoreMap":235},[239,1699,1700,1702,1704,1706],{"class":152,"line":241},[239,1701,274],{"class":273},[239,1703,278],{"class":277},[239,1705,281],{"class":273},[239,1707,284],{"class":277},[239,1709,1710],{"class":152,"line":287},[239,1711,291],{"emptyLinePlaceholder":290},[239,1713,1714,1716,1719,1722,1724,1727,1730,1732,1735],{"class":152,"line":294},[239,1715,903],{"class":273},[239,1717,1718],{"class":906}," write_long",[239,1720,1721],{"class":277},"(long, path, sheet_name",[239,1723,300],{"class":273},[239,1725,1726],{"class":248},"\"Data\"",[239,1728,1729],{"class":277},", table_name",[239,1731,300],{"class":273},[239,1733,1734],{"class":248},"\"Observations\"",[239,1736,924],{"class":277},[239,1738,1739],{"class":152,"line":306},[239,1740,1741],{"class":248},"    \"\"\"Write the unpivoted frame as a named table, ready to pivot from.\"\"\"\n",[239,1743,1744,1747,1750,1753,1755,1758],{"class":152,"line":332},[239,1745,1746],{"class":273},"    with",[239,1748,1749],{"class":277}," pd.ExcelWriter(path, ",[239,1751,1752],{"class":442},"engine",[239,1754,300],{"class":273},[239,1756,1757],{"class":248},"\"xlsxwriter\"",[239,1759,652],{"class":277},[239,1761,1762,1765,1767,1770,1773,1775],{"class":152,"line":355},[239,1763,1764],{"class":442},"                        date_format",[239,1766,300],{"class":273},[239,1768,1769],{"class":248},"\"yyyy-mm-dd\"",[239,1771,1772],{"class":277},") ",[239,1774,281],{"class":273},[239,1776,1777],{"class":277}," writer:\n",[239,1779,1780,1783,1786,1789,1791,1794,1796,1798,1800],{"class":152,"line":379},[239,1781,1782],{"class":442},"        long",[239,1784,1785],{"class":277},".to_excel(writer, ",[239,1787,1788],{"class":442},"sheet_name",[239,1790,300],{"class":273},[239,1792,1793],{"class":277},"sheet_name, ",[239,1795,443],{"class":442},[239,1797,300],{"class":273},[239,1799,448],{"class":363},[239,1801,451],{"class":277},[239,1803,1804,1807,1809],{"class":152,"line":402},[239,1805,1806],{"class":277},"        sheet ",[239,1808,300],{"class":273},[239,1810,1811],{"class":277}," writer.sheets[sheet_name]\n",[239,1813,1814],{"class":152,"line":425},[239,1815,291],{"emptyLinePlaceholder":290},[239,1817,1818,1821,1823,1825,1827,1829,1831,1833,1835,1838,1840,1842,1844,1847,1850,1853],{"class":152,"line":431},[239,1819,1820],{"class":277},"        sheet.add_table(",[239,1822,47],{"class":363},[239,1824,318],{"class":277},[239,1826,47],{"class":363},[239,1828,318],{"class":277},[239,1830,1586],{"class":363},[239,1832,576],{"class":277},[239,1834,501],{"class":442},[239,1836,1837],{"class":277},"), ",[239,1839,1586],{"class":363},[239,1841,576],{"class":277},[239,1843,501],{"class":442},[239,1845,1846],{"class":277},".columns) ",[239,1848,1849],{"class":273},"-",[239,1851,1852],{"class":363}," 1",[239,1854,652],{"class":277},[239,1856,1857,1860,1863],{"class":152,"line":584},[239,1858,1859],{"class":277},"                        {",[239,1861,1862],{"class":248},"\"name\"",[239,1864,1865],{"class":277},": table_name,\n",[239,1867,1868,1871,1874,1877,1879,1882,1885,1888,1891,1894,1896],{"class":152,"line":590},[239,1869,1870],{"class":248},"                         \"columns\"",[239,1872,1873],{"class":277},": [{",[239,1875,1876],{"class":248},"\"header\"",[239,1878,1674],{"class":277},[239,1880,1881],{"class":363},"str",[239,1883,1884],{"class":277},"(c)} ",[239,1886,1887],{"class":273},"for",[239,1889,1890],{"class":277}," c ",[239,1892,1893],{"class":273},"in",[239,1895,819],{"class":442},[239,1897,1898],{"class":277},".columns],\n",[239,1900,1901,1904,1906,1909],{"class":152,"line":596},[239,1902,1903],{"class":248},"                         \"style\"",[239,1905,1674],{"class":277},[239,1907,1908],{"class":248},"\"Table Style Medium 2\"",[239,1910,428],{"class":277},[239,1912,1914,1917,1919,1922,1925,1927,1930],{"class":152,"line":1913},14,[239,1915,1916],{"class":277},"        money ",[239,1918,300],{"class":273},[239,1920,1921],{"class":277}," writer.book.add_format({",[239,1923,1924],{"class":248},"\"num_format\"",[239,1926,1674],{"class":277},[239,1928,1929],{"class":248},"\"#,##0.00\"",[239,1931,428],{"class":277},[239,1933,1935,1938,1941,1943,1946],{"class":152,"line":1934},15,[239,1936,1937],{"class":277},"        sheet.set_column(",[239,1939,1940],{"class":248},"\"A:B\"",[239,1942,318],{"class":277},[239,1944,1945],{"class":363},"16",[239,1947,451],{"class":277},[239,1949,1951,1953,1956,1958,1961],{"class":152,"line":1950},16,[239,1952,1937],{"class":277},[239,1954,1955],{"class":248},"\"C:C\"",[239,1957,318],{"class":277},[239,1959,1960],{"class":363},"13",[239,1962,451],{"class":277},[239,1964,1966,1968,1971,1973,1975],{"class":152,"line":1965},17,[239,1967,1937],{"class":277},[239,1969,1970],{"class":248},"\"D:D\"",[239,1972,318],{"class":277},[239,1974,1456],{"class":363},[239,1976,1977],{"class":277},", money)\n",[239,1979,1981,1984,1986,1988,1990],{"class":152,"line":1980},18,[239,1982,1983],{"class":277},"        sheet.freeze_panes(",[239,1985,1308],{"class":363},[239,1987,318],{"class":277},[239,1989,47],{"class":363},[239,1991,451],{"class":277},[239,1993,1995],{"class":152,"line":1994},19,[239,1996,291],{"emptyLinePlaceholder":290},[239,1998,2000,2002],{"class":152,"line":1999},20,[239,2001,1060],{"class":273},[239,2003,2004],{"class":277}," path\n",[239,2006,2008],{"class":152,"line":2007},21,[239,2009,291],{"emptyLinePlaceholder":290},[239,2011,2013,2016,2019],{"class":152,"line":2012},22,[239,2014,2015],{"class":277},"write_long(observed, ",[239,2017,2018],{"class":248},"\"long_report.xlsx\"",[239,2020,451],{"class":277},[10,2022,2023,2024,23],{},"Writing it as a named table is deliberate: a reader can then insert a pivot over it and reproduce the original wide view interactively, which is strictly better than the fixed wide sheet you started with. That round trip is described in ",[19,2025,2027],{"href":2026},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-a-native-excel-pivot-table-with-python\u002F","adding a native Excel pivot table with Python",[10,2029,2030,2031,2034],{},"Going back to wide, when a printed report needs it, is a ",[14,2032,2033],{},"pivot",":",[230,2036,2038],{"className":264,"code":2037,"language":266,"meta":235,"style":235},"back_to_wide = observed.pivot(index=[\"region\", \"owner\"],\n                              columns=\"month\", values=\"revenue\").reset_index()\n",[14,2039,2040,2064],{"__ignoreMap":235},[239,2041,2042,2045,2047,2050,2052,2054,2056,2058,2060,2062],{"class":152,"line":241},[239,2043,2044],{"class":277},"back_to_wide ",[239,2046,300],{"class":273},[239,2048,2049],{"class":277}," observed.pivot(",[239,2051,443],{"class":442},[239,2053,300],{"class":273},[239,2055,517],{"class":277},[239,2057,520],{"class":248},[239,2059,318],{"class":277},[239,2061,525],{"class":248},[239,2063,329],{"class":277},[239,2065,2066,2069,2071,2073,2075,2078,2080,2082],{"class":152,"line":287},[239,2067,2068],{"class":442},"                              columns",[239,2070,300],{"class":273},[239,2072,542],{"class":248},[239,2074,318],{"class":277},[239,2076,2077],{"class":442},"values",[239,2079,300],{"class":273},[239,2081,558],{"class":248},[239,2083,2084],{"class":277},").reset_index()\n",[225,2086,2088],{"id":2087},"common-pitfalls-and-fixes","Common pitfalls and fixes",[2090,2091,2092,2108],"table",{},[2093,2094,2095],"thead",{},[2096,2097,2098,2102,2105],"tr",{},[2099,2100,2101],"th",{},"Symptom",[2099,2103,2104],{},"Cause",[2099,2106,2107],{},"Fix",[2109,2110,2111,2128,2141,2156,2170,2186,2201,2216],"tbody",{},[2096,2112,2113,2117,2122],{},[2114,2115,2116],"td",{},"New month column ignored",[2114,2118,2119,2121],{},[14,2120,612],{}," hard-coded",[2114,2123,2124,2125,2127],{},"Name ",[14,2126,623],{}," instead.",[2096,2129,2130,2133,2138],{},[2114,2131,2132],{},"Identifier columns became values",[2114,2134,2135,2136],{},"Not listed in ",[14,2137,623],{},[2114,2139,2140],{},"Add every identifier to the list.",[2096,2142,2143,2146,2149],{},[2114,2144,2145],{},"Month column sorts alphabetically",[2114,2147,2148],{},"Still text",[2114,2150,2151,2152,2155],{},"Parse it with ",[14,2153,2154],{},"to_datetime"," after melting.",[2096,2157,2158,2161,2164],{},[2114,2159,2160],{},"Row count exploded",[2114,2162,2163],{},"Empty grid cells became rows",[2114,2165,2166,2169],{},[14,2167,2168],{},"dropna(subset=[value])",", or fill deliberately.",[2096,2171,2172,2180,2183],{},[2114,2173,2174,2177,2178],{},[14,2175,2176],{},"KeyError"," on ",[14,2179,623],{},[2114,2181,2182],{},"Column name has whitespace",[2114,2184,2185],{},"Normalise the headers first.",[2096,2187,2188,2191,2196],{},[2114,2189,2190],{},"Two header rows produce tuples",[2114,2192,2193,2195],{},[14,2194,1421],{}," columns",[2114,2197,2198,2200],{},[14,2199,1411],{}," the levels, or flatten first.",[2096,2202,2203,2210,2213],{},[2114,2204,2205,2206,2209],{},"Values became ",[14,2207,2208],{},"object"," dtype",[2114,2211,2212],{},"Mixed types across the wide columns",[2114,2214,2215],{},"Coerce after melting, in one column.",[2096,2217,2218,2221,2226],{},[2114,2219,2220],{},"Rows silently disappeared",[2114,2222,2223,2225],{},[14,2224,1411],{}," dropped missing values",[2114,2227,2228,2229,23],{},"Pass ",[14,2230,1425],{},[225,2232,2234],{"id":2233},"performance-and-scale-notes","Performance and scale notes",[10,2236,2237,2239],{},[14,2238,167],{}," allocates one long frame roughly the size of the wide one, and copies the identifier columns once per value column. A frame with two identifiers and twenty-four months therefore repeats each identifier twenty-four times — which is the memory cost of the long shape, not of the operation.",[10,2241,2242,2243,2246],{},"Three habits keep that manageable. ",[603,2244,2245],{},"Melt before cleaning the values",", since cleaning one long column is far cheaper than cleaning twenty-four wide ones:",[230,2248,2250],{"className":264,"code":2249,"language":266,"meta":235,"style":235},"import pandas as pd\n\nlong = wide.melt(id_vars=[\"region\", \"owner\"],\n                 var_name=\"month\", value_name=\"revenue\")\nlong[\"revenue\"] = pd.to_numeric(long[\"revenue\"], errors=\"coerce\")   # one pass\n",[14,2251,2252,2262,2266,2288,2306],{"__ignoreMap":235},[239,2253,2254,2256,2258,2260],{"class":152,"line":241},[239,2255,274],{"class":273},[239,2257,278],{"class":277},[239,2259,281],{"class":273},[239,2261,284],{"class":277},[239,2263,2264],{"class":152,"line":287},[239,2265,291],{"emptyLinePlaceholder":290},[239,2267,2268,2270,2272,2274,2276,2278,2280,2282,2284,2286],{"class":152,"line":294},[239,2269,501],{"class":442},[239,2271,504],{"class":273},[239,2273,643],{"class":277},[239,2275,623],{"class":442},[239,2277,300],{"class":273},[239,2279,517],{"class":277},[239,2281,520],{"class":248},[239,2283,318],{"class":277},[239,2285,525],{"class":248},[239,2287,329],{"class":277},[239,2289,2290,2292,2294,2296,2298,2300,2302,2304],{"class":152,"line":306},[239,2291,713],{"class":442},[239,2293,300],{"class":273},[239,2295,542],{"class":248},[239,2297,318],{"class":277},[239,2299,722],{"class":442},[239,2301,300],{"class":273},[239,2303,558],{"class":248},[239,2305,451],{"class":277},[239,2307,2308,2310,2312,2314,2316,2318,2321,2323,2325,2327,2329,2331,2333,2335,2338],{"class":152,"line":332},[239,2309,501],{"class":442},[239,2311,517],{"class":277},[239,2313,558],{"class":248},[239,2315,771],{"class":277},[239,2317,300],{"class":273},[239,2319,2320],{"class":277}," pd.to_numeric(",[239,2322,501],{"class":442},[239,2324,517],{"class":277},[239,2326,558],{"class":248},[239,2328,785],{"class":277},[239,2330,798],{"class":442},[239,2332,300],{"class":273},[239,2334,803],{"class":248},[239,2336,2337],{"class":277},")   ",[239,2339,2340],{"class":531},"# one pass\n",[10,2342,2343,2346],{},[603,2344,2345],{},"Make the repeated identifiers categorical"," after melting. A region name repeated twenty-four times is stored once with an integer code, which on a large frame is a substantial saving:",[230,2348,2350],{"className":264,"code":2349,"language":266,"meta":235,"style":235},"for name in (\"region\", \"owner\"):\n    long[name] = long[name].astype(\"category\")\n",[14,2351,2352,2372],{"__ignoreMap":235},[239,2353,2354,2356,2359,2361,2364,2366,2368,2370],{"class":152,"line":241},[239,2355,1887],{"class":273},[239,2357,2358],{"class":277}," name ",[239,2360,1893],{"class":273},[239,2362,2363],{"class":277}," (",[239,2365,520],{"class":248},[239,2367,318],{"class":277},[239,2369,525],{"class":248},[239,2371,924],{"class":277},[239,2373,2374,2377,2380,2382,2384,2387,2390],{"class":152,"line":287},[239,2375,2376],{"class":442},"    long",[239,2378,2379],{"class":277},"[name] ",[239,2381,300],{"class":273},[239,2383,819],{"class":442},[239,2385,2386],{"class":277},"[name].astype(",[239,2388,2389],{"class":248},"\"category\"",[239,2391,451],{"class":277},[10,2393,2394,2397],{},[603,2395,2396],{},"Drop the empty cells early."," A sparse grid can more than double the row count with rows carrying no information, and every subsequent operation pays for them.",[10,2399,2400],{},"For a genuinely large wide sheet, melt column-group by column-group and concatenate, so peak memory holds one slice rather than the whole long frame at once:",[230,2402,2404],{"className":264,"code":2403,"language":266,"meta":235,"style":235},"import pandas as pd\n\nidentifiers = [\"region\", \"owner\"]\nperiods = [c for c in wide.columns if c not in identifiers]\n\npieces = []\nfor batch_start in range(0, len(periods), 6):\n    batch = periods[batch_start:batch_start + 6]\n    piece = wide[identifiers + batch].melt(\n        id_vars=identifiers, var_name=\"month\", value_name=\"revenue\"\n    ).dropna(subset=[\"revenue\"])\n    pieces.append(piece)\n\nlong = pd.concat(pieces, ignore_index=True)\n",[14,2405,2406,2416,2420,2437,2469,2473,2483,2510,2528,2543,2569,2584,2589,2593],{"__ignoreMap":235},[239,2407,2408,2410,2412,2414],{"class":152,"line":241},[239,2409,274],{"class":273},[239,2411,278],{"class":277},[239,2413,281],{"class":273},[239,2415,284],{"class":277},[239,2417,2418],{"class":152,"line":287},[239,2419,291],{"emptyLinePlaceholder":290},[239,2421,2422,2425,2427,2429,2431,2433,2435],{"class":152,"line":294},[239,2423,2424],{"class":277},"identifiers ",[239,2426,300],{"class":273},[239,2428,1329],{"class":277},[239,2430,520],{"class":248},[239,2432,318],{"class":277},[239,2434,525],{"class":248},[239,2436,1340],{"class":277},[239,2438,2439,2442,2444,2447,2449,2451,2453,2456,2458,2460,2463,2466],{"class":152,"line":306},[239,2440,2441],{"class":277},"periods ",[239,2443,300],{"class":273},[239,2445,2446],{"class":277}," [c ",[239,2448,1887],{"class":273},[239,2450,1890],{"class":277},[239,2452,1893],{"class":273},[239,2454,2455],{"class":277}," wide.columns ",[239,2457,831],{"class":273},[239,2459,1890],{"class":277},[239,2461,2462],{"class":273},"not",[239,2464,2465],{"class":273}," in",[239,2467,2468],{"class":277}," identifiers]\n",[239,2470,2471],{"class":152,"line":332},[239,2472,291],{"emptyLinePlaceholder":290},[239,2474,2475,2478,2480],{"class":152,"line":355},[239,2476,2477],{"class":277},"pieces ",[239,2479,300],{"class":273},[239,2481,2482],{"class":277}," []\n",[239,2484,2485,2487,2490,2492,2495,2497,2499,2501,2503,2506,2508],{"class":152,"line":379},[239,2486,1887],{"class":273},[239,2488,2489],{"class":277}," batch_start ",[239,2491,1893],{"class":273},[239,2493,2494],{"class":363}," range",[239,2496,576],{"class":277},[239,2498,47],{"class":363},[239,2500,318],{"class":277},[239,2502,1586],{"class":363},[239,2504,2505],{"class":277},"(periods), ",[239,2507,1459],{"class":363},[239,2509,924],{"class":277},[239,2511,2512,2515,2517,2520,2523,2526],{"class":152,"line":402},[239,2513,2514],{"class":277},"    batch ",[239,2516,300],{"class":273},[239,2518,2519],{"class":277}," periods[batch_start:batch_start ",[239,2521,2522],{"class":273},"+",[239,2524,2525],{"class":363}," 6",[239,2527,1340],{"class":277},[239,2529,2530,2533,2535,2538,2540],{"class":152,"line":425},[239,2531,2532],{"class":277},"    piece ",[239,2534,300],{"class":273},[239,2536,2537],{"class":277}," wide[identifiers ",[239,2539,2522],{"class":273},[239,2541,2542],{"class":277}," batch].melt(\n",[239,2544,2545,2548,2550,2553,2556,2558,2560,2562,2564,2566],{"class":152,"line":431},[239,2546,2547],{"class":442},"        id_vars",[239,2549,300],{"class":273},[239,2551,2552],{"class":277},"identifiers, ",[239,2554,2555],{"class":442},"var_name",[239,2557,300],{"class":273},[239,2559,542],{"class":248},[239,2561,318],{"class":277},[239,2563,722],{"class":442},[239,2565,300],{"class":273},[239,2567,2568],{"class":248},"\"revenue\"\n",[239,2570,2571,2574,2576,2578,2580,2582],{"class":152,"line":584},[239,2572,2573],{"class":277},"    ).dropna(",[239,2575,1640],{"class":442},[239,2577,300],{"class":273},[239,2579,517],{"class":277},[239,2581,558],{"class":248},[239,2583,677],{"class":277},[239,2585,2586],{"class":152,"line":590},[239,2587,2588],{"class":277},"    pieces.append(piece)\n",[239,2590,2591],{"class":152,"line":596},[239,2592,291],{"emptyLinePlaceholder":290},[239,2594,2595,2597,2599,2602,2605,2607,2609],{"class":152,"line":1913},[239,2596,501],{"class":442},[239,2598,504],{"class":273},[239,2600,2601],{"class":277}," pd.concat(pieces, ",[239,2603,2604],{"class":442},"ignore_index",[239,2606,300],{"class":273},[239,2608,1374],{"class":363},[239,2610,451],{"class":277},[10,2612,2613,2614,2618],{},"Dropping inside each batch is what makes this worthwhile — the empty cells never accumulate. For files too large to read at all, the chunked approach in ",[19,2615,2617],{"href":2616},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002F","reading large Excel files in chunks"," composes with this cleanly, because melting is row-independent.",[225,2620,2622],{"id":2621},"conclusion","Conclusion",[10,2624,2625,2627],{},[14,2626,16],{}," turns a sideways-growing spreadsheet into the shape every analysis tool wants, and the single most important choice is to name the identifier columns rather than the value columns — that is what makes the script survive a new month being added. Parse the resulting period column into real dates so it sorts and groups properly, decide deliberately whether an empty grid cell means \"no data\" or \"zero\", and write the long form back as a named table so readers can pivot it into whatever view they need. The wide sheet was one view; the long form is the data.",[225,2629,2631],{"id":2630},"frequently-asked-questions","Frequently asked questions",[10,2633,2634,2642,2644,2645,2647,2648,2650,2651,2653],{},[603,2635,2636,2637,2639,2640,984],{},"What is the difference between ",[14,2638,167],{}," and ",[14,2641,1411],{},[14,2643,167],{}," works on columns and returns a flat DataFrame with the former column names in a variable column. ",[14,2646,1411],{}," works on the index and returns a Series with a ",[14,2649,1421],{},". ",[14,2652,167],{}," is almost always the clearer choice when unpivoting a spreadsheet.",[10,2655,2656,2659,2660,2662],{},[603,2657,2658],{},"How do I keep more than one identifier column?","\nPass them all as a list to ",[14,2661,623],{},". Everything not listed there is treated as a value column, so listing the identifiers is safer than listing the values when new period columns get added each month.",[10,2664,2665,2668,2669,2672],{},[603,2666,2667],{},"The column names are months — how do I turn them into dates?","\nMelt first, then parse the resulting variable column with ",[14,2670,2671],{},"pd.to_datetime"," and a format string matching the header text. Parsing after melting means one conversion over a column instead of one per header.",[10,2674,2675,2678,2679,2681,2682,2684],{},[603,2676,2677],{},"What if the sheet has two header rows?","\nRead it with ",[14,2680,1297],{}," set to a list so the columns become a ",[14,2683,1421],{},", then stack the levels. pandas will produce one column per header level, which is usually exactly what you want.",[10,2686,2687,2690],{},[603,2688,2689],{},"Should I unpivot before or after cleaning?","\nUnpivot first when the cleaning applies to values, because one long value column is far easier to clean than twelve wide ones. Clean the identifier columns before, since they are unaffected by the reshape.",[225,2692,2694],{"id":2693},"related","Related",[2696,2697,2698,2705,2712,2718,2724],"ul",{},[2699,2700,2701,2702,2704],"li",{},"Up to the parent: ",[19,2703,22],{"href":21}," — the opposite reshape.",[2699,2706,2707,2711],{},[19,2708,2710],{"href":2709},"\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"," — going from long back to wide.",[2699,2713,2714,2717],{},[19,2715,2716],{"href":2026},"Add a Native Excel Pivot Table with Python"," — letting readers pivot the long form themselves.",[2699,2719,2720,2723],{},[19,2721,2722],{"href":1084},"Group Excel Rows by Month and Quarter with pandas"," — what the parsed period column unlocks.",[2699,2725,2726,2729],{},[19,2727,2728],{"href":1429},"Skip Rows and Set the Header When Reading Excel with pandas"," — reading the two-row headers this handles.",[2731,2732,2733],"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-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}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}",{"title":235,"searchDepth":287,"depth":287,"links":2735},[2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746],{"id":227,"depth":287,"text":228},{"id":454,"depth":287,"text":455},{"id":734,"depth":287,"text":735},{"id":1089,"depth":287,"text":1090},{"id":1433,"depth":287,"text":1434},{"id":1688,"depth":287,"text":1689},{"id":2087,"depth":287,"text":2088},{"id":2233,"depth":287,"text":2234},{"id":2621,"depth":287,"text":2622},{"id":2630,"depth":287,"text":2631},{"id":2693,"depth":287,"text":2694},"2026-08-15","Turn a month-per-column spreadsheet into tidy rows — pd.melt, choosing id_vars, parsing the column names into real dates, and handling multi-level headers.","md",[2751,2754,2756,2758,2760],{"q":2752,"a":2753},"What is the difference between melt and stack?","melt works on columns and returns a flat DataFrame with the former column names in a variable column. stack works on the index and returns a Series with a MultiIndex. melt is almost always the clearer choice when unpivoting a spreadsheet.",{"q":2658,"a":2755},"Pass them all as a list to id_vars. Everything not listed there is treated as a value column, so listing the identifiers is safer than listing the values when new period columns get added each month.",{"q":2667,"a":2757},"Melt first, then parse the resulting variable column with pd.to_datetime and a format string matching the header text. Parsing after melting means one conversion over a column instead of one per header.",{"q":2677,"a":2759},"Read it with header set to a list so the columns become a MultiIndex, then melt with the level names as var_name. pandas will produce one column per header level, which is usually exactly what you want.",{"q":2689,"a":2761},"Unpivot first when the cleaning applies to values, because one long value column is far easier to clean than twelve wide ones. Clean the identifier columns before, since they are unaffected by the reshape.",{},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Funpivot-a-wide-excel-sheet-with-pandas-melt",{"title":5,"description":2765},"Reshape a spreadsheet with one column per month into long format using pd.melt: id_vars and value_vars, wide_to_long, parsing header names, and writing the result back.","unpivot-a-wide-excel-sheet-with-pandas-melt","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Funpivot-a-wide-excel-sheet-with-pandas-melt\u002Findex","how-to","qkvHzoK1vshRVH1EeYdefnETIALiujLglWJN1gH5nTs",[2771,2775],{"title":2772,"path":2773,"stem":2774,"children":-1},"Export a Pandas Pivot Table to Excel (Formatted)","\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted","advanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fexport-pandas-pivot-table-to-excel-formatted\u002Findex",{"title":2776,"path":2777,"stem":2778,"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",1786800028554]