[{"data":1,"prerenderedAt":2533},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas":2499,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas":2508,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas":2513,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas":2514},{"id":4,"title":5,"body":6,"dateModified":2463,"datePublished":2463,"description":2464,"extension":2465,"faq":2466,"keyword":2475,"kind":2476,"meta":2477,"navigation":354,"path":2487,"seo":2488,"slug":2491,"stem":2492,"topics":2493,"type":2497,"__hash__":2498},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas\u002Findex.md","Standardise Excel Column Names with pandas",{"type":7,"value":8,"toc":2449},"minimark",[9,24,291,296,323,326,585,612,616,619,1036,1067,1071,1081,1241,1245,1252,1320,1330,1334,1337,1525,1531,1659,1663,1666,1849,1856,2000,2004,2007,2132,2135,2139,2142,2317,2324,2328,2377,2381,2384,2388,2394,2400,2406,2412,2416,2445],[10,11,12,13,17,18,23],"p",{},"Headers in real workbooks are written for people, not code. \" Order ID\" has a leading space, \"Customer↵Name\" contains a line break, \"Amount (€)\" appears twice, one column has no header at all and the monthly columns are real dates. Every script that reads such a sheet ends up full of ",[14,15,16],"code",{},"df[\"Customer\\nName\"]",", and every new export from a different system breaks it again. Cleaning the labels once, right after reading, fixes that. This guide, part of ",[19,20,22],"a",{"href":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002F","Cleaning Excel Data with pandas",", builds a header cleaner, maps synonyms from different sources onto one schema and checks that the columns you need are present.",[25,26,34,35,34,39,34,43,34,51,34,60,34,64,34,71,34,75,34,80,34,82,34,86,34,89,34,93,34,95,34,99,34,102,34,106,34,108,34,111,34,114,34,118,34,120,34,123,34,126,34,130,34,132,34,135,34,138,34,142,34,144,34,147,34,150,34,154,34,156,34,159,34,162,34,166,34,168,34,171,34,174,34,178,34,181,34,183,34,186,34,188,34,191,34,193,34,195,34,197,34,200,34,202,34,204,34,206,34,209,34,211,34,213,34,215,34,218,34,220,34,222,34,224,34,227,34,229,34,231,34,233,34,236,34,238,34,240,34,242,34,245,34,247,34,249,34,251,34,254,34,260,34,264,34,272,34,277,34,285],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"104 0 552 304","img",[30,31],"cn-before-after-t","cn-before-after-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:552px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[36,37,38],"title",{"id":30},"Messy Excel headers become predictable names",[40,41,42],"desc",{"id":31},"Leading spaces, line breaks, currency symbols, a duplicate header, an empty header, a hyphen, a date header and an accented word are cleaned into lower-case names joined by underscores, with the duplicate numbered and the date written as year and month.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"104","0","552","304","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"120.2","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[44,61],{"x":62,"y":54,"width":63,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"152.2","158.5",[65,66,70],"text",{"x":67,"y":68,"style":69},"231.5","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[44,72],{"x":53,"y":73,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"64.0","28.0",[65,76,79],{"x":77,"y":78,"style":69},"136.2","83.0","1",[44,81],{"x":62,"y":73,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,83,85],{"x":67,"y":78,"style":84},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle"," Order ID",[44,87],{"x":53,"y":88,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"92.0",[65,90,92],{"x":77,"y":91,"style":69},"111.0","2",[44,94],{"x":62,"y":88,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,96,98],{"x":67,"y":91,"style":97},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Customer\\nName",[44,100],{"x":53,"y":101,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"120.0",[65,103,105],{"x":77,"y":104,"style":69},"139.0","3",[44,107],{"x":62,"y":101,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,109,110],{"x":67,"y":104,"style":97},"Amount (€)",[44,112],{"x":53,"y":113,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"148.0",[65,115,117],{"x":77,"y":116,"style":69},"167.0","4",[44,119],{"x":62,"y":113,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,121,122],{"x":67,"y":116,"style":97},"Amount (€).1",[44,124],{"x":53,"y":125,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"176.0",[65,127,129],{"x":77,"y":128,"style":69},"195.0","5",[44,131],{"x":62,"y":125,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,133,134],{"x":67,"y":128,"style":97},"Unnamed: 4",[44,136],{"x":53,"y":137,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"204.0",[65,139,141],{"x":77,"y":140,"style":69},"223.0","6",[44,143],{"x":62,"y":137,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,145,146],{"x":67,"y":140,"style":97},"Ship-Date",[44,148],{"x":53,"y":149,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"232.0",[65,151,153],{"x":77,"y":152,"style":69},"251.0","7",[44,155],{"x":62,"y":149,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,157,158],{"x":67,"y":152,"style":97},"2026-01-01 00:00:00",[44,160],{"x":53,"y":161,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},"260.0",[65,163,165],{"x":77,"y":164,"style":69},"279.0","8",[44,167],{"x":62,"y":161,"width":63,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,169,170],{"x":67,"y":164,"style":97},"Café",[44,172],{"x":173,"y":54,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"484.8",[44,175],{"x":176,"y":54,"width":177,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"516.8","123.0",[65,179,70],{"x":180,"y":68,"style":69},"578.3",[44,182],{"x":173,"y":73,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,184,79],{"x":185,"y":78,"style":69},"500.8",[44,187],{"x":176,"y":73,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,189,190],{"x":180,"y":78,"style":84},"order_id",[44,192],{"x":173,"y":88,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,194,92],{"x":185,"y":91,"style":69},[44,196],{"x":176,"y":88,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,198,199],{"x":180,"y":91,"style":97},"customer_name",[44,201],{"x":173,"y":101,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,203,105],{"x":185,"y":104,"style":69},[44,205],{"x":176,"y":101,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,207,208],{"x":180,"y":104,"style":97},"amount_eur",[44,210],{"x":173,"y":113,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,212,117],{"x":185,"y":116,"style":69},[44,214],{"x":176,"y":113,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,216,217],{"x":180,"y":116,"style":97},"amount_eur_2",[44,219],{"x":173,"y":125,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,221,129],{"x":185,"y":128,"style":69},[44,223],{"x":176,"y":125,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,225,226],{"x":180,"y":128,"style":97},"unnamed_4",[44,228],{"x":173,"y":137,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,230,141],{"x":185,"y":140,"style":69},[44,232],{"x":176,"y":137,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,234,235],{"x":180,"y":140,"style":97},"ship_date",[44,237],{"x":173,"y":149,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,239,153],{"x":185,"y":152,"style":69},[44,241],{"x":176,"y":149,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,243,244],{"x":180,"y":152,"style":97},"m_2026_01",[44,246],{"x":173,"y":161,"width":55,"height":74,"rx":47,"fill":57,"stroke":58,"style":59},[65,248,165],{"x":185,"y":164,"style":69},[44,250],{"x":176,"y":161,"width":177,"height":74,"rx":47,"fill":50,"stroke":58,"style":59},[65,252,253],{"x":180,"y":164,"style":97},"cafe",[65,255,259],{"x":256,"y":257,"style":258},"215.5","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[65,261,263],{"x":262,"y":257,"style":258},"562.3","after",[265,266],"line",{"x1":267,"y1":268,"x2":269,"y2":268,"stroke":270,"style":271},"320.8","164.0","470.8","var(--brand,#5b5cf0)","stroke-width:2px",[273,274],"polygon",{"points":275,"fill":276},"474.8,164.0 465.8,169.0 465.8,159.0","#5b5cf0",[44,278],{"x":279,"y":280,"width":281,"height":282,"rx":283,"fill":50,"stroke":58,"style":284},"344.2","131.0","107.2","22.0","11","stroke-width:1.2px",[65,286,290],{"x":287,"y":288,"style":289},"397.8","146.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","clean_header",[292,293,295],"h2",{"id":294},"prerequisites","Prerequisites",[297,298,303],"pre",{"className":299,"code":300,"language":301,"meta":302,"style":302},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,304,305],{"__ignoreMap":302},[306,307,309,313,317,320],"span",{"class":265,"line":308},1,[306,310,312],{"class":311},"sMTad","pip",[306,314,316],{"class":315},"srMev"," install",[306,318,319],{"class":315}," pandas",[306,321,322],{"class":315}," openpyxl\n",[10,324,325],{},"A sample sheet with the typical problems:",[297,327,331],{"className":328,"code":329,"language":330,"meta":302,"style":302},"language-python shiki shiki-themes github-light github-dark-high-contrast","import datetime as dt\n\nimport pandas as pd\nfrom openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.append([\" Order ID\", \"Customer\\nName\", \"Amount (€)\", \"Amount (€)\", None,\n           \"Ship-Date\", dt.datetime(2026, 1, 1), dt.datetime(2026, 2, 1), \"Café\"])\nws.append([1, \"Acme\", 10, 11, None, \"2026-01-02\", 5, 6, \"yes\"])\nwb.save(\"headers.xlsx\")\n\ndf = pd.read_excel(\"headers.xlsx\")\nprint(list(df.columns))\n","python",[14,332,333,349,356,369,383,388,400,411,451,493,538,550,555,570],{"__ignoreMap":302},[306,334,335,339,343,346],{"class":265,"line":308},[306,336,338],{"class":337},"s-kum","import",[306,340,342],{"class":341},"skGVy"," datetime ",[306,344,345],{"class":337},"as",[306,347,348],{"class":341}," dt\n",[306,350,352],{"class":265,"line":351},2,[306,353,355],{"emptyLinePlaceholder":354},true,"\n",[306,357,359,361,364,366],{"class":265,"line":358},3,[306,360,338],{"class":337},[306,362,363],{"class":341}," pandas ",[306,365,345],{"class":337},[306,367,368],{"class":341}," pd\n",[306,370,372,375,378,380],{"class":265,"line":371},4,[306,373,374],{"class":337},"from",[306,376,377],{"class":341}," openpyxl ",[306,379,338],{"class":337},[306,381,382],{"class":341}," Workbook\n",[306,384,386],{"class":265,"line":385},5,[306,387,355],{"emptyLinePlaceholder":354},[306,389,391,394,397],{"class":265,"line":390},6,[306,392,393],{"class":341},"wb ",[306,395,396],{"class":337},"=",[306,398,399],{"class":341}," Workbook()\n",[306,401,403,406,408],{"class":265,"line":402},7,[306,404,405],{"class":341},"ws ",[306,407,396],{"class":337},[306,409,410],{"class":341}," wb.active\n",[306,412,414,417,420,423,426,430,433,435,438,440,442,444,448],{"class":265,"line":413},8,[306,415,416],{"class":341},"ws.append([",[306,418,419],{"class":315},"\" Order ID\"",[306,421,422],{"class":341},", ",[306,424,425],{"class":315},"\"Customer",[306,427,429],{"class":428},"sSjpA","\\n",[306,431,432],{"class":315},"Name\"",[306,434,422],{"class":341},[306,436,437],{"class":315},"\"Amount (€)\"",[306,439,422],{"class":341},[306,441,437],{"class":315},[306,443,422],{"class":341},[306,445,447],{"class":446},"sP0c6","None",[306,449,450],{"class":341},",\n",[306,452,454,457,460,463,465,467,469,471,474,476,478,480,482,484,487,490],{"class":265,"line":453},9,[306,455,456],{"class":315},"           \"Ship-Date\"",[306,458,459],{"class":341},", dt.datetime(",[306,461,462],{"class":446},"2026",[306,464,422],{"class":341},[306,466,79],{"class":446},[306,468,422],{"class":341},[306,470,79],{"class":446},[306,472,473],{"class":341},"), dt.datetime(",[306,475,462],{"class":446},[306,477,422],{"class":341},[306,479,92],{"class":446},[306,481,422],{"class":341},[306,483,79],{"class":446},[306,485,486],{"class":341},"), ",[306,488,489],{"class":315},"\"Café\"",[306,491,492],{"class":341},"])\n",[306,494,496,498,500,502,505,507,510,512,514,516,518,520,523,525,527,529,531,533,536],{"class":265,"line":495},10,[306,497,416],{"class":341},[306,499,79],{"class":446},[306,501,422],{"class":341},[306,503,504],{"class":315},"\"Acme\"",[306,506,422],{"class":341},[306,508,509],{"class":446},"10",[306,511,422],{"class":341},[306,513,283],{"class":446},[306,515,422],{"class":341},[306,517,447],{"class":446},[306,519,422],{"class":341},[306,521,522],{"class":315},"\"2026-01-02\"",[306,524,422],{"class":341},[306,526,129],{"class":446},[306,528,422],{"class":341},[306,530,141],{"class":446},[306,532,422],{"class":341},[306,534,535],{"class":315},"\"yes\"",[306,537,492],{"class":341},[306,539,541,544,547],{"class":265,"line":540},11,[306,542,543],{"class":341},"wb.save(",[306,545,546],{"class":315},"\"headers.xlsx\"",[306,548,549],{"class":341},")\n",[306,551,553],{"class":265,"line":552},12,[306,554,355],{"emptyLinePlaceholder":354},[306,556,558,561,563,566,568],{"class":265,"line":557},13,[306,559,560],{"class":341},"df ",[306,562,396],{"class":337},[306,564,565],{"class":341}," pd.read_excel(",[306,567,546],{"class":315},[306,569,549],{"class":341},[306,571,573,576,579,582],{"class":265,"line":572},14,[306,574,575],{"class":446},"print",[306,577,578],{"class":341},"(",[306,580,581],{"class":446},"list",[306,583,584],{"class":341},"(df.columns))\n",[10,586,587,588,591,592,595,596,599,600,603,604,607,608,611],{},"pandas has already changed two things. The second \"Amount (€)\" is now ",[14,589,590],{},"\"Amount (€).1\"",", because column labels must be unique, and the empty header became ",[14,593,594],{},"\"Unnamed: 4\"",". The two month columns are ",[14,597,598],{},"datetime"," objects, not strings — anything that assumes ",[14,601,602],{},"str"," labels, such as ",[14,605,606],{},"df.columns.str.lower()",", will produce ",[14,609,610],{},"NaN"," for them.",[292,613,615],{"id":614},"step-1-a-header-cleaning-function","Step 1 — A header cleaning function",[10,617,618],{},"Convert every label to text, fold accents to plain letters, spell out the symbols that carry meaning, and collapse everything else into single underscores:",[297,620,622],{"className":328,"code":621,"language":330,"meta":302,"style":302},"import re\nimport unicodedata\n\nSYMBOLS = {\"€\": \" eur \", \"$\": \" usd \", \"£\": \" gbp \", \"%\": \" pct \", \"#\": \" no \", \"&\": \" and \"}\n\ndef clean_header(label) -> str:\n    if isinstance(label, (dt.date, dt.datetime, pd.Timestamp)):\n        return f\"m_{label:%Y_%m}\"\n    text = str(label)\n    text = re.sub(r\"\\.(\\d+)$\", lambda m: f\"_{int(m.group(1)) + 1}\", text)   # pandas' .1 suffix\n    for symbol, word in SYMBOLS.items():\n        text = text.replace(symbol, word)\n    text = unicodedata.normalize(\"NFKD\", text).encode(\"ascii\", \"ignore\").decode()\n    text = re.sub(r\"[^0-9A-Za-z]+\", \"_\", text).strip(\"_\").lower()\n    if not text or text[0].isdigit():\n        text = f\"c_{text}\"\n    return text\n\nprint([clean_header(c) for c in df.columns])\n",[14,623,624,631,638,642,715,719,736,747,781,794,867,884,894,920,961,983,1003,1012,1017],{"__ignoreMap":302},[306,625,626,628],{"class":265,"line":308},[306,627,338],{"class":337},[306,629,630],{"class":341}," re\n",[306,632,633,635],{"class":265,"line":351},[306,634,338],{"class":337},[306,636,637],{"class":341}," unicodedata\n",[306,639,640],{"class":265,"line":358},[306,641,355],{"emptyLinePlaceholder":354},[306,643,644,647,650,653,656,659,662,664,667,669,672,674,677,679,682,684,687,689,692,694,697,699,702,704,707,709,712],{"class":265,"line":371},[306,645,646],{"class":446},"SYMBOLS",[306,648,649],{"class":337}," =",[306,651,652],{"class":341}," {",[306,654,655],{"class":315},"\"€\"",[306,657,658],{"class":341},": ",[306,660,661],{"class":315},"\" eur \"",[306,663,422],{"class":341},[306,665,666],{"class":315},"\"$\"",[306,668,658],{"class":341},[306,670,671],{"class":315},"\" usd \"",[306,673,422],{"class":341},[306,675,676],{"class":315},"\"£\"",[306,678,658],{"class":341},[306,680,681],{"class":315},"\" gbp \"",[306,683,422],{"class":341},[306,685,686],{"class":315},"\"%\"",[306,688,658],{"class":341},[306,690,691],{"class":315},"\" pct \"",[306,693,422],{"class":341},[306,695,696],{"class":315},"\"#\"",[306,698,658],{"class":341},[306,700,701],{"class":315},"\" no \"",[306,703,422],{"class":341},[306,705,706],{"class":315},"\"&\"",[306,708,658],{"class":341},[306,710,711],{"class":315},"\" and \"",[306,713,714],{"class":341},"}\n",[306,716,717],{"class":265,"line":385},[306,718,355],{"emptyLinePlaceholder":354},[306,720,721,724,728,731,733],{"class":265,"line":390},[306,722,723],{"class":337},"def",[306,725,727],{"class":726},"s_Opv"," clean_header",[306,729,730],{"class":341},"(label) -> ",[306,732,602],{"class":446},[306,734,735],{"class":341},":\n",[306,737,738,741,744],{"class":265,"line":402},[306,739,740],{"class":337},"    if",[306,742,743],{"class":446}," isinstance",[306,745,746],{"class":341},"(label, (dt.date, dt.datetime, pd.Timestamp)):\n",[306,748,749,752,755,758,761,764,767,770,772,775,778],{"class":265,"line":413},[306,750,751],{"class":337},"        return",[306,753,754],{"class":337}," f",[306,756,757],{"class":315},"\"m_",[306,759,760],{"class":428},"{",[306,762,763],{"class":341},"label:",[306,765,766],{"class":337},"%",[306,768,769],{"class":341},"Y_",[306,771,766],{"class":337},[306,773,774],{"class":341},"m",[306,776,777],{"class":428},"}",[306,779,780],{"class":315},"\"\n",[306,782,783,786,788,791],{"class":265,"line":453},[306,784,785],{"class":341},"    text ",[306,787,396],{"class":337},[306,789,790],{"class":446}," str",[306,792,793],{"class":341},"(label)\n",[306,795,796,798,800,803,806,809,813,816,819,822,824,826,829,832,835,838,840,843,846,848,851,853,856,858,860,863],{"class":265,"line":495},[306,797,785],{"class":341},[306,799,396],{"class":337},[306,801,802],{"class":341}," re.sub(",[306,804,805],{"class":337},"r",[306,807,808],{"class":315},"\"",[306,810,812],{"class":811},"s_b0D","\\.",[306,814,815],{"class":446},"(\\d",[306,817,818],{"class":337},"+",[306,820,821],{"class":446},")$",[306,823,808],{"class":315},[306,825,422],{"class":341},[306,827,828],{"class":337},"lambda",[306,830,831],{"class":341}," m: ",[306,833,834],{"class":337},"f",[306,836,837],{"class":315},"\"_",[306,839,760],{"class":428},[306,841,842],{"class":446},"int",[306,844,845],{"class":341},"(m.group(",[306,847,79],{"class":446},[306,849,850],{"class":341},")) ",[306,852,818],{"class":337},[306,854,855],{"class":446}," 1",[306,857,777],{"class":428},[306,859,808],{"class":315},[306,861,862],{"class":341},", text)   ",[306,864,866],{"class":865},"s-wDw","# pandas' .1 suffix\n",[306,868,869,872,875,878,881],{"class":265,"line":540},[306,870,871],{"class":337},"    for",[306,873,874],{"class":341}," symbol, word ",[306,876,877],{"class":337},"in",[306,879,880],{"class":446}," SYMBOLS",[306,882,883],{"class":341},".items():\n",[306,885,886,889,891],{"class":265,"line":552},[306,887,888],{"class":341},"        text ",[306,890,396],{"class":337},[306,892,893],{"class":341}," text.replace(symbol, word)\n",[306,895,896,898,900,903,906,909,912,914,917],{"class":265,"line":557},[306,897,785],{"class":341},[306,899,396],{"class":337},[306,901,902],{"class":341}," unicodedata.normalize(",[306,904,905],{"class":315},"\"NFKD\"",[306,907,908],{"class":341},", text).encode(",[306,910,911],{"class":315},"\"ascii\"",[306,913,422],{"class":341},[306,915,916],{"class":315},"\"ignore\"",[306,918,919],{"class":341},").decode()\n",[306,921,922,924,926,928,930,932,935,938,941,944,946,948,950,953,956,958],{"class":265,"line":572},[306,923,785],{"class":341},[306,925,396],{"class":337},[306,927,802],{"class":341},[306,929,805],{"class":337},[306,931,808],{"class":315},[306,933,934],{"class":446},"[",[306,936,937],{"class":337},"^",[306,939,940],{"class":428},"0-9A-Za-z",[306,942,943],{"class":446},"]",[306,945,818],{"class":337},[306,947,808],{"class":315},[306,949,422],{"class":341},[306,951,952],{"class":315},"\"_\"",[306,954,955],{"class":341},", text).strip(",[306,957,952],{"class":315},[306,959,960],{"class":341},").lower()\n",[306,962,964,966,969,972,975,978,980],{"class":265,"line":963},15,[306,965,740],{"class":337},[306,967,968],{"class":337}," not",[306,970,971],{"class":341}," text ",[306,973,974],{"class":337},"or",[306,976,977],{"class":341}," text[",[306,979,47],{"class":446},[306,981,982],{"class":341},"].isdigit():\n",[306,984,986,988,990,992,995,997,999,1001],{"class":265,"line":985},16,[306,987,888],{"class":341},[306,989,396],{"class":337},[306,991,754],{"class":337},[306,993,994],{"class":315},"\"c_",[306,996,760],{"class":428},[306,998,65],{"class":341},[306,1000,777],{"class":428},[306,1002,780],{"class":315},[306,1004,1006,1009],{"class":265,"line":1005},17,[306,1007,1008],{"class":337},"    return",[306,1010,1011],{"class":341}," text\n",[306,1013,1015],{"class":265,"line":1014},18,[306,1016,355],{"emptyLinePlaceholder":354},[306,1018,1020,1022,1025,1028,1031,1033],{"class":265,"line":1019},19,[306,1021,575],{"class":446},[306,1023,1024],{"class":341},"([clean_header(c) ",[306,1026,1027],{"class":337},"for",[306,1029,1030],{"class":341}," c ",[306,1032,877],{"class":337},[306,1034,1035],{"class":341}," df.columns])\n",[10,1037,1038,1039,1042,1043,1046,1047,1050,1051,1054,1055,1058,1059,1062,1063,1066],{},"Each rule solves a specific problem. Spelling out ",[14,1040,1041],{},"€"," as ",[14,1044,1045],{},"eur"," keeps the currency in the name — dropping symbols outright would turn \"Amount (€)\" and \"Amount ($)\" into the same ",[14,1048,1049],{},"amount",". Turning pandas' ",[14,1052,1053],{},".1"," suffix into ",[14,1056,1057],{},"_2"," makes duplicates read naturally. ",[14,1060,1061],{},"NFKD"," normalisation splits \"é\" into \"e\" plus an accent mark, and the ASCII encode drops the mark. Names that would start with a digit get a prefix, so they remain valid Python identifiers and work with ",[14,1064,1065],{},"df.itertuples()",".",[292,1068,1070],{"id":1069},"step-2-guarantee-uniqueness-after-cleaning","Step 2 — Guarantee uniqueness after cleaning",[10,1072,1073,1074,1076,1077,1080],{},"Cleaning can create new collisions: \"Amount €\" and \"amount (EUR)\" both become ",[14,1075,208],{},". pandas allows duplicate labels, but ",[14,1078,1079],{},"df[\"amount_eur\"]"," then returns a DataFrame instead of a Series and most code breaks later in a confusing way. Number the repeats deterministically:",[297,1082,1084],{"className":328,"code":1083,"language":330,"meta":302,"style":302},"def unique(names):\n    seen = {}\n    out = []\n    for name in names:\n        seen[name] = seen.get(name, 0) + 1\n        out.append(name if seen[name] == 1 else f\"{name}_{seen[name]}\")\n    return out\n\ndf.columns = unique([clean_header(c) for c in df.columns])\nprint(list(df.columns))\n# ['order_id', 'customer_name', 'amount_eur', 'amount_eur_2', 'unnamed_4',\n#  'ship_date', 'm_2026_01', 'm_2026_02', 'cafe']\n",[14,1085,1086,1096,1106,1116,1128,1148,1192,1199,1203,1221,1231,1236],{"__ignoreMap":302},[306,1087,1088,1090,1093],{"class":265,"line":308},[306,1089,723],{"class":337},[306,1091,1092],{"class":726}," unique",[306,1094,1095],{"class":341},"(names):\n",[306,1097,1098,1101,1103],{"class":265,"line":351},[306,1099,1100],{"class":341},"    seen ",[306,1102,396],{"class":337},[306,1104,1105],{"class":341}," {}\n",[306,1107,1108,1111,1113],{"class":265,"line":358},[306,1109,1110],{"class":341},"    out ",[306,1112,396],{"class":337},[306,1114,1115],{"class":341}," []\n",[306,1117,1118,1120,1123,1125],{"class":265,"line":371},[306,1119,871],{"class":337},[306,1121,1122],{"class":341}," name ",[306,1124,877],{"class":337},[306,1126,1127],{"class":341}," names:\n",[306,1129,1130,1133,1135,1138,1140,1143,1145],{"class":265,"line":385},[306,1131,1132],{"class":341},"        seen[name] ",[306,1134,396],{"class":337},[306,1136,1137],{"class":341}," seen.get(name, ",[306,1139,47],{"class":446},[306,1141,1142],{"class":341},") ",[306,1144,818],{"class":337},[306,1146,1147],{"class":446}," 1\n",[306,1149,1150,1153,1156,1159,1162,1164,1167,1169,1171,1173,1176,1178,1181,1183,1186,1188,1190],{"class":265,"line":390},[306,1151,1152],{"class":341},"        out.append(name ",[306,1154,1155],{"class":337},"if",[306,1157,1158],{"class":341}," seen[name] ",[306,1160,1161],{"class":337},"==",[306,1163,855],{"class":446},[306,1165,1166],{"class":337}," else",[306,1168,754],{"class":337},[306,1170,808],{"class":315},[306,1172,760],{"class":428},[306,1174,1175],{"class":341},"name",[306,1177,777],{"class":428},[306,1179,1180],{"class":315},"_",[306,1182,760],{"class":428},[306,1184,1185],{"class":341},"seen[name]",[306,1187,777],{"class":428},[306,1189,808],{"class":315},[306,1191,549],{"class":341},[306,1193,1194,1196],{"class":265,"line":402},[306,1195,1008],{"class":337},[306,1197,1198],{"class":341}," out\n",[306,1200,1201],{"class":265,"line":413},[306,1202,355],{"emptyLinePlaceholder":354},[306,1204,1205,1208,1210,1213,1215,1217,1219],{"class":265,"line":453},[306,1206,1207],{"class":341},"df.columns ",[306,1209,396],{"class":337},[306,1211,1212],{"class":341}," unique([clean_header(c) ",[306,1214,1027],{"class":337},[306,1216,1030],{"class":341},[306,1218,877],{"class":337},[306,1220,1035],{"class":341},[306,1222,1223,1225,1227,1229],{"class":265,"line":495},[306,1224,575],{"class":446},[306,1226,578],{"class":341},[306,1228,581],{"class":446},[306,1230,584],{"class":341},[306,1232,1233],{"class":265,"line":540},[306,1234,1235],{"class":865},"# ['order_id', 'customer_name', 'amount_eur', 'amount_eur_2', 'unnamed_4',\n",[306,1237,1238],{"class":265,"line":552},[306,1239,1240],{"class":865},"#  'ship_date', 'm_2026_01', 'm_2026_02', 'cafe']\n",[292,1242,1244],{"id":1243},"step-3-drop-the-columns-that-are-not-really-there","Step 3 — Drop the columns that are not really there",[10,1246,1247,1248,1251],{},"An ",[14,1249,1250],{},"unnamed_*"," column that is entirely empty is a spacer or leftover formatting, not data. Drop it — but only if it is empty, because an unnamed column with values usually means the header is in a different row:",[297,1253,1255],{"className":328,"code":1254,"language":330,"meta":302,"style":302},"empty_unnamed = [c for c in df.columns if c.startswith(\"unnamed_\") and df[c].isna().all()]\ndf = df.drop(columns=empty_unnamed)\nprint(empty_unnamed)              # ['unnamed_4']\n",[14,1256,1257,1292,1310],{"__ignoreMap":302},[306,1258,1259,1262,1264,1267,1269,1271,1273,1276,1278,1281,1284,1286,1289],{"class":265,"line":308},[306,1260,1261],{"class":341},"empty_unnamed ",[306,1263,396],{"class":337},[306,1265,1266],{"class":341}," [c ",[306,1268,1027],{"class":337},[306,1270,1030],{"class":341},[306,1272,877],{"class":337},[306,1274,1275],{"class":341}," df.columns ",[306,1277,1155],{"class":337},[306,1279,1280],{"class":341}," c.startswith(",[306,1282,1283],{"class":315},"\"unnamed_\"",[306,1285,1142],{"class":341},[306,1287,1288],{"class":337},"and",[306,1290,1291],{"class":341}," df[c].isna().all()]\n",[306,1293,1294,1296,1298,1301,1305,1307],{"class":265,"line":351},[306,1295,560],{"class":341},[306,1297,396],{"class":337},[306,1299,1300],{"class":341}," df.drop(",[306,1302,1304],{"class":1303},"sa561","columns",[306,1306,396],{"class":337},[306,1308,1309],{"class":341},"empty_unnamed)\n",[306,1311,1312,1314,1317],{"class":265,"line":358},[306,1313,575],{"class":446},[306,1315,1316],{"class":341},"(empty_unnamed)              ",[306,1318,1319],{"class":865},"# ['unnamed_4']\n",[10,1321,1322,1323,1325,1326,1066],{},"If many columns come out as ",[14,1324,1250],{}," and contain values, the table does not start on the first row; detect the real header first, as shown in ",[19,1327,1329],{"href":1328},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Ffind-the-header-row-in-an-excel-sheet-with-a-shifting-layout\u002F","Find the Header Row in an Excel Sheet with a Shifting Layout",[292,1331,1333],{"id":1332},"step-4-map-synonyms-to-one-canonical-schema","Step 4 — Map synonyms to one canonical schema",[10,1335,1336],{},"The same field arrives under different names from different systems: \"Order ID\", \"Order No\", \"Ord #\". After cleaning, map each known alias to the name your code uses. Keep the aliases in one table so adding a new source is a one-line change:",[297,1338,1340],{"className":328,"code":1339,"language":330,"meta":302,"style":302},"ALIASES = {\n    \"order_id\": [\"order_id\", \"order_no\", \"order_number\", \"ord_no\"],\n    \"customer\": [\"customer_name\", \"customer\", \"client\", \"account_name\"],\n    \"amount\": [\"amount_eur\", \"amount\", \"net_amount\", \"value_eur\"],\n    \"ship_date\": [\"ship_date\", \"shipped_on\", \"dispatch_date\"],\n}\nrename = {alias: canon for canon, aliases in ALIASES.items() for alias in aliases}\ndf = df.rename(columns=rename)\nprint(list(df.columns))\n# ['order_id', 'customer', 'amount', 'amount_eur_2', 'ship_date', 'm_2026_01', 'm_2026_02', 'cafe']\n",[14,1341,1342,1352,1381,1408,1435,1457,1461,1494,1510,1520],{"__ignoreMap":302},[306,1343,1344,1347,1349],{"class":265,"line":308},[306,1345,1346],{"class":446},"ALIASES",[306,1348,649],{"class":337},[306,1350,1351],{"class":341}," {\n",[306,1353,1354,1357,1360,1363,1365,1368,1370,1373,1375,1378],{"class":265,"line":351},[306,1355,1356],{"class":315},"    \"order_id\"",[306,1358,1359],{"class":341},": [",[306,1361,1362],{"class":315},"\"order_id\"",[306,1364,422],{"class":341},[306,1366,1367],{"class":315},"\"order_no\"",[306,1369,422],{"class":341},[306,1371,1372],{"class":315},"\"order_number\"",[306,1374,422],{"class":341},[306,1376,1377],{"class":315},"\"ord_no\"",[306,1379,1380],{"class":341},"],\n",[306,1382,1383,1386,1388,1391,1393,1396,1398,1401,1403,1406],{"class":265,"line":358},[306,1384,1385],{"class":315},"    \"customer\"",[306,1387,1359],{"class":341},[306,1389,1390],{"class":315},"\"customer_name\"",[306,1392,422],{"class":341},[306,1394,1395],{"class":315},"\"customer\"",[306,1397,422],{"class":341},[306,1399,1400],{"class":315},"\"client\"",[306,1402,422],{"class":341},[306,1404,1405],{"class":315},"\"account_name\"",[306,1407,1380],{"class":341},[306,1409,1410,1413,1415,1418,1420,1423,1425,1428,1430,1433],{"class":265,"line":371},[306,1411,1412],{"class":315},"    \"amount\"",[306,1414,1359],{"class":341},[306,1416,1417],{"class":315},"\"amount_eur\"",[306,1419,422],{"class":341},[306,1421,1422],{"class":315},"\"amount\"",[306,1424,422],{"class":341},[306,1426,1427],{"class":315},"\"net_amount\"",[306,1429,422],{"class":341},[306,1431,1432],{"class":315},"\"value_eur\"",[306,1434,1380],{"class":341},[306,1436,1437,1440,1442,1445,1447,1450,1452,1455],{"class":265,"line":385},[306,1438,1439],{"class":315},"    \"ship_date\"",[306,1441,1359],{"class":341},[306,1443,1444],{"class":315},"\"ship_date\"",[306,1446,422],{"class":341},[306,1448,1449],{"class":315},"\"shipped_on\"",[306,1451,422],{"class":341},[306,1453,1454],{"class":315},"\"dispatch_date\"",[306,1456,1380],{"class":341},[306,1458,1459],{"class":265,"line":390},[306,1460,714],{"class":341},[306,1462,1463,1466,1468,1471,1473,1476,1478,1481,1484,1486,1489,1491],{"class":265,"line":402},[306,1464,1465],{"class":341},"rename ",[306,1467,396],{"class":337},[306,1469,1470],{"class":341}," {alias: canon ",[306,1472,1027],{"class":337},[306,1474,1475],{"class":341}," canon, aliases ",[306,1477,877],{"class":337},[306,1479,1480],{"class":446}," ALIASES",[306,1482,1483],{"class":341},".items() ",[306,1485,1027],{"class":337},[306,1487,1488],{"class":341}," alias ",[306,1490,877],{"class":337},[306,1492,1493],{"class":341}," aliases}\n",[306,1495,1496,1498,1500,1503,1505,1507],{"class":265,"line":413},[306,1497,560],{"class":341},[306,1499,396],{"class":337},[306,1501,1502],{"class":341}," df.rename(",[306,1504,1304],{"class":1303},[306,1506,396],{"class":337},[306,1508,1509],{"class":341},"rename)\n",[306,1511,1512,1514,1516,1518],{"class":265,"line":453},[306,1513,575],{"class":446},[306,1515,578],{"class":341},[306,1517,581],{"class":446},[306,1519,584],{"class":341},[306,1521,1522],{"class":265,"line":495},[306,1523,1524],{"class":865},"# ['order_id', 'customer', 'amount', 'amount_eur_2', 'ship_date', 'm_2026_01', 'm_2026_02', 'cafe']\n",[10,1526,1527,1530],{},[14,1528,1529],{},"rename"," ignores labels that are not in the dictionary, so unknown columns pass through with their cleaned names. That is deliberate: a new column should be noticed and added to the table, not dropped silently.",[25,1532,34,1538,34,1541,34,1544,34,1548,34,1557,34,1568,34,1574,34,1579,34,1585,34,1592,34,1598,34,1603,34,1606,34,1609,34,1613,34,1620,34,1624,34,1628,34,1631,34,1633,34,1638,34,1645,34,1650,34,1654,34,1657],{"viewBox":1533,"role":28,"ariaLabelledBy":1534,"xmlns":32,"style":1537},"4 0 752 332",[1535,1536],"cn-aliases-t","cn-aliases-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,1539,1540],{"id":1535},"Three sources, one set of column names",[40,1542,1543],{"id":1536},"A web shop export with Order No and Client, an ERP export with Order Number and Account Name, and a manual sheet with Ord",[44,1545],{"x":117,"y":47,"width":1546,"height":1547,"fill":50},"752","332",[44,1549],{"x":1550,"y":1551,"width":1552,"height":1553,"rx":1554,"fill":1555,"stroke":1556,"style":271},"520.0","119.5","220.0","93.0","12","#d9f4f1","var(--teal,#0f9488)",[1558,1559,1561],"g",{"transform":1560},"translate(618.0,132.5) scale(1.000)",[1562,1563],"path",{"d":1564,"fill":1565,"stroke":1566,"style":1567},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--teal-ink,#0b6157)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[65,1569,1573],{"x":1570,"y":1571,"style":1572},"630.0","179.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Canonical schema",[65,1575,1578],{"x":1570,"y":1576,"style":1577},"198.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","order_id, customer, amount",[44,1580],{"x":1581,"y":1582,"width":1583,"height":88,"rx":1554,"fill":1584,"stroke":270,"style":271},"20.0","16.0","250.0","#f0f4ff",[1558,1586,1588],{"transform":1587},"translate(133.0,29.0) scale(1.000)",[1562,1589],{"d":1590,"fill":1565,"stroke":1591,"style":1567},"M12 3a9 9 0 1 0 .01 0z M3 12h18 M12 3c3.2 3 3.2 15 0 18 M12 3c-3.2 3-3.2 15 0 18","var(--brand-strong,#4338ca)",[65,1593,1597],{"x":1594,"y":1595,"style":1596},"145.0","75.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Web shop export",[65,1599,1602],{"x":1594,"y":1600,"style":1601},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Order No, Client",[1562,1604],{"d":1605,"fill":1565,"stroke":270,"style":271},"M274.0,62.0 C394.0,62.0 394.0,166.0 510.0,166.0",[273,1607],{"points":1608,"fill":276},"514.0,166.0 505.0,171.0 505.0,161.0",[44,1610],{"x":1581,"y":101,"width":1583,"height":88,"rx":1554,"fill":1611,"stroke":1612,"style":271},"#fdefd8","var(--gold,#b4740a)",[1558,1614,1616],{"transform":1615},"translate(133.0,133.0) scale(1.000)",[1562,1617],{"d":1618,"fill":1565,"stroke":1619,"style":1567},"M4 4h16v6H4z M4 14h16v6H4z M8 7h.01 M8 17h.01","var(--gold-ink,#7a4e06)",[65,1621,1623],{"x":1594,"y":1571,"style":1622},"font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","ERP export",[65,1625,1627],{"x":1594,"y":1626,"style":1601},"198.0","Order Number, Account Name",[1562,1629],{"d":1630,"fill":1565,"stroke":270,"style":271},"M274.0,166.0 C394.0,166.0 394.0,166.0 510.0,166.0",[273,1632],{"points":1608,"fill":276},[44,1634],{"x":1581,"y":1635,"width":1583,"height":88,"rx":1554,"fill":1636,"stroke":1637,"style":271},"224.0","#fee8f2","var(--accent,#f43f8f)",[1558,1639,1641],{"transform":1640},"translate(133.0,237.0) scale(1.000)",[1562,1642],{"d":1643,"fill":1565,"stroke":1644,"style":1567},"M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8z M4 21c0-4 3.6-7 8-7s8 3 8 7","var(--accent-ink,#be185d)",[65,1646,1649],{"x":1594,"y":1647,"style":1648},"283.0","font-size:13px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Manual sheet",[65,1651,1653],{"x":1594,"y":1652,"style":1601},"302.0","Ord #, Customer Name",[1562,1655],{"d":1656,"fill":1565,"stroke":270,"style":271},"M274.0,270.0 C394.0,270.0 394.0,166.0 510.0,166.0",[273,1658],{"points":1608,"fill":276},[292,1660,1662],{"id":1661},"step-5-check-the-required-columns","Step 5 — Check the required columns",[10,1664,1665],{},"After mapping, confirm that every column the rest of the pipeline needs is present, and report both what is missing and what arrived unrecognised — the unrecognised list usually tells you what the missing column is now called:",[297,1667,1669],{"className":328,"code":1668,"language":330,"meta":302,"style":302},"REQUIRED = {\"order_id\", \"customer\", \"amount\", \"ship_date\"}\nknown = set(ALIASES) | {c for c in df.columns if c.startswith(\"m_\")}\n\nmissing = REQUIRED - set(df.columns)\nunrecognised = [c for c in df.columns if c not in known]\nif missing:\n    raise ValueError(f\"missing columns: {sorted(missing)}; unrecognised: {unrecognised}\")\nprint(\"unrecognised:\", unrecognised)      # unrecognised: ['amount_eur_2', 'cafe']\n",[14,1670,1671,1696,1736,1740,1758,1788,1795,1834],{"__ignoreMap":302},[306,1672,1673,1676,1678,1680,1682,1684,1686,1688,1690,1692,1694],{"class":265,"line":308},[306,1674,1675],{"class":446},"REQUIRED",[306,1677,649],{"class":337},[306,1679,652],{"class":341},[306,1681,1362],{"class":315},[306,1683,422],{"class":341},[306,1685,1395],{"class":315},[306,1687,422],{"class":341},[306,1689,1422],{"class":315},[306,1691,422],{"class":341},[306,1693,1444],{"class":315},[306,1695,714],{"class":341},[306,1697,1698,1701,1703,1706,1708,1710,1712,1715,1718,1720,1722,1724,1726,1728,1730,1733],{"class":265,"line":351},[306,1699,1700],{"class":341},"known ",[306,1702,396],{"class":337},[306,1704,1705],{"class":446}," set",[306,1707,578],{"class":341},[306,1709,1346],{"class":446},[306,1711,1142],{"class":341},[306,1713,1714],{"class":337},"|",[306,1716,1717],{"class":341}," {c ",[306,1719,1027],{"class":337},[306,1721,1030],{"class":341},[306,1723,877],{"class":337},[306,1725,1275],{"class":341},[306,1727,1155],{"class":337},[306,1729,1280],{"class":341},[306,1731,1732],{"class":315},"\"m_\"",[306,1734,1735],{"class":341},")}\n",[306,1737,1738],{"class":265,"line":358},[306,1739,355],{"emptyLinePlaceholder":354},[306,1741,1742,1745,1747,1750,1753,1755],{"class":265,"line":371},[306,1743,1744],{"class":341},"missing ",[306,1746,396],{"class":337},[306,1748,1749],{"class":446}," REQUIRED",[306,1751,1752],{"class":337}," -",[306,1754,1705],{"class":446},[306,1756,1757],{"class":341},"(df.columns)\n",[306,1759,1760,1763,1765,1767,1769,1771,1773,1775,1777,1779,1782,1785],{"class":265,"line":385},[306,1761,1762],{"class":341},"unrecognised ",[306,1764,396],{"class":337},[306,1766,1266],{"class":341},[306,1768,1027],{"class":337},[306,1770,1030],{"class":341},[306,1772,877],{"class":337},[306,1774,1275],{"class":341},[306,1776,1155],{"class":337},[306,1778,1030],{"class":341},[306,1780,1781],{"class":337},"not",[306,1783,1784],{"class":337}," in",[306,1786,1787],{"class":341}," known]\n",[306,1789,1790,1792],{"class":265,"line":390},[306,1791,1155],{"class":337},[306,1793,1794],{"class":341}," missing:\n",[306,1796,1797,1800,1803,1805,1807,1810,1812,1815,1818,1820,1823,1825,1828,1830,1832],{"class":265,"line":402},[306,1798,1799],{"class":337},"    raise",[306,1801,1802],{"class":446}," ValueError",[306,1804,578],{"class":341},[306,1806,834],{"class":337},[306,1808,1809],{"class":315},"\"missing columns: ",[306,1811,760],{"class":428},[306,1813,1814],{"class":446},"sorted",[306,1816,1817],{"class":341},"(missing)",[306,1819,777],{"class":428},[306,1821,1822],{"class":315},"; unrecognised: ",[306,1824,760],{"class":428},[306,1826,1827],{"class":341},"unrecognised",[306,1829,777],{"class":428},[306,1831,808],{"class":315},[306,1833,549],{"class":341},[306,1835,1836,1838,1840,1843,1846],{"class":265,"line":413},[306,1837,575],{"class":446},[306,1839,578],{"class":341},[306,1841,1842],{"class":315},"\"unrecognised:\"",[306,1844,1845],{"class":341},", unrecognised)      ",[306,1847,1848],{"class":865},"# unrecognised: ['amount_eur_2', 'cafe']\n",[10,1850,1851,1852,1066],{},"Failing early with both lists turns \"KeyError: 'amount'\" deep inside a calculation into a message the person who owns the export can act on. Wider checks on types and values are covered in ",[19,1853,1855],{"href":1854},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002F","Validating Excel Data with Python",[25,1857,34,1862,34,1865,34,1868,34,1871,34,1875,34,1881,34,1887,34,1891,34,1895,34,1900,34,1903,34,1906,34,1912,34,1916,34,1919,34,1923,34,1926,34,1929,34,1935,34,1939,34,1942,34,1945,34,1949,34,1952,34,1955,34,1961,34,1965,34,1968,34,1972,34,1975,34,1978,34,1984,34,1990,34,1993,34,1996],{"viewBox":1858,"role":28,"ariaLabelledBy":1859,"xmlns":32,"style":1537},"4 0 752 160",[1860,1861],"cn-flow-t","cn-flow-d",[36,1863,1864],{"id":1860},"From raw headers to a checked schema",[40,1866,1867],{"id":1861},"Read the sheet as it is, clean every label, make the names unique, drop empty unnamed columns, map aliases to canonical names, then check required columns and report unrecognised ones before any processing starts.",[44,1869],{"x":117,"y":47,"width":1546,"height":1870,"fill":50},"160",[44,1872],{"x":1581,"y":1582,"width":1873,"height":1874,"rx":1554,"fill":1584,"stroke":270,"style":271},"115.2","128.0",[1558,1876,1878],{"transform":1877},"translate(65.6,37.8) scale(1.000)",[1562,1879],{"d":1880,"fill":1565,"stroke":1591,"style":1567},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16",[65,1882,1886],{"x":1883,"y":1884,"style":1885},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Read as is",[65,1888,1890],{"x":1883,"y":1889,"style":1577},"103.8","no names=",[65,1892,1894],{"x":1883,"y":1893,"style":1577},"121.2","argument",[265,1896],{"x1":1897,"y1":1898,"x2":1899,"y2":1898,"stroke":270,"style":271},"140.2","80.0","162.2",[273,1901],{"points":1902,"fill":276},"166.2,80.0 157.2,85.0 157.2,75.0",[44,1904],{"x":1905,"y":1582,"width":1873,"height":1874,"rx":1554,"fill":1584,"stroke":270,"style":271},"171.2",[1558,1907,1909],{"transform":1908},"translate(216.8,46.5) scale(1.000)",[1562,1910],{"d":1911,"fill":1565,"stroke":1591,"style":1567},"M3 5h18l-7 8v6l-4 2v-8z",[65,1913,1915],{"x":1914,"y":1553,"style":1885},"228.8","Clean labels",[65,1917,290],{"x":1914,"y":1918,"style":1577},"112.5",[265,1920],{"x1":1921,"y1":1898,"x2":1922,"y2":1898,"stroke":270,"style":271},"291.4","313.4",[273,1924],{"points":1925,"fill":276},"317.4,80.0 308.4,85.0 308.4,75.0",[44,1927],{"x":1928,"y":1582,"width":1873,"height":1874,"rx":1554,"fill":1584,"stroke":270,"style":271},"322.4",[1558,1930,1932],{"transform":1931},"translate(368.0,37.8) scale(1.000)",[1562,1933],{"d":1934,"fill":1565,"stroke":1591,"style":1567},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[65,1936,1938],{"x":1937,"y":1884,"style":1885},"380.0","Make unique",[65,1940,1941],{"x":1937,"y":1889,"style":1577},"numbered",[65,1943,1944],{"x":1937,"y":1893,"style":1577},"repeats",[265,1946],{"x1":1947,"y1":1898,"x2":1948,"y2":1898,"stroke":270,"style":271},"442.6","464.6",[273,1950],{"points":1951,"fill":276},"468.6,80.0 459.6,85.0 459.6,75.0",[44,1953],{"x":1954,"y":1582,"width":1873,"height":1874,"rx":1554,"fill":1555,"stroke":1556,"style":271},"473.6",[1558,1956,1958],{"transform":1957},"translate(519.2,46.5) scale(1.000)",[1562,1959],{"d":1960,"fill":1565,"stroke":1566,"style":1567},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1",[65,1962,1964],{"x":1963,"y":1553,"style":1572},"531.2","Map aliases",[65,1966,1967],{"x":1963,"y":1918,"style":1577},"one alias table",[265,1969],{"x1":1970,"y1":1898,"x2":1971,"y2":1898,"stroke":270,"style":271},"593.8","615.8",[273,1973],{"points":1974,"fill":276},"619.8,80.0 610.8,85.0 610.8,75.0",[44,1976],{"x":1977,"y":1582,"width":1873,"height":1874,"rx":1554,"fill":1611,"stroke":1612,"style":271},"624.8",[1558,1979,1981],{"transform":1980},"translate(670.4,29.0) scale(1.000)",[1562,1982],{"d":1983,"fill":1565,"stroke":1619,"style":1567},"M5 12.5l4.5 4.5L19 7",[65,1985,1989],{"x":1986,"y":1987,"style":1988},"682.4","75.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Check",[65,1991,1992],{"x":1986,"y":1553,"style":1988},"schema",[65,1994,1995],{"x":1986,"y":1918,"style":1577},"missing and",[65,1997,1999],{"x":1986,"y":1998,"style":1577},"130.0","unknown",[292,2001,2003],{"id":2002},"step-6-restore-friendly-labels-on-output","Step 6 — Restore friendly labels on output",[10,2005,2006],{},"Clean names are for code. The report you write should use labels people recognise. Keep one dictionary from canonical names to display labels and apply it at the very end:",[297,2008,2010],{"className":328,"code":2009,"language":330,"meta":302,"style":302},"DISPLAY = {\"order_id\": \"Order ID\", \"customer\": \"Customer\", \"amount\": \"Amount (€)\", \"ship_date\": \"Ship date\"}\nout = df[[\"order_id\", \"customer\", \"amount\", \"ship_date\"]].rename(columns=DISPLAY)\nout.to_excel(\"orders_clean.xlsx\", index=False)\nprint(list(pd.read_excel(\"orders_clean.xlsx\").columns))\n# ['Order ID', 'Customer', 'Amount (€)', 'Ship date']\n",[14,2011,2012,2056,2091,2111,2127],{"__ignoreMap":302},[306,2013,2014,2017,2019,2021,2023,2025,2028,2030,2032,2034,2037,2039,2041,2043,2045,2047,2049,2051,2054],{"class":265,"line":308},[306,2015,2016],{"class":446},"DISPLAY",[306,2018,649],{"class":337},[306,2020,652],{"class":341},[306,2022,1362],{"class":315},[306,2024,658],{"class":341},[306,2026,2027],{"class":315},"\"Order ID\"",[306,2029,422],{"class":341},[306,2031,1395],{"class":315},[306,2033,658],{"class":341},[306,2035,2036],{"class":315},"\"Customer\"",[306,2038,422],{"class":341},[306,2040,1422],{"class":315},[306,2042,658],{"class":341},[306,2044,437],{"class":315},[306,2046,422],{"class":341},[306,2048,1444],{"class":315},[306,2050,658],{"class":341},[306,2052,2053],{"class":315},"\"Ship date\"",[306,2055,714],{"class":341},[306,2057,2058,2061,2063,2066,2068,2070,2072,2074,2076,2078,2080,2083,2085,2087,2089],{"class":265,"line":351},[306,2059,2060],{"class":341},"out ",[306,2062,396],{"class":337},[306,2064,2065],{"class":341}," df[[",[306,2067,1362],{"class":315},[306,2069,422],{"class":341},[306,2071,1395],{"class":315},[306,2073,422],{"class":341},[306,2075,1422],{"class":315},[306,2077,422],{"class":341},[306,2079,1444],{"class":315},[306,2081,2082],{"class":341},"]].rename(",[306,2084,1304],{"class":1303},[306,2086,396],{"class":337},[306,2088,2016],{"class":446},[306,2090,549],{"class":341},[306,2092,2093,2096,2099,2101,2104,2106,2109],{"class":265,"line":358},[306,2094,2095],{"class":341},"out.to_excel(",[306,2097,2098],{"class":315},"\"orders_clean.xlsx\"",[306,2100,422],{"class":341},[306,2102,2103],{"class":1303},"index",[306,2105,396],{"class":337},[306,2107,2108],{"class":446},"False",[306,2110,549],{"class":341},[306,2112,2113,2115,2117,2119,2122,2124],{"class":265,"line":371},[306,2114,575],{"class":446},[306,2116,578],{"class":341},[306,2118,581],{"class":446},[306,2120,2121],{"class":341},"(pd.read_excel(",[306,2123,2098],{"class":315},[306,2125,2126],{"class":341},").columns))\n",[306,2128,2129],{"class":265,"line":385},[306,2130,2131],{"class":865},"# ['Order ID', 'Customer', 'Amount (€)', 'Ship date']\n",[10,2133,2134],{},"Because the display labels live in one place, changing the wording of a report header never touches the calculation code.",[292,2136,2138],{"id":2137},"step-7-keep-a-record-of-what-was-renamed","Step 7 — Keep a record of what was renamed",[10,2140,2141],{},"When a figure in the report looks wrong, the first question is which source column it came from. Build the rename log in the same pass, from the raw labels to the final names, and save it with the output:",[297,2143,2145],{"className":328,"code":2144,"language":330,"meta":302,"style":302},"raw = pd.read_excel(\"headers.xlsx\", nrows=0).columns\nsteps = unique([clean_header(c) for c in raw])\nlog = pd.DataFrame({\n    \"source_header\": [str(c).replace(\"\\n\", \" \") for c in raw],\n    \"cleaned\": steps,\n    \"canonical\": [rename.get(c, \"\") for c in steps],\n})\nlog.to_csv(\"column_map.csv\", index=False)\nprint(log.loc[log[\"canonical\"] != \"\", [\"source_header\", \"canonical\"]].values.tolist())\n",[14,2146,2147,2170,2188,2198,2232,2240,2262,2267,2285],{"__ignoreMap":302},[306,2148,2149,2152,2154,2156,2158,2160,2163,2165,2167],{"class":265,"line":308},[306,2150,2151],{"class":341},"raw ",[306,2153,396],{"class":337},[306,2155,565],{"class":341},[306,2157,546],{"class":315},[306,2159,422],{"class":341},[306,2161,2162],{"class":1303},"nrows",[306,2164,396],{"class":337},[306,2166,47],{"class":446},[306,2168,2169],{"class":341},").columns\n",[306,2171,2172,2175,2177,2179,2181,2183,2185],{"class":265,"line":351},[306,2173,2174],{"class":341},"steps ",[306,2176,396],{"class":337},[306,2178,1212],{"class":341},[306,2180,1027],{"class":337},[306,2182,1030],{"class":341},[306,2184,877],{"class":337},[306,2186,2187],{"class":341}," raw])\n",[306,2189,2190,2193,2195],{"class":265,"line":358},[306,2191,2192],{"class":341},"log ",[306,2194,396],{"class":337},[306,2196,2197],{"class":341}," pd.DataFrame({\n",[306,2199,2200,2203,2205,2207,2210,2212,2214,2216,2218,2221,2223,2225,2227,2229],{"class":265,"line":371},[306,2201,2202],{"class":315},"    \"source_header\"",[306,2204,1359],{"class":341},[306,2206,602],{"class":446},[306,2208,2209],{"class":341},"(c).replace(",[306,2211,808],{"class":315},[306,2213,429],{"class":428},[306,2215,808],{"class":315},[306,2217,422],{"class":341},[306,2219,2220],{"class":315},"\" \"",[306,2222,1142],{"class":341},[306,2224,1027],{"class":337},[306,2226,1030],{"class":341},[306,2228,877],{"class":337},[306,2230,2231],{"class":341}," raw],\n",[306,2233,2234,2237],{"class":265,"line":385},[306,2235,2236],{"class":315},"    \"cleaned\"",[306,2238,2239],{"class":341},": steps,\n",[306,2241,2242,2245,2248,2251,2253,2255,2257,2259],{"class":265,"line":390},[306,2243,2244],{"class":315},"    \"canonical\"",[306,2246,2247],{"class":341},": [rename.get(c, ",[306,2249,2250],{"class":315},"\"\"",[306,2252,1142],{"class":341},[306,2254,1027],{"class":337},[306,2256,1030],{"class":341},[306,2258,877],{"class":337},[306,2260,2261],{"class":341}," steps],\n",[306,2263,2264],{"class":265,"line":402},[306,2265,2266],{"class":341},"})\n",[306,2268,2269,2272,2275,2277,2279,2281,2283],{"class":265,"line":413},[306,2270,2271],{"class":341},"log.to_csv(",[306,2273,2274],{"class":315},"\"column_map.csv\"",[306,2276,422],{"class":341},[306,2278,2103],{"class":1303},[306,2280,396],{"class":337},[306,2282,2108],{"class":446},[306,2284,549],{"class":341},[306,2286,2287,2289,2292,2295,2298,2301,2304,2307,2310,2312,2314],{"class":265,"line":453},[306,2288,575],{"class":446},[306,2290,2291],{"class":341},"(log.loc[log[",[306,2293,2294],{"class":315},"\"canonical\"",[306,2296,2297],{"class":341},"] ",[306,2299,2300],{"class":337},"!=",[306,2302,2303],{"class":315}," \"\"",[306,2305,2306],{"class":341},", [",[306,2308,2309],{"class":315},"\"source_header\"",[306,2311,422],{"class":341},[306,2313,2294],{"class":315},[306,2315,2316],{"class":341},"]].values.tolist())\n",[10,2318,2319,2320,2323],{},"Reading with ",[14,2321,2322],{},"nrows=0"," fetches only the header row, so the log costs almost nothing even on large files. Comparing today's log with last month's is also the quickest way to spot that a supplier has renamed or added a column before it causes a problem downstream.",[292,2325,2327],{"id":2326},"common-pitfalls","Common pitfalls",[2329,2330,2331,2343,2356,2365,2371],"ul",{},[2332,2333,2334,2338,2339,2342],"li",{},[2335,2336,2337],"strong",{},"Using names= to rename."," ",[14,2340,2341],{},"read_excel(names=[...])"," assigns labels by position. When the source inserts a column, every label after it is wrong and nothing fails.",[2332,2344,2345,2348,2349,2352,2353,2355],{},[2335,2346,2347],{},"Calling .str methods on the columns."," Date and number headers are not strings; ",[14,2350,2351],{},"df.columns.str.strip()"," turns them into ",[14,2354,610],{},". Convert with a function that handles every type, as in Step 1.",[2332,2357,2358,2361,2362,2364],{},[2335,2359,2360],{},"Stripping symbols that carry meaning."," Dropping ",[14,2363,766],{}," from \"Margin %\" or the currency from \"Amount (€)\" can merge distinct columns. Spell them out.",[2332,2366,2367,2370],{},[2335,2368,2369],{},"Case-sensitive aliases."," Map aliases after cleaning, never before; the cleaner already lower-cases and normalises, so the alias table only needs one spelling of each name.",[2332,2372,2373,2376],{},[2335,2374,2375],{},"Cleaning inside every script."," Put the cleaner and alias table in one shared module so every job agrees on the names.",[292,2378,2380],{"id":2379},"conclusion","Conclusion",[10,2382,2383],{},"Read the sheet as it is, then give every column a predictable name with one cleaning function: text for every label, accents folded, meaningful symbols spelled out, underscores for everything else and numbered duplicates. Drop empty unnamed columns, map aliases from each source onto one canonical schema, fail early when required columns are missing, and restore friendly labels only when writing the report.",[292,2385,2387],{"id":2386},"frequently-asked-questions","Frequently asked questions",[10,2389,2390,2393],{},[2335,2391,2392],{},"Why does pandas add .1 to some column names?","\nThe sheet has two columns with the same header. pandas keeps both and renames the second one to \"Name.1\", the third to \"Name.2\", and so on, so that every column label is unique.",[10,2395,2396,2399],{},[2335,2397,2398],{},"Where do \"Unnamed: 4\" columns come from?","\nA column whose header cell is empty. pandas invents the name from the column position. It often means an empty spacer column, a stray value far to the right or a header that sits in a different row.",[10,2401,2402,2405],{},[2335,2403,2404],{},"Should I rename columns before or after reading?","\nAfter. Read the sheet as it is, clean the labels with a function, and map them to your canonical names. The names= argument of read_excel replaces headers by position, which silently mislabels data if the source adds or reorders a column.",[10,2407,2408,2411],{},[2335,2409,2410],{},"How do I get the original header text back when writing the report?","\nKeep the dictionary from canonical names to display labels and rename with it just before writing. Your code uses stable names; readers see friendly ones.",[292,2413,2415],{"id":2414},"related","Related",[2329,2417,2418,2424,2431,2438],{},[2332,2419,2420,2421,2423],{},"Up one level: ",[19,2422,22],{"href":21}," — the full cleaning toolkit.",[2332,2425,2426,2430],{},[19,2427,2429],{"href":2428},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstrip-whitespace-and-normalise-text-columns-with-pandas\u002F","Strip Whitespace and Normalise Text Columns with pandas"," — the same ideas applied to cell values.",[2332,2432,2433,2437],{},[19,2434,2436],{"href":2435},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Freplace-values-in-an-excel-column-with-pandas\u002F","Replace Values in an Excel Column with pandas"," — mapping tables for values rather than headers.",[2332,2439,2440,2444],{},[19,2441,2443],{"href":2442},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fconcatenate-excel-sheets-with-different-columns\u002F","Concatenate Excel Sheets with Different Columns"," — combining sources once their names agree.",[2446,2447,2448],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":302,"searchDepth":351,"depth":351,"links":2450},[2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462],{"id":294,"depth":351,"text":295},{"id":614,"depth":351,"text":615},{"id":1069,"depth":351,"text":1070},{"id":1243,"depth":351,"text":1244},{"id":1332,"depth":351,"text":1333},{"id":1661,"depth":351,"text":1662},{"id":2002,"depth":351,"text":2003},{"id":2137,"depth":351,"text":2138},{"id":2326,"depth":351,"text":2327},{"id":2379,"depth":351,"text":2380},{"id":2386,"depth":351,"text":2387},{"id":2414,"depth":351,"text":2415},"2026-09-23","Turn messy Excel headers — stray spaces, line breaks, symbols, duplicates, blanks and dates as headers — into consistent snake_case names, map synonyms from different sources to one canonical schema, and check required columns before processing.","md",[2467,2469,2471,2473],{"q":2392,"a":2468},"The sheet has two columns with the same header. pandas keeps both and renames the second one to \"Name.1\", the third to \"Name.2\", and so on, so that every column label is unique.",{"q":2398,"a":2470},"A column whose header cell is empty. pandas invents the name from the column position. It often means an empty spacer column, a stray value far to the right or a header that sits in a different row.",{"q":2404,"a":2472},"After. Read the sheet as it is, clean the labels with a function, and map them to your canonical names. The names= argument of read_excel replaces headers by position, which silently mislabels data if the source adds or reorders a column.",{"q":2410,"a":2474},"Keep the dictionary from canonical names to display labels and rename with it just before writing. Your code uses stable names; readers see friendly ones.","standardise excel column names with pandas",null,{"breadcrumb":2478},[2479,2482,2485],{"name":2480,"item":2481},"Home","\u002F",{"name":2483,"item":2484},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":2486,"item":21},"Cleaning Excel Data With Pandas: A Production Workflow","\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas",{"title":2489,"description":2490},"Standardise Excel Column Names with pandas (clean headers)","Clean Excel column headers with pandas — snake_case names, accents and symbols, duplicate and Unnamed columns, date headers, an alias table for different sources, required-column checks and restoring display names.","standardise-excel-column-names-with-pandas","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas\u002Findex",[2494,2495,2496],"data-cleaning","reading-excel","pandas","how-to","r3M6fINO1GTUi22v6VnfFWy9dJz8L4M2AAsrYVaGkek",[2500,2504],{"title":2501,"path":2502,"stem":2503,"children":-1},"Standardise Country Names and Codes with Python","\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-country-names-and-codes-with-python","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-country-names-and-codes-with-python\u002Findex",{"title":2505,"path":2506,"stem":2507,"children":-1},"Standardise Phone Numbers in Excel Data with Python","\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-phone-numbers-in-excel-data-with-python","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-phone-numbers-in-excel-data-with-python\u002Findex",[2509,2511],{"path":2510,"title":2486},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas",{"path":2512,"title":2483},"\u002Fadvanced-data-transformation-and-cleaning",[],[2515,2518,2521,2524,2527,2530],{"path":2516,"title":2517,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Ffind-duplicate-rows-in-excel-with-python","Find Duplicate Rows in Excel with Python",{"path":2519,"title":2520,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-duplicate-values-in-excel-with-openpyxl","Highlight Duplicate Values in Excel with openpyxl",{"path":2522,"title":2523,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fclean-mixed-currency-amounts-into-amount-and-currency-columns","Clean Mixed-Currency Amounts into Amount and Currency Columns",{"path":2525,"title":2526,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fcoalesce-several-excel-columns-into-one-with-pandas","Coalesce Several Excel Columns into One with pandas",{"path":2528,"title":2529,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas","Convert Currency Strings Like \"$1,234.56\" and \"(500)\" to Numbers with pandas",{"path":2531,"title":2532,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas","Convert Excel Text Columns to Numbers with pandas",1790972086031]