[{"data":1,"prerenderedAt":1821},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel":1785,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel":1794,"children:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel":1799,"related:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel":1800},{"id":4,"title":5,"body":6,"dateModified":1750,"datePublished":1750,"description":1751,"extension":1752,"faq":1753,"keyword":1762,"kind":1763,"meta":1764,"navigation":364,"path":1773,"seo":1774,"slug":1777,"stem":1778,"topics":1779,"type":1783,"__hash__":1784},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel\u002Findex.md","Fix Long ID Numbers Losing Digits or Turning into 1.23E+18 in Excel",{"type":7,"value":8,"toc":1736},"minimark",[9,38,47,205,210,240,293,297,304,496,507,511,518,674,688,832,836,845,881,888,947,958,962,969,1141,1157,1161,1164,1203,1218,1413,1417,1420,1576,1592,1596,1642,1646,1652,1656,1671,1675,1681,1687,1693,1699,1703,1732],[10,11,12,13,17,18,21,22,25,26,29,30,33,34,37],"p",{},"A column of account numbers goes into a workbook and comes out different. A 19-digit transaction ID becomes ",[14,15,16],"code",{},"1.23457E+18","; widen the column and it reads ",[14,19,20],{},"1234567890123456800"," — the last digits are gone. A 16-digit card number looks fine until someone edits the file in Excel, after which it ends in ",[14,23,24],{},"0",". And IDs with leading zeros, like ",[14,27,28],{},"000123",", come back from ",[14,31,32],{},"read_excel"," as ",[14,35,36],{},"123",".",[10,39,40,41,46],{},"These are three different mechanisms with one cure: an identifier is not a number, so it should never be stored as one. This page, part of ",[42,43,45],"a",{"href":44},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",", shows where each loss happens and how to keep the digits intact on the way in and on the way out.",[48,49,57,58,57,62,57,66,57,73,57,83,57,94,57,101,57,106,57,110,57,117,57,122,57,127,57,134,57,140,57,144,57,148,57,152,57,156,57,159,57,164,57,171,57,176,57,179,57,182,57,186,57,189,57,192,57,198,57,202],"svg",{"viewBox":50,"role":51,"ariaLabelledBy":52,"xmlns":55,"style":56},"4 0 752 160","img",[53,54],"lid-where-t","lid-where-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[59,60,61],"title",{"id":53},"Where a long ID loses digits",[63,64,65],"desc",{"id":54},"Three stages. Python holds the integer 1234567890123456789 exactly. When openpyxl or xlsxwriter writes it as a number, it is formatted with 16 significant digits and becomes 1.234567890123457E+18 in the file. Excel keeps 15 significant digits, so the value it shows ends in zeros. Writing the ID as text skips both losses.",[67,68],"rect",{"x":69,"y":24,"width":70,"height":71,"fill":72},"4","752","160","#ffffff",[67,74],{"x":75,"y":76,"width":77,"height":78,"rx":79,"fill":80,"stroke":81,"style":82},"20.0","16.0","153.0","128.0","12","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[84,85,87],"g",{"transform":86},"translate(84.5,37.8) scale(1.000)",[88,89],"path",{"d":90,"fill":91,"stroke":92,"style":93},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","none","var(--teal-ink,#0b6157)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[95,96,100],"text",{"x":97,"y":98,"style":99},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Python int",[95,102,105],{"x":97,"y":103,"style":104},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","1234567890123456789",[95,107,109],{"x":97,"y":108,"style":104},"121.2","— exact",[111,112],"line",{"x1":113,"y1":114,"x2":115,"y2":114,"stroke":116,"style":82},"178.0","80.0","200.0","var(--brand,#5b5cf0)",[118,119],"polygon",{"points":120,"fill":121},"204.0,80.0 195.0,85.0 195.0,75.0","#5b5cf0",[67,123],{"x":124,"y":76,"width":77,"height":78,"rx":79,"fill":125,"stroke":126,"style":82},"209.0","#fdefd8","var(--gold,#b4740a)",[84,128,130],{"transform":129},"translate(273.5,29.0) scale(1.000)",[88,131],{"d":132,"fill":91,"stroke":133,"style":93},"M6 3h9l4 4v14H6z M15 3v4h4","var(--gold-ink,#7a4e06)",[95,135,139],{"x":136,"y":137,"style":138},"285.5","75.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Written as a",[95,141,143],{"x":136,"y":142,"style":138},"93.0","number",[95,145,147],{"x":136,"y":146,"style":104},"112.5","16 significant digits →",[95,149,151],{"x":136,"y":150,"style":104},"130.0","1.234567890123457E+18",[111,153],{"x1":154,"y1":114,"x2":155,"y2":114,"stroke":116,"style":82},"367.0","389.0",[118,157],{"points":158,"fill":121},"393.0,80.0 384.0,85.0 384.0,75.0",[67,160],{"x":161,"y":76,"width":77,"height":78,"rx":79,"fill":162,"stroke":163,"style":82},"398.0","#fee8f2","var(--accent,#f43f8f)",[84,165,167],{"transform":166},"translate(462.5,37.8) scale(1.000)",[88,168],{"d":169,"fill":91,"stroke":170,"style":93},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--accent-ink,#be185d)",[95,172,175],{"x":173,"y":98,"style":174},"474.5","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Held by Excel",[95,177,178],{"x":173,"y":103,"style":104},"15 significant digits →",[95,180,181],{"x":173,"y":108,"style":104},"…456800",[111,183],{"x1":184,"y1":114,"x2":185,"y2":114,"stroke":116,"style":82},"556.0","578.0",[118,187],{"points":188,"fill":121},"582.0,80.0 573.0,85.0 573.0,75.0",[67,190],{"x":191,"y":76,"width":77,"height":78,"rx":79,"fill":80,"stroke":81,"style":82},"587.0",[84,193,195],{"transform":194},"translate(651.5,46.5) scale(1.000)",[88,196],{"d":197,"fill":91,"stroke":92,"style":93},"M5 12.5l4.5 4.5L19 7",[95,199,201],{"x":200,"y":142,"style":99},"663.5","Written as text",[95,203,204],{"x":200,"y":146,"style":104},"every digit kept",[206,207,209],"h2",{"id":208},"prerequisites","Prerequisites",[211,212,217],"pre",{"className":213,"code":214,"language":215,"meta":216,"style":216},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[14,218,219],{"__ignoreMap":216},[220,221,223,227,231,234,237],"span",{"class":111,"line":222},1,[220,224,226],{"class":225},"sMTad","pip",[220,228,230],{"class":229},"srMev"," install",[220,232,233],{"class":229}," pandas",[220,235,236],{"class":229}," openpyxl",[220,238,239],{"class":229}," xlsxwriter\n",[211,241,245],{"className":242,"code":243,"language":244,"meta":216,"style":216},"language-python shiki shiki-themes github-light github-dark-high-contrast","import re\nimport zipfile\nimport pandas as pd\nfrom openpyxl import Workbook, load_workbook\n","python",[14,246,247,257,265,279],{"__ignoreMap":216},[220,248,249,253],{"class":111,"line":222},[220,250,252],{"class":251},"s-kum","import",[220,254,256],{"class":255},"skGVy"," re\n",[220,258,260,262],{"class":111,"line":259},2,[220,261,252],{"class":251},[220,263,264],{"class":255}," zipfile\n",[220,266,268,270,273,276],{"class":111,"line":267},3,[220,269,252],{"class":251},[220,271,272],{"class":255}," pandas ",[220,274,275],{"class":251},"as",[220,277,278],{"class":255}," pd\n",[220,280,282,285,288,290],{"class":111,"line":281},4,[220,283,284],{"class":251},"from",[220,286,287],{"class":255}," openpyxl ",[220,289,252],{"class":251},[220,291,292],{"class":255}," Workbook, load_workbook\n",[206,294,296],{"id":295},"step-1-see-the-loss-in-the-file-itself","Step 1 — See the loss in the file itself",[10,298,299,300,303],{},"The most surprising part is that digits can disappear before Excel is involved at all. openpyxl and xlsxwriter both format numbers for the file with Python's ",[14,301,302],{},"%.16g"," — sixteen significant digits. That is enough for any value a float can hold exactly, but an integer with more digits is rounded as it is written:",[211,305,307],{"className":242,"code":306,"language":244,"meta":216,"style":216},"ids = pd.DataFrame({\"txn_id\": [4111111111111111, 1234567890123456789]})\nids.to_excel(\"ids_as_numbers.xlsx\", index=False)\n\nwith zipfile.ZipFile(\"ids_as_numbers.xlsx\") as z:\n    xml = z.read(\"xl\u002Fworksheets\u002Fsheet1.xml\").decode()\nprint(re.findall(r'\u003Cc r=\"A[23]\"[^>]*>\u003Cv>(.*?)\u003C\u002Fv>', xml))\n# ['4111111111111111', '1.234567890123457E+18']\n\nprint(pd.read_excel(\"ids_as_numbers.xlsx\")[\"txn_id\"].tolist())\n# [4111111111111111, 1234567890123457024]\n",[14,308,309,338,360,366,384,401,460,467,472,490],{"__ignoreMap":216},[220,310,311,314,317,320,323,326,330,333,335],{"class":111,"line":222},[220,312,313],{"class":255},"ids ",[220,315,316],{"class":251},"=",[220,318,319],{"class":255}," pd.DataFrame({",[220,321,322],{"class":229},"\"txn_id\"",[220,324,325],{"class":255},": [",[220,327,329],{"class":328},"sP0c6","4111111111111111",[220,331,332],{"class":255},", ",[220,334,105],{"class":328},[220,336,337],{"class":255},"]})\n",[220,339,340,343,346,348,352,354,357],{"class":111,"line":259},[220,341,342],{"class":255},"ids.to_excel(",[220,344,345],{"class":229},"\"ids_as_numbers.xlsx\"",[220,347,332],{"class":255},[220,349,351],{"class":350},"sa561","index",[220,353,316],{"class":251},[220,355,356],{"class":328},"False",[220,358,359],{"class":255},")\n",[220,361,362],{"class":111,"line":267},[220,363,365],{"emptyLinePlaceholder":364},true,"\n",[220,367,368,371,374,376,379,381],{"class":111,"line":281},[220,369,370],{"class":251},"with",[220,372,373],{"class":255}," zipfile.ZipFile(",[220,375,345],{"class":229},[220,377,378],{"class":255},") ",[220,380,275],{"class":251},[220,382,383],{"class":255}," z:\n",[220,385,387,390,392,395,398],{"class":111,"line":386},5,[220,388,389],{"class":255},"    xml ",[220,391,316],{"class":251},[220,393,394],{"class":255}," z.read(",[220,396,397],{"class":229},"\"xl\u002Fworksheets\u002Fsheet1.xml\"",[220,399,400],{"class":255},").decode()\n",[220,402,404,407,410,413,416,419,423,426,429,431,434,437,439,442,445,448,451,454,457],{"class":111,"line":403},6,[220,405,406],{"class":328},"print",[220,408,409],{"class":255},"(re.findall(",[220,411,412],{"class":251},"r",[220,414,415],{"class":229},"'\u003Cc r=\"A",[220,417,418],{"class":328},"[",[220,420,422],{"class":421},"sSjpA","23",[220,424,425],{"class":328},"]",[220,427,428],{"class":229},"\"",[220,430,418],{"class":328},[220,432,433],{"class":251},"^",[220,435,436],{"class":421},">",[220,438,425],{"class":328},[220,440,441],{"class":251},"*",[220,443,444],{"class":229},">\u003Cv>",[220,446,447],{"class":328},"(.",[220,449,450],{"class":251},"*?",[220,452,453],{"class":328},")",[220,455,456],{"class":229},"\u003C\u002Fv>'",[220,458,459],{"class":255},", xml))\n",[220,461,463],{"class":111,"line":462},7,[220,464,466],{"class":465},"s-wDw","# ['4111111111111111', '1.234567890123457E+18']\n",[220,468,470],{"class":111,"line":469},8,[220,471,365],{"emptyLinePlaceholder":364},[220,473,475,477,480,482,485,487],{"class":111,"line":474},9,[220,476,406],{"class":328},[220,478,479],{"class":255},"(pd.read_excel(",[220,481,345],{"class":229},[220,483,484],{"class":255},")[",[220,486,322],{"class":229},[220,488,489],{"class":255},"].tolist())\n",[220,491,493],{"class":111,"line":492},10,[220,494,495],{"class":465},"# [4111111111111111, 1234567890123457024]\n",[10,497,498,499,502,503,506],{},"The 19-digit ID is already wrong in the file: reading it back gives ",[14,500,501],{},"…457024",", not ",[14,504,505],{},"…456789",". The 16-digit card number survives the write, but it is now at the mercy of the next program that stores it. Excel keeps 15 significant digits — a documented limit of the product — so when Excel takes in that value, for example when someone edits and saves the sheet, the sixteenth digit becomes 0.",[206,508,510],{"id":509},"step-2-write-identifiers-as-text","Step 2 — Write identifiers as text",[10,512,513,514,517],{},"Convert ID columns to strings before writing. A Python ",[14,515,516],{},"str"," becomes a text cell, which Excel stores and displays character for character, however long:",[211,519,521],{"className":242,"code":520,"language":244,"meta":216,"style":216},"ids = pd.DataFrame({\n    \"txn_id\": [4111111111111111, 1234567890123456789],\n    \"account\": [\"000123\", \"004567\"],\n})\nids[\"txn_id\"] = ids[\"txn_id\"].astype(str)\n\nids.to_excel(\"ids_as_text.xlsx\", index=False)\nwith zipfile.ZipFile(\"ids_as_text.xlsx\") as z:\n    xml = z.read(\"xl\u002Fworksheets\u002Fsheet1.xml\").decode()\nprint('t=\"s\"' in xml or 't=\"inlineStr\"' in xml)\n# True — the cells are text\n",[14,522,523,532,548,565,570,594,598,615,629,641,668],{"__ignoreMap":216},[220,524,525,527,529],{"class":111,"line":222},[220,526,313],{"class":255},[220,528,316],{"class":251},[220,530,531],{"class":255}," pd.DataFrame({\n",[220,533,534,537,539,541,543,545],{"class":111,"line":259},[220,535,536],{"class":229},"    \"txn_id\"",[220,538,325],{"class":255},[220,540,329],{"class":328},[220,542,332],{"class":255},[220,544,105],{"class":328},[220,546,547],{"class":255},"],\n",[220,549,550,553,555,558,560,563],{"class":111,"line":267},[220,551,552],{"class":229},"    \"account\"",[220,554,325],{"class":255},[220,556,557],{"class":229},"\"000123\"",[220,559,332],{"class":255},[220,561,562],{"class":229},"\"004567\"",[220,564,547],{"class":255},[220,566,567],{"class":111,"line":281},[220,568,569],{"class":255},"})\n",[220,571,572,575,577,580,582,585,587,590,592],{"class":111,"line":386},[220,573,574],{"class":255},"ids[",[220,576,322],{"class":229},[220,578,579],{"class":255},"] ",[220,581,316],{"class":251},[220,583,584],{"class":255}," ids[",[220,586,322],{"class":229},[220,588,589],{"class":255},"].astype(",[220,591,516],{"class":328},[220,593,359],{"class":255},[220,595,596],{"class":111,"line":403},[220,597,365],{"emptyLinePlaceholder":364},[220,599,600,602,605,607,609,611,613],{"class":111,"line":462},[220,601,342],{"class":255},[220,603,604],{"class":229},"\"ids_as_text.xlsx\"",[220,606,332],{"class":255},[220,608,351],{"class":350},[220,610,316],{"class":251},[220,612,356],{"class":328},[220,614,359],{"class":255},[220,616,617,619,621,623,625,627],{"class":111,"line":469},[220,618,370],{"class":251},[220,620,373],{"class":255},[220,622,604],{"class":229},[220,624,378],{"class":255},[220,626,275],{"class":251},[220,628,383],{"class":255},[220,630,631,633,635,637,639],{"class":111,"line":474},[220,632,389],{"class":255},[220,634,316],{"class":251},[220,636,394],{"class":255},[220,638,397],{"class":229},[220,640,400],{"class":255},[220,642,643,645,648,651,654,657,660,663,665],{"class":111,"line":492},[220,644,406],{"class":328},[220,646,647],{"class":255},"(",[220,649,650],{"class":229},"'t=\"s\"'",[220,652,653],{"class":251}," in",[220,655,656],{"class":255}," xml ",[220,658,659],{"class":251},"or",[220,661,662],{"class":229}," 't=\"inlineStr\"'",[220,664,653],{"class":251},[220,666,667],{"class":255}," xml)\n",[220,669,671],{"class":111,"line":670},11,[220,672,673],{"class":465},"# True — the cells are text\n",[10,675,676,677,680,681,683,684,687],{},"Convert at the point the data enters your code, not just before writing. If the IDs come from a database or an API as integers, they may already have been through arithmetic-capable types; the moment an 18-digit value is converted to a float anywhere in the pipeline, the digits are gone and no later ",[14,678,679],{},"astype(str)"," brings them back. Keep them as ",[14,682,516],{}," (or as Python ",[14,685,686],{},"int",", which is exact) from the source.",[48,689,57,695,57,698,57,701,57,706,57,715,57,719,57,725,57,729,57,734,57,736,57,740,57,743,57,747,57,749,57,752,57,755,57,759,57,762,57,765,57,768,57,772,57,775,57,777,57,780,57,782,57,784,57,786,57,788,57,790,57,793,57,795,57,797,57,799,57,801,57,807,57,811,57,816,57,819,57,827],{"viewBox":690,"role":51,"ariaLabelledBy":691,"xmlns":55,"style":694},"72 0 615 164",[692,693],"lid-text-t","lid-text-d","width:100%;max-width:615px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[59,696,697],{"id":692},"The same IDs stored as numbers and as text",[63,699,700],{"id":693},"Left: IDs written as numbers. The 16-digit card number survives, but the 19-digit ID is stored as 1.234567890123457E+18. Right: the same IDs written as text keep every digit.",[67,702],{"x":703,"y":24,"width":704,"height":705,"fill":72},"72","615","164",[67,707],{"x":708,"y":709,"width":710,"height":711,"rx":24,"fill":712,"stroke":713,"style":714},"88.8","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[67,716],{"x":717,"y":709,"width":718,"height":711,"rx":24,"fill":712,"stroke":713,"style":714},"120.8","181.5",[95,720,724],{"x":721,"y":722,"style":723},"211.5","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[67,726],{"x":708,"y":727,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},"64.0","28.0",[95,730,733],{"x":731,"y":732,"style":723},"104.8","83.0","1",[67,735],{"x":717,"y":727,"width":718,"height":728,"rx":24,"fill":72,"stroke":713,"style":714},[95,737,739],{"x":721,"y":732,"style":738},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","txn_id",[67,741],{"x":708,"y":742,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},"92.0",[95,744,746],{"x":731,"y":745,"style":723},"111.0","2",[67,748],{"x":717,"y":742,"width":718,"height":728,"rx":24,"fill":72,"stroke":713,"style":714},[95,750,329],{"x":721,"y":745,"style":751},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[67,753],{"x":708,"y":754,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},"120.0",[95,756,758],{"x":731,"y":757,"style":723},"139.0","3",[67,760],{"x":717,"y":754,"width":718,"height":728,"rx":24,"fill":162,"stroke":163,"style":761},"stroke-width:1.5px",[95,763,151],{"x":721,"y":757,"style":764},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[67,766],{"x":767,"y":709,"width":710,"height":711,"rx":24,"fill":712,"stroke":713,"style":714},"467.2",[67,769],{"x":770,"y":709,"width":771,"height":711,"rx":24,"fill":712,"stroke":713,"style":714},"499.2","172.0",[95,773,724],{"x":774,"y":722,"style":723},"585.2",[67,776],{"x":767,"y":727,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},[95,778,733],{"x":779,"y":732,"style":723},"483.2",[67,781],{"x":770,"y":727,"width":771,"height":728,"rx":24,"fill":72,"stroke":713,"style":714},[95,783,739],{"x":774,"y":732,"style":738},[67,785],{"x":767,"y":742,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},[95,787,746],{"x":779,"y":745,"style":723},[67,789],{"x":770,"y":742,"width":771,"height":728,"rx":24,"fill":80,"stroke":81,"style":761},[95,791,329],{"x":774,"y":745,"style":792},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[67,794],{"x":767,"y":754,"width":710,"height":728,"rx":24,"fill":712,"stroke":713,"style":714},[95,796,758],{"x":779,"y":757,"style":723},[67,798],{"x":770,"y":754,"width":771,"height":728,"rx":24,"fill":80,"stroke":81,"style":761},[95,800,105],{"x":774,"y":757,"style":792},[95,802,806],{"x":803,"y":804,"style":805},"195.5","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","written as numbers",[95,808,810],{"x":809,"y":804,"style":805},"569.2","written as text",[111,812],{"x1":813,"y1":814,"x2":815,"y2":814,"stroke":116,"style":82},"312.3","94.0","453.2",[118,817],{"points":818,"fill":121},"457.2,94.0 448.2,99.0 448.2,89.0",[67,820],{"x":821,"y":822,"width":823,"height":824,"rx":825,"fill":72,"stroke":713,"style":826},"334.8","61.0","100.0","22.0","11","stroke-width:1.2px",[95,828,679],{"x":829,"y":830,"style":831},"384.7","76.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",[206,833,835],{"id":834},"step-3-read-them-back-as-text","Step 3 — Read them back as text",[10,837,838,839,841,842,844],{},"The reading side has its own trap. pandas' Excel reader infers types for text columns the way its CSV reader does, so a text cell holding ",[14,840,28],{}," comes back as the integer ",[14,843,36],{},", and very long text IDs can come back as integers too:",[211,846,848],{"className":242,"code":847,"language":244,"meta":216,"style":216},"back = pd.read_excel(\"ids_as_text.xlsx\")\nprint(back.dtypes.to_dict(), back[\"account\"].tolist())\n# {'txn_id': dtype('int64'), 'account': dtype('int64')} [123, 4567]\n",[14,849,850,864,876],{"__ignoreMap":216},[220,851,852,855,857,860,862],{"class":111,"line":222},[220,853,854],{"class":255},"back ",[220,856,316],{"class":251},[220,858,859],{"class":255}," pd.read_excel(",[220,861,604],{"class":229},[220,863,359],{"class":255},[220,865,866,868,871,874],{"class":111,"line":259},[220,867,406],{"class":328},[220,869,870],{"class":255},"(back.dtypes.to_dict(), back[",[220,872,873],{"class":229},"\"account\"",[220,875,489],{"class":255},[220,877,878],{"class":111,"line":267},[220,879,880],{"class":465},"# {'txn_id': dtype('int64'), 'account': dtype('int64')} [123, 4567]\n",[10,882,883,884,887],{},"Name the identifier columns in ",[14,885,886],{},"dtype"," and they arrive exactly as stored:",[211,889,891],{"className":242,"code":890,"language":244,"meta":216,"style":216},"back = pd.read_excel(\"ids_as_text.xlsx\", dtype={\"txn_id\": str, \"account\": str})\nprint(back.to_dict(\"records\"))\n# [{'txn_id': '4111111111111111', 'account': '000123'}, {'txn_id': '1234567890123456789', 'account': '004567'}]\n",[14,892,893,929,942],{"__ignoreMap":216},[220,894,895,897,899,901,903,905,907,909,912,914,917,919,921,923,925,927],{"class":111,"line":222},[220,896,854],{"class":255},[220,898,316],{"class":251},[220,900,859],{"class":255},[220,902,604],{"class":229},[220,904,332],{"class":255},[220,906,886],{"class":350},[220,908,316],{"class":251},[220,910,911],{"class":255},"{",[220,913,322],{"class":229},[220,915,916],{"class":255},": ",[220,918,516],{"class":328},[220,920,332],{"class":255},[220,922,873],{"class":229},[220,924,916],{"class":255},[220,926,516],{"class":328},[220,928,569],{"class":255},[220,930,931,933,936,939],{"class":111,"line":259},[220,932,406],{"class":328},[220,934,935],{"class":255},"(back.to_dict(",[220,937,938],{"class":229},"\"records\"",[220,940,941],{"class":255},"))\n",[220,943,944],{"class":111,"line":267},[220,945,946],{"class":465},"# [{'txn_id': '4111111111111111', 'account': '000123'}, {'txn_id': '1234567890123456789', 'account': '004567'}]\n",[10,948,949,952,953,957],{},[14,950,951],{},"dtype=str"," for the whole frame also works, and is a reasonable choice for staging tables where every column is converted deliberately afterwards. ",[42,954,956],{"href":955},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas\u002F","Keep Leading Zeros When Reading Excel with pandas"," covers the reading options in more detail, including converters.",[206,959,961],{"id":960},"step-4-protect-the-column-for-people-who-edit-the-file","Step 4 — Protect the column for people who edit the file",[10,963,964,965,968],{},"Text cells stay text when Excel saves them, so the digits are safe in the cells you wrote. The remaining risk is new rows: a person typing a 16-digit number into an ordinary cell gets a number, and the fifteen-digit limit applies. Give the ID column the Text number format (",[14,966,967],{},"@",") so anything typed there is stored as text too:",[211,970,972],{"className":242,"code":971,"language":244,"meta":216,"style":216},"wb = load_workbook(\"ids_as_text.xlsx\")\nws = wb.active\nfor row in range(2, 1001):\n    ws.cell(row=row, column=1).number_format = \"@\"\n    ws.cell(row=row, column=2).number_format = \"@\"\nws.column_dimensions[\"A\"].width = 24\nws.column_dimensions[\"B\"].width = 12\nwb.save(\"ids_protected.xlsx\")\n\nprint(load_workbook(\"ids_protected.xlsx\").active[\"A500\"].number_format)\n# @\n",[14,973,974,988,998,1024,1052,1074,1090,1104,1114,1118,1136],{"__ignoreMap":216},[220,975,976,979,981,984,986],{"class":111,"line":222},[220,977,978],{"class":255},"wb ",[220,980,316],{"class":251},[220,982,983],{"class":255}," load_workbook(",[220,985,604],{"class":229},[220,987,359],{"class":255},[220,989,990,993,995],{"class":111,"line":259},[220,991,992],{"class":255},"ws ",[220,994,316],{"class":251},[220,996,997],{"class":255}," wb.active\n",[220,999,1000,1003,1006,1009,1012,1014,1016,1018,1021],{"class":111,"line":267},[220,1001,1002],{"class":251},"for",[220,1004,1005],{"class":255}," row ",[220,1007,1008],{"class":251},"in",[220,1010,1011],{"class":328}," range",[220,1013,647],{"class":255},[220,1015,746],{"class":328},[220,1017,332],{"class":255},[220,1019,1020],{"class":328},"1001",[220,1022,1023],{"class":255},"):\n",[220,1025,1026,1029,1032,1034,1037,1040,1042,1044,1047,1049],{"class":111,"line":281},[220,1027,1028],{"class":255},"    ws.cell(",[220,1030,1031],{"class":350},"row",[220,1033,316],{"class":251},[220,1035,1036],{"class":255},"row, ",[220,1038,1039],{"class":350},"column",[220,1041,316],{"class":251},[220,1043,733],{"class":328},[220,1045,1046],{"class":255},").number_format ",[220,1048,316],{"class":251},[220,1050,1051],{"class":229}," \"@\"\n",[220,1053,1054,1056,1058,1060,1062,1064,1066,1068,1070,1072],{"class":111,"line":386},[220,1055,1028],{"class":255},[220,1057,1031],{"class":350},[220,1059,316],{"class":251},[220,1061,1036],{"class":255},[220,1063,1039],{"class":350},[220,1065,316],{"class":251},[220,1067,746],{"class":328},[220,1069,1046],{"class":255},[220,1071,316],{"class":251},[220,1073,1051],{"class":229},[220,1075,1076,1079,1082,1085,1087],{"class":111,"line":403},[220,1077,1078],{"class":255},"ws.column_dimensions[",[220,1080,1081],{"class":229},"\"A\"",[220,1083,1084],{"class":255},"].width ",[220,1086,316],{"class":251},[220,1088,1089],{"class":328}," 24\n",[220,1091,1092,1094,1097,1099,1101],{"class":111,"line":462},[220,1093,1078],{"class":255},[220,1095,1096],{"class":229},"\"B\"",[220,1098,1084],{"class":255},[220,1100,316],{"class":251},[220,1102,1103],{"class":328}," 12\n",[220,1105,1106,1109,1112],{"class":111,"line":469},[220,1107,1108],{"class":255},"wb.save(",[220,1110,1111],{"class":229},"\"ids_protected.xlsx\"",[220,1113,359],{"class":255},[220,1115,1116],{"class":111,"line":474},[220,1117,365],{"emptyLinePlaceholder":364},[220,1119,1120,1122,1125,1127,1130,1133],{"class":111,"line":492},[220,1121,406],{"class":328},[220,1123,1124],{"class":255},"(load_workbook(",[220,1126,1111],{"class":229},[220,1128,1129],{"class":255},").active[",[220,1131,1132],{"class":229},"\"A500\"",[220,1134,1135],{"class":255},"].number_format)\n",[220,1137,1138],{"class":111,"line":670},[220,1139,1140],{"class":465},"# @\n",[10,1142,1143,1144,1147,1148,1152,1153,1156],{},"Formatting a thousand empty rows is a trade-off: it makes the sheet safer to extend by hand, and it also makes ",[14,1145,1146],{},"max_row"," report 1000 in openpyxl — see ",[42,1149,1151],{"href":1150},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-max-row-counting-empty-rows\u002F","Fix openpyxl max_row Counting Empty Rows"," before appending to such a sheet from code. For a form that people fill in, an Excel table (",[14,1154,1155],{},"ws.add_table",") whose ID column is formatted as text is often a tidier alternative, since new rows inherit the table's formats.",[206,1158,1160],{"id":1159},"step-5-do-not-use-csv-as-the-hand-off","Step 5 — Do not use CSV as the hand-off",[10,1162,1163],{},"A CSV file carries no types, so the digits in it are only as safe as the program that opens it. Python writes them correctly:",[211,1165,1167],{"className":242,"code":1166,"language":244,"meta":216,"style":216},"ids.to_csv(\"ids.csv\", index=False)\nprint(open(\"ids.csv\").read())\n",[14,1168,1169,1187],{"__ignoreMap":216},[220,1170,1171,1174,1177,1179,1181,1183,1185],{"class":111,"line":222},[220,1172,1173],{"class":255},"ids.to_csv(",[220,1175,1176],{"class":229},"\"ids.csv\"",[220,1178,332],{"class":255},[220,1180,351],{"class":350},[220,1182,316],{"class":251},[220,1184,356],{"class":328},[220,1186,359],{"class":255},[220,1188,1189,1191,1193,1196,1198,1200],{"class":111,"line":259},[220,1190,406],{"class":328},[220,1192,647],{"class":255},[220,1194,1195],{"class":328},"open",[220,1197,647],{"class":255},[220,1199,1176],{"class":229},[220,1201,1202],{"class":255},").read())\n",[10,1204,1205,1206,1208,1209,1212,1213,1217],{},"but a spreadsheet opening the CSV by double-click decides for itself that ",[14,1207,105],{}," is a number, and the fifteen-digit rule applies again. If the recipient uses Excel, send an ",[14,1210,1211],{},".xlsx"," with text cells. If it must be CSV, tell the recipient to import it through the spreadsheet's import dialog with the ID column set to Text, rather than opening it directly — the import dialog is the only place a CSV's column types can be chosen. ",[42,1214,1216],{"href":1215},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fexcel-vs-csv-vs-parquet-for-python-data-pipelines\u002F","Excel vs CSV vs Parquet for Python Data Pipelines"," covers the wider trade-off.",[48,1219,57,1224,57,1227,57,1230,57,1233,57,1236,57,1241,57,1247,57,1250,57,1254,57,1257,57,1261,57,1264,57,1268,57,1272,57,1277,57,1279,57,1286,57,1288,57,1293,57,1295,57,1300,57,1302,57,1307,57,1310,57,1314,57,1316,57,1322,57,1324,57,1329,57,1331,57,1336,57,1338,57,1343,57,1346,57,1350,57,1352,57,1357,57,1359,57,1364,57,1366,57,1371,57,1373,57,1378,57,1381,57,1385,57,1387,57,1392,57,1394,57,1399,57,1401,57,1406,57,1408],{"viewBox":1220,"role":51,"ariaLabelledBy":1221,"xmlns":55,"style":56},"4 0 752 208",[1222,1223],"lid-choices-t","lid-choices-d",[59,1225,1226],{"id":1222},"Which storage keeps a long identifier intact",[63,1228,1229],{"id":1223},"A grid of storage choices against three risks: digits after the 16th, digits after the 15th, and leading zeros. A number cell loses all three. A text cell keeps all three. A text cell plus the @ number format also protects rows typed later. A CSV opened directly in a spreadsheet loses all three.",[67,1231],{"x":69,"y":24,"width":70,"height":1232,"fill":72},"208",[67,1234],{"x":75,"y":76,"width":1235,"height":709,"rx":24,"fill":72,"stroke":713,"style":714},"171.8",[67,1237],{"x":1238,"y":76,"width":1239,"height":709,"rx":24,"fill":1240,"stroke":116,"style":826},"191.8","137.0","#f0f4ff",[95,1242,1246],{"x":1243,"y":1244,"style":1245},"260.4","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Digits past 16th",[67,1248],{"x":1249,"y":76,"width":1239,"height":709,"rx":24,"fill":1240,"stroke":116,"style":826},"328.9",[95,1251,1253],{"x":1252,"y":1244,"style":1245},"397.4","Digits past 15th",[67,1255],{"x":1256,"y":76,"width":1239,"height":709,"rx":24,"fill":1240,"stroke":116,"style":826},"465.9",[95,1258,1260],{"x":1259,"y":1244,"style":1245},"534.4","Leading zeros",[67,1262],{"x":1263,"y":76,"width":1239,"height":709,"rx":24,"fill":1240,"stroke":116,"style":826},"603.0",[95,1265,1267],{"x":1266,"y":1244,"style":1245},"671.5","Rows typed later",[67,1269],{"x":75,"y":1270,"width":1235,"height":1271,"rx":24,"fill":712,"stroke":713,"style":714},"56.0","34.0",[95,1273,1276],{"x":710,"y":1274,"style":1275},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Number cell",[67,1278],{"x":1238,"y":1270,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1280,1282],{"transform":1281},"translate(252.4,65.0) scale(0.667)",[88,1283],{"d":1284,"fill":91,"stroke":163,"style":1285},"M6 6l12 12 M18 6L6 18","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[67,1287],{"x":1249,"y":1270,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1289,1291],{"transform":1290},"translate(389.4,65.0) scale(0.667)",[88,1292],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1294],{"x":1256,"y":1270,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1296,1298],{"transform":1297},"translate(526.4,65.0) scale(0.667)",[88,1299],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1301],{"x":1263,"y":1270,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1303,1305],{"transform":1304},"translate(663.5,65.0) scale(0.667)",[88,1306],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1308],{"x":75,"y":1309,"width":1235,"height":1271,"rx":24,"fill":712,"stroke":713,"style":714},"90.0",[95,1311,1313],{"x":710,"y":1312,"style":1275},"112.0","Text cell",[67,1315],{"x":1238,"y":1309,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1317,1319],{"transform":1318},"translate(251.4,98.0) scale(0.750)",[88,1320],{"d":197,"fill":91,"stroke":81,"style":1321},"stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[67,1323],{"x":1249,"y":1309,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1325,1327],{"transform":1326},"translate(388.4,98.0) scale(0.750)",[88,1328],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1330],{"x":1256,"y":1309,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1332,1334],{"transform":1333},"translate(525.4,98.0) scale(0.750)",[88,1335],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1337],{"x":1263,"y":1309,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1339,1341],{"transform":1340},"translate(663.5,99.0) scale(0.667)",[88,1342],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1344],{"x":75,"y":1345,"width":1235,"height":1271,"rx":24,"fill":712,"stroke":713,"style":714},"124.0",[95,1347,1349],{"x":710,"y":1348,"style":1275},"146.0","Text cell + \"@\" format",[67,1351],{"x":1238,"y":1345,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1353,1355],{"transform":1354},"translate(251.4,132.0) scale(0.750)",[88,1356],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1358],{"x":1249,"y":1345,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1360,1362],{"transform":1361},"translate(388.4,132.0) scale(0.750)",[88,1363],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1365],{"x":1256,"y":1345,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1367,1369],{"transform":1368},"translate(525.4,132.0) scale(0.750)",[88,1370],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1372],{"x":1263,"y":1345,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1374,1376],{"transform":1375},"translate(662.5,132.0) scale(0.750)",[88,1377],{"d":197,"fill":91,"stroke":81,"style":1321},[67,1379],{"x":75,"y":1380,"width":1235,"height":1271,"rx":24,"fill":712,"stroke":713,"style":714},"158.0",[95,1382,1384],{"x":710,"y":1383,"style":1275},"180.0","CSV opened directly",[67,1386],{"x":1238,"y":1380,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1388,1390],{"transform":1389},"translate(252.4,167.0) scale(0.667)",[88,1391],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1393],{"x":1249,"y":1380,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1395,1397],{"transform":1396},"translate(389.4,167.0) scale(0.667)",[88,1398],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1400],{"x":1256,"y":1380,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1402,1404],{"transform":1403},"translate(526.4,167.0) scale(0.667)",[88,1405],{"d":1284,"fill":91,"stroke":163,"style":1285},[67,1407],{"x":1263,"y":1380,"width":1239,"height":1271,"rx":24,"fill":72,"stroke":713,"style":714},[84,1409,1411],{"transform":1410},"translate(663.5,167.0) scale(0.667)",[88,1412],{"d":1284,"fill":91,"stroke":163,"style":1285},[206,1414,1416],{"id":1415},"which-columns-count-as-identifiers","Which columns count as identifiers?",[10,1418,1419],{},"A useful test: would you ever add, average or sort this value numerically? Account numbers, card numbers, IBANs, postcodes, phone numbers, product codes, national ID numbers and order references fail that test — they are labels made of digits. Quantities, amounts and counts pass it. Write the first group as text and the second as numbers, and state the rule in the job's configuration so it is applied the same way every run. A short list of column names is enough:",[211,1421,1423],{"className":242,"code":1422,"language":244,"meta":216,"style":216},"ID_COLUMNS = [\"txn_id\", \"account\"]\n\ndef prepare_for_excel(frame):\n    out = frame.copy()\n    for col in ID_COLUMNS:\n        if col in out.columns:\n            out[col] = out[col].map(lambda v: v if isinstance(v, str) else str(v))\n    return out\n\nprint(prepare_for_excel(pd.DataFrame({\"txn_id\": [1234567890123456789], \"amount\": [9.5]})).dtypes.to_dict())\n",[14,1424,1425,1445,1449,1461,1471,1487,1499,1537,1545,1549],{"__ignoreMap":216},[220,1426,1427,1430,1433,1436,1438,1440,1442],{"class":111,"line":222},[220,1428,1429],{"class":328},"ID_COLUMNS",[220,1431,1432],{"class":251}," =",[220,1434,1435],{"class":255}," [",[220,1437,322],{"class":229},[220,1439,332],{"class":255},[220,1441,873],{"class":229},[220,1443,1444],{"class":255},"]\n",[220,1446,1447],{"class":111,"line":259},[220,1448,365],{"emptyLinePlaceholder":364},[220,1450,1451,1454,1458],{"class":111,"line":267},[220,1452,1453],{"class":251},"def",[220,1455,1457],{"class":1456},"s_Opv"," prepare_for_excel",[220,1459,1460],{"class":255},"(frame):\n",[220,1462,1463,1466,1468],{"class":111,"line":281},[220,1464,1465],{"class":255},"    out ",[220,1467,316],{"class":251},[220,1469,1470],{"class":255}," frame.copy()\n",[220,1472,1473,1476,1479,1481,1484],{"class":111,"line":386},[220,1474,1475],{"class":251},"    for",[220,1477,1478],{"class":255}," col ",[220,1480,1008],{"class":251},[220,1482,1483],{"class":328}," ID_COLUMNS",[220,1485,1486],{"class":255},":\n",[220,1488,1489,1492,1494,1496],{"class":111,"line":403},[220,1490,1491],{"class":251},"        if",[220,1493,1478],{"class":255},[220,1495,1008],{"class":251},[220,1497,1498],{"class":255}," out.columns:\n",[220,1500,1501,1504,1506,1509,1512,1515,1518,1521,1524,1526,1528,1531,1534],{"class":111,"line":462},[220,1502,1503],{"class":255},"            out[col] ",[220,1505,316],{"class":251},[220,1507,1508],{"class":255}," out[col].map(",[220,1510,1511],{"class":251},"lambda",[220,1513,1514],{"class":255}," v: v ",[220,1516,1517],{"class":251},"if",[220,1519,1520],{"class":328}," isinstance",[220,1522,1523],{"class":255},"(v, ",[220,1525,516],{"class":328},[220,1527,378],{"class":255},[220,1529,1530],{"class":251},"else",[220,1532,1533],{"class":328}," str",[220,1535,1536],{"class":255},"(v))\n",[220,1538,1539,1542],{"class":111,"line":469},[220,1540,1541],{"class":251},"    return",[220,1543,1544],{"class":255}," out\n",[220,1546,1547],{"class":111,"line":474},[220,1548,365],{"emptyLinePlaceholder":364},[220,1550,1551,1553,1556,1558,1560,1562,1565,1568,1570,1573],{"class":111,"line":492},[220,1552,406],{"class":328},[220,1554,1555],{"class":255},"(prepare_for_excel(pd.DataFrame({",[220,1557,322],{"class":229},[220,1559,325],{"class":255},[220,1561,105],{"class":328},[220,1563,1564],{"class":255},"], ",[220,1566,1567],{"class":229},"\"amount\"",[220,1569,325],{"class":255},[220,1571,1572],{"class":328},"9.5",[220,1574,1575],{"class":255},"]})).dtypes.to_dict())\n",[10,1577,1578,1579,1581,1582,1584,1585,1587,1588,1591],{},"Mapping each value with ",[14,1580,516],{}," rather than calling ",[14,1583,679],{}," on a float column is deliberate: if a missing value has already turned an integer column into floats, ",[14,1586,679],{}," gives ",[14,1589,1590],{},"'1.2345678901234568e+18'",". Convert before anything turns the column into floats, and treat a float-typed ID column as a bug upstream.",[206,1593,1595],{"id":1594},"common-pitfalls","Common pitfalls",[1597,1598,1599,1610,1620,1631],"ul",{},[1600,1601,1602,1609],"li",{},[1603,1604,1605,1606,1608],"strong",{},"Formatting the cell as ",[14,1607,24],{}," to hide the E+ notation."," It changes the display, not the stored value. The digits beyond the fifteenth or sixteenth are already gone.",[1600,1611,1612,1615,1616,1619],{},[1603,1613,1614],{},"Converting after a float has touched the column."," A ",[14,1617,1618],{},"NaN"," in an integer column turns it into floats in pandas; convert IDs to text before any operation that could introduce missing values.",[1600,1621,1622,1625,1626,1628,1629,37],{},[1603,1623,1624],{},"Forgetting the reading side."," Text in the file is not enough if ",[14,1627,32],{}," converts it back; pass ",[14,1630,886],{},[1600,1632,1633,1636,1637,1641],{},[1603,1634,1635],{},"Silencing the green triangle by converting to numbers."," The \"Number Stored as Text\" indicator is correct for IDs; ",[42,1638,1640],{"href":1639},"\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"," shows how to leave IDs as text while fixing real numbers.",[206,1643,1645],{"id":1644},"performance-notes","Performance notes",[10,1647,1648,1649,1651],{},"Text cells cost slightly more space than numbers, because each distinct string goes into the workbook's shared-strings table. For a million unique 19-digit IDs that is a few megabytes, and it is still the only representation that keeps the value. Reading with ",[14,1650,951],{}," is marginally faster than letting pandas infer types, since the inference step is skipped.",[206,1653,1655],{"id":1654},"conclusion","Conclusion",[10,1657,1658,1659,1661,1662,1664,1665,1667,1668,1670],{},"Long identifiers lose digits in two places: Python's Excel writers keep 16 significant digits, and Excel itself keeps 15. Leading zeros are lost a third way, when ",[14,1660,32],{}," infers numbers from text. Store every identifier as text from the moment it enters your code, write it as text, read it back with ",[14,1663,951],{},", format the column as ",[14,1666,967],{}," for rows people will type, and hand files over as ",[14,1669,1211],{}," rather than CSV.",[206,1672,1674],{"id":1673},"frequently-asked-questions","Frequently asked questions",[10,1676,1677,1680],{},[1603,1678,1679],{},"Why does Excel change the last digit of my 16-digit number to 0?","\nExcel stores numbers with 15 significant digits of precision. Any digits after the fifteenth are replaced with zeros when Excel takes in the value, so a 16-digit card number ends in 0. Store identifiers as text instead.",[10,1682,1683,1686],{},[1603,1684,1685],{},"Why did the digits change before I even opened the file?","\nopenpyxl and xlsxwriter write numbers with 16 significant digits. An integer with 17 or more digits is written in scientific notation with the tail rounded away, so the file itself no longer contains the original value.",[10,1688,1689,1692],{},[1603,1690,1691],{},"Will writing IDs as text cause \"Number Stored as Text\" warnings?","\nExcel shows its small green triangle on text that looks like a number. For identifiers that is correct and harmless; the warning can be turned off per range. Treating IDs as numbers is the thing to avoid.",[10,1694,1695,1698],{},[1603,1696,1697],{},"How do I read the IDs back without losing leading zeros?","\nPass dtype={\"id_column\": str} to read_excel. Without it, pandas converts text that looks numeric back into integers, which drops leading zeros and can lose precision on very long values.",[206,1700,1702],{"id":1701},"related","Related",[1597,1704,1705,1711,1718,1725],{},[1600,1706,1707,1708,1710],{},"Up one level: ",[42,1709,45],{"href":44}," — other ways data changes between Python and Excel.",[1600,1712,1713,1717],{},[42,1714,1716],{"href":1715},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-zip-codes-and-ids-with-leading-zeros\u002F","Format ZIP Codes and IDs with Leading Zeros in Excel"," — when a fixed-width number format is the better choice.",[1600,1719,1720,1724],{},[42,1721,1723],{"href":1722},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-floating-point-noise-in-excel-output\u002F","Fix Floating-Point Noise Like 0.1 + 0.2 ≠ 0.3 in Excel Output"," — the precision story for real numbers.",[1600,1726,1727,1731],{},[42,1728,1730],{"href":1729},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-excel-data-types-with-pandas\u002F","Check Excel Data Types with pandas"," — catching float-typed ID columns before they are written.",[1733,1734,1735],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}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}",{"title":216,"searchDepth":259,"depth":259,"links":1737},[1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749],{"id":208,"depth":259,"text":209},{"id":295,"depth":259,"text":296},{"id":509,"depth":259,"text":510},{"id":834,"depth":259,"text":835},{"id":960,"depth":259,"text":961},{"id":1159,"depth":259,"text":1160},{"id":1415,"depth":259,"text":1416},{"id":1594,"depth":259,"text":1595},{"id":1644,"depth":259,"text":1645},{"id":1654,"depth":259,"text":1655},{"id":1673,"depth":259,"text":1674},{"id":1701,"depth":259,"text":1702},"2026-09-25","Account numbers, card numbers and 18-digit IDs lose their last digits when written to Excel as numbers — Python writers keep 16 significant digits and Excel keeps 15. Write identifiers as text, read them back as text, and stop the leading zeros and trailing digits from changing.","md",[1754,1756,1758,1760],{"q":1679,"a":1755},"Excel stores numbers with 15 significant digits of precision. Any digits after the fifteenth are replaced with zeros when Excel takes in the value, so a 16-digit card number ends in 0. Store identifiers as text instead.",{"q":1685,"a":1757},"openpyxl and xlsxwriter write numbers with 16 significant digits. An integer with 17 or more digits is written in scientific notation with the tail rounded away, so the file itself no longer contains the original value.",{"q":1691,"a":1759},"Excel shows its small green triangle on text that looks like a number. For identifiers that is correct and harmless; the warning can be turned off per range. Treating IDs as numbers is the thing to avoid.",{"q":1697,"a":1761},"Pass dtype={\"id_column\": str} to read_excel. Without it, pandas converts text that looks numeric back into integers, which drops leading zeros and can lose precision on very long values.","fix long id numbers losing digits in excel",null,{"breadcrumb":1765},[1766,1769,1772],{"name":1767,"item":1768},"Home","\u002F",{"name":1770,"item":1771},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":45,"item":44},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel",{"title":1775,"description":1776},"Keep Long Numbers Exact in Excel from Python (15-Digit Limit)","Why 16- to 19-digit IDs become 1.23457E+18 or end in zeros in Excel, how openpyxl and xlsxwriter serialise large integers, and how to write and read IDs as text with pandas.","fix-long-id-numbers-losing-digits-in-excel","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-long-id-numbers-losing-digits-in-excel\u002Findex",[1780,1781,1782],"troubleshooting","writing-excel","number-formats","how-to","dqb8sxNn-m233ILZJlj8enhpWaDwza6SBEAiqdHjL6o",[1786,1790],{"title":1787,"path":1788,"stem":1789,"children":-1},"Fix \"Filesize would require ZIP64 extensions\" (LargeZipFile) in XlsxWriter","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-largezipfile-error-in-xlsxwriter","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-largezipfile-error-in-xlsxwriter\u002Findex",{"title":1791,"path":1792,"stem":1793,"children":-1},"Fix \"MergedCell object attribute value is read-only\" in openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-mergedcell-value-is-read-only-in-openpyxl","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-mergedcell-value-is-read-only-in-openpyxl\u002Findex",[1795,1797],{"path":1796,"title":1770},"\u002Fgetting-started-with-python-excel-automation",{"path":1798,"title":45},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors",[],[1801,1805,1808,1811,1814,1817],{"path":1802,"title":1803,"kind":1804},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fhandle-permission-denied-when-writing-excel-in-python","Handle Permission Denied When Writing Excel in Python","fix",{"path":1806,"title":1807,"kind":1783},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook","What openpyxl Loses When You Save an Existing Workbook",{"path":1809,"title":1810,"kind":1804},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates","Fix Excel Serial Numbers Showing Instead of Dates",{"path":1812,"title":1813,"kind":1783},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fhandle-daylight-saving-time-in-excel-timestamps","Handle Daylight Saving Time in Excel Timestamps",{"path":1815,"title":1816,"kind":1783},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel","Handle Microsoft Graph Throttling in Excel Automation",{"path":1818,"title":1819,"kind":1820},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation","Error Handling and Logging in Excel Automation","guide",1790972065917]