[{"data":1,"prerenderedAt":2502},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output":2466,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output":2475,"children:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output":2480,"related:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output":2481},{"id":4,"title":5,"body":6,"dateModified":2432,"datePublished":2432,"description":2433,"extension":2434,"faq":2435,"keyword":2444,"kind":2445,"meta":2446,"navigation":299,"path":2454,"seo":2455,"slug":2458,"stem":2459,"topics":2460,"type":2464,"__hash__":2465},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output\u002Findex.md","Fix \"Number Stored as Text\" Warnings in Excel Output",{"type":7,"value":8,"toc":2417},"minimark",[9,29,232,237,268,271,438,442,457,533,541,545,556,951,965,1059,1063,1068,1117,1124,1128,1135,1245,1259,1361,1365,1368,1550,1561,1565,1568,1875,1879,1882,2168,2189,2193,2310,2314,2326,2330,2342,2346,2353,2359,2365,2371,2375,2413],[10,11,12,13,17,18,22,23,28],"p",{},"The report looks right until someone clicks a cell. A small green triangle sits in the corner of every amount,\nthe numbers are left-aligned, ",[14,15,16],"code",{},"SUM"," returns zero, and a pivot table counts the column instead of adding it up.\nExcel is telling you that those cells contain ",[19,20,21],"em",{},"text that looks like a number",". The script did exactly what it\nwas asked: the values were strings in Python, so they were written as strings. This guide, part of\n",[24,25,27],"a",{"href":26},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",",\nfixes the cause, and explains when the warning is actually correct.",[30,31,39,40,39,44,39,48,39,55,39,63,39,67,39,74,39,78,39,82,39,86,39,90,39,93,39,97,39,100,39,104,39,106,39,110,39,112,39,115,39,117,39,120,39,122,39,125,39,128,39,131,39,135,39,139,39,143,39,147,39,149,39,152,39,154,39,157,39,164,39,169,39,172,39,176,39,178,39,181,39,183,39,186,39,188,39,191,39,193,39,196,39,199,39,203,39,205,39,209,39,213,39,215,39,217,39,219,39,222,39,226],"svg",{"viewBox":32,"role":33,"ariaLabelledBy":34,"xmlns":37,"style":38},"0 0 576 224","img",[35,36],"nst-sheet-t","nst-sheet-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:576px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[41,42,43],"title",{"id":35},"A sheet written with numbers stored as text",[45,46,47],"desc",{"id":36},"Amount, quantity and percentage columns were written as strings, so Excel shows warning triangles, left-aligns them and SUM returns zero. The order ID column is also text, which is correct for an identifier.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","576","224","#ffffff",[49,56],{"x":57,"y":57,"width":58,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"20","36","30","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[49,64],{"x":65,"y":57,"width":66,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"56","90",[68,69,73],"text",{"x":70,"y":71,"style":72},"101","40","font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[49,75],{"x":76,"y":57,"width":77,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"146","100",[68,79,81],{"x":80,"y":71,"style":72},"196","B",[49,83],{"x":84,"y":57,"width":85,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"246","70",[68,87,89],{"x":88,"y":71,"style":72},"281","C",[49,91],{"x":92,"y":57,"width":85,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"316",[68,94,96],{"x":95,"y":71,"style":72},"351","D",[49,98],{"x":57,"y":99,"width":58,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"50",[68,101,103],{"x":102,"y":85,"style":72},"38","1",[49,105],{"x":65,"y":99,"width":66,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},[68,107,109],{"x":70,"y":85,"style":108},"font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","OrderID",[49,111],{"x":76,"y":99,"width":77,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},[68,113,114],{"x":80,"y":85,"style":108},"Amount",[49,116],{"x":84,"y":99,"width":85,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},[68,118,119],{"x":88,"y":85,"style":108},"Qty",[49,121],{"x":92,"y":99,"width":85,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},[68,123,124],{"x":95,"y":85,"style":108},"Pct",[49,126],{"x":57,"y":127,"width":58,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"80",[68,129,130],{"x":102,"y":77,"style":72},"2",[49,132],{"x":65,"y":127,"width":66,"height":59,"rx":51,"fill":133,"stroke":134,"style":62},"#d9f4f1","var(--teal,#0f9488)",[68,136,138],{"x":70,"y":77,"style":137},"font-size:11.5px;font-weight:600;fill:var(--teal-ink,#0b6157);text-anchor:middle","00017",[49,140],{"x":76,"y":127,"width":77,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},"#fee8f2","var(--accent,#f43f8f)",[68,144,146],{"x":80,"y":77,"style":145},"font-size:11.5px;font-weight:600;fill:var(--accent-ink,#be185d);text-anchor:middle","1,250.50 ▲",[49,148],{"x":84,"y":127,"width":85,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},[68,150,151],{"x":88,"y":77,"style":145},"3 ▲",[49,153],{"x":92,"y":127,"width":85,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},[68,155,156],{"x":95,"y":77,"style":145},"12% ▲",[49,158],{"x":159,"y":160,"width":161,"height":162,"rx":163,"fill":141,"stroke":142,"style":62},"406","82","150","26","8",[68,165,168],{"x":166,"y":167,"style":145},"481","99","▲ = warning triangle",[49,170],{"x":57,"y":171,"width":58,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"110",[68,173,175],{"x":102,"y":174,"style":72},"130","3",[49,177],{"x":65,"y":171,"width":66,"height":59,"rx":51,"fill":133,"stroke":134,"style":62},[68,179,180],{"x":70,"y":174,"style":137},"00018",[49,182],{"x":76,"y":171,"width":77,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},[68,184,185],{"x":80,"y":174,"style":145},"980 ▲",[49,187],{"x":84,"y":171,"width":85,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},[68,189,190],{"x":88,"y":174,"style":145},"12 ▲",[49,192],{"x":92,"y":171,"width":85,"height":59,"rx":51,"fill":141,"stroke":142,"style":62},[68,194,195],{"x":95,"y":174,"style":145},"8.5% ▲",[49,197],{"x":57,"y":198,"width":58,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},"140",[68,200,202],{"x":102,"y":201,"style":72},"160","4",[49,204],{"x":65,"y":198,"width":66,"height":59,"rx":51,"fill":60,"stroke":61,"style":62},[49,206],{"x":76,"y":198,"width":77,"height":59,"rx":51,"fill":207,"stroke":208,"style":62},"#fdefd8","var(--gold,#b4740a)",[68,210,212],{"x":80,"y":201,"style":211},"font-size:11.5px;font-weight:600;fill:var(--gold-ink,#7a4e06);text-anchor:middle","SUM = 0",[49,214],{"x":84,"y":198,"width":85,"height":59,"rx":51,"fill":207,"stroke":208,"style":62},[68,216,212],{"x":88,"y":201,"style":211},[49,218],{"x":92,"y":198,"width":85,"height":59,"rx":51,"fill":54,"stroke":61,"style":62},[49,220],{"x":159,"y":221,"width":161,"height":162,"rx":163,"fill":207,"stroke":208,"style":62},"142",[68,223,225],{"x":166,"y":224,"style":211},"159","text is not summed",[68,227,231],{"x":228,"y":229,"style":230},"288","202","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","the ID column should stay text; the others should be numbers",[233,234,236],"h2",{"id":235},"prerequisites","Prerequisites",[238,239,244],"pre",{"className":240,"code":241,"language":242,"meta":243,"style":243},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[14,245,246],{"__ignoreMap":243},[247,248,251,255,259,262,265],"span",{"class":249,"line":250},"line",1,[247,252,254],{"class":253},"sMTad","pip",[247,256,258],{"class":257},"srMev"," install",[247,260,261],{"class":257}," pandas",[247,263,264],{"class":257}," openpyxl",[247,266,267],{"class":257}," xlsxwriter\n",[10,269,270],{},"This reproduces the problem with a DataFrame of strings, the shape data takes after reading a CSV without dtypes, scraping a web page or parsing a text report:",[238,272,276],{"className":273,"code":274,"language":275,"meta":243,"style":243},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\norders = pd.DataFrame({\n    \"OrderID\": [\"00017\", \"00018\", \"00019\"],\n    \"Amount\": [\"1,250.50\", \"980\", \"2,015.25\"],\n    \"Qty\": [\"3\", \"12\", \"7\"],\n    \"Pct\": [\"12%\", \"8.5%\", \"10%\"],\n})\norders.to_excel(\"orders_text.xlsx\", index=False)\n","python",[14,277,278,294,301,313,339,362,385,408,414],{"__ignoreMap":243},[247,279,280,284,288,291],{"class":249,"line":250},[247,281,283],{"class":282},"s-kum","import",[247,285,287],{"class":286},"skGVy"," pandas ",[247,289,290],{"class":282},"as",[247,292,293],{"class":286}," pd\n",[247,295,297],{"class":249,"line":296},2,[247,298,300],{"emptyLinePlaceholder":299},true,"\n",[247,302,304,307,310],{"class":249,"line":303},3,[247,305,306],{"class":286},"orders ",[247,308,309],{"class":282},"=",[247,311,312],{"class":286}," pd.DataFrame({\n",[247,314,316,319,322,325,328,331,333,336],{"class":249,"line":315},4,[247,317,318],{"class":257},"    \"OrderID\"",[247,320,321],{"class":286},": [",[247,323,324],{"class":257},"\"00017\"",[247,326,327],{"class":286},", ",[247,329,330],{"class":257},"\"00018\"",[247,332,327],{"class":286},[247,334,335],{"class":257},"\"00019\"",[247,337,338],{"class":286},"],\n",[247,340,342,345,347,350,352,355,357,360],{"class":249,"line":341},5,[247,343,344],{"class":257},"    \"Amount\"",[247,346,321],{"class":286},[247,348,349],{"class":257},"\"1,250.50\"",[247,351,327],{"class":286},[247,353,354],{"class":257},"\"980\"",[247,356,327],{"class":286},[247,358,359],{"class":257},"\"2,015.25\"",[247,361,338],{"class":286},[247,363,365,368,370,373,375,378,380,383],{"class":249,"line":364},6,[247,366,367],{"class":257},"    \"Qty\"",[247,369,321],{"class":286},[247,371,372],{"class":257},"\"3\"",[247,374,327],{"class":286},[247,376,377],{"class":257},"\"12\"",[247,379,327],{"class":286},[247,381,382],{"class":257},"\"7\"",[247,384,338],{"class":286},[247,386,388,391,393,396,398,401,403,406],{"class":249,"line":387},7,[247,389,390],{"class":257},"    \"Pct\"",[247,392,321],{"class":286},[247,394,395],{"class":257},"\"12%\"",[247,397,327],{"class":286},[247,399,400],{"class":257},"\"8.5%\"",[247,402,327],{"class":286},[247,404,405],{"class":257},"\"10%\"",[247,407,338],{"class":286},[247,409,411],{"class":249,"line":410},8,[247,412,413],{"class":286},"})\n",[247,415,417,420,423,425,429,431,435],{"class":249,"line":416},9,[247,418,419],{"class":286},"orders.to_excel(",[247,421,422],{"class":257},"\"orders_text.xlsx\"",[247,424,327],{"class":286},[247,426,428],{"class":427},"sa561","index",[247,430,309],{"class":282},[247,432,434],{"class":433},"sP0c6","False",[247,436,437],{"class":286},")\n",[233,439,441],{"id":440},"step-1-confirm-what-was-written","Step 1 — Confirm what was written",[10,443,444,445,448,449,452,453,456],{},"Open the file with openpyxl and look at each cell's ",[14,446,447],{},"data_type",": ",[14,450,451],{},"\"n\""," is a number and ",[14,454,455],{},"\"s\""," is a string. Checking in code is faster and more reliable than clicking cells.",[238,458,460],{"className":273,"code":459,"language":275,"meta":243,"style":243},"from openpyxl import load_workbook\n\nsheet = load_workbook(\"orders_text.xlsx\").active\nfor cell in sheet[2]:\n    print(cell.coordinate, repr(cell.value), cell.data_type)\n# A2 '00017' s   B2 '1,250.50' s   C2 '3' s   D2 '12%' s\n",[14,461,462,475,479,494,513,527],{"__ignoreMap":243},[247,463,464,467,470,472],{"class":249,"line":250},[247,465,466],{"class":282},"from",[247,468,469],{"class":286}," openpyxl ",[247,471,283],{"class":282},[247,473,474],{"class":286}," load_workbook\n",[247,476,477],{"class":249,"line":296},[247,478,300],{"emptyLinePlaceholder":299},[247,480,481,484,486,489,491],{"class":249,"line":303},[247,482,483],{"class":286},"sheet ",[247,485,309],{"class":282},[247,487,488],{"class":286}," load_workbook(",[247,490,422],{"class":257},[247,492,493],{"class":286},").active\n",[247,495,496,499,502,505,508,510],{"class":249,"line":315},[247,497,498],{"class":282},"for",[247,500,501],{"class":286}," cell ",[247,503,504],{"class":282},"in",[247,506,507],{"class":286}," sheet[",[247,509,130],{"class":433},[247,511,512],{"class":286},"]:\n",[247,514,515,518,521,524],{"class":249,"line":341},[247,516,517],{"class":433},"    print",[247,519,520],{"class":286},"(cell.coordinate, ",[247,522,523],{"class":433},"repr",[247,525,526],{"class":286},"(cell.value), cell.data_type)\n",[247,528,529],{"class":249,"line":364},[247,530,532],{"class":531},"s-wDw","# A2 '00017' s   B2 '1,250.50' s   C2 '3' s   D2 '12%' s\n",[10,534,535,536,540],{},"Every cell is a string, which is what triggers the warning. The same check is the quickest way to prove the fix worked, and a good line to add to an automated test of report\noutput as described in ",[24,537,539],{"href":538},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-excel-output-with-pytest\u002F","Test Excel Output with pytest",".",[233,542,544],{"id":543},"step-2-convert-real-quantities-before-writing","Step 2 — Convert real quantities before writing",[10,546,547,548,551,552,555],{},"Numbers belong in Excel as numbers. Convert each quantity column with ",[14,549,550],{},"pd.to_numeric",", stripping thousands separators and percent signs first. Percentages become fractions so\nthat a ",[14,553,554],{},"0%"," format displays them correctly.",[238,557,559],{"className":273,"code":558,"language":275,"meta":243,"style":243},"def to_number(series: pd.Series, percent: bool = False) -> pd.Series:\n    cleaned = series.astype(\"string\").str.replace(\",\", \"\", regex=False).str.strip()\n    if percent:\n        cleaned = cleaned.str.rstrip(\"%\")\n    numbers = pd.to_numeric(cleaned, errors=\"coerce\")\n    return numbers \u002F 100 if percent else numbers\n\nclean = orders.assign(\n    Amount=to_number(orders[\"Amount\"]),\n    Qty=to_number(orders[\"Qty\"]).astype(\"Int64\"),\n    Pct=to_number(orders[\"Pct\"], percent=True),\n)\nprint(clean.dtypes.to_dict())\n\nwith pd.ExcelWriter(\"orders_numbers.xlsx\", engine=\"openpyxl\") as writer:\n    clean.to_excel(writer, index=False, sheet_name=\"Orders\")\n    ws = writer.sheets[\"Orders\"]\n    for row in range(2, len(clean) + 2):\n        ws[f\"B{row}\"].number_format = \"#,##0.00\"\n        ws[f\"D{row}\"].number_format = \"0.0%\"\n",[14,560,561,585,621,629,644,664,690,694,704,720,742,768,773,782,787,817,841,857,893,926],{"__ignoreMap":243},[247,562,563,566,570,573,576,579,582],{"class":249,"line":250},[247,564,565],{"class":282},"def",[247,567,569],{"class":568},"s_Opv"," to_number",[247,571,572],{"class":286},"(series: pd.Series, percent: ",[247,574,575],{"class":433},"bool",[247,577,578],{"class":282}," =",[247,580,581],{"class":433}," False",[247,583,584],{"class":286},") -> pd.Series:\n",[247,586,587,590,592,595,598,601,604,606,609,611,614,616,618],{"class":249,"line":296},[247,588,589],{"class":286},"    cleaned ",[247,591,309],{"class":282},[247,593,594],{"class":286}," series.astype(",[247,596,597],{"class":257},"\"string\"",[247,599,600],{"class":286},").str.replace(",[247,602,603],{"class":257},"\",\"",[247,605,327],{"class":286},[247,607,608],{"class":257},"\"\"",[247,610,327],{"class":286},[247,612,613],{"class":427},"regex",[247,615,309],{"class":282},[247,617,434],{"class":433},[247,619,620],{"class":286},").str.strip()\n",[247,622,623,626],{"class":249,"line":303},[247,624,625],{"class":282},"    if",[247,627,628],{"class":286}," percent:\n",[247,630,631,634,636,639,642],{"class":249,"line":315},[247,632,633],{"class":286},"        cleaned ",[247,635,309],{"class":282},[247,637,638],{"class":286}," cleaned.str.rstrip(",[247,640,641],{"class":257},"\"%\"",[247,643,437],{"class":286},[247,645,646,649,651,654,657,659,662],{"class":249,"line":341},[247,647,648],{"class":286},"    numbers ",[247,650,309],{"class":282},[247,652,653],{"class":286}," pd.to_numeric(cleaned, ",[247,655,656],{"class":427},"errors",[247,658,309],{"class":282},[247,660,661],{"class":257},"\"coerce\"",[247,663,437],{"class":286},[247,665,666,669,672,675,678,681,684,687],{"class":249,"line":364},[247,667,668],{"class":282},"    return",[247,670,671],{"class":286}," numbers ",[247,673,674],{"class":282},"\u002F",[247,676,677],{"class":433}," 100",[247,679,680],{"class":282}," if",[247,682,683],{"class":286}," percent ",[247,685,686],{"class":282},"else",[247,688,689],{"class":286}," numbers\n",[247,691,692],{"class":249,"line":387},[247,693,300],{"emptyLinePlaceholder":299},[247,695,696,699,701],{"class":249,"line":410},[247,697,698],{"class":286},"clean ",[247,700,309],{"class":282},[247,702,703],{"class":286}," orders.assign(\n",[247,705,706,709,711,714,717],{"class":249,"line":416},[247,707,708],{"class":427},"    Amount",[247,710,309],{"class":282},[247,712,713],{"class":286},"to_number(orders[",[247,715,716],{"class":257},"\"Amount\"",[247,718,719],{"class":286},"]),\n",[247,721,723,726,728,730,733,736,739],{"class":249,"line":722},10,[247,724,725],{"class":427},"    Qty",[247,727,309],{"class":282},[247,729,713],{"class":286},[247,731,732],{"class":257},"\"Qty\"",[247,734,735],{"class":286},"]).astype(",[247,737,738],{"class":257},"\"Int64\"",[247,740,741],{"class":286},"),\n",[247,743,745,748,750,752,755,758,761,763,766],{"class":249,"line":744},11,[247,746,747],{"class":427},"    Pct",[247,749,309],{"class":282},[247,751,713],{"class":286},[247,753,754],{"class":257},"\"Pct\"",[247,756,757],{"class":286},"], ",[247,759,760],{"class":427},"percent",[247,762,309],{"class":282},[247,764,765],{"class":433},"True",[247,767,741],{"class":286},[247,769,771],{"class":249,"line":770},12,[247,772,437],{"class":286},[247,774,776,779],{"class":249,"line":775},13,[247,777,778],{"class":433},"print",[247,780,781],{"class":286},"(clean.dtypes.to_dict())\n",[247,783,785],{"class":249,"line":784},14,[247,786,300],{"emptyLinePlaceholder":299},[247,788,790,793,796,799,801,804,806,809,812,814],{"class":249,"line":789},15,[247,791,792],{"class":282},"with",[247,794,795],{"class":286}," pd.ExcelWriter(",[247,797,798],{"class":257},"\"orders_numbers.xlsx\"",[247,800,327],{"class":286},[247,802,803],{"class":427},"engine",[247,805,309],{"class":282},[247,807,808],{"class":257},"\"openpyxl\"",[247,810,811],{"class":286},") ",[247,813,290],{"class":282},[247,815,816],{"class":286}," writer:\n",[247,818,820,823,825,827,829,831,834,836,839],{"class":249,"line":819},16,[247,821,822],{"class":286},"    clean.to_excel(writer, ",[247,824,428],{"class":427},[247,826,309],{"class":282},[247,828,434],{"class":433},[247,830,327],{"class":286},[247,832,833],{"class":427},"sheet_name",[247,835,309],{"class":282},[247,837,838],{"class":257},"\"Orders\"",[247,840,437],{"class":286},[247,842,844,847,849,852,854],{"class":249,"line":843},17,[247,845,846],{"class":286},"    ws ",[247,848,309],{"class":282},[247,850,851],{"class":286}," writer.sheets[",[247,853,838],{"class":257},[247,855,856],{"class":286},"]\n",[247,858,860,863,866,868,871,874,876,878,881,884,887,890],{"class":249,"line":859},18,[247,861,862],{"class":282},"    for",[247,864,865],{"class":286}," row ",[247,867,504],{"class":282},[247,869,870],{"class":433}," range",[247,872,873],{"class":286},"(",[247,875,130],{"class":433},[247,877,327],{"class":286},[247,879,880],{"class":433},"len",[247,882,883],{"class":286},"(clean) ",[247,885,886],{"class":282},"+",[247,888,889],{"class":433}," 2",[247,891,892],{"class":286},"):\n",[247,894,896,899,902,905,909,912,915,918,921,923],{"class":249,"line":895},19,[247,897,898],{"class":286},"        ws[",[247,900,901],{"class":282},"f",[247,903,904],{"class":257},"\"B",[247,906,908],{"class":907},"sSjpA","{",[247,910,911],{"class":286},"row",[247,913,914],{"class":907},"}",[247,916,917],{"class":257},"\"",[247,919,920],{"class":286},"].number_format ",[247,922,309],{"class":282},[247,924,925],{"class":257}," \"#,##0.00\"\n",[247,927,929,931,933,936,938,940,942,944,946,948],{"class":249,"line":928},20,[247,930,898],{"class":286},[247,932,901],{"class":282},[247,934,935],{"class":257},"\"D",[247,937,908],{"class":907},[247,939,911],{"class":286},[247,941,914],{"class":907},[247,943,917],{"class":257},[247,945,920],{"class":286},[247,947,309],{"class":282},[247,949,950],{"class":257}," \"0.0%\"\n",[10,952,953,956,957,960,961,540],{},[14,954,955],{},"errors=\"coerce\""," turns anything that cannot be parsed into a missing value instead of raising — follow it with a count of new missing values if silent data loss would matter. The\nnumber formats restore the separators and percent signs as ",[19,958,959],{},"display",", which is where they belong. The same conversion on the reading side is covered in\n",[24,962,964],{"href":963},"\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",[30,966,39,972,39,975,39,978,39,982,39,987,39,991,39,996,39,1002,39,1007,39,1011,39,1016,39,1019,39,1023,39,1026,39,1029,39,1033,39,1036,39,1040,39,1043,39,1046,39,1051,39,1054],{"viewBox":967,"role":33,"ariaLabelledBy":968,"xmlns":37,"style":971},"0 0 760 136",[969,970],"nst-fix-t","nst-fix-d","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[41,973,974],{"id":969},"Turning numeric text into real numbers before writing",[45,976,977],{"id":970},"Take the string column, remove thousands separators and percent signs, convert with to_numeric, divide percentages by one hundred, and apply a number format when writing so the display looks the same.",[49,979],{"x":51,"y":51,"width":980,"height":981,"fill":54},"760","136",[49,983],{"x":57,"y":57,"width":161,"height":984,"rx":985,"fill":141,"stroke":142,"style":986},"66","12","stroke-width:2px",[68,988,349],{"x":989,"y":99,"style":990},"95","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[68,992,995],{"x":989,"y":993,"style":994},"72","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","string in pandas",[249,997],{"x1":998,"y1":999,"x2":1000,"y2":999,"stroke":1001,"style":986},"175","53","204","var(--brand,#5b5cf0)",[1003,1004],"polygon",{"points":1005,"fill":1006},"207,53 198,48 198,58","#5b5cf0",[49,1008],{"x":1009,"y":57,"width":161,"height":984,"rx":985,"fill":1010,"stroke":1001,"style":986},"210","#f0f4ff",[68,1012,1015],{"x":1013,"y":99,"style":1014},"285","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","strip , and %",[68,1017,1018],{"x":1013,"y":993,"style":994},"str.replace",[249,1020],{"x1":1021,"y1":999,"x2":1022,"y2":999,"stroke":1001,"style":986},"365","394",[1003,1024],{"points":1025,"fill":1006},"397,53 388,48 388,58",[49,1027],{"x":1028,"y":57,"width":161,"height":984,"rx":985,"fill":1010,"stroke":1001,"style":986},"400",[68,1030,1032],{"x":1031,"y":99,"style":1014},"475","to_numeric",[68,1034,1035],{"x":1031,"y":993,"style":994},"float or Int64",[249,1037],{"x1":1038,"y1":999,"x2":1039,"y2":999,"stroke":1001,"style":986},"555","584",[1003,1041],{"points":1042,"fill":1006},"587,53 578,48 578,58",[49,1044],{"x":1045,"y":57,"width":161,"height":984,"rx":985,"fill":133,"stroke":134,"style":986},"590",[68,1047,1050],{"x":1048,"y":99,"style":1049},"665","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","number_format",[68,1052,1053],{"x":1048,"y":993,"style":994},"#,##0.00 display",[68,1055,1058],{"x":1056,"y":1057,"style":230},"380","120","separators are formatting; store the value, format the display",[233,1060,1062],{"id":1061},"step-3-keep-identifiers-as-text-on-purpose","Step 3 — Keep identifiers as text on purpose",[10,1064,1065,1067],{},[14,1066,109],{}," looks numeric but is not a quantity. Converting it would drop the leading zeros and invite someone to sum order numbers. The warning triangle on an ID column is Excel\nbeing cautious about a value that is correctly text. Leave the column as strings — which pandas already does — and decide separately whether to hide the warning.",[238,1069,1071],{"className":273,"code":1070,"language":275,"meta":243,"style":243},"check = load_workbook(\"orders_numbers.xlsx\")[\"Orders\"]\nprint([(cell.coordinate, cell.value, cell.data_type) for cell in check[2]])\n# OrderID stays 's'; Amount, Qty and Pct are now 'n'\n",[14,1072,1073,1091,1112],{"__ignoreMap":243},[247,1074,1075,1078,1080,1082,1084,1087,1089],{"class":249,"line":250},[247,1076,1077],{"class":286},"check ",[247,1079,309],{"class":282},[247,1081,488],{"class":286},[247,1083,798],{"class":257},[247,1085,1086],{"class":286},")[",[247,1088,838],{"class":257},[247,1090,856],{"class":286},[247,1092,1093,1095,1098,1100,1102,1104,1107,1109],{"class":249,"line":296},[247,1094,778],{"class":433},[247,1096,1097],{"class":286},"([(cell.coordinate, cell.value, cell.data_type) ",[247,1099,498],{"class":282},[247,1101,501],{"class":286},[247,1103,504],{"class":282},[247,1105,1106],{"class":286}," check[",[247,1108,130],{"class":433},[247,1110,1111],{"class":286},"]])\n",[247,1113,1114],{"class":249,"line":303},[247,1115,1116],{"class":531},"# OrderID stays 's'; Amount, Qty and Pct are now 'n'\n",[10,1118,1119,1120,540],{},"A useful rule: if you would never add, average or compare the values as magnitudes — IDs, postcodes, phone numbers, account codes — they are text. Keeping them as text end to end\navoids a class of bugs described in\n",[24,1121,1123],{"href":1122},"\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",[233,1125,1127],{"id":1126},"step-4-beware-of-strings_to_numbers","Step 4 — Beware of strings_to_numbers",[10,1129,1130,1131,1134],{},"xlsxwriter has a workbook option, ",[14,1132,1133],{},"strings_to_numbers",", that converts any string that parses as a number while writing. It looks like a one-line fix, and on mixed data it is a trap:\nit converts identifiers along with quantities, while missing the amounts that contain a thousands separator.",[238,1136,1138],{"className":273,"code":1137,"language":275,"meta":243,"style":243},"with pd.ExcelWriter(\"orders_auto.xlsx\", engine=\"xlsxwriter\",\n                    engine_kwargs={\"options\": {\"strings_to_numbers\": True}}) as writer:\n    orders.to_excel(writer, index=False)\n\nauto = load_workbook(\"orders_auto.xlsx\").active\nprint([(cell.value, cell.data_type) for cell in auto[2]])\n# [(17, 'n'), ('1,250.50', 's'), (3, 'n'), ('12%', 's')]\n",[14,1139,1140,1161,1190,1203,1207,1220,1240],{"__ignoreMap":243},[247,1141,1142,1144,1146,1149,1151,1153,1155,1158],{"class":249,"line":250},[247,1143,792],{"class":282},[247,1145,795],{"class":286},[247,1147,1148],{"class":257},"\"orders_auto.xlsx\"",[247,1150,327],{"class":286},[247,1152,803],{"class":427},[247,1154,309],{"class":282},[247,1156,1157],{"class":257},"\"xlsxwriter\"",[247,1159,1160],{"class":286},",\n",[247,1162,1163,1166,1168,1170,1173,1176,1179,1181,1183,1186,1188],{"class":249,"line":296},[247,1164,1165],{"class":427},"                    engine_kwargs",[247,1167,309],{"class":282},[247,1169,908],{"class":286},[247,1171,1172],{"class":257},"\"options\"",[247,1174,1175],{"class":286},": {",[247,1177,1178],{"class":257},"\"strings_to_numbers\"",[247,1180,448],{"class":286},[247,1182,765],{"class":433},[247,1184,1185],{"class":286},"}}) ",[247,1187,290],{"class":282},[247,1189,816],{"class":286},[247,1191,1192,1195,1197,1199,1201],{"class":249,"line":303},[247,1193,1194],{"class":286},"    orders.to_excel(writer, ",[247,1196,428],{"class":427},[247,1198,309],{"class":282},[247,1200,434],{"class":433},[247,1202,437],{"class":286},[247,1204,1205],{"class":249,"line":315},[247,1206,300],{"emptyLinePlaceholder":299},[247,1208,1209,1212,1214,1216,1218],{"class":249,"line":341},[247,1210,1211],{"class":286},"auto ",[247,1213,309],{"class":282},[247,1215,488],{"class":286},[247,1217,1148],{"class":257},[247,1219,493],{"class":286},[247,1221,1222,1224,1227,1229,1231,1233,1236,1238],{"class":249,"line":364},[247,1223,778],{"class":433},[247,1225,1226],{"class":286},"([(cell.value, cell.data_type) ",[247,1228,498],{"class":282},[247,1230,501],{"class":286},[247,1232,504],{"class":282},[247,1234,1235],{"class":286}," auto[",[247,1237,130],{"class":433},[247,1239,1111],{"class":286},[247,1241,1242],{"class":249,"line":387},[247,1243,1244],{"class":531},"# [(17, 'n'), ('1,250.50', 's'), (3, 'n'), ('12%', 's')]\n",[10,1246,1247,1248,1251,1252,1255,1256,1258],{},"The order ID lost its zeros, while ",[14,1249,1250],{},"1,250.50"," and ",[14,1253,1254],{},"12%"," are still text. Converting explicitly per column, as in Step 2, is more code and far more predictable. Reserve\n",[14,1257,1133],{}," for data where every numeric-looking string really is a plain number.",[30,1260,39,1266,39,1269,39,1272,39,1276,39,1280,39,1286,39,1292,39,1296,39,1303,39,1308,39,1310,39,1314,39,1317,39,1320,39,1324,39,1327,39,1331,39,1334,39,1337,39,1341,39,1344,39,1348,39,1351,39,1354,39,1356],{"viewBox":1261,"role":33,"ariaLabelledBy":1262,"xmlns":37,"style":1265},"0 0 720 256",[1263,1264],"nst-auto-t","nst-auto-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[41,1267,1268],{"id":1263},"What strings_to_numbers does to each column",[45,1270,1271],{"id":1264},"With strings_to_numbers, the OrderID column is converted to numbers and loses its leading zeros, Qty is converted correctly, while Amount with thousands separators and Pct with percent signs stay as text.",[49,1273],{"x":51,"y":51,"width":1274,"height":1275,"fill":54},"720","256",[68,1277,1279],{"x":57,"y":59,"style":1278},"font-size:12px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","result per column with strings_to_numbers=True",[49,1281],{"x":57,"y":1282,"width":1283,"height":1284,"rx":1285,"fill":54,"stroke":61,"style":62},"46","170","28","6",[68,1287,1291],{"x":1288,"y":1289,"style":1290},"105","65","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:middle","OrderID \"00017\"",[49,1293],{"x":1000,"y":1294,"width":1295,"height":162,"rx":1285,"fill":60,"stroke":61,"style":62},"47","330",[49,1297],{"x":1298,"y":1299,"width":1300,"height":1301,"rx":1302,"fill":141,"stroke":142,"style":62},"205","48","295","24","5",[68,1304,1307],{"x":1305,"y":1289,"style":1306},"546","font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:start","17 — zeros lost",[49,1309],{"x":57,"y":66,"width":1283,"height":1284,"rx":1285,"fill":54,"stroke":61,"style":62},[68,1311,1313],{"x":1288,"y":1312,"style":1290},"109","Qty \"3\"",[49,1315],{"x":1000,"y":1316,"width":1295,"height":162,"rx":1285,"fill":60,"stroke":61,"style":62},"91",[49,1318],{"x":1298,"y":1319,"width":1300,"height":1301,"rx":1302,"fill":133,"stroke":134,"style":62},"92",[68,1321,1323],{"x":1305,"y":1312,"style":1322},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","3 — correct",[49,1325],{"x":57,"y":1326,"width":1283,"height":1284,"rx":1285,"fill":54,"stroke":61,"style":62},"134",[68,1328,1330],{"x":1288,"y":1329,"style":1290},"153","Amount \"1,250.50\"",[49,1332],{"x":1000,"y":1333,"width":1295,"height":162,"rx":1285,"fill":60,"stroke":61,"style":62},"135",[49,1335],{"x":1298,"y":981,"width":1336,"height":1301,"rx":1302,"fill":207,"stroke":208,"style":62},"97",[68,1338,1340],{"x":1305,"y":1329,"style":1339},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","still text",[49,1342],{"x":57,"y":1343,"width":1283,"height":1284,"rx":1285,"fill":54,"stroke":61,"style":62},"178",[68,1345,1347],{"x":1288,"y":1346,"style":1290},"197","Pct \"12%\"",[49,1349],{"x":1000,"y":1350,"width":1295,"height":162,"rx":1285,"fill":60,"stroke":61,"style":62},"179",[49,1352],{"x":1298,"y":1353,"width":1336,"height":1301,"rx":1302,"fill":207,"stroke":208,"style":62},"180",[68,1355,1340],{"x":1305,"y":1346,"style":1339},[68,1357,1360],{"x":1358,"y":1359,"style":230},"360","238","it converts the wrong column and misses the ones you needed",[233,1362,1364],{"id":1363},"step-5-silence-the-warning-where-text-is-correct","Step 5 — Silence the warning where text is correct",[10,1366,1367],{},"For an ID column that must stay text, xlsxwriter can tell Excel to ignore the \"number stored as text\" check for a range, so readers do not see a column of triangles. openpyxl\nhas no equivalent option when writing.",[238,1369,1371],{"className":273,"code":1370,"language":275,"meta":243,"style":243},"with pd.ExcelWriter(\"orders_final.xlsx\", engine=\"xlsxwriter\") as writer:\n    clean.to_excel(writer, index=False, sheet_name=\"Orders\")\n    book, sheet = writer.book, writer.sheets[\"Orders\"]\n    sheet.set_column(\"B:B\", 12, book.add_format({\"num_format\": \"#,##0.00\"}))\n    sheet.set_column(\"D:D\", 8, book.add_format({\"num_format\": \"0.0%\"}))\n    sheet.ignore_errors({\"number_stored_as_text\": f\"A2:A{len(clean) + 1}\"})\n\nfinal = load_workbook(\"orders_final.xlsx\")[\"Orders\"]\nprint([(cell.value, cell.data_type) for cell in final[2]])\n",[14,1372,1373,1396,1416,1430,1456,1478,1510,1514,1531],{"__ignoreMap":243},[247,1374,1375,1377,1379,1382,1384,1386,1388,1390,1392,1394],{"class":249,"line":250},[247,1376,792],{"class":282},[247,1378,795],{"class":286},[247,1380,1381],{"class":257},"\"orders_final.xlsx\"",[247,1383,327],{"class":286},[247,1385,803],{"class":427},[247,1387,309],{"class":282},[247,1389,1157],{"class":257},[247,1391,811],{"class":286},[247,1393,290],{"class":282},[247,1395,816],{"class":286},[247,1397,1398,1400,1402,1404,1406,1408,1410,1412,1414],{"class":249,"line":296},[247,1399,822],{"class":286},[247,1401,428],{"class":427},[247,1403,309],{"class":282},[247,1405,434],{"class":433},[247,1407,327],{"class":286},[247,1409,833],{"class":427},[247,1411,309],{"class":282},[247,1413,838],{"class":257},[247,1415,437],{"class":286},[247,1417,1418,1421,1423,1426,1428],{"class":249,"line":303},[247,1419,1420],{"class":286},"    book, sheet ",[247,1422,309],{"class":282},[247,1424,1425],{"class":286}," writer.book, writer.sheets[",[247,1427,838],{"class":257},[247,1429,856],{"class":286},[247,1431,1432,1435,1438,1440,1442,1445,1448,1450,1453],{"class":249,"line":315},[247,1433,1434],{"class":286},"    sheet.set_column(",[247,1436,1437],{"class":257},"\"B:B\"",[247,1439,327],{"class":286},[247,1441,985],{"class":433},[247,1443,1444],{"class":286},", book.add_format({",[247,1446,1447],{"class":257},"\"num_format\"",[247,1449,448],{"class":286},[247,1451,1452],{"class":257},"\"#,##0.00\"",[247,1454,1455],{"class":286},"}))\n",[247,1457,1458,1460,1463,1465,1467,1469,1471,1473,1476],{"class":249,"line":341},[247,1459,1434],{"class":286},[247,1461,1462],{"class":257},"\"D:D\"",[247,1464,327],{"class":286},[247,1466,163],{"class":433},[247,1468,1444],{"class":286},[247,1470,1447],{"class":257},[247,1472,448],{"class":286},[247,1474,1475],{"class":257},"\"0.0%\"",[247,1477,1455],{"class":286},[247,1479,1480,1483,1486,1488,1490,1493,1495,1497,1499,1501,1504,1506,1508],{"class":249,"line":364},[247,1481,1482],{"class":286},"    sheet.ignore_errors({",[247,1484,1485],{"class":257},"\"number_stored_as_text\"",[247,1487,448],{"class":286},[247,1489,901],{"class":282},[247,1491,1492],{"class":257},"\"A2:A",[247,1494,908],{"class":907},[247,1496,880],{"class":433},[247,1498,883],{"class":286},[247,1500,886],{"class":282},[247,1502,1503],{"class":433}," 1",[247,1505,914],{"class":907},[247,1507,917],{"class":257},[247,1509,413],{"class":286},[247,1511,1512],{"class":249,"line":387},[247,1513,300],{"emptyLinePlaceholder":299},[247,1515,1516,1519,1521,1523,1525,1527,1529],{"class":249,"line":410},[247,1517,1518],{"class":286},"final ",[247,1520,309],{"class":282},[247,1522,488],{"class":286},[247,1524,1381],{"class":257},[247,1526,1086],{"class":286},[247,1528,838],{"class":257},[247,1530,856],{"class":286},[247,1532,1533,1535,1537,1539,1541,1543,1546,1548],{"class":249,"line":416},[247,1534,778],{"class":433},[247,1536,1226],{"class":286},[247,1538,498],{"class":282},[247,1540,501],{"class":286},[247,1542,504],{"class":282},[247,1544,1545],{"class":286}," final[",[247,1547,130],{"class":433},[247,1549,1111],{"class":286},[10,1551,1552,1553,1556,1557,540],{},"The ignore range should cover exactly the text columns and nothing else, so that a genuine mistake elsewhere in the sheet still shows its warning. Column formats with ",[14,1554,1555],{},"set_column","\nare covered in ",[24,1558,1560],{"href":1559},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter\u002F","Write a Formatted Excel Report with xlsxwriter",[233,1562,1564],{"id":1563},"step-6-catch-it-before-it-ships","Step 6 — Catch it before it ships",[10,1566,1567],{},"Add a check to the end of the report job that fails when a column meant to be numeric contains text cells. It takes a few lines and prevents the kind of report that looks fine and\nsums to zero.",[238,1569,1571],{"className":273,"code":1570,"language":275,"meta":243,"style":243},"def assert_numeric_columns(path: str, sheet: str, columns: list[str]) -> None:\n    ws = load_workbook(path, read_only=True)[sheet]\n    rows = ws.iter_rows(values_only=False)\n    header = [cell.value for cell in next(rows)]\n    positions = {name: header.index(name) for name in columns}\n    offenders = []\n    for row in rows:\n        for name, position in positions.items():\n            cell = row[position]\n            if cell.value is not None and cell.data_type != \"n\":\n                offenders.append(f\"{name}{cell.row}={cell.value!r}\")\n    if offenders:\n        raise ValueError(\"text in numeric columns: \" + \", \".join(offenders[:10]))\n\nassert_numeric_columns(\"orders_final.xlsx\", \"Orders\", [\"Amount\", \"Qty\", \"Pct\"])\nprint(\"numeric columns verified\")\n",[14,1572,1573,1605,1624,1643,1665,1685,1695,1706,1719,1729,1760,1798,1805,1833,1837,1864],{"__ignoreMap":243},[247,1574,1575,1577,1580,1583,1586,1589,1591,1594,1596,1599,1602],{"class":249,"line":250},[247,1576,565],{"class":282},[247,1578,1579],{"class":568}," assert_numeric_columns",[247,1581,1582],{"class":286},"(path: ",[247,1584,1585],{"class":433},"str",[247,1587,1588],{"class":286},", sheet: ",[247,1590,1585],{"class":433},[247,1592,1593],{"class":286},", columns: list[",[247,1595,1585],{"class":433},[247,1597,1598],{"class":286},"]) -> ",[247,1600,1601],{"class":433},"None",[247,1603,1604],{"class":286},":\n",[247,1606,1607,1609,1611,1614,1617,1619,1621],{"class":249,"line":296},[247,1608,846],{"class":286},[247,1610,309],{"class":282},[247,1612,1613],{"class":286}," load_workbook(path, ",[247,1615,1616],{"class":427},"read_only",[247,1618,309],{"class":282},[247,1620,765],{"class":433},[247,1622,1623],{"class":286},")[sheet]\n",[247,1625,1626,1629,1631,1634,1637,1639,1641],{"class":249,"line":303},[247,1627,1628],{"class":286},"    rows ",[247,1630,309],{"class":282},[247,1632,1633],{"class":286}," ws.iter_rows(",[247,1635,1636],{"class":427},"values_only",[247,1638,309],{"class":282},[247,1640,434],{"class":433},[247,1642,437],{"class":286},[247,1644,1645,1648,1650,1653,1655,1657,1659,1662],{"class":249,"line":315},[247,1646,1647],{"class":286},"    header ",[247,1649,309],{"class":282},[247,1651,1652],{"class":286}," [cell.value ",[247,1654,498],{"class":282},[247,1656,501],{"class":286},[247,1658,504],{"class":282},[247,1660,1661],{"class":433}," next",[247,1663,1664],{"class":286},"(rows)]\n",[247,1666,1667,1670,1672,1675,1677,1680,1682],{"class":249,"line":341},[247,1668,1669],{"class":286},"    positions ",[247,1671,309],{"class":282},[247,1673,1674],{"class":286}," {name: header.index(name) ",[247,1676,498],{"class":282},[247,1678,1679],{"class":286}," name ",[247,1681,504],{"class":282},[247,1683,1684],{"class":286}," columns}\n",[247,1686,1687,1690,1692],{"class":249,"line":364},[247,1688,1689],{"class":286},"    offenders ",[247,1691,309],{"class":282},[247,1693,1694],{"class":286}," []\n",[247,1696,1697,1699,1701,1703],{"class":249,"line":387},[247,1698,862],{"class":282},[247,1700,865],{"class":286},[247,1702,504],{"class":282},[247,1704,1705],{"class":286}," rows:\n",[247,1707,1708,1711,1714,1716],{"class":249,"line":410},[247,1709,1710],{"class":282},"        for",[247,1712,1713],{"class":286}," name, position ",[247,1715,504],{"class":282},[247,1717,1718],{"class":286}," positions.items():\n",[247,1720,1721,1724,1726],{"class":249,"line":416},[247,1722,1723],{"class":286},"            cell ",[247,1725,309],{"class":282},[247,1727,1728],{"class":286}," row[position]\n",[247,1730,1731,1734,1737,1740,1743,1746,1749,1752,1755,1758],{"class":249,"line":722},[247,1732,1733],{"class":282},"            if",[247,1735,1736],{"class":286}," cell.value ",[247,1738,1739],{"class":282},"is",[247,1741,1742],{"class":282}," not",[247,1744,1745],{"class":433}," None",[247,1747,1748],{"class":282}," and",[247,1750,1751],{"class":286}," cell.data_type ",[247,1753,1754],{"class":282},"!=",[247,1756,1757],{"class":257}," \"n\"",[247,1759,1604],{"class":286},[247,1761,1762,1765,1767,1769,1771,1774,1777,1780,1782,1784,1786,1789,1792,1794,1796],{"class":249,"line":744},[247,1763,1764],{"class":286},"                offenders.append(",[247,1766,901],{"class":282},[247,1768,917],{"class":257},[247,1770,908],{"class":907},[247,1772,1773],{"class":286},"name",[247,1775,1776],{"class":907},"}{",[247,1778,1779],{"class":286},"cell.row",[247,1781,914],{"class":907},[247,1783,309],{"class":257},[247,1785,908],{"class":907},[247,1787,1788],{"class":286},"cell.value",[247,1790,1791],{"class":282},"!r",[247,1793,914],{"class":907},[247,1795,917],{"class":257},[247,1797,437],{"class":286},[247,1799,1800,1802],{"class":249,"line":770},[247,1801,625],{"class":282},[247,1803,1804],{"class":286}," offenders:\n",[247,1806,1807,1810,1813,1815,1818,1821,1824,1827,1830],{"class":249,"line":775},[247,1808,1809],{"class":282},"        raise",[247,1811,1812],{"class":433}," ValueError",[247,1814,873],{"class":286},[247,1816,1817],{"class":257},"\"text in numeric columns: \"",[247,1819,1820],{"class":282}," +",[247,1822,1823],{"class":257}," \", \"",[247,1825,1826],{"class":286},".join(offenders[:",[247,1828,1829],{"class":433},"10",[247,1831,1832],{"class":286},"]))\n",[247,1834,1835],{"class":249,"line":784},[247,1836,300],{"emptyLinePlaceholder":299},[247,1838,1839,1842,1844,1846,1848,1851,1853,1855,1857,1859,1861],{"class":249,"line":789},[247,1840,1841],{"class":286},"assert_numeric_columns(",[247,1843,1381],{"class":257},[247,1845,327],{"class":286},[247,1847,838],{"class":257},[247,1849,1850],{"class":286},", [",[247,1852,716],{"class":257},[247,1854,327],{"class":286},[247,1856,732],{"class":257},[247,1858,327],{"class":286},[247,1860,754],{"class":257},[247,1862,1863],{"class":286},"])\n",[247,1865,1866,1868,1870,1873],{"class":249,"line":819},[247,1867,778],{"class":433},[247,1869,873],{"class":286},[247,1871,1872],{"class":257},"\"numeric columns verified\"",[247,1874,437],{"class":286},[233,1876,1878],{"id":1877},"step-7-fix-the-source-instead-of-the-symptom","Step 7 — Fix the source instead of the symptom",[10,1880,1881],{},"Converting at the end of the pipeline works, but the durable fix is to stop numbers becoming strings in the first place. Three sources account for nearly every case, and\neach has a small change upstream.",[238,1883,1885],{"className":273,"code":1884,"language":275,"meta":243,"style":243},"from io import StringIO\n\n# 1. CSV input read without telling pandas about thousands separators\ncsv_text = 'OrderID,Amount\\n00017,\"1,250.50\"\\n00018,980\\n'\nraw = pd.read_csv(StringIO(csv_text))\ntyped = pd.read_csv(StringIO(csv_text), dtype={\"OrderID\": \"string\"}, thousands=\",\")\nprint(raw.dtypes.to_dict(), typed.dtypes.to_dict())\n\n# 2. Formatting numbers for display inside the DataFrame\nreport = clean.copy()\nreport[\"Amount (display)\"] = report[\"Amount\"].map(\"{:,.2f}\".format)   # text on purpose — keep it out of sums\nprint(report[[\"Amount\", \"Amount (display)\"]].dtypes.to_dict())\n\n# 3. Building rows from strings, e.g. parsed text files\nlines = [\"00020|315.75|4\", \"00021|88|1\"]\nparsed = pd.DataFrame([line.split(\"|\") for line in lines], columns=[\"OrderID\", \"Amount\", \"Qty\"])\nparsed = parsed.astype({\"Amount\": \"float64\", \"Qty\": \"int64\"})\nprint(parsed.dtypes.to_dict())\n",[14,1886,1887,1899,1903,1908,1934,1944,1980,1987,1991,1996,2006,2044,2060,2064,2069,2089,2134,2161],{"__ignoreMap":243},[247,1888,1889,1891,1894,1896],{"class":249,"line":250},[247,1890,466],{"class":282},[247,1892,1893],{"class":286}," io ",[247,1895,283],{"class":282},[247,1897,1898],{"class":286}," StringIO\n",[247,1900,1901],{"class":249,"line":296},[247,1902,300],{"emptyLinePlaceholder":299},[247,1904,1905],{"class":249,"line":303},[247,1906,1907],{"class":531},"# 1. CSV input read without telling pandas about thousands separators\n",[247,1909,1910,1913,1915,1918,1921,1924,1926,1929,1931],{"class":249,"line":315},[247,1911,1912],{"class":286},"csv_text ",[247,1914,309],{"class":282},[247,1916,1917],{"class":257}," 'OrderID,Amount",[247,1919,1920],{"class":907},"\\n",[247,1922,1923],{"class":257},"00017,\"1,250.50\"",[247,1925,1920],{"class":907},[247,1927,1928],{"class":257},"00018,980",[247,1930,1920],{"class":907},[247,1932,1933],{"class":257},"'\n",[247,1935,1936,1939,1941],{"class":249,"line":341},[247,1937,1938],{"class":286},"raw ",[247,1940,309],{"class":282},[247,1942,1943],{"class":286}," pd.read_csv(StringIO(csv_text))\n",[247,1945,1946,1949,1951,1954,1957,1959,1961,1964,1966,1968,1971,1974,1976,1978],{"class":249,"line":364},[247,1947,1948],{"class":286},"typed ",[247,1950,309],{"class":282},[247,1952,1953],{"class":286}," pd.read_csv(StringIO(csv_text), ",[247,1955,1956],{"class":427},"dtype",[247,1958,309],{"class":282},[247,1960,908],{"class":286},[247,1962,1963],{"class":257},"\"OrderID\"",[247,1965,448],{"class":286},[247,1967,597],{"class":257},[247,1969,1970],{"class":286},"}, ",[247,1972,1973],{"class":427},"thousands",[247,1975,309],{"class":282},[247,1977,603],{"class":257},[247,1979,437],{"class":286},[247,1981,1982,1984],{"class":249,"line":387},[247,1983,778],{"class":433},[247,1985,1986],{"class":286},"(raw.dtypes.to_dict(), typed.dtypes.to_dict())\n",[247,1988,1989],{"class":249,"line":410},[247,1990,300],{"emptyLinePlaceholder":299},[247,1992,1993],{"class":249,"line":416},[247,1994,1995],{"class":531},"# 2. Formatting numbers for display inside the DataFrame\n",[247,1997,1998,2001,2003],{"class":249,"line":722},[247,1999,2000],{"class":286},"report ",[247,2002,309],{"class":282},[247,2004,2005],{"class":286}," clean.copy()\n",[247,2007,2008,2011,2014,2017,2019,2022,2024,2027,2029,2031,2034,2036,2038,2041],{"class":249,"line":744},[247,2009,2010],{"class":286},"report[",[247,2012,2013],{"class":257},"\"Amount (display)\"",[247,2015,2016],{"class":286},"] ",[247,2018,309],{"class":282},[247,2020,2021],{"class":286}," report[",[247,2023,716],{"class":257},[247,2025,2026],{"class":286},"].map(",[247,2028,917],{"class":257},[247,2030,908],{"class":907},[247,2032,2033],{"class":282},":,.2f",[247,2035,914],{"class":907},[247,2037,917],{"class":257},[247,2039,2040],{"class":286},".format)   ",[247,2042,2043],{"class":531},"# text on purpose — keep it out of sums\n",[247,2045,2046,2048,2051,2053,2055,2057],{"class":249,"line":770},[247,2047,778],{"class":433},[247,2049,2050],{"class":286},"(report[[",[247,2052,716],{"class":257},[247,2054,327],{"class":286},[247,2056,2013],{"class":257},[247,2058,2059],{"class":286},"]].dtypes.to_dict())\n",[247,2061,2062],{"class":249,"line":775},[247,2063,300],{"emptyLinePlaceholder":299},[247,2065,2066],{"class":249,"line":784},[247,2067,2068],{"class":531},"# 3. Building rows from strings, e.g. parsed text files\n",[247,2070,2071,2074,2076,2079,2082,2084,2087],{"class":249,"line":789},[247,2072,2073],{"class":286},"lines ",[247,2075,309],{"class":282},[247,2077,2078],{"class":286}," [",[247,2080,2081],{"class":257},"\"00020|315.75|4\"",[247,2083,327],{"class":286},[247,2085,2086],{"class":257},"\"00021|88|1\"",[247,2088,856],{"class":286},[247,2090,2091,2094,2096,2099,2102,2104,2106,2109,2111,2114,2117,2119,2122,2124,2126,2128,2130,2132],{"class":249,"line":819},[247,2092,2093],{"class":286},"parsed ",[247,2095,309],{"class":282},[247,2097,2098],{"class":286}," pd.DataFrame([line.split(",[247,2100,2101],{"class":257},"\"|\"",[247,2103,811],{"class":286},[247,2105,498],{"class":282},[247,2107,2108],{"class":286}," line ",[247,2110,504],{"class":282},[247,2112,2113],{"class":286}," lines], ",[247,2115,2116],{"class":427},"columns",[247,2118,309],{"class":282},[247,2120,2121],{"class":286},"[",[247,2123,1963],{"class":257},[247,2125,327],{"class":286},[247,2127,716],{"class":257},[247,2129,327],{"class":286},[247,2131,732],{"class":257},[247,2133,1863],{"class":286},[247,2135,2136,2138,2140,2143,2145,2147,2150,2152,2154,2156,2159],{"class":249,"line":843},[247,2137,2093],{"class":286},[247,2139,309],{"class":282},[247,2141,2142],{"class":286}," parsed.astype({",[247,2144,716],{"class":257},[247,2146,448],{"class":286},[247,2148,2149],{"class":257},"\"float64\"",[247,2151,327],{"class":286},[247,2153,732],{"class":257},[247,2155,448],{"class":286},[247,2157,2158],{"class":257},"\"int64\"",[247,2160,413],{"class":286},[247,2162,2163,2165],{"class":249,"line":859},[247,2164,778],{"class":433},[247,2166,2167],{"class":286},"(parsed.dtypes.to_dict())\n",[10,2169,2170,2173,2174,2177,2178,2180,2181,2184,2185,2188],{},[14,2171,2172],{},"read_csv"," with ",[14,2175,2176],{},"thousands=\",\""," parses ",[14,2179,1250],{}," as a number directly, and ",[14,2182,2183],{},"dtype={\"OrderID\": \"string\"}"," keeps the ID text, so neither column needs repair later. Formatting numbers\nas strings for display belongs in the number format, not in the data; when a text version is genuinely needed — for an email body, say — keep it in a separate column and never\nwrite it where people expect to calculate. And anything parsed from text arrives as strings until ",[14,2186,2187],{},"astype"," says otherwise, so type the columns at the moment they are created.",[233,2190,2192],{"id":2191},"common-pitfalls","Common pitfalls",[2194,2195,2196,2212],"table",{},[2197,2198,2199],"thead",{},[2200,2201,2202,2206,2209],"tr",{},[2203,2204,2205],"th",{},"Symptom",[2203,2207,2208],{},"Cause",[2203,2210,2211],{},"Fix",[2213,2214,2215,2230,2251,2269,2282,2296],"tbody",{},[2200,2216,2217,2221,2224],{},[2218,2219,2220],"td",{},"Green triangles on numbers",[2218,2222,2223],{},"Values were strings in the DataFrame",[2218,2225,2226,2227,2229],{},"Convert with ",[14,2228,550],{}," before writing",[2200,2231,2232,2239,2242],{},[2218,2233,2234,2236,2237],{},[14,2235,1032],{}," raises on ",[14,2238,349],{},[2218,2240,2241],{},"Thousands separator",[2218,2243,2244,2245,2248,2249],{},"Remove ",[14,2246,2247],{},","," first, or use ",[14,2250,955],{},[2200,2252,2253,2256,2262],{},[2218,2254,2255],{},"Percentages show as 1200%",[2218,2257,2258,2259,2261],{},"Converted ",[14,2260,1254],{}," to 12 instead of 0.12",[2218,2263,2264,2265,2268],{},"Divide by 100 and apply a ",[14,2266,2267],{},"%"," format",[2200,2270,2271,2274,2279],{},[2218,2272,2273],{},"IDs lose leading zeros",[2218,2275,2276,2278],{},[14,2277,1133],{}," or blanket conversion",[2218,2280,2281],{},"Convert only quantity columns",[2200,2283,2284,2287,2290],{},[2218,2285,2286],{},"Warning on correct text columns",[2218,2288,2289],{},"Excel's check applies to all numeric-looking text",[2218,2291,2292,2295],{},[14,2293,2294],{},"ignore_errors"," in xlsxwriter for that range",[2200,2297,2298,2304,2307],{},[2218,2299,2300,2301],{},"Converted values become ",[14,2302,2303],{},"NaN",[2218,2305,2306],{},"Unparseable strings coerced",[2218,2308,2309],{},"Count new missing values and report them",[233,2311,2313],{"id":2312},"performance-and-scale-notes","Performance and scale notes",[10,2315,2316,2318,2319,2322,2323,2325],{},[14,2317,550],{}," and the ",[14,2320,2321],{},".str"," cleanup are vectorised and fast even for millions of values; the conversion adds almost nothing to a report job. Setting a number format per cell with\nopenpyxl is slower on large sheets than a single ",[14,2324,1555],{}," format with xlsxwriter, so prefer xlsxwriter for large outputs. The verification step reads in read-only mode and streams the\nsheet, which keeps it cheap to run on every report.",[233,2327,2329],{"id":2328},"conclusion","Conclusion",[10,2331,2332,2333,2335,2336,2338,2339,2341],{},"\"Number stored as text\" in a script's output means the values were strings when they were written. Convert quantity columns explicitly — strip separators and percent signs, use\n",[14,2334,550],{},", divide percentages by 100 — and restore the look with number formats. Keep identifiers as text deliberately, avoid ",[14,2337,1133],{}," on mixed data, silence the warning\nfor true text columns with xlsxwriter's ",[14,2340,2294],{},", and verify numeric columns at the end of the job so the problem never reaches a reader.",[233,2343,2345],{"id":2344},"frequently-asked-questions","Frequently asked questions",[10,2347,2348,2352],{},[2349,2350,2351],"strong",{},"Why does pandas write my numbers as text?","\nBecause they are strings in the DataFrame, usually from a CSV read without dtypes, text parsing or string formatting. Check df.dtypes; object or string columns are written as text.",[10,2354,2355,2358],{},[2349,2356,2357],{},"Can openpyxl hide the green triangle warning?","\nNot when writing. openpyxl does not write the ignored-errors setting, so use xlsxwriter's ignore_errors, or convert the values to numbers if they are real quantities.",[10,2360,2361,2364],{},[2349,2362,2363],{},"Will formatting a text cell as Number fix it?","\nNo. A number format changes how numbers display; a text value stays text. The value itself has to be written as a number.",[10,2366,2367,2370],{},[2349,2368,2369],{},"Should phone numbers be numbers or text?","\nText. They are identifiers with meaningful leading zeros and plus signs, and nobody calculates with them.",[233,2372,2374],{"id":2373},"related","Related",[2376,2377,2378,2385,2392,2399,2406],"ul",{},[2379,2380,2381,2382,2384],"li",{},"Up one level: ",[24,2383,27],{"href":26}," — decoding the errors scripts hit most often.",[2379,2386,2387,2391],{},[24,2388,2390],{"href":2389},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-illegalcharactererror-when-writing-excel-with-openpyxl\u002F","Fix IllegalCharacterError When Writing Excel with openpyxl"," — another text problem that appears at write time.",[2379,2393,2394,2398],{},[24,2395,2397],{"href":2396},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-excel-cells-as-currency-with-python\u002F","Format Excel Cells as Currency with Python"," — formats for the converted amounts.",[2379,2400,2401,2405],{},[24,2402,2404],{"href":2403},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-as-percentages-in-excel-with-python\u002F","Format Numbers as Percentages in Excel with Python"," — fractions and percent formats.",[2379,2407,2408,2412],{},[24,2409,2411],{"href":2410},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-excel-data-types-with-pandas\u002F","Check Excel Data Types with pandas"," — dtype checks before writing.",[2414,2415,2416],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}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}",{"title":243,"searchDepth":296,"depth":296,"links":2418},[2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431],{"id":235,"depth":296,"text":236},{"id":440,"depth":296,"text":441},{"id":543,"depth":296,"text":544},{"id":1061,"depth":296,"text":1062},{"id":1126,"depth":296,"text":1127},{"id":1363,"depth":296,"text":1364},{"id":1563,"depth":296,"text":1564},{"id":1877,"depth":296,"text":1878},{"id":2191,"depth":296,"text":2192},{"id":2312,"depth":296,"text":2313},{"id":2328,"depth":296,"text":2329},{"id":2344,"depth":296,"text":2345},{"id":2373,"depth":296,"text":2374},"2026-09-17","Green triangles all over a workbook your script wrote mean numbers went in as strings. Convert them to real numbers before writing, keep identifiers as text on purpose, and silence the warning where text is correct.","md",[2436,2438,2440,2442],{"q":2351,"a":2437},"Because they are strings in the DataFrame, usually from a CSV read without dtypes, text parsing or string formatting. Check df.dtypes; object or string columns are written as text.",{"q":2357,"a":2439},"Not when writing. openpyxl does not write the ignored-errors setting, so use xlsxwriter's ignore_errors, or convert the values to numbers if they are real quantities.",{"q":2363,"a":2441},"No. A number format changes how numbers display; a text value stays text. The value itself has to be written as a number.",{"q":2369,"a":2443},"Text. They are identifiers with meaningful leading zeros and plus signs, and nobody calculates with them.","fix number stored as text warnings in excel output",null,{"breadcrumb":2447},[2448,2450,2453],{"name":2449,"item":674},"Home",{"name":2451,"item":2452},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":27,"item":26},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output",{"title":2456,"description":2457},"Fix Number Stored as Text Warnings in Python Excel Output","Why pandas and openpyxl write numbers as text, how to convert amounts, quantities and percentages before writing, why strings_to_numbers can destroy IDs, and how to ignore the warning with xlsxwriter.","fix-number-stored-as-text-warnings-in-excel-output","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output\u002Findex",[2461,2462,2463],"troubleshooting","number-formats","writing-excel","how-to","un-b--zBhBLapsZNs2LAQ3m7OhbMdEjpIBtw-TtTJ0U",[2467,2471],{"title":2468,"path":2469,"stem":2470,"children":-1},"Fix NoneType Errors When Reading Cells with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl\u002Findex",{"title":2472,"path":2473,"stem":2474,"children":-1},"Fix \"openpyxl does not support the old .xls file format\"","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-does-not-support-the-old-xls-format","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-does-not-support-the-old-xls-format\u002Findex",[2476,2478],{"path":2477,"title":2451},"\u002Fgetting-started-with-python-excel-automation",{"path":2479,"title":27},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors",[],[2482,2486,2489,2492,2495,2498],{"path":2483,"title":2484,"kind":2485},"\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":2487,"title":2488,"kind":2464},"\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":2490,"title":2491,"kind":2485},"\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":2493,"title":2494,"kind":2464},"\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":2496,"title":2497,"kind":2464},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel","Handle Microsoft Graph Throttling in Excel Automation",{"path":2499,"title":2500,"kind":2501},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation","Error Handling and Logging in Excel Automation","guide",1790972067223]