[{"data":1,"prerenderedAt":2410},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas":2376,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas":2384,"children:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas":2389,"related:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas":2390},{"id":4,"title":5,"body":6,"dateModified":2342,"datePublished":2342,"description":2343,"extension":2344,"faq":2345,"keyword":2354,"kind":2355,"meta":2356,"navigation":222,"path":2365,"seo":2366,"slug":2368,"stem":2369,"topics":2370,"type":2374,"__hash__":2375},"docs\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas\u002Findex.md","Keep Leading Zeros When Reading Excel with pandas",{"type":7,"value":8,"toc":2327},"minimark",[9,53,144,149,176,194,405,409,416,494,497,501,508,581,589,749,753,780,885,888,892,905,1255,1266,1270,1273,1391,1404,1511,1515,1522,1692,1702,1706,1719,2065,2088,2092,2213,2217,2230,2234,2252,2256,2263,2269,2275,2281,2285,2323],[10,11,12,13,17,18,21,22,17,25,28,29,31,32,36,37,39,40,43,44,46,47,52],"p",{},"Customer ",[14,15,16],"code",{},"00123"," becomes ",[14,19,20],{},"123",", ZIP code ",[14,23,24],{},"02134",[14,26,27],{},"2134",", and a join against the CRM export quietly\nmatches nothing. Losing leading zeros is one of the most common silent data bugs in Excel pipelines, and it has\ntwo different causes that look identical on screen. Sometimes the cell really contains the text ",[14,30,16],{}," and\npandas converts it to a number. Sometimes the cell contains the ",[33,34,35],"em",{},"number"," ",[14,38,27],{}," and only a ",[14,41,42],{},"00000"," number\nformat makes Excel show ",[14,45,24],{},". The fixes differ. This guide, part of\n",[48,49,51],"a",{"href":50},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002F","Reading Excel Files with Pandas",",\ncovers both and how to tell which one you have.",[54,55,63,64,63,68,63,72,63,79,63,88,63,95,63,102,63,107,63,111,63,115,63,120,63,125,63,129,63,132,63,135,63,138],"svg",{"viewBox":56,"role":57,"ariaLabelledBy":58,"xmlns":61,"style":62},"0 0 760 191","img",[59,60],"lz-causes-t","lz-causes-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[65,66,67],"title",{"id":59},"Two causes of lost leading zeros",[69,70,71],"desc",{"id":60},"When the cell stores the text 00123, pandas type inference converts it to the number 123, and dtype str prevents that. When the cell stores the number 2134 with a 00000 number format, the zero never existed in the data, so it must be restored from the format.",[73,74],"rect",{"x":75,"y":75,"width":76,"height":77,"fill":78},"0","760","191","#ffffff",[73,80],{"x":81,"y":81,"width":82,"height":83,"rx":84,"fill":85,"stroke":86,"style":87},"20","350","127","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[89,90,94],"text",{"x":91,"y":92,"style":93},"195","46","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","text that looks numeric",[96,97],"line",{"x1":98,"y1":99,"x2":100,"y2":99,"stroke":86,"style":101},"36","56","354","stroke-width:1px",[89,103,106],{"x":91,"y":104,"style":105},"78","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","cell holds \"00123\"",[89,108,110],{"x":91,"y":109,"style":105},"101","pandas infers int → 123",[89,112,114],{"x":91,"y":113,"style":105},"124","fix: dtype=str on read",[73,116],{"x":117,"y":81,"width":82,"height":83,"rx":84,"fill":118,"stroke":119,"style":87},"390","#fdefd8","var(--gold,#b4740a)",[89,121,124],{"x":122,"y":92,"style":123},"565","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","number with a zero format",[96,126],{"x1":127,"y1":99,"x2":128,"y2":99,"stroke":119,"style":101},"406","724",[89,130,131],{"x":122,"y":104,"style":105},"cell holds 2134",[89,133,134],{"x":122,"y":109,"style":105},"format 00000 shows 02134",[89,136,137],{"x":122,"y":113,"style":105},"fix: pad using the format",[89,139,143],{"x":140,"y":141,"style":142},"380","177","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","check which one you have before choosing the fix",[145,146,148],"h2",{"id":147},"prerequisites","Prerequisites",[150,151,156],"pre",{"className":152,"code":153,"language":154,"meta":155,"style":155},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,157,158],{"__ignoreMap":155},[159,160,162,166,170,173],"span",{"class":96,"line":161},1,[159,163,165],{"class":164},"sMTad","pip",[159,167,169],{"class":168},"srMev"," install",[159,171,172],{"class":168}," pandas",[159,174,175],{"class":168}," openpyxl\n",[10,177,178,179,182,183,186,187,182,190,193],{},"This workbook has both kinds: ",[14,180,181],{},"CustomerID"," and ",[14,184,185],{},"Phone"," stored as text with zeros, and ",[14,188,189],{},"Zip",[14,191,192],{},"Account"," stored as numbers displayed with zero-padded formats:",[150,195,199],{"className":196,"code":197,"language":198,"meta":155,"style":155},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Customers\"\nws.append([\"CustomerID\", \"Zip\", \"Phone\", \"Account\"])\nws.append([\"00123\", 2134, \"0612345678\", 42])\nws.append([\"00456\", 501, \"0687654321\", 7])\nfor row in (2, 3):\n    ws.cell(row, 2).number_format = \"00000\"\n    ws.cell(row, 4).number_format = \"000000\"\nwb.save(\"customers.xlsx\")\n","python",[14,200,201,217,224,236,247,258,286,311,336,362,378,393],{"__ignoreMap":155},[159,202,203,207,211,214],{"class":96,"line":161},[159,204,206],{"class":205},"s-kum","from",[159,208,210],{"class":209},"skGVy"," openpyxl ",[159,212,213],{"class":205},"import",[159,215,216],{"class":209}," Workbook\n",[159,218,220],{"class":96,"line":219},2,[159,221,223],{"emptyLinePlaceholder":222},true,"\n",[159,225,227,230,233],{"class":96,"line":226},3,[159,228,229],{"class":209},"wb ",[159,231,232],{"class":205},"=",[159,234,235],{"class":209}," Workbook()\n",[159,237,239,242,244],{"class":96,"line":238},4,[159,240,241],{"class":209},"ws ",[159,243,232],{"class":205},[159,245,246],{"class":209}," wb.active\n",[159,248,250,253,255],{"class":96,"line":249},5,[159,251,252],{"class":209},"ws.title ",[159,254,232],{"class":205},[159,256,257],{"class":168}," \"Customers\"\n",[159,259,261,264,267,270,273,275,278,280,283],{"class":96,"line":260},6,[159,262,263],{"class":209},"ws.append([",[159,265,266],{"class":168},"\"CustomerID\"",[159,268,269],{"class":209},", ",[159,271,272],{"class":168},"\"Zip\"",[159,274,269],{"class":209},[159,276,277],{"class":168},"\"Phone\"",[159,279,269],{"class":209},[159,281,282],{"class":168},"\"Account\"",[159,284,285],{"class":209},"])\n",[159,287,289,291,294,296,299,301,304,306,309],{"class":96,"line":288},7,[159,290,263],{"class":209},[159,292,293],{"class":168},"\"00123\"",[159,295,269],{"class":209},[159,297,27],{"class":298},"sP0c6",[159,300,269],{"class":209},[159,302,303],{"class":168},"\"0612345678\"",[159,305,269],{"class":209},[159,307,308],{"class":298},"42",[159,310,285],{"class":209},[159,312,314,316,319,321,324,326,329,331,334],{"class":96,"line":313},8,[159,315,263],{"class":209},[159,317,318],{"class":168},"\"00456\"",[159,320,269],{"class":209},[159,322,323],{"class":298},"501",[159,325,269],{"class":209},[159,327,328],{"class":168},"\"0687654321\"",[159,330,269],{"class":209},[159,332,333],{"class":298},"7",[159,335,285],{"class":209},[159,337,339,342,345,348,351,354,356,359],{"class":96,"line":338},9,[159,340,341],{"class":205},"for",[159,343,344],{"class":209}," row ",[159,346,347],{"class":205},"in",[159,349,350],{"class":209}," (",[159,352,353],{"class":298},"2",[159,355,269],{"class":209},[159,357,358],{"class":298},"3",[159,360,361],{"class":209},"):\n",[159,363,365,368,370,373,375],{"class":96,"line":364},10,[159,366,367],{"class":209},"    ws.cell(row, ",[159,369,353],{"class":298},[159,371,372],{"class":209},").number_format ",[159,374,232],{"class":205},[159,376,377],{"class":168}," \"00000\"\n",[159,379,381,383,386,388,390],{"class":96,"line":380},11,[159,382,367],{"class":209},[159,384,385],{"class":298},"4",[159,387,372],{"class":209},[159,389,232],{"class":205},[159,391,392],{"class":168}," \"000000\"\n",[159,394,396,399,402],{"class":96,"line":395},12,[159,397,398],{"class":209},"wb.save(",[159,400,401],{"class":168},"\"customers.xlsx\"",[159,403,404],{"class":209},")\n",[145,406,408],{"id":407},"step-1-see-the-problem","Step 1 — See the problem",[10,410,411,412,415],{},"A default read converts every column to integers, including the two that were text in the workbook. pandas' ",[14,413,414],{},"read_excel"," runs its values through the same type\ninference used for CSV files, and a column where every value parses as a number becomes numeric.",[150,417,419],{"className":196,"code":418,"language":198,"meta":155,"style":155},"import pandas as pd\n\nraw = pd.read_excel(\"customers.xlsx\", sheet_name=\"Customers\")\nprint(raw)\nprint(raw.dtypes)\n#    CustomerID   Zip      Phone  Account\n# 0         123  2134  612345678       42\n# 1         456   501  687654321        7\n",[14,420,421,434,438,463,471,478,484,489],{"__ignoreMap":155},[159,422,423,425,428,431],{"class":96,"line":161},[159,424,213],{"class":205},[159,426,427],{"class":209}," pandas ",[159,429,430],{"class":205},"as",[159,432,433],{"class":209}," pd\n",[159,435,436],{"class":96,"line":219},[159,437,223],{"emptyLinePlaceholder":222},[159,439,440,443,445,448,450,452,456,458,461],{"class":96,"line":226},[159,441,442],{"class":209},"raw ",[159,444,232],{"class":205},[159,446,447],{"class":209}," pd.read_excel(",[159,449,401],{"class":168},[159,451,269],{"class":209},[159,453,455],{"class":454},"sa561","sheet_name",[159,457,232],{"class":205},[159,459,460],{"class":168},"\"Customers\"",[159,462,404],{"class":209},[159,464,465,468],{"class":96,"line":238},[159,466,467],{"class":298},"print",[159,469,470],{"class":209},"(raw)\n",[159,472,473,475],{"class":96,"line":249},[159,474,467],{"class":298},[159,476,477],{"class":209},"(raw.dtypes)\n",[159,479,480],{"class":96,"line":260},[159,481,483],{"class":482},"s-wDw","#    CustomerID   Zip      Phone  Account\n",[159,485,486],{"class":96,"line":288},[159,487,488],{"class":482},"# 0         123  2134  612345678       42\n",[159,490,491],{"class":96,"line":313},[159,492,493],{"class":482},"# 1         456   501  687654321        7\n",[10,495,496],{},"Nothing here raises an error, which is exactly what makes it dangerous. The IDs are still unique, the phone numbers still look like phone numbers, and the damage\nonly appears when the values are joined, looked up or written back.",[145,498,500],{"id":499},"step-2-keep-text-cells-as-text-with-dtype","Step 2 — Keep text cells as text with dtype",[10,502,503,504,507],{},"For values stored as text, tell pandas not to infer. ",[14,505,506],{},"dtype=str"," applies to every column; a dictionary targets specific ones, which keeps real numeric columns\nnumeric.",[150,509,511],{"className":196,"code":510,"language":198,"meta":155,"style":155},"by_column = pd.read_excel(\"customers.xlsx\", sheet_name=\"Customers\",\n                          dtype={\"CustomerID\": str, \"Phone\": str})\nprint(by_column)\n#   CustomerID   Zip       Phone  Account\n# 0      00123  2134  0612345678       42\n",[14,512,513,535,564,571,576],{"__ignoreMap":155},[159,514,515,518,520,522,524,526,528,530,532],{"class":96,"line":161},[159,516,517],{"class":209},"by_column ",[159,519,232],{"class":205},[159,521,447],{"class":209},[159,523,401],{"class":168},[159,525,269],{"class":209},[159,527,455],{"class":454},[159,529,232],{"class":205},[159,531,460],{"class":168},[159,533,534],{"class":209},",\n",[159,536,537,540,542,545,547,550,553,555,557,559,561],{"class":96,"line":219},[159,538,539],{"class":454},"                          dtype",[159,541,232],{"class":205},[159,543,544],{"class":209},"{",[159,546,266],{"class":168},[159,548,549],{"class":209},": ",[159,551,552],{"class":298},"str",[159,554,269],{"class":209},[159,556,277],{"class":168},[159,558,549],{"class":209},[159,560,552],{"class":298},[159,562,563],{"class":209},"})\n",[159,565,566,568],{"class":96,"line":226},[159,567,467],{"class":298},[159,569,570],{"class":209},"(by_column)\n",[159,572,573],{"class":96,"line":238},[159,574,575],{"class":482},"#   CustomerID   Zip       Phone  Account\n",[159,577,578],{"class":96,"line":249},[159,579,580],{"class":482},"# 0      00123  2134  0612345678       42\n",[10,582,583,584,588],{},"Identifier columns should essentially always be read as strings, zeros or not. An ID is a label, not a quantity: nobody adds customer numbers together, and treating\nthem as text also protects against long numeric IDs losing precision as floats. The general rules for choosing dtypes on read are in\n",[48,585,587],{"href":586},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhow-to-read-excel-with-pandas-step-by-step\u002F","How to Read Excel Files With Pandas: A Step-by-Step Guide",".",[54,590,63,596,63,599,63,602,63,606,63,611,63,614,63,620,63,624,63,628,63,632,63,636,63,640,63,644,63,647,63,651,63,653,63,657,63,659,63,661,63,663,63,665,63,667,63,669,63,672,63,675,63,677,63,680,63,684,63,687,63,689,63,691,63,693,63,696,63,698,63,701,63,703,63,706,63,710,63,713,63,715,63,717,63,719,63,722,63,724,63,727,63,729,63,732,63,734,63,736,63,738,63,740,63,742,63,744],{"viewBox":591,"role":57,"ariaLabelledBy":592,"xmlns":61,"style":595},"0 0 486 224",[593,594],"lz-read-t","lz-read-d","width:100%;max-width:486px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[65,597,598],{"id":593},"The same row read three ways",[69,600,601],{"id":594},"A default read turns every column numeric and drops zeros. A dtype dictionary for CustomerID and Phone keeps their text zeros. Zip still reads as 2134 because the zero only existed in its number format.",[73,603],{"x":75,"y":75,"width":604,"height":605,"fill":78},"486","224",[73,607],{"x":81,"y":81,"width":98,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"30","#e7ebef","var(--line,#cdd5e6)",[73,612],{"x":99,"y":81,"width":613,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"140",[89,615,619],{"x":616,"y":617,"style":618},"126","40","font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[73,621],{"x":622,"y":81,"width":623,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"196","90",[89,625,627],{"x":626,"y":617,"style":618},"241","B",[73,629],{"x":630,"y":81,"width":631,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"286","70",[89,633,635],{"x":634,"y":617,"style":618},"321","C",[73,637],{"x":638,"y":81,"width":639,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"356","110",[89,641,643],{"x":642,"y":617,"style":618},"411","D",[73,645],{"x":81,"y":646,"width":98,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"50",[89,648,650],{"x":649,"y":631,"style":618},"38","1",[73,652],{"x":99,"y":646,"width":613,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,654,656],{"x":616,"y":631,"style":655},"font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","read",[73,658],{"x":622,"y":646,"width":623,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,660,181],{"x":626,"y":631,"style":655},[73,662],{"x":630,"y":646,"width":631,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,664,189],{"x":634,"y":631,"style":655},[73,666],{"x":638,"y":646,"width":639,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,668,185],{"x":642,"y":631,"style":655},[73,670],{"x":81,"y":671,"width":98,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},"80",[89,673,353],{"x":649,"y":674,"style":618},"100",[73,676],{"x":99,"y":671,"width":613,"height":608,"rx":75,"fill":78,"stroke":610,"style":101},[89,678,679],{"x":616,"y":674,"style":105},"default",[73,681],{"x":622,"y":671,"width":623,"height":608,"rx":75,"fill":682,"stroke":683,"style":101},"#fee8f2","var(--accent,#f43f8f)",[89,685,20],{"x":626,"y":674,"style":686},"font-size:11.5px;font-weight:600;fill:var(--accent-ink,#be185d);text-anchor:middle",[73,688],{"x":630,"y":671,"width":631,"height":608,"rx":75,"fill":682,"stroke":683,"style":101},[89,690,27],{"x":634,"y":674,"style":686},[73,692],{"x":638,"y":671,"width":639,"height":608,"rx":75,"fill":682,"stroke":683,"style":101},[89,694,695],{"x":642,"y":674,"style":686},"612345678",[73,697],{"x":81,"y":639,"width":98,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,699,358],{"x":649,"y":700,"style":618},"130",[73,702],{"x":99,"y":639,"width":613,"height":608,"rx":75,"fill":78,"stroke":610,"style":101},[89,704,705],{"x":616,"y":700,"style":105},"dtype for text cols",[73,707],{"x":622,"y":639,"width":623,"height":608,"rx":75,"fill":708,"stroke":709,"style":101},"#d9f4f1","var(--teal,#0f9488)",[89,711,16],{"x":626,"y":700,"style":712},"font-size:11.5px;font-weight:600;fill:var(--teal-ink,#0b6157);text-anchor:middle",[73,714],{"x":630,"y":639,"width":631,"height":608,"rx":75,"fill":682,"stroke":683,"style":101},[89,716,27],{"x":634,"y":700,"style":686},[73,718],{"x":638,"y":639,"width":639,"height":608,"rx":75,"fill":708,"stroke":709,"style":101},[89,720,721],{"x":642,"y":700,"style":712},"0612345678",[73,723],{"x":81,"y":613,"width":98,"height":608,"rx":75,"fill":609,"stroke":610,"style":101},[89,725,385],{"x":649,"y":726,"style":618},"160",[73,728],{"x":99,"y":613,"width":613,"height":608,"rx":75,"fill":78,"stroke":610,"style":101},[89,730,731],{"x":616,"y":726,"style":105},"+ pad from format",[73,733],{"x":622,"y":613,"width":623,"height":608,"rx":75,"fill":708,"stroke":709,"style":101},[89,735,16],{"x":626,"y":726,"style":712},[73,737],{"x":630,"y":613,"width":631,"height":608,"rx":75,"fill":708,"stroke":709,"style":101},[89,739,24],{"x":634,"y":726,"style":712},[73,741],{"x":638,"y":613,"width":639,"height":608,"rx":75,"fill":708,"stroke":709,"style":101},[89,743,721],{"x":642,"y":726,"style":712},[89,745,748],{"x":746,"y":747,"style":142},"243","202","dtype fixes stored text; formats need a second step",[145,750,752],{"id":751},"step-3-recognise-zeros-that-only-exist-in-the-format","Step 3 — Recognise zeros that only exist in the format",[10,754,755,757,758,760,761,763,764,767,768,771,772,775,776,779],{},[14,756,189],{}," is still ",[14,759,27],{}," even with ",[14,762,506],{},", because the cell's value genuinely is the number 2134. The zero is display formatting, like a currency symbol. Check\nwith openpyxl: a cell's ",[14,765,766],{},"data_type"," is ",[14,769,770],{},"\"s\""," for text and ",[14,773,774],{},"\"n\""," for numbers, and ",[14,777,778],{},"number_format"," shows the pattern.",[150,781,783],{"className":196,"code":782,"language":198,"meta":155,"style":155},"from openpyxl import load_workbook\n\nsheet = load_workbook(\"customers.xlsx\")[\"Customers\"]\nfor ref in (\"A2\", \"B2\", \"D2\"):\n    cell = sheet[ref]\n    print(ref, repr(cell.value), cell.data_type, cell.number_format)\n# A2 '00123' s General\n# B2 2134 n 00000\n# D2 42 n 000000\n",[14,784,785,796,800,820,846,856,870,875,880],{"__ignoreMap":155},[159,786,787,789,791,793],{"class":96,"line":161},[159,788,206],{"class":205},[159,790,210],{"class":209},[159,792,213],{"class":205},[159,794,795],{"class":209}," load_workbook\n",[159,797,798],{"class":96,"line":219},[159,799,223],{"emptyLinePlaceholder":222},[159,801,802,805,807,810,812,815,817],{"class":96,"line":226},[159,803,804],{"class":209},"sheet ",[159,806,232],{"class":205},[159,808,809],{"class":209}," load_workbook(",[159,811,401],{"class":168},[159,813,814],{"class":209},")[",[159,816,460],{"class":168},[159,818,819],{"class":209},"]\n",[159,821,822,824,827,829,831,834,836,839,841,844],{"class":96,"line":238},[159,823,341],{"class":205},[159,825,826],{"class":209}," ref ",[159,828,347],{"class":205},[159,830,350],{"class":209},[159,832,833],{"class":168},"\"A2\"",[159,835,269],{"class":209},[159,837,838],{"class":168},"\"B2\"",[159,840,269],{"class":209},[159,842,843],{"class":168},"\"D2\"",[159,845,361],{"class":209},[159,847,848,851,853],{"class":96,"line":249},[159,849,850],{"class":209},"    cell ",[159,852,232],{"class":205},[159,854,855],{"class":209}," sheet[ref]\n",[159,857,858,861,864,867],{"class":96,"line":260},[159,859,860],{"class":298},"    print",[159,862,863],{"class":209},"(ref, ",[159,865,866],{"class":298},"repr",[159,868,869],{"class":209},"(cell.value), cell.data_type, cell.number_format)\n",[159,871,872],{"class":96,"line":288},[159,873,874],{"class":482},"# A2 '00123' s General\n",[159,876,877],{"class":96,"line":313},[159,878,879],{"class":482},"# B2 2134 n 00000\n",[159,881,882],{"class":96,"line":338},[159,883,884],{"class":482},"# D2 42 n 000000\n",[10,886,887],{},"This check is worth running once on any unfamiliar workbook before choosing a fix. It also explains a common confusion: someone insists the file \"has the zeros\"\nbecause they can see them, while every script reads the number without them.",[145,889,891],{"id":890},"step-4-restore-zeros-from-the-number-format","Step 4 — Restore zeros from the number format",[10,893,894,895,269,897,900,901,904],{},"A format made only of zeros — ",[14,896,42],{},[14,898,899],{},"000000"," — means \"pad to this many digits\". Read each column's format from the first data row, and pad the values with\n",[14,902,903],{},"zfill"," to that width. This works whatever the width, so one function handles ZIP codes, account numbers and employee IDs alike.",[150,906,908],{"className":196,"code":907,"language":198,"meta":155,"style":155},"import re\n\ndef zero_pad_widths(path: str, sheet_name: str, header_row: int = 1) -> dict[str, int]:\n    ws = load_workbook(path, read_only=True)[sheet_name]\n    rows = ws.iter_rows(min_row=header_row, max_row=header_row + 1)\n    header, first = next(rows), next(rows)\n    widths = {}\n    for head, cell in zip(header, first):\n        if cell.data_type == \"n\" and re.fullmatch(r\"0+\", cell.number_format or \"\"):\n            widths[head.value] = len(cell.number_format)\n    return widths\n\nwidths = zero_pad_widths(\"customers.xlsx\", \"Customers\")\nprint(widths)                                        # {'Zip': 5, 'Account': 6}\n\ncustomers = pd.read_excel(\"customers.xlsx\", sheet_name=\"Customers\",\n                          dtype={\"CustomerID\": str, \"Phone\": str})\nfor column, width in widths.items():\n    customers[column] = customers[column].astype(\"Int64\").astype(str).str.zfill(width)\nprint(customers)\n",[14,909,910,917,921,964,985,1018,1037,1047,1063,1105,1118,1126,1130,1149,1160,1165,1187,1212,1225,1247],{"__ignoreMap":155},[159,911,912,914],{"class":96,"line":161},[159,913,213],{"class":205},[159,915,916],{"class":209}," re\n",[159,918,919],{"class":96,"line":219},[159,920,223],{"emptyLinePlaceholder":222},[159,922,923,926,930,933,935,938,940,943,946,949,952,955,957,959,961],{"class":96,"line":226},[159,924,925],{"class":205},"def",[159,927,929],{"class":928},"s_Opv"," zero_pad_widths",[159,931,932],{"class":209},"(path: ",[159,934,552],{"class":298},[159,936,937],{"class":209},", sheet_name: ",[159,939,552],{"class":298},[159,941,942],{"class":209},", header_row: ",[159,944,945],{"class":298},"int",[159,947,948],{"class":205}," =",[159,950,951],{"class":298}," 1",[159,953,954],{"class":209},") -> dict[",[159,956,552],{"class":298},[159,958,269],{"class":209},[159,960,945],{"class":298},[159,962,963],{"class":209},"]:\n",[159,965,966,969,971,974,977,979,982],{"class":96,"line":238},[159,967,968],{"class":209},"    ws ",[159,970,232],{"class":205},[159,972,973],{"class":209}," load_workbook(path, ",[159,975,976],{"class":454},"read_only",[159,978,232],{"class":205},[159,980,981],{"class":298},"True",[159,983,984],{"class":209},")[sheet_name]\n",[159,986,987,990,992,995,998,1000,1003,1006,1008,1011,1014,1016],{"class":96,"line":249},[159,988,989],{"class":209},"    rows ",[159,991,232],{"class":205},[159,993,994],{"class":209}," ws.iter_rows(",[159,996,997],{"class":454},"min_row",[159,999,232],{"class":205},[159,1001,1002],{"class":209},"header_row, ",[159,1004,1005],{"class":454},"max_row",[159,1007,232],{"class":205},[159,1009,1010],{"class":209},"header_row ",[159,1012,1013],{"class":205},"+",[159,1015,951],{"class":298},[159,1017,404],{"class":209},[159,1019,1020,1023,1025,1028,1031,1034],{"class":96,"line":260},[159,1021,1022],{"class":209},"    header, first ",[159,1024,232],{"class":205},[159,1026,1027],{"class":298}," next",[159,1029,1030],{"class":209},"(rows), ",[159,1032,1033],{"class":298},"next",[159,1035,1036],{"class":209},"(rows)\n",[159,1038,1039,1042,1044],{"class":96,"line":288},[159,1040,1041],{"class":209},"    widths ",[159,1043,232],{"class":205},[159,1045,1046],{"class":209}," {}\n",[159,1048,1049,1052,1055,1057,1060],{"class":96,"line":313},[159,1050,1051],{"class":205},"    for",[159,1053,1054],{"class":209}," head, cell ",[159,1056,347],{"class":205},[159,1058,1059],{"class":298}," zip",[159,1061,1062],{"class":209},"(header, first):\n",[159,1064,1065,1068,1071,1074,1077,1080,1083,1086,1089,1091,1094,1097,1100,1103],{"class":96,"line":338},[159,1066,1067],{"class":205},"        if",[159,1069,1070],{"class":209}," cell.data_type ",[159,1072,1073],{"class":205},"==",[159,1075,1076],{"class":168}," \"n\"",[159,1078,1079],{"class":205}," and",[159,1081,1082],{"class":209}," re.fullmatch(",[159,1084,1085],{"class":205},"r",[159,1087,1088],{"class":168},"\"0",[159,1090,1013],{"class":205},[159,1092,1093],{"class":168},"\"",[159,1095,1096],{"class":209},", cell.number_format ",[159,1098,1099],{"class":205},"or",[159,1101,1102],{"class":168}," \"\"",[159,1104,361],{"class":209},[159,1106,1107,1110,1112,1115],{"class":96,"line":364},[159,1108,1109],{"class":209},"            widths[head.value] ",[159,1111,232],{"class":205},[159,1113,1114],{"class":298}," len",[159,1116,1117],{"class":209},"(cell.number_format)\n",[159,1119,1120,1123],{"class":96,"line":380},[159,1121,1122],{"class":205},"    return",[159,1124,1125],{"class":209}," widths\n",[159,1127,1128],{"class":96,"line":395},[159,1129,223],{"emptyLinePlaceholder":222},[159,1131,1133,1136,1138,1141,1143,1145,1147],{"class":96,"line":1132},13,[159,1134,1135],{"class":209},"widths ",[159,1137,232],{"class":205},[159,1139,1140],{"class":209}," zero_pad_widths(",[159,1142,401],{"class":168},[159,1144,269],{"class":209},[159,1146,460],{"class":168},[159,1148,404],{"class":209},[159,1150,1152,1154,1157],{"class":96,"line":1151},14,[159,1153,467],{"class":298},[159,1155,1156],{"class":209},"(widths)                                        ",[159,1158,1159],{"class":482},"# {'Zip': 5, 'Account': 6}\n",[159,1161,1163],{"class":96,"line":1162},15,[159,1164,223],{"emptyLinePlaceholder":222},[159,1166,1168,1171,1173,1175,1177,1179,1181,1183,1185],{"class":96,"line":1167},16,[159,1169,1170],{"class":209},"customers ",[159,1172,232],{"class":205},[159,1174,447],{"class":209},[159,1176,401],{"class":168},[159,1178,269],{"class":209},[159,1180,455],{"class":454},[159,1182,232],{"class":205},[159,1184,460],{"class":168},[159,1186,534],{"class":209},[159,1188,1190,1192,1194,1196,1198,1200,1202,1204,1206,1208,1210],{"class":96,"line":1189},17,[159,1191,539],{"class":454},[159,1193,232],{"class":205},[159,1195,544],{"class":209},[159,1197,266],{"class":168},[159,1199,549],{"class":209},[159,1201,552],{"class":298},[159,1203,269],{"class":209},[159,1205,277],{"class":168},[159,1207,549],{"class":209},[159,1209,552],{"class":298},[159,1211,563],{"class":209},[159,1213,1215,1217,1220,1222],{"class":96,"line":1214},18,[159,1216,341],{"class":205},[159,1218,1219],{"class":209}," column, width ",[159,1221,347],{"class":205},[159,1223,1224],{"class":209}," widths.items():\n",[159,1226,1228,1231,1233,1236,1239,1242,1244],{"class":96,"line":1227},19,[159,1229,1230],{"class":209},"    customers[column] ",[159,1232,232],{"class":205},[159,1234,1235],{"class":209}," customers[column].astype(",[159,1237,1238],{"class":168},"\"Int64\"",[159,1240,1241],{"class":209},").astype(",[159,1243,552],{"class":298},[159,1245,1246],{"class":209},").str.zfill(width)\n",[159,1248,1250,1252],{"class":96,"line":1249},20,[159,1251,467],{"class":298},[159,1253,1254],{"class":209},"(customers)\n",[10,1256,1257,1258,1261,1262,1265],{},"Converting through the nullable ",[14,1259,1260],{},"Int64"," type first avoids a trailing ",[14,1263,1264],{},".0"," when a column has blank cells and pandas stored it as float. If the format is not uniform\ndown the column — a mix of padded and unpadded cells — check more than the first row, or pad from a known width for that field instead.",[145,1267,1269],{"id":1268},"step-5-use-converters-for-fixed-known-formats","Step 5 — Use converters for fixed, known formats",[10,1271,1272],{},"When the width is part of the data's definition — US ZIP codes are five digits, a company's employee IDs are eight — a converter applies it during the read without\nlooking at formats at all. Converters receive each raw cell value and return what should go into the frame.",[150,1274,1276],{"className":196,"code":1275,"language":198,"meta":155,"style":155},"def five_digit_zip(value) -> str:\n    return f\"{int(value):05d}\" if pd.notna(value) and str(value).strip() else \"\"\n\nzips = pd.read_excel(\"customers.xlsx\", sheet_name=\"Customers\", converters={\"Zip\": five_digit_zip})\nprint(zips[\"Zip\"].tolist())                          # ['02134', '00501']\n",[14,1277,1278,1293,1339,1343,1376],{"__ignoreMap":155},[159,1279,1280,1282,1285,1288,1290],{"class":96,"line":161},[159,1281,925],{"class":205},[159,1283,1284],{"class":928}," five_digit_zip",[159,1286,1287],{"class":209},"(value) -> ",[159,1289,552],{"class":298},[159,1291,1292],{"class":209},":\n",[159,1294,1295,1297,1300,1302,1305,1307,1310,1313,1316,1318,1321,1324,1327,1330,1333,1336],{"class":96,"line":219},[159,1296,1122],{"class":205},[159,1298,1299],{"class":205}," f",[159,1301,1093],{"class":168},[159,1303,544],{"class":1304},"sSjpA",[159,1306,945],{"class":298},[159,1308,1309],{"class":209},"(value)",[159,1311,1312],{"class":205},":05d",[159,1314,1315],{"class":1304},"}",[159,1317,1093],{"class":168},[159,1319,1320],{"class":205}," if",[159,1322,1323],{"class":209}," pd.notna(value) ",[159,1325,1326],{"class":205},"and",[159,1328,1329],{"class":298}," str",[159,1331,1332],{"class":209},"(value).strip() ",[159,1334,1335],{"class":205},"else",[159,1337,1338],{"class":168}," \"\"\n",[159,1340,1341],{"class":96,"line":226},[159,1342,223],{"emptyLinePlaceholder":222},[159,1344,1345,1348,1350,1352,1354,1356,1358,1360,1362,1364,1367,1369,1371,1373],{"class":96,"line":238},[159,1346,1347],{"class":209},"zips ",[159,1349,232],{"class":205},[159,1351,447],{"class":209},[159,1353,401],{"class":168},[159,1355,269],{"class":209},[159,1357,455],{"class":454},[159,1359,232],{"class":205},[159,1361,460],{"class":168},[159,1363,269],{"class":209},[159,1365,1366],{"class":454},"converters",[159,1368,232],{"class":205},[159,1370,544],{"class":209},[159,1372,272],{"class":168},[159,1374,1375],{"class":209},": five_digit_zip})\n",[159,1377,1378,1380,1383,1385,1388],{"class":96,"line":249},[159,1379,467],{"class":298},[159,1381,1382],{"class":209},"(zips[",[159,1384,272],{"class":168},[159,1386,1387],{"class":209},"].tolist())                          ",[159,1389,1390],{"class":482},"# ['02134', '00501']\n",[10,1392,1393,1394,1397,1398,1400,1401,1403],{},"Converters handle both causes at once — a text ",[14,1395,1396],{},"\"02134\""," and a number ",[14,1399,27],{}," both come out as ",[14,1402,1396],{}," — which makes them robust for files assembled by several people\nwith different habits. The trade-off is that they are slower than a dtype, because they run in Python for every cell.",[54,1405,63,1410,63,1413,63,1416,63,1419,63,1425,63,1429,63,1432,63,1436,63,1438,63,1443,63,1448,63,1453,63,1456,63,1459,63,1463,63,1465,63,1468,63,1471,63,1475,63,1479,63,1482,63,1485,63,1487,63,1490,63,1493,63,1497,63,1501,63,1504,63,1507],{"viewBox":1406,"role":57,"ariaLabelledBy":1407,"xmlns":61,"style":62},"0 0 760 282",[1408,1409],"lz-choose-t","lz-choose-d",[65,1411,1412],{"id":1408},"Choosing a fix for leading zeros",[69,1414,1415],{"id":1409},"If the cell stores text, use dtype str. If it stores a number with a zero-padded format, pad using the format width. If the width is fixed by definition, a converter handles both text and numbers.",[73,1417],{"x":75,"y":75,"width":76,"height":1418,"fill":78},"282",[73,1420],{"x":1421,"y":81,"width":1422,"height":1423,"rx":1424,"fill":85,"stroke":86,"style":87},"230","300","52","12",[89,1426,1428],{"x":140,"y":1423,"style":1427},"font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","what does the cell store?",[96,1430],{"x1":140,"y1":1431,"x2":140,"y2":674,"stroke":86,"style":87},"72",[96,1433],{"x1":1434,"y1":674,"x2":1435,"y2":674,"stroke":86,"style":87},"133.3","626.7",[96,1437],{"x1":1434,"y1":674,"x2":1434,"y2":113,"stroke":86,"style":87},[1439,1440],"polygon",{"points":1441,"fill":1442},"133.3,127 128.3,118 138.3,118","#5b5cf0",[73,1444],{"x":81,"y":1445,"width":1446,"height":1447,"rx":1424,"fill":85,"stroke":86,"style":87},"128","226.7","104",[89,1449,1452],{"x":1434,"y":1450,"style":1451},"150","font-size:11px;font-weight:600;fill:var(--brand-strong,#4338ca);text-anchor:middle","text \"00123\"",[89,1454,506],{"x":1434,"y":1455,"style":1427},"174",[89,1457,1458],{"x":1434,"y":622,"style":105},"fast, exact,",[89,1460,1462],{"x":1434,"y":1461,"style":105},"216","keeps any width",[96,1464],{"x1":140,"y1":674,"x2":140,"y2":113,"stroke":86,"style":87},[1439,1466],{"points":1467,"fill":1442},"380,127 375,118 385,118",[73,1469],{"x":1470,"y":1445,"width":1446,"height":1447,"rx":1424,"fill":118,"stroke":119,"style":87},"266.7",[89,1472,1474],{"x":140,"y":1450,"style":1473},"font-size:11px;font-weight:600;fill:var(--gold-ink,#7a4e06);text-anchor:middle","number + 00000 format",[89,1476,1478],{"x":140,"y":1455,"style":1477},"font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","pad from format",[89,1480,1481],{"x":140,"y":622,"style":105},"openpyxl width",[89,1483,1484],{"x":140,"y":1461,"style":105},"then zfill",[96,1486],{"x1":1435,"y1":674,"x2":1435,"y2":113,"stroke":86,"style":87},[1439,1488],{"points":1489,"fill":1442},"626.7,127 621.7,118 631.7,118",[73,1491],{"x":1492,"y":1445,"width":1446,"height":1447,"rx":1424,"fill":708,"stroke":709,"style":87},"513.3",[89,1494,1496],{"x":1435,"y":1450,"style":1495},"font-size:11px;font-weight:600;fill:var(--teal-ink,#0b6157);text-anchor:middle","either; width fixed",[89,1498,1500],{"x":1435,"y":1455,"style":1499},"font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","converter",[89,1502,1503],{"x":1435,"y":622,"style":105},"f\"{int(v):05d}\"",[89,1505,1506],{"x":1435,"y":1461,"style":105},"per cell",[89,1508,1510],{"x":140,"y":1509,"style":142},"266","identifiers should end up as strings in every case",[145,1512,1514],{"id":1513},"step-6-keep-the-zeros-when-writing-back","Step 6 — Keep the zeros when writing back",[10,1516,1517,1518,1521],{},"The same values can lose their zeros again on the way out. A string column written with ",[14,1519,1520],{},"to_excel"," is stored as text and keeps its zeros, but Excel then marks those\ncells with the \"number stored as text\" warning triangle. When the output must stay numeric — for a system that expects numbers — write numbers and a padded format instead.",[150,1523,1525],{"className":196,"code":1524,"language":198,"meta":155,"style":155},"with pd.ExcelWriter(\"customers_out.xlsx\", engine=\"openpyxl\") as writer:\n    customers.to_excel(writer, sheet_name=\"Customers\", index=False)\n    out = writer.sheets[\"Customers\"]\n    for cell in out[\"B\"][1:]:\n        cell.value = int(cell.value)                 # store the number…\n        cell.number_format = \"00000\"                 # …and show it padded\n\ncheck = load_workbook(\"customers_out.xlsx\")[\"Customers\"]\nprint(check[\"A2\"].value, check[\"A2\"].data_type, check[\"B2\"].value, check[\"B2\"].number_format)\n",[14,1526,1527,1556,1579,1593,1616,1632,1645,1649,1666],{"__ignoreMap":155},[159,1528,1529,1532,1535,1538,1540,1543,1545,1548,1551,1553],{"class":96,"line":161},[159,1530,1531],{"class":205},"with",[159,1533,1534],{"class":209}," pd.ExcelWriter(",[159,1536,1537],{"class":168},"\"customers_out.xlsx\"",[159,1539,269],{"class":209},[159,1541,1542],{"class":454},"engine",[159,1544,232],{"class":205},[159,1546,1547],{"class":168},"\"openpyxl\"",[159,1549,1550],{"class":209},") ",[159,1552,430],{"class":205},[159,1554,1555],{"class":209}," writer:\n",[159,1557,1558,1561,1563,1565,1567,1569,1572,1574,1577],{"class":96,"line":219},[159,1559,1560],{"class":209},"    customers.to_excel(writer, ",[159,1562,455],{"class":454},[159,1564,232],{"class":205},[159,1566,460],{"class":168},[159,1568,269],{"class":209},[159,1570,1571],{"class":454},"index",[159,1573,232],{"class":205},[159,1575,1576],{"class":298},"False",[159,1578,404],{"class":209},[159,1580,1581,1584,1586,1589,1591],{"class":96,"line":226},[159,1582,1583],{"class":209},"    out ",[159,1585,232],{"class":205},[159,1587,1588],{"class":209}," writer.sheets[",[159,1590,460],{"class":168},[159,1592,819],{"class":209},[159,1594,1595,1597,1600,1602,1605,1608,1611,1613],{"class":96,"line":238},[159,1596,1051],{"class":205},[159,1598,1599],{"class":209}," cell ",[159,1601,347],{"class":205},[159,1603,1604],{"class":209}," out[",[159,1606,1607],{"class":168},"\"B\"",[159,1609,1610],{"class":209},"][",[159,1612,650],{"class":298},[159,1614,1615],{"class":209},":]:\n",[159,1617,1618,1621,1623,1626,1629],{"class":96,"line":249},[159,1619,1620],{"class":209},"        cell.value ",[159,1622,232],{"class":205},[159,1624,1625],{"class":298}," int",[159,1627,1628],{"class":209},"(cell.value)                 ",[159,1630,1631],{"class":482},"# store the number…\n",[159,1633,1634,1637,1639,1642],{"class":96,"line":260},[159,1635,1636],{"class":209},"        cell.number_format ",[159,1638,232],{"class":205},[159,1640,1641],{"class":168}," \"00000\"",[159,1643,1644],{"class":482},"                 # …and show it padded\n",[159,1646,1647],{"class":96,"line":288},[159,1648,223],{"emptyLinePlaceholder":222},[159,1650,1651,1654,1656,1658,1660,1662,1664],{"class":96,"line":313},[159,1652,1653],{"class":209},"check ",[159,1655,232],{"class":205},[159,1657,809],{"class":209},[159,1659,1537],{"class":168},[159,1661,814],{"class":209},[159,1663,460],{"class":168},[159,1665,819],{"class":209},[159,1667,1668,1670,1673,1675,1678,1680,1683,1685,1687,1689],{"class":96,"line":338},[159,1669,467],{"class":298},[159,1671,1672],{"class":209},"(check[",[159,1674,833],{"class":168},[159,1676,1677],{"class":209},"].value, check[",[159,1679,833],{"class":168},[159,1681,1682],{"class":209},"].data_type, check[",[159,1684,838],{"class":168},[159,1686,1677],{"class":209},[159,1688,838],{"class":168},[159,1690,1691],{"class":209},"].number_format)\n",[10,1693,1694,1695,1697,1698,588],{},"Identifiers such as ",[14,1696,181],{}," belong as text; codes that downstream systems treat as numbers can be written as numbers with a format. Silencing the warning triangle for\ntext IDs is covered in ",[48,1699,1701],{"href":1700},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output\u002F","Fix \"Number Stored as Text\" Warnings in Excel Output",[145,1703,1705],{"id":1704},"step-7-normalise-keys-before-joining-two-sources","Step 7 — Normalise keys before joining two sources",[10,1707,1708,1709,1711,1712,1714,1715,1718],{},"The most expensive consequence of lost zeros is a join that silently drops rows: the CRM export has ",[14,1710,16],{},", the finance workbook has ",[14,1713,20],{},", and a merge returns\nnothing for that customer. Normalise identifier columns on ",[33,1716,1717],{},"both"," sides to one canonical form before merging, and check the match rate so a mismatch is loud.",[150,1720,1722],{"className":196,"code":1721,"language":198,"meta":155,"style":155},"def canonical_id(series: pd.Series, width: int) -> pd.Series:\n    text = series.astype(\"string\").str.strip().str.replace(r\"\\.0$\", \"\", regex=True)\n    return text.str.zfill(width)\n\ncrm = pd.DataFrame({\"CustomerID\": [\"00123\", \"00456\", \"00789\"], \"Segment\": [\"A\", \"B\", \"A\"]})\nfinance = pd.DataFrame({\"CustomerID\": [123, 456, 999], \"Balance\": [250.0, 90.0, 12.5]})\n\ncrm[\"key\"] = canonical_id(crm[\"CustomerID\"], 5)\nfinance[\"key\"] = canonical_id(finance[\"CustomerID\"], 5)\njoined = crm.merge(finance[[\"key\", \"Balance\"]], on=\"key\", how=\"left\", indicator=True)\n\nmatch_rate = (joined[\"_merge\"] == \"both\").mean()\nprint(joined[[\"CustomerID\", \"Balance\", \"_merge\"]])\nprint(f\"matched {match_rate:.0%} of CRM customers\")\n",[14,1723,1724,1739,1786,1793,1797,1845,1892,1896,1921,1943,1990,1994,2017,2037],{"__ignoreMap":155},[159,1725,1726,1728,1731,1734,1736],{"class":96,"line":161},[159,1727,925],{"class":205},[159,1729,1730],{"class":928}," canonical_id",[159,1732,1733],{"class":209},"(series: pd.Series, width: ",[159,1735,945],{"class":298},[159,1737,1738],{"class":209},") -> pd.Series:\n",[159,1740,1741,1744,1746,1749,1752,1755,1757,1759,1763,1765,1768,1770,1772,1775,1777,1780,1782,1784],{"class":96,"line":219},[159,1742,1743],{"class":209},"    text ",[159,1745,232],{"class":205},[159,1747,1748],{"class":209}," series.astype(",[159,1750,1751],{"class":168},"\"string\"",[159,1753,1754],{"class":209},").str.strip().str.replace(",[159,1756,1085],{"class":205},[159,1758,1093],{"class":168},[159,1760,1762],{"class":1761},"s_b0D","\\.",[159,1764,75],{"class":168},[159,1766,1767],{"class":298},"$",[159,1769,1093],{"class":168},[159,1771,269],{"class":209},[159,1773,1774],{"class":168},"\"\"",[159,1776,269],{"class":209},[159,1778,1779],{"class":454},"regex",[159,1781,232],{"class":205},[159,1783,981],{"class":298},[159,1785,404],{"class":209},[159,1787,1788,1790],{"class":96,"line":226},[159,1789,1122],{"class":205},[159,1791,1792],{"class":209}," text.str.zfill(width)\n",[159,1794,1795],{"class":96,"line":238},[159,1796,223],{"emptyLinePlaceholder":222},[159,1798,1799,1802,1804,1807,1809,1812,1814,1816,1818,1820,1823,1826,1829,1831,1834,1836,1838,1840,1842],{"class":96,"line":249},[159,1800,1801],{"class":209},"crm ",[159,1803,232],{"class":205},[159,1805,1806],{"class":209}," pd.DataFrame({",[159,1808,266],{"class":168},[159,1810,1811],{"class":209},": [",[159,1813,293],{"class":168},[159,1815,269],{"class":209},[159,1817,318],{"class":168},[159,1819,269],{"class":209},[159,1821,1822],{"class":168},"\"00789\"",[159,1824,1825],{"class":209},"], ",[159,1827,1828],{"class":168},"\"Segment\"",[159,1830,1811],{"class":209},[159,1832,1833],{"class":168},"\"A\"",[159,1835,269],{"class":209},[159,1837,1607],{"class":168},[159,1839,269],{"class":209},[159,1841,1833],{"class":168},[159,1843,1844],{"class":209},"]})\n",[159,1846,1847,1850,1852,1854,1856,1858,1860,1862,1865,1867,1870,1872,1875,1877,1880,1882,1885,1887,1890],{"class":96,"line":260},[159,1848,1849],{"class":209},"finance ",[159,1851,232],{"class":205},[159,1853,1806],{"class":209},[159,1855,266],{"class":168},[159,1857,1811],{"class":209},[159,1859,20],{"class":298},[159,1861,269],{"class":209},[159,1863,1864],{"class":298},"456",[159,1866,269],{"class":209},[159,1868,1869],{"class":298},"999",[159,1871,1825],{"class":209},[159,1873,1874],{"class":168},"\"Balance\"",[159,1876,1811],{"class":209},[159,1878,1879],{"class":298},"250.0",[159,1881,269],{"class":209},[159,1883,1884],{"class":298},"90.0",[159,1886,269],{"class":209},[159,1888,1889],{"class":298},"12.5",[159,1891,1844],{"class":209},[159,1893,1894],{"class":96,"line":288},[159,1895,223],{"emptyLinePlaceholder":222},[159,1897,1898,1901,1904,1907,1909,1912,1914,1916,1919],{"class":96,"line":313},[159,1899,1900],{"class":209},"crm[",[159,1902,1903],{"class":168},"\"key\"",[159,1905,1906],{"class":209},"] ",[159,1908,232],{"class":205},[159,1910,1911],{"class":209}," canonical_id(crm[",[159,1913,266],{"class":168},[159,1915,1825],{"class":209},[159,1917,1918],{"class":298},"5",[159,1920,404],{"class":209},[159,1922,1923,1926,1928,1930,1932,1935,1937,1939,1941],{"class":96,"line":338},[159,1924,1925],{"class":209},"finance[",[159,1927,1903],{"class":168},[159,1929,1906],{"class":209},[159,1931,232],{"class":205},[159,1933,1934],{"class":209}," canonical_id(finance[",[159,1936,266],{"class":168},[159,1938,1825],{"class":209},[159,1940,1918],{"class":298},[159,1942,404],{"class":209},[159,1944,1945,1948,1950,1953,1955,1957,1959,1962,1965,1967,1969,1971,1974,1976,1979,1981,1984,1986,1988],{"class":96,"line":364},[159,1946,1947],{"class":209},"joined ",[159,1949,232],{"class":205},[159,1951,1952],{"class":209}," crm.merge(finance[[",[159,1954,1903],{"class":168},[159,1956,269],{"class":209},[159,1958,1874],{"class":168},[159,1960,1961],{"class":209},"]], ",[159,1963,1964],{"class":454},"on",[159,1966,232],{"class":205},[159,1968,1903],{"class":168},[159,1970,269],{"class":209},[159,1972,1973],{"class":454},"how",[159,1975,232],{"class":205},[159,1977,1978],{"class":168},"\"left\"",[159,1980,269],{"class":209},[159,1982,1983],{"class":454},"indicator",[159,1985,232],{"class":205},[159,1987,981],{"class":298},[159,1989,404],{"class":209},[159,1991,1992],{"class":96,"line":380},[159,1993,223],{"emptyLinePlaceholder":222},[159,1995,1996,1999,2001,2004,2007,2009,2011,2014],{"class":96,"line":395},[159,1997,1998],{"class":209},"match_rate ",[159,2000,232],{"class":205},[159,2002,2003],{"class":209}," (joined[",[159,2005,2006],{"class":168},"\"_merge\"",[159,2008,1906],{"class":209},[159,2010,1073],{"class":205},[159,2012,2013],{"class":168}," \"both\"",[159,2015,2016],{"class":209},").mean()\n",[159,2018,2019,2021,2024,2026,2028,2030,2032,2034],{"class":96,"line":1132},[159,2020,467],{"class":298},[159,2022,2023],{"class":209},"(joined[[",[159,2025,266],{"class":168},[159,2027,269],{"class":209},[159,2029,1874],{"class":168},[159,2031,269],{"class":209},[159,2033,2006],{"class":168},[159,2035,2036],{"class":209},"]])\n",[159,2038,2039,2041,2044,2047,2050,2052,2055,2058,2060,2063],{"class":96,"line":1151},[159,2040,467],{"class":298},[159,2042,2043],{"class":209},"(",[159,2045,2046],{"class":205},"f",[159,2048,2049],{"class":168},"\"matched ",[159,2051,544],{"class":1304},[159,2053,2054],{"class":209},"match_rate",[159,2056,2057],{"class":205},":.0%",[159,2059,1315],{"class":1304},[159,2061,2062],{"class":168}," of CRM customers\"",[159,2064,404],{"class":209},[10,2066,2067,2068,2071,2072,2075,2076,2079,2080,2083,2084,588],{},"The ",[14,2069,2070],{},"str.replace(r\"\\.0$\", \"\")"," handles identifiers that passed through a float somewhere upstream and arrive as ",[14,2073,2074],{},"\"123.0\"",". ",[14,2077,2078],{},"indicator=True"," adds a ",[14,2081,2082],{},"_merge"," column\nthat makes unmatched rows visible, and logging the match rate on every run turns a formatting regression in either source into an obvious drop in a number you already\nwatch. More merge diagnostics are in\n",[48,2085,2087],{"href":2086},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Ffind-rows-in-one-excel-file-missing-from-another\u002F","Find Rows in One Excel File Missing from Another",[145,2089,2091],{"id":2090},"common-pitfalls","Common pitfalls",[2093,2094,2095,2111],"table",{},[2096,2097,2098],"thead",{},[2099,2100,2101,2105,2108],"tr",{},[2102,2103,2104],"th",{},"Symptom",[2102,2106,2107],{},"Cause",[2102,2109,2110],{},"Fix",[2112,2113,2114,2132,2151,2169,2186,2200],"tbody",{},[2099,2115,2116,2124,2127],{},[2117,2118,2119,2121,2122],"td",{},[14,2120,16],{}," read as ",[14,2123,20],{},[2117,2125,2126],{},"Type inference on a text column",[2117,2128,2129],{},[14,2130,2131],{},"dtype={\"CustomerID\": str}",[2099,2133,2134,2142,2148],{},[2117,2135,2136,2137,2139,2140],{},"Still ",[14,2138,27],{}," with ",[14,2141,506],{},[2117,2143,2144,2145,2147],{},"Zero exists only in the ",[14,2146,42],{}," format",[2117,2149,2150],{},"Pad from the format width, or a converter",[2099,2152,2153,2158,2161],{},[2117,2154,2155,2156],{},"Padded values end in ",[14,2157,1264],{},[2117,2159,2160],{},"Column became float because of blanks",[2117,2162,2163,2164,2166,2167],{},"Convert through ",[14,2165,1260],{}," before ",[14,2168,552],{},[2099,2170,2171,2177,2180],{},[2117,2172,2173,2174],{},"Long IDs show as ",[14,2175,2176],{},"1.23457E+15",[2117,2178,2179],{},"Numeric ID stored as a float",[2117,2181,2182,2183,2185],{},"Read as ",[14,2184,552],{},"; store IDs as text",[2099,2187,2188,2191,2194],{},[2117,2189,2190],{},"Zeros lost after a CSV step",[2117,2192,2193],{},"CSV written and re-read without dtype",[2117,2195,2196,2197,2199],{},"Pass ",[14,2198,506],{}," when reading the CSV too",[2099,2201,2202,2205,2208],{},[2117,2203,2204],{},"Joins match nothing",[2117,2206,2207],{},"One side padded, the other not",[2117,2209,2210,2211],{},"Normalise both sides with the same ",[14,2212,903],{},[145,2214,2216],{"id":2215},"performance-and-scale-notes","Performance and scale notes",[10,2218,2219,2222,2223,2225,2226,2229],{},[14,2220,2221],{},"dtype"," is applied during parsing and costs nothing extra. The format check with openpyxl reads only the header and first data row in read-only mode, so it stays fast on\nlarge files. Converters are the slow option — a Python call per cell — which is noticeable above a few hundred thousand rows; for large files, read with ",[14,2224,506],{}," and\napply a vectorised ",[14,2227,2228],{},"str.zfill"," afterwards, which does the same work far faster.",[145,2231,2233],{"id":2232},"conclusion","Conclusion",[10,2235,2236,2237,2239,2240,2242,2243,2245,2246,2248,2249,2251],{},"Leading zeros disappear either because pandas infers numbers from text or because the zeros were never data, only a ",[14,2238,42],{}," format. Check which with openpyxl's ",[14,2241,766],{},"\nand ",[14,2244,778],{},". Read text identifiers with ",[14,2247,506],{},", restore format-only zeros with ",[14,2250,903],{}," to the format's width, and use converters when the width is fixed by\ndefinition. Treat identifiers as strings throughout the pipeline, normalise both sides before joins, and decide deliberately between text and formatted numbers when writing back.",[145,2253,2255],{"id":2254},"frequently-asked-questions","Frequently asked questions",[10,2257,2258,2262],{},[2259,2260,2261],"strong",{},"Why does pandas remove leading zeros from Excel?","\nEither the column held numeric-looking text that pandas inferred as numbers, or the cells were numbers displayed with a zero-padded format. dtype=str fixes the first; padding from the format fixes the second.",[10,2264,2265,2268],{},[2259,2266,2267],{},"Does dtype=str affect dates and real numbers?","\nYes, if applied to the whole sheet — dates become strings too. Pass a dictionary with only the identifier columns to keep other columns typed normally.",[10,2270,2271,2274],{},[2259,2272,2273],{},"How do I keep leading zeros in phone numbers with a plus sign?","\nNumbers like +44 are text in Excel, so dtype=str keeps them exactly. Store phone numbers as text end to end rather than as numbers.",[10,2276,2277,2280],{},[2259,2278,2279],{},"Can I fix the zeros in the Excel file itself?","\nYes. With openpyxl, convert numeric cells to padded strings, or keep the numbers and apply a 00000 number format. Which is right depends on whether downstream tools expect text or numbers.",[145,2282,2284],{"id":2283},"related","Related",[2286,2287,2288,2295,2302,2309,2316],"ul",{},[2289,2290,2291,2292,2294],"li",{},"Up one level: ",[48,2293,51],{"href":50}," — every read_excel option that shapes your data.",[2289,2296,2297,2301],{},[48,2298,2300],{"href":2299},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-specific-columns-from-excel-with-pandas\u002F","Read Specific Columns From Excel With Pandas"," — combining usecols with dtype.",[2289,2303,2304,2308],{},[48,2305,2307],{"href":2306},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-excel-data-types-with-pandas\u002F","Check Excel Data Types with pandas"," — validating dtypes after the read.",[2289,2310,2311,2315],{},[48,2312,2314],{"href":2313},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas\u002F","Convert Excel Text Columns to Numbers with pandas"," — the opposite conversion, for real quantities.",[2289,2317,2318,2322],{},[48,2319,2321],{"href":2320},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-two-excel-files-on-common-column-python\u002F","Merge Two Excel Files on a Common Column in Python"," — where mismatched zeros break joins.",[2324,2325,2326],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}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}",{"title":155,"searchDepth":219,"depth":219,"links":2328},[2329,2330,2331,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341],{"id":147,"depth":219,"text":148},{"id":407,"depth":219,"text":408},{"id":499,"depth":219,"text":500},{"id":751,"depth":219,"text":752},{"id":890,"depth":219,"text":891},{"id":1268,"depth":219,"text":1269},{"id":1513,"depth":219,"text":1514},{"id":1704,"depth":219,"text":1705},{"id":2090,"depth":219,"text":2091},{"id":2215,"depth":219,"text":2216},{"id":2232,"depth":219,"text":2233},{"id":2254,"depth":219,"text":2255},{"id":2283,"depth":219,"text":2284},"2026-09-17","Customer IDs, ZIP codes and phone numbers lose their leading zeros in read_excel. Fix it for text cells with dtype, and for numbers that only look padded because of a 00000 format, by reading the format with openpyxl.","md",[2346,2348,2350,2352],{"q":2261,"a":2347},"Either the column held numeric-looking text that pandas inferred as numbers, or the cells were numbers displayed with a zero-padded format. dtype=str fixes the first; padding from the format fixes the second.",{"q":2267,"a":2349},"Yes, if applied to the whole sheet — dates become strings too. Pass a dictionary with only the identifier columns to keep other columns typed normally.",{"q":2273,"a":2351},"Numbers like +44 are text in Excel, so dtype=str keeps them exactly. Store phone numbers as text end to end rather than as numbers.",{"q":2279,"a":2353},"Yes. With openpyxl, convert numeric cells to padded strings, or keep the numbers and apply a 00000 number format. Which is right depends on whether downstream tools expect text or numbers.","keep leading zeros when reading excel with pandas",null,{"breadcrumb":2357},[2358,2361,2364],{"name":2359,"item":2360},"Home","\u002F",{"name":2362,"item":2363},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":51,"item":50},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas",{"title":5,"description":2367},"Stop pandas read_excel from dropping leading zeros in IDs, ZIP codes and phone numbers: dtype=str, converters, zfill, reading 00000 number formats with openpyxl, and writing zeros back.","keep-leading-zeros-when-reading-excel-with-pandas","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas\u002Findex",[2371,2372,2373],"reading-excel","data-cleaning","pandas","how-to","3VgvAREiphv2KDYEzrJ5n-LQK1YGNDc1fTkrkbyAsgY",[2377,2380],{"title":587,"path":2378,"stem":2379,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhow-to-read-excel-with-pandas-step-by-step","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhow-to-read-excel-with-pandas-step-by-step\u002Findex",{"title":2381,"path":2382,"stem":2383,"children":-1},"List Sheet Names and Column Headers Without Loading the Data","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Flist-sheet-names-and-column-headers-without-loading-data","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Flist-sheet-names-and-column-headers-without-loading-data\u002Findex",[2385,2387],{"path":2386,"title":2362},"\u002Fgetting-started-with-python-excel-automation",{"path":2388,"title":51},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas",[],[2391,2394,2397,2400,2403,2406],{"path":2392,"title":2393,"kind":2374},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-an-excel-file-from-a-url-or-bytes-in-python","Read an Excel File from a URL or Bytes in Python",{"path":2395,"title":2396,"kind":2374},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl","Find the Last Row with Data in openpyxl",{"path":2398,"title":2399,"kind":2374},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl","Iterate over Rows and Columns with openpyxl",{"path":2401,"title":2402,"kind":2374},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl","Read a Cell Value From Excel With openpyxl",{"path":2404,"title":2405,"kind":2374},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas","Standardise Excel Column Names with pandas",{"path":2407,"title":2408,"kind":2409},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets","Extracting Data from Messy Excel Sheets","guide",1790972065045]