[{"data":1,"prerenderedAt":1986},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output":1952,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output":1961,"children:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output":1966,"related:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output":1967},{"id":4,"title":5,"body":6,"dateModified":1918,"datePublished":1918,"description":1919,"extension":1920,"faq":1921,"keyword":1930,"kind":1931,"meta":1932,"navigation":260,"path":1940,"seo":1941,"slug":1944,"stem":1945,"topics":1946,"type":1950,"__hash__":1951},"docs\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output\u002Findex.md","Control How NaN, None and Infinity Appear in Excel Output",{"type":7,"value":8,"toc":1904},"minimark",[9,24,171,176,205,472,476,526,555,568,572,578,725,735,739,742,923,938,948,952,965,1018,1034,1131,1143,1261,1264,1424,1432,1436,1439,1627,1655,1659,1665,1730,1753,1757,1800,1804,1813,1817,1839,1843,1849,1855,1861,1867,1871,1900],[10,11,12,13,17,18,23],"p",{},"A DataFrame almost always has holes: a customer with no region, an order not yet shipped, a growth rate divided by a zero baseline. When it goes to Excel, each of those holes has to become ",[14,15,16],"em",{},"something"," in a cell, and the choice affects every formula a reader writes on top of the sheet. pandas makes sensible defaults for missing values and a surprising one for infinity. This page, part of ",[19,20,22],"a",{"href":21},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with Pandas",", shows what gets written, what Excel does with it, and how to control it per column.",[25,26,34,35,34,39,34,43,34,51,34,61,34,72,34,79,34,84,34,92,34,99,34,105,34,110,34,113,34,118,34,123,34,130,34,134,34,138,34,141,34,144,34,149,34,156,34,161,34,165,34,168],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"4 0 752 332","img",[30,31],"nan-map-t","nan-map-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  ",[36,37,38],"title",{"id":30},"What to_excel writes for each kind of missing value",[40,41,42],"desc",{"id":31},"A DataFrame with missing values fans out to what ends up in Excel. NaN, None, pd.NA and NaT all become empty cells. Positive and negative infinity become the text inf and -inf. With na_rep, every missing value in every column becomes the given text instead.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"4","0","752","332","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":57,"fill":58,"stroke":59,"style":60},"20.0","119.5","220.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[62,63,65],"g",{"transform":64},"translate(118.0,132.5) scale(1.000)",[66,67],"path",{"d":68,"fill":69,"stroke":70,"style":71},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[73,74,78],"text",{"x":75,"y":76,"style":77},"130.0","179.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","DataFrame values",[73,80,83],{"x":75,"y":81,"style":82},"198.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","NaN, None, NA, NaT, inf",[44,85],{"x":86,"y":87,"width":88,"height":89,"rx":57,"fill":90,"stroke":91,"style":60},"470.0","16.0","270.0","92.0","#d9f4f1","var(--teal,#0f9488)",[62,93,95],{"transform":94},"translate(593.0,29.0) scale(1.000)",[66,96],{"d":97,"fill":69,"stroke":98,"style":71},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--teal-ink,#0b6157)",[73,100,104],{"x":101,"y":102,"style":103},"605.0","75.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Empty cell",[73,106,109],{"x":101,"y":107,"style":108},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","NaN, None, pd.NA, NaT (default)",[66,111],{"d":112,"fill":69,"stroke":59,"style":60},"M244.0,166.0 C354.0,166.0 354.0,62.0 460.0,62.0",[114,115],"polygon",{"points":116,"fill":117},"464.0,62.0 455.0,67.0 455.0,57.0","#5b5cf0",[44,119],{"x":86,"y":120,"width":88,"height":89,"rx":57,"fill":121,"stroke":122,"style":60},"120.0","#fee8f2","var(--accent,#f43f8f)",[62,124,126],{"transform":125},"translate(593.0,133.0) scale(1.000)",[66,127],{"d":128,"fill":69,"stroke":129,"style":71},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)",[73,131,133],{"x":101,"y":76,"style":132},"font-size:13px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Text \"inf\" \u002F \"-inf\"",[73,135,137],{"x":101,"y":136,"style":108},"198.0","np.inf, -np.inf (inf_rep default)",[66,139],{"d":140,"fill":69,"stroke":59,"style":60},"M244.0,166.0 C354.0,166.0 354.0,166.0 460.0,166.0",[114,142],{"points":143,"fill":117},"464.0,166.0 455.0,171.0 455.0,161.0",[44,145],{"x":86,"y":146,"width":88,"height":89,"rx":57,"fill":147,"stroke":148,"style":60},"224.0","#fdefd8","var(--gold,#b4740a)",[62,150,152],{"transform":151},"translate(593.0,237.0) scale(1.000)",[66,153],{"d":154,"fill":69,"stroke":155,"style":71},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--gold-ink,#7a4e06)",[73,157,160],{"x":101,"y":158,"style":159},"283.0","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Your na_rep text",[73,162,164],{"x":101,"y":163,"style":108},"302.0","every missing value, every column",[66,166],{"d":167,"fill":69,"stroke":59,"style":60},"M244.0,166.0 C354.0,166.0 354.0,270.0 460.0,270.0",[114,169],{"points":170,"fill":117},"464.0,270.0 455.0,275.0 455.0,265.0",[172,173,175],"h2",{"id":174},"prerequisites","Prerequisites",[177,178,183],"pre",{"className":179,"code":180,"language":181,"meta":182,"style":182},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[184,185,186],"code",{"__ignoreMap":182},[187,188,191,195,199,202],"span",{"class":189,"line":190},"line",1,[187,192,194],{"class":193},"sMTad","pip",[187,196,198],{"class":197},"srMev"," install",[187,200,201],{"class":197}," pandas",[187,203,204],{"class":197}," openpyxl\n",[177,206,210],{"className":207,"code":208,"language":209,"meta":182,"style":182},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\nfrom openpyxl import load_workbook\n\nframe = pd.DataFrame({\n    \"region\": [\"North\", None, \"East\", \"West\"],\n    \"units\": pd.array([12, None, 7, 9], dtype=\"Int64\"),\n    \"growth\": [0.10, np.nan, np.inf, -np.inf],\n    \"shipped\": pd.to_datetime([\"2026-03-01\", None, \"2026-03-03\", \"2026-03-04\"]),\n})\n\ndef cells(path):\n    ws = load_workbook(path).active\n    return [[c.value for c in row] for row in ws.iter_rows(min_row=2)]\n","python",[184,211,212,228,241,255,262,274,306,346,366,395,401,406,419,430],{"__ignoreMap":182},[187,213,214,218,222,225],{"class":189,"line":190},[187,215,217],{"class":216},"s-kum","import",[187,219,221],{"class":220},"skGVy"," numpy ",[187,223,224],{"class":216},"as",[187,226,227],{"class":220}," np\n",[187,229,231,233,236,238],{"class":189,"line":230},2,[187,232,217],{"class":216},[187,234,235],{"class":220}," pandas ",[187,237,224],{"class":216},[187,239,240],{"class":220}," pd\n",[187,242,244,247,250,252],{"class":189,"line":243},3,[187,245,246],{"class":216},"from",[187,248,249],{"class":220}," openpyxl ",[187,251,217],{"class":216},[187,253,254],{"class":220}," load_workbook\n",[187,256,258],{"class":189,"line":257},4,[187,259,261],{"emptyLinePlaceholder":260},true,"\n",[187,263,265,268,271],{"class":189,"line":264},5,[187,266,267],{"class":220},"frame ",[187,269,270],{"class":216},"=",[187,272,273],{"class":220}," pd.DataFrame({\n",[187,275,277,280,283,286,289,293,295,298,300,303],{"class":189,"line":276},6,[187,278,279],{"class":197},"    \"region\"",[187,281,282],{"class":220},": [",[187,284,285],{"class":197},"\"North\"",[187,287,288],{"class":220},", ",[187,290,292],{"class":291},"sP0c6","None",[187,294,288],{"class":220},[187,296,297],{"class":197},"\"East\"",[187,299,288],{"class":220},[187,301,302],{"class":197},"\"West\"",[187,304,305],{"class":220},"],\n",[187,307,309,312,315,317,319,321,323,326,328,331,334,338,340,343],{"class":189,"line":308},7,[187,310,311],{"class":197},"    \"units\"",[187,313,314],{"class":220},": pd.array([",[187,316,57],{"class":291},[187,318,288],{"class":220},[187,320,292],{"class":291},[187,322,288],{"class":220},[187,324,325],{"class":291},"7",[187,327,288],{"class":220},[187,329,330],{"class":291},"9",[187,332,333],{"class":220},"], ",[187,335,337],{"class":336},"sa561","dtype",[187,339,270],{"class":216},[187,341,342],{"class":197},"\"Int64\"",[187,344,345],{"class":220},"),\n",[187,347,349,352,354,357,360,363],{"class":189,"line":348},8,[187,350,351],{"class":197},"    \"growth\"",[187,353,282],{"class":220},[187,355,356],{"class":291},"0.10",[187,358,359],{"class":220},", np.nan, np.inf, ",[187,361,362],{"class":216},"-",[187,364,365],{"class":220},"np.inf],\n",[187,367,369,372,375,378,380,382,384,387,389,392],{"class":189,"line":368},9,[187,370,371],{"class":197},"    \"shipped\"",[187,373,374],{"class":220},": pd.to_datetime([",[187,376,377],{"class":197},"\"2026-03-01\"",[187,379,288],{"class":220},[187,381,292],{"class":291},[187,383,288],{"class":220},[187,385,386],{"class":197},"\"2026-03-03\"",[187,388,288],{"class":220},[187,390,391],{"class":197},"\"2026-03-04\"",[187,393,394],{"class":220},"]),\n",[187,396,398],{"class":189,"line":397},10,[187,399,400],{"class":220},"})\n",[187,402,404],{"class":189,"line":403},11,[187,405,261],{"emptyLinePlaceholder":260},[187,407,409,412,416],{"class":189,"line":408},12,[187,410,411],{"class":216},"def",[187,413,415],{"class":414},"s_Opv"," cells",[187,417,418],{"class":220},"(path):\n",[187,420,422,425,427],{"class":189,"line":421},13,[187,423,424],{"class":220},"    ws ",[187,426,270],{"class":216},[187,428,429],{"class":220}," load_workbook(path).active\n",[187,431,433,436,439,442,445,448,451,453,456,458,461,464,466,469],{"class":189,"line":432},14,[187,434,435],{"class":216},"    return",[187,437,438],{"class":220}," [[c.value ",[187,440,441],{"class":216},"for",[187,443,444],{"class":220}," c ",[187,446,447],{"class":216},"in",[187,449,450],{"class":220}," row] ",[187,452,441],{"class":216},[187,454,455],{"class":220}," row ",[187,457,447],{"class":216},[187,459,460],{"class":220}," ws.iter_rows(",[187,462,463],{"class":336},"min_row",[187,465,270],{"class":216},[187,467,468],{"class":291},"2",[187,470,471],{"class":220},")]\n",[172,473,475],{"id":474},"step-1-see-the-defaults","Step 1 — See the defaults",[177,477,479],{"className":207,"code":478,"language":209,"meta":182,"style":182},"frame.to_excel(\"defaults.xlsx\", index=False)\nfor row in cells(\"defaults.xlsx\"):\n    print(row)\n",[184,480,481,502,518],{"__ignoreMap":182},[187,482,483,486,489,491,494,496,499],{"class":189,"line":190},[187,484,485],{"class":220},"frame.to_excel(",[187,487,488],{"class":197},"\"defaults.xlsx\"",[187,490,288],{"class":220},[187,492,493],{"class":336},"index",[187,495,270],{"class":216},[187,497,498],{"class":291},"False",[187,500,501],{"class":220},")\n",[187,503,504,506,508,510,513,515],{"class":189,"line":230},[187,505,441],{"class":216},[187,507,455],{"class":220},[187,509,447],{"class":216},[187,511,512],{"class":220}," cells(",[187,514,488],{"class":197},[187,516,517],{"class":220},"):\n",[187,519,520,523],{"class":189,"line":243},[187,521,522],{"class":291},"    print",[187,524,525],{"class":220},"(row)\n",[10,527,528,529,531,532,535,536,539,540,543,544,288,547,550,551,554],{},"Every missing value — ",[184,530,292],{}," in the text column, ",[184,533,534],{},"\u003CNA>"," in the nullable integer column, ",[184,537,538],{},"NaN"," in the float column, ",[184,541,542],{},"NaT"," in the dates — is written as an empty cell. That is the right default for a spreadsheet: Excel's ",[184,545,546],{},"SUM",[184,548,549],{},"AVERAGE"," and ",[184,552,553],{},"COUNT"," skip empty cells, just as pandas' aggregations skip missing values, so a reader's formulas agree with your numbers.",[10,556,557,558,560,561,550,564,567],{},"The infinities are the exception. Excel has no infinity, so pandas writes the ",[14,559,73],{}," ",[184,562,563],{},"\"inf\"",[184,565,566],{},"\"-inf\"",". That turns one cell in a numeric column into a string, and Excel treats it very differently from a blank.",[172,569,571],{"id":570},"step-2-understand-what-excel-does-with-blanks-and-inf","Step 2 — Understand what Excel does with blanks and \"inf\"",[10,573,574,575,577],{},"The consequences show up in formulas. Recalculating a sheet with a blank and an ",[184,576,563],{}," in a column of growth rates (0.1, \"inf\", blank, 0.3) gives:",[25,579,34,584,34,587,34,590,34,593,34,599,34,604,34,610,34,613,34,617,34,622,34,628,34,630,34,634,34,636,34,639,34,642,34,646,34,648,34,652,34,654,34,656,34,659,34,663,34,665,34,668,34,670,34,673,34,676,34,680,34,682,34,685,34,687,34,690,34,693,34,697,34,699,34,703,34,705,34,708,34,711,34,715,34,717,34,720,34,722],{"viewBox":580,"role":28,"ariaLabelledBy":581,"xmlns":32,"style":33},"4 0 752 276",[582,583],"nan-formulas-t","nan-formulas-d",[36,585,586],{"id":582},"How formulas treat a blank cell and the text \"inf\"",[40,588,589],{"id":583},"Results of formulas over a column holding 0.1, the text inf, a blank and 0.3, recalculated in LibreOffice. SUM gives 0.4 and AVERAGE 0.2, silently skipping both. COUNT gives 2 and COUNTA gives 3, because the text counts as a value. Multiplying the inf cell by 100 gives #VALUE!, while multiplying the blank cell by 100 gives 0.",[44,591],{"x":46,"y":47,"width":48,"height":592,"fill":50},"276",[44,594],{"x":53,"y":87,"width":595,"height":596,"rx":47,"fill":50,"stroke":597,"style":598},"151.0","40.0","var(--line,#cdd5e6)","stroke-width:1px",[44,600],{"x":601,"y":87,"width":602,"height":596,"rx":47,"fill":58,"stroke":59,"style":603},"171.0","284.5","stroke-width:1.2px",[73,605,609],{"x":606,"y":607,"style":608},"313.3","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Result",[44,611],{"x":612,"y":87,"width":602,"height":596,"rx":47,"fill":58,"stroke":59,"style":603},"455.5",[73,614,616],{"x":615,"y":607,"style":608},"597.8","What happened",[44,618],{"x":53,"y":619,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"56.0","34.0","#e7ebef",[73,623,627],{"x":624,"y":625,"style":626},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","=SUM(A2:A5)",[44,629],{"x":601,"y":619,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,631,633],{"x":606,"y":632,"style":82},"77.0","0.4",[44,635],{"x":612,"y":619,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,637,638],{"x":615,"y":632,"style":82},"text and blank skipped",[44,640],{"x":53,"y":641,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"90.0",[73,643,645],{"x":624,"y":644,"style":626},"112.0","=AVERAGE(A2:A5)",[44,647],{"x":601,"y":641,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,649,651],{"x":606,"y":650,"style":82},"111.0","0.2",[44,653],{"x":612,"y":641,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,655,638],{"x":615,"y":650,"style":82},[44,657],{"x":53,"y":658,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"124.0",[73,660,662],{"x":624,"y":661,"style":626},"146.0","=COUNT(A2:A5)",[44,664],{"x":601,"y":658,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,666,468],{"x":606,"y":667,"style":82},"145.0",[44,669],{"x":612,"y":658,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,671,672],{"x":615,"y":667,"style":82},"numbers only",[44,674],{"x":53,"y":675,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"158.0",[73,677,679],{"x":624,"y":678,"style":626},"180.0","=COUNTA(A2:A5)",[44,681],{"x":601,"y":675,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,683,684],{"x":606,"y":76,"style":82},"3",[44,686],{"x":612,"y":675,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,688,689],{"x":615,"y":76,"style":82},"\"inf\" counts, blank does not",[44,691],{"x":53,"y":692,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"192.0",[73,694,696],{"x":624,"y":695,"style":626},"214.0","=A3*100  (\"inf\")",[44,698],{"x":601,"y":692,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,700,702],{"x":606,"y":701,"style":82},"213.0","#VALUE!",[44,704],{"x":612,"y":692,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,706,707],{"x":615,"y":701,"style":82},"text in arithmetic",[44,709],{"x":53,"y":710,"width":595,"height":620,"rx":47,"fill":621,"stroke":597,"style":598},"226.0",[73,712,714],{"x":624,"y":713,"style":626},"248.0","=A4*100  (blank)",[44,716],{"x":601,"y":710,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,718,47],{"x":606,"y":719,"style":82},"247.0",[44,721],{"x":612,"y":710,"width":602,"height":620,"rx":47,"fill":50,"stroke":597,"style":598},[73,723,724],{"x":615,"y":719,"style":82},"blank treated as 0",[10,726,727,728,730,731,734],{},"Two lessons. First, ",[184,729,563],{}," quietly disappears from totals and averages, so a report can look complete while a row's value is excluded — and any formula that uses the cell directly breaks. Second, a blank in direct arithmetic counts as zero. That is usually harmless, but a reader who computes ",[184,732,733],{},"=B4*C4"," for a row with a missing price gets 0, not an error. Neither is a reason to avoid blanks; both are reasons to know exactly which cells are blank.",[172,736,738],{"id":737},"step-3-replace-infinities-before-writing","Step 3 — Replace infinities before writing",[10,740,741],{},"Infinity in a report almost always comes from dividing by zero — growth against a zero baseline, a rate per zero hours. Decide what it means and write that instead. Usually the honest answer is \"not defined\", which is a blank:",[177,743,745],{"className":207,"code":744,"language":209,"meta":182,"style":182},"sales = pd.DataFrame({\"region\": [\"North\", \"South\", \"East\"], \"last_year\": [100, 0, 80], \"this_year\": [120, 45, 80]})\nsales[\"growth\"] = sales[\"this_year\"] \u002F sales[\"last_year\"] - 1\nprint(sales[\"growth\"].tolist())\n# [0.19999999999999996, inf, 0.0]\n\nsales[\"growth\"] = sales[\"growth\"].replace([np.inf, -np.inf], np.nan)\nsales.to_excel(\"growth.xlsx\", index=False)\nprint(cells(\"growth.xlsx\"))\n",[184,746,747,814,848,861,867,871,893,911],{"__ignoreMap":182},[187,748,749,752,754,757,760,762,764,766,769,771,773,775,778,780,783,785,787,789,792,794,797,799,802,804,807,809,811],{"class":189,"line":190},[187,750,751],{"class":220},"sales ",[187,753,270],{"class":216},[187,755,756],{"class":220}," pd.DataFrame({",[187,758,759],{"class":197},"\"region\"",[187,761,282],{"class":220},[187,763,285],{"class":197},[187,765,288],{"class":220},[187,767,768],{"class":197},"\"South\"",[187,770,288],{"class":220},[187,772,297],{"class":197},[187,774,333],{"class":220},[187,776,777],{"class":197},"\"last_year\"",[187,779,282],{"class":220},[187,781,782],{"class":291},"100",[187,784,288],{"class":220},[187,786,47],{"class":291},[187,788,288],{"class":220},[187,790,791],{"class":291},"80",[187,793,333],{"class":220},[187,795,796],{"class":197},"\"this_year\"",[187,798,282],{"class":220},[187,800,801],{"class":291},"120",[187,803,288],{"class":220},[187,805,806],{"class":291},"45",[187,808,288],{"class":220},[187,810,791],{"class":291},[187,812,813],{"class":220},"]})\n",[187,815,816,819,822,825,827,830,832,834,837,839,841,843,845],{"class":189,"line":230},[187,817,818],{"class":220},"sales[",[187,820,821],{"class":197},"\"growth\"",[187,823,824],{"class":220},"] ",[187,826,270],{"class":216},[187,828,829],{"class":220}," sales[",[187,831,796],{"class":197},[187,833,824],{"class":220},[187,835,836],{"class":216},"\u002F",[187,838,829],{"class":220},[187,840,777],{"class":197},[187,842,824],{"class":220},[187,844,362],{"class":216},[187,846,847],{"class":291}," 1\n",[187,849,850,853,856,858],{"class":189,"line":243},[187,851,852],{"class":291},"print",[187,854,855],{"class":220},"(sales[",[187,857,821],{"class":197},[187,859,860],{"class":220},"].tolist())\n",[187,862,863],{"class":189,"line":257},[187,864,866],{"class":865},"s-wDw","# [0.19999999999999996, inf, 0.0]\n",[187,868,869],{"class":189,"line":264},[187,870,261],{"emptyLinePlaceholder":260},[187,872,873,875,877,879,881,883,885,888,890],{"class":189,"line":276},[187,874,818],{"class":220},[187,876,821],{"class":197},[187,878,824],{"class":220},[187,880,270],{"class":216},[187,882,829],{"class":220},[187,884,821],{"class":197},[187,886,887],{"class":220},"].replace([np.inf, ",[187,889,362],{"class":216},[187,891,892],{"class":220},"np.inf], np.nan)\n",[187,894,895,898,901,903,905,907,909],{"class":189,"line":308},[187,896,897],{"class":220},"sales.to_excel(",[187,899,900],{"class":197},"\"growth.xlsx\"",[187,902,288],{"class":220},[187,904,493],{"class":336},[187,906,270],{"class":216},[187,908,498],{"class":291},[187,910,501],{"class":220},[187,912,913,915,918,920],{"class":189,"line":348},[187,914,852],{"class":291},[187,916,917],{"class":220},"(cells(",[187,919,900],{"class":197},[187,921,922],{"class":220},"))\n",[10,924,925,926,928,929,932,933,937],{},"South now has an empty growth cell rather than ",[184,927,563],{},". If readers need to know ",[14,930,931],{},"why"," it is empty, add a note column or a comment rather than putting text in a number column. ",[19,934,936],{"href":935},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fadd-comments-and-notes-to-excel-cells-with-python\u002F","Add Comments and Notes to Excel Cells with Python"," shows how.",[10,939,940,943,944,947],{},[184,941,942],{},"inf_rep"," changes the text written for infinity (",[184,945,946],{},"inf_rep=\"∞\"","), but it is still text, with every problem from Step 2. Treat it as a display choice for sheets nobody calculates with.",[172,949,951],{"id":950},"step-4-use-placeholders-per-column-not-with-na_rep","Step 4 — Use placeholders per column, not with na_rep",[10,953,954,957,958,961,962,964],{},[184,955,956],{},"na_rep"," replaces ",[14,959,960],{},"every"," missing value in ",[14,963,960],{}," column with one string:",[177,966,968],{"className":207,"code":967,"language":209,"meta":182,"style":182},"frame.to_excel(\"na_rep.xlsx\", index=False, na_rep=\"n\u002Fa\")\nprint(cells(\"na_rep.xlsx\")[1])\n# ['n\u002Fa', 'n\u002Fa', 'n\u002Fa', 'n\u002Fa']\n",[184,969,970,996,1013],{"__ignoreMap":182},[187,971,972,974,977,979,981,983,985,987,989,991,994],{"class":189,"line":190},[187,973,485],{"class":220},[187,975,976],{"class":197},"\"na_rep.xlsx\"",[187,978,288],{"class":220},[187,980,493],{"class":336},[187,982,270],{"class":216},[187,984,498],{"class":291},[187,986,288],{"class":220},[187,988,956],{"class":336},[187,990,270],{"class":216},[187,992,993],{"class":197},"\"n\u002Fa\"",[187,995,501],{"class":220},[187,997,998,1000,1002,1004,1007,1010],{"class":189,"line":230},[187,999,852],{"class":291},[187,1001,917],{"class":220},[187,1003,976],{"class":197},[187,1005,1006],{"class":220},")[",[187,1008,1009],{"class":291},"1",[187,1011,1012],{"class":220},"])\n",[187,1014,1015],{"class":189,"line":243},[187,1016,1017],{"class":865},"# ['n\u002Fa', 'n\u002Fa', 'n\u002Fa', 'n\u002Fa']\n",[10,1019,1020,1021,288,1024,550,1027,1030,1031,1033],{},"In the text column that is exactly what a reader wants. In ",[184,1022,1023],{},"units",[184,1025,1026],{},"growth",[184,1028,1029],{},"shipped"," it puts text into numeric and date columns, so sums skip the row, arithmetic returns ",[184,1032,702],{},", and the date column no longer sorts cleanly. Fill placeholders in the columns where they belong and leave the rest blank:",[177,1035,1037],{"className":207,"code":1036,"language":209,"meta":182,"style":182},"out = frame.copy()\nout[\"region\"] = out[\"region\"].fillna(\"Unassigned\")\nout[\"growth\"] = out[\"growth\"].replace([np.inf, -np.inf], np.nan)\nout.to_excel(\"per_column.xlsx\", index=False)\nfor row in cells(\"per_column.xlsx\"):\n    print(row)\n",[184,1038,1039,1049,1073,1093,1111,1125],{"__ignoreMap":182},[187,1040,1041,1044,1046],{"class":189,"line":190},[187,1042,1043],{"class":220},"out ",[187,1045,270],{"class":216},[187,1047,1048],{"class":220}," frame.copy()\n",[187,1050,1051,1054,1056,1058,1060,1063,1065,1068,1071],{"class":189,"line":230},[187,1052,1053],{"class":220},"out[",[187,1055,759],{"class":197},[187,1057,824],{"class":220},[187,1059,270],{"class":216},[187,1061,1062],{"class":220}," out[",[187,1064,759],{"class":197},[187,1066,1067],{"class":220},"].fillna(",[187,1069,1070],{"class":197},"\"Unassigned\"",[187,1072,501],{"class":220},[187,1074,1075,1077,1079,1081,1083,1085,1087,1089,1091],{"class":189,"line":243},[187,1076,1053],{"class":220},[187,1078,821],{"class":197},[187,1080,824],{"class":220},[187,1082,270],{"class":216},[187,1084,1062],{"class":220},[187,1086,821],{"class":197},[187,1088,887],{"class":220},[187,1090,362],{"class":216},[187,1092,892],{"class":220},[187,1094,1095,1098,1101,1103,1105,1107,1109],{"class":189,"line":257},[187,1096,1097],{"class":220},"out.to_excel(",[187,1099,1100],{"class":197},"\"per_column.xlsx\"",[187,1102,288],{"class":220},[187,1104,493],{"class":336},[187,1106,270],{"class":216},[187,1108,498],{"class":291},[187,1110,501],{"class":220},[187,1112,1113,1115,1117,1119,1121,1123],{"class":189,"line":264},[187,1114,441],{"class":216},[187,1116,455],{"class":220},[187,1118,447],{"class":216},[187,1120,512],{"class":220},[187,1122,1100],{"class":197},[187,1124,517],{"class":220},[187,1126,1127,1129],{"class":189,"line":276},[187,1128,522],{"class":291},[187,1130,525],{"class":220},[10,1132,1133,1134,1137,1138,1142],{},"For numeric columns where a reader must ",[14,1135,1136],{},"see"," that a value is missing, use formatting rather than text: a light fill on blank cells, applied with conditional formatting, keeps the column numeric. ",[19,1139,1141],{"href":1140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-blank-cells-with-openpyxl\u002F","Highlight Blank Cells in Excel with openpyxl"," builds that rule.",[25,1144,34,1149,34,1152,34,1155,34,1158,34,1163,34,1169,34,1174,34,1179,34,1184,34,1191,34,1195,34,1200,34,1204,34,1209,34,1212,34,1217,34,1222,34,1226,34,1230,34,1235,34,1238,34,1243,34,1246,34,1252,34,1256],{"viewBox":1145,"role":28,"ariaLabelledBy":1146,"xmlns":32,"style":33},"4 0 752 233",[1147,1148],"nan-choices-t","nan-choices-d",[36,1150,1151],{"id":1147},"Placeholders that keep columns usable",[40,1153,1154],{"id":1148},"Two panels. For text columns, a placeholder such as Unassigned is fine: it sorts, filters and reads naturally. For numeric and date columns, leave missing values blank and show them with a conditional format; text placeholders would break sums, arithmetic and sorting.",[44,1156],{"x":46,"y":47,"width":48,"height":1157,"fill":50},"233",[44,1159],{"x":53,"y":87,"width":1160,"height":1161,"rx":1162,"fill":90,"stroke":91,"style":60},"351.0","201.0","14",[62,1164,1166],{"transform":1165},"translate(182.5,29.0) scale(1.083)",[66,1167],{"d":97,"fill":69,"stroke":98,"style":1168},"stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[73,1170,1173],{"x":1171,"y":625,"style":1172},"195.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Text columns",[189,1175],{"x1":1176,"y1":1177,"x2":1178,"y2":1177,"stroke":91,"style":598},"36.0","91.0","355.0",[73,1180,1183],{"x":1181,"y":650,"style":1182},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","fillna(\"Unassigned\") per column",[62,1185,1187],{"transform":1186},"translate(38.5,99.5) scale(0.625)",[66,1188],{"d":1189,"fill":69,"stroke":91,"style":1190},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[73,1192,1194],{"x":1181,"y":1193,"style":1182},"137.0","reads naturally in filters",[62,1196,1198],{"transform":1197},"translate(38.5,125.5) scale(0.625)",[66,1199],{"d":1189,"fill":69,"stroke":91,"style":1190},[73,1201,1203],{"x":1181,"y":1202,"style":1182},"163.0","no formulas affected",[62,1205,1207],{"transform":1206},"translate(38.5,151.5) scale(0.625)",[66,1208],{"d":1189,"fill":69,"stroke":91,"style":1190},[44,1210],{"x":1211,"y":87,"width":1160,"height":1161,"rx":1162,"fill":58,"stroke":59,"style":60},"389.0",[62,1213,1215],{"transform":1214},"translate(551.5,29.0) scale(1.083)",[66,1216],{"d":154,"fill":69,"stroke":70,"style":1168},[73,1218,1221],{"x":1219,"y":625,"style":1220},"564.5","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Number and date columns",[189,1223],{"x1":1224,"y1":1177,"x2":1225,"y2":1177,"stroke":59,"style":598},"405.0","724.0",[73,1227,1229],{"x":1228,"y":650,"style":1182},"429.0","leave missing values blank",[62,1231,1233],{"transform":1232},"translate(407.5,99.5) scale(0.625)",[66,1234],{"d":1189,"fill":69,"stroke":91,"style":1190},[73,1236,1237],{"x":1228,"y":1193,"style":1182},"show them with a fill rule",[62,1239,1241],{"transform":1240},"translate(407.5,125.5) scale(0.625)",[66,1242],{"d":1189,"fill":69,"stroke":91,"style":1190},[73,1244,1245],{"x":1228,"y":1202,"style":1182},"never \"n\u002Fa\" text",[62,1247,1249],{"transform":1248},"translate(407.5,151.5) scale(0.625)",[66,1250],{"d":1251,"fill":69,"stroke":122,"style":1190},"M6 6l12 12 M18 6L6 18",[73,1253,1255],{"x":1228,"y":1254,"style":1182},"189.0","never \"inf\" text",[62,1257,1259],{"transform":1258},"translate(407.5,177.5) scale(0.625)",[66,1260],{"d":1251,"fill":69,"stroke":122,"style":1190},[10,1262,1263],{},"Blank cells are easy to overlook, so a report with gaps should say how many there are. A small summary sheet next to the data does that without touching the data itself:",[177,1265,1267],{"className":207,"code":1266,"language":209,"meta":182,"style":182},"clean = frame.copy()\nclean[\"growth\"] = clean[\"growth\"].replace([np.inf, -np.inf], np.nan)\ngaps = clean.isna().sum().rename(\"missing\").to_frame()\ngaps[\"share\"] = (gaps[\"missing\"] \u002F len(clean)).round(3)\n\nwith pd.ExcelWriter(\"with_gaps.xlsx\") as writer:\n    clean.to_excel(writer, sheet_name=\"Data\", index=False)\n    gaps.to_excel(writer, sheet_name=\"Missing values\", index_label=\"column\")\nprint(gaps)\n",[184,1268,1269,1278,1300,1316,1347,1351,1370,1393,1417],{"__ignoreMap":182},[187,1270,1271,1274,1276],{"class":189,"line":190},[187,1272,1273],{"class":220},"clean ",[187,1275,270],{"class":216},[187,1277,1048],{"class":220},[187,1279,1280,1283,1285,1287,1289,1292,1294,1296,1298],{"class":189,"line":230},[187,1281,1282],{"class":220},"clean[",[187,1284,821],{"class":197},[187,1286,824],{"class":220},[187,1288,270],{"class":216},[187,1290,1291],{"class":220}," clean[",[187,1293,821],{"class":197},[187,1295,887],{"class":220},[187,1297,362],{"class":216},[187,1299,892],{"class":220},[187,1301,1302,1305,1307,1310,1313],{"class":189,"line":243},[187,1303,1304],{"class":220},"gaps ",[187,1306,270],{"class":216},[187,1308,1309],{"class":220}," clean.isna().sum().rename(",[187,1311,1312],{"class":197},"\"missing\"",[187,1314,1315],{"class":220},").to_frame()\n",[187,1317,1318,1321,1324,1326,1328,1331,1333,1335,1337,1340,1343,1345],{"class":189,"line":257},[187,1319,1320],{"class":220},"gaps[",[187,1322,1323],{"class":197},"\"share\"",[187,1325,824],{"class":220},[187,1327,270],{"class":216},[187,1329,1330],{"class":220}," (gaps[",[187,1332,1312],{"class":197},[187,1334,824],{"class":220},[187,1336,836],{"class":216},[187,1338,1339],{"class":291}," len",[187,1341,1342],{"class":220},"(clean)).round(",[187,1344,684],{"class":291},[187,1346,501],{"class":220},[187,1348,1349],{"class":189,"line":264},[187,1350,261],{"emptyLinePlaceholder":260},[187,1352,1353,1356,1359,1362,1365,1367],{"class":189,"line":276},[187,1354,1355],{"class":216},"with",[187,1357,1358],{"class":220}," pd.ExcelWriter(",[187,1360,1361],{"class":197},"\"with_gaps.xlsx\"",[187,1363,1364],{"class":220},") ",[187,1366,224],{"class":216},[187,1368,1369],{"class":220}," writer:\n",[187,1371,1372,1375,1378,1380,1383,1385,1387,1389,1391],{"class":189,"line":308},[187,1373,1374],{"class":220},"    clean.to_excel(writer, ",[187,1376,1377],{"class":336},"sheet_name",[187,1379,270],{"class":216},[187,1381,1382],{"class":197},"\"Data\"",[187,1384,288],{"class":220},[187,1386,493],{"class":336},[187,1388,270],{"class":216},[187,1390,498],{"class":291},[187,1392,501],{"class":220},[187,1394,1395,1398,1400,1402,1405,1407,1410,1412,1415],{"class":189,"line":348},[187,1396,1397],{"class":220},"    gaps.to_excel(writer, ",[187,1399,1377],{"class":336},[187,1401,270],{"class":216},[187,1403,1404],{"class":197},"\"Missing values\"",[187,1406,288],{"class":220},[187,1408,1409],{"class":336},"index_label",[187,1411,270],{"class":216},[187,1413,1414],{"class":197},"\"column\"",[187,1416,501],{"class":220},[187,1418,1419,1421],{"class":189,"line":368},[187,1420,852],{"class":291},[187,1422,1423],{"class":220},"(gaps)\n",[10,1425,1426,1427,1431],{},"Readers see at a glance that three of the four growth values are missing — one original gap and the two infinities replaced with NaN in the second line — and the count is computed from the same DataFrame that was written, so it cannot drift from the data. For a fuller treatment, ",[19,1428,1430],{"href":1429},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffind-and-report-missing-values-in-an-excel-file\u002F","Find and Report Missing Values in an Excel File"," builds a report per column and per row.",[172,1433,1435],{"id":1434},"step-5-writing-with-openpyxl-or-xlsxwriter-directly","Step 5 — Writing with openpyxl or xlsxwriter directly",[10,1437,1438],{},"pandas converts missing values before handing cells to the engine. Code that writes cells itself gets the libraries' own behaviour, which differs:",[177,1440,1442],{"className":207,"code":1441,"language":209,"meta":182,"style":182},"from openpyxl import Workbook\nimport xlsxwriter\n\nwb = Workbook()\nws = wb.active\nws[\"A1\"] = None          # no cell content — the normal empty cell\nws[\"A2\"] = float(\"nan\")  # written as a number cell with an empty value\nwb.save(\"direct_openpyxl.xlsx\")\n\nbook = xlsxwriter.Workbook(\"direct_xlsxwriter.xlsx\")\nsheet = book.add_worksheet()\ntry:\n    sheet.write_number(0, 0, float(\"nan\"))\nexcept TypeError as exc:\n    print(exc)\nbook.close()\n",[184,1443,1444,1455,1462,1466,1476,1486,1504,1530,1540,1544,1559,1569,1577,1599,1613,1621],{"__ignoreMap":182},[187,1445,1446,1448,1450,1452],{"class":189,"line":190},[187,1447,246],{"class":216},[187,1449,249],{"class":220},[187,1451,217],{"class":216},[187,1453,1454],{"class":220}," Workbook\n",[187,1456,1457,1459],{"class":189,"line":230},[187,1458,217],{"class":216},[187,1460,1461],{"class":220}," xlsxwriter\n",[187,1463,1464],{"class":189,"line":243},[187,1465,261],{"emptyLinePlaceholder":260},[187,1467,1468,1471,1473],{"class":189,"line":257},[187,1469,1470],{"class":220},"wb ",[187,1472,270],{"class":216},[187,1474,1475],{"class":220}," Workbook()\n",[187,1477,1478,1481,1483],{"class":189,"line":264},[187,1479,1480],{"class":220},"ws ",[187,1482,270],{"class":216},[187,1484,1485],{"class":220}," wb.active\n",[187,1487,1488,1491,1494,1496,1498,1501],{"class":189,"line":276},[187,1489,1490],{"class":220},"ws[",[187,1492,1493],{"class":197},"\"A1\"",[187,1495,824],{"class":220},[187,1497,270],{"class":216},[187,1499,1500],{"class":291}," None",[187,1502,1503],{"class":865},"          # no cell content — the normal empty cell\n",[187,1505,1506,1508,1511,1513,1515,1518,1521,1524,1527],{"class":189,"line":308},[187,1507,1490],{"class":220},[187,1509,1510],{"class":197},"\"A2\"",[187,1512,824],{"class":220},[187,1514,270],{"class":216},[187,1516,1517],{"class":291}," float",[187,1519,1520],{"class":220},"(",[187,1522,1523],{"class":197},"\"nan\"",[187,1525,1526],{"class":220},")  ",[187,1528,1529],{"class":865},"# written as a number cell with an empty value\n",[187,1531,1532,1535,1538],{"class":189,"line":348},[187,1533,1534],{"class":220},"wb.save(",[187,1536,1537],{"class":197},"\"direct_openpyxl.xlsx\"",[187,1539,501],{"class":220},[187,1541,1542],{"class":189,"line":368},[187,1543,261],{"emptyLinePlaceholder":260},[187,1545,1546,1549,1551,1554,1557],{"class":189,"line":397},[187,1547,1548],{"class":220},"book ",[187,1550,270],{"class":216},[187,1552,1553],{"class":220}," xlsxwriter.Workbook(",[187,1555,1556],{"class":197},"\"direct_xlsxwriter.xlsx\"",[187,1558,501],{"class":220},[187,1560,1561,1564,1566],{"class":189,"line":403},[187,1562,1563],{"class":220},"sheet ",[187,1565,270],{"class":216},[187,1567,1568],{"class":220}," book.add_worksheet()\n",[187,1570,1571,1574],{"class":189,"line":408},[187,1572,1573],{"class":216},"try",[187,1575,1576],{"class":220},":\n",[187,1578,1579,1582,1584,1586,1588,1590,1593,1595,1597],{"class":189,"line":421},[187,1580,1581],{"class":220},"    sheet.write_number(",[187,1583,47],{"class":291},[187,1585,288],{"class":220},[187,1587,47],{"class":291},[187,1589,288],{"class":220},[187,1591,1592],{"class":291},"float",[187,1594,1520],{"class":220},[187,1596,1523],{"class":197},[187,1598,922],{"class":220},[187,1600,1601,1604,1607,1610],{"class":189,"line":432},[187,1602,1603],{"class":216},"except",[187,1605,1606],{"class":291}," TypeError",[187,1608,1609],{"class":216}," as",[187,1611,1612],{"class":220}," exc:\n",[187,1614,1616,1618],{"class":189,"line":1615},15,[187,1617,522],{"class":291},[187,1619,1620],{"class":220},"(exc)\n",[187,1622,1624],{"class":189,"line":1623},16,[187,1625,1626],{"class":220},"book.close()\n",[10,1628,1629,1630,1632,1633,1636,1637,1640,1641,1644,1645,1648,1649,1651,1652,1654],{},"openpyxl accepts ",[184,1631,538],{}," and writes a number cell with no value in it, which is not the same as an ordinary empty cell. xlsxwriter refuses with a ",[184,1634,1635],{},"TypeError"," unless the workbook is created with ",[184,1638,1639],{},"{\"nan_inf_to_errors\": True}",", in which case it writes Excel error values — ",[184,1642,1643],{},"#NUM!"," for NaN and ",[184,1646,1647],{},"#DIV\u002F0!"," for infinity. Error values are honest but they propagate through every formula that touches them. In both libraries, the clean approach is the same as in pandas: convert ",[184,1650,538],{}," to ",[184,1653,292],{}," before writing, and decide explicitly what infinity should become.",[172,1656,1658],{"id":1657},"step-6-read-the-file-back-with-the-right-types","Step 6 — Read the file back with the right types",[10,1660,1661,1662,1664],{},"Blank cells come back as ",[184,1663,538],{},", which changes some types on the way in:",[177,1666,1668],{"className":207,"code":1667,"language":209,"meta":182,"style":182},"back = pd.read_excel(\"per_column.xlsx\")\nprint(back.dtypes.to_dict())\n\nback = pd.read_excel(\"per_column.xlsx\", dtype={\"units\": \"Int64\"})\nprint(back.dtypes.to_dict())\n",[184,1669,1670,1684,1691,1695,1724],{"__ignoreMap":182},[187,1671,1672,1675,1677,1680,1682],{"class":189,"line":190},[187,1673,1674],{"class":220},"back ",[187,1676,270],{"class":216},[187,1678,1679],{"class":220}," pd.read_excel(",[187,1681,1100],{"class":197},[187,1683,501],{"class":220},[187,1685,1686,1688],{"class":189,"line":230},[187,1687,852],{"class":291},[187,1689,1690],{"class":220},"(back.dtypes.to_dict())\n",[187,1692,1693],{"class":189,"line":243},[187,1694,261],{"emptyLinePlaceholder":260},[187,1696,1697,1699,1701,1703,1705,1707,1709,1711,1714,1717,1720,1722],{"class":189,"line":257},[187,1698,1674],{"class":220},[187,1700,270],{"class":216},[187,1702,1679],{"class":220},[187,1704,1100],{"class":197},[187,1706,288],{"class":220},[187,1708,337],{"class":336},[187,1710,270],{"class":216},[187,1712,1713],{"class":220},"{",[187,1715,1716],{"class":197},"\"units\"",[187,1718,1719],{"class":220},": ",[187,1721,342],{"class":197},[187,1723,400],{"class":220},[187,1725,1726,1728],{"class":189,"line":264},[187,1727,852],{"class":291},[187,1729,1690],{"class":220},[10,1731,1732,1733,1735,1736,1739,1740,1743,1744,1746,1747,1749,1750,1752],{},"The ",[184,1734,1023],{}," column was ",[184,1737,1738],{},"Int64"," when written and comes back as ",[184,1741,1742],{},"float64",", because a NumPy integer column cannot hold a missing value. Asking for ",[184,1745,342],{}," in ",[184,1748,337],{}," restores the nullable integer. ",[184,1751,563],{}," text, if any is left in a file, is converted back to a float infinity by pandas' type inference — so a round trip hides the problem that Excel users see.",[172,1754,1756],{"id":1755},"common-pitfalls","Common pitfalls",[1758,1759,1760,1770,1780,1789],"ul",{},[1761,1762,1763,1769],"li",{},[1764,1765,1766,1768],"strong",{},[184,1767,956],{}," on a whole report."," It turns every numeric column with a gap into mixed text. Fill per column.",[1761,1771,1772,1779],{},[1764,1773,1774,1775,1778],{},"Leaving ",[184,1776,1777],{},"inf"," in."," It becomes text that totals ignore silently and arithmetic rejects.",[1761,1781,1782,1788],{},[1764,1783,1784,1785,1787],{},"Writing ",[184,1786,47],{}," for missing."," It changes averages and lies about the data. Blank is the neutral choice.",[1761,1790,1791,560,1794,1796,1797,1799],{},[1764,1792,1793],{},"Forgetting dates.",[184,1795,542],{}," becomes a blank, which is right; ",[184,1798,956],{}," would turn it into text in a date column.",[172,1801,1803],{"id":1802},"performance-notes","Performance notes",[10,1805,1806,550,1809,1812],{},[184,1807,1808],{},"replace([np.inf, -np.inf], np.nan)",[184,1810,1811],{},"fillna"," are vectorised and cheap even on millions of rows. The costly mistake is doing replacements cell by cell with openpyxl after writing; clean the DataFrame first and let pandas write the final values in one pass.",[172,1814,1816],{"id":1815},"conclusion","Conclusion",[10,1818,1819,1820,288,1822,288,1824,550,1827,1829,1830,1832,1833,1835,1836,1838],{},"pandas writes ",[184,1821,538],{},[184,1823,292],{},[184,1825,1826],{},"pd.NA",[184,1828,542],{}," as empty cells, which Excel's aggregates skip just as pandas does, and infinity as the text ",[184,1831,563],{},", which totals ignore and arithmetic rejects. Replace infinities with ",[184,1834,538],{}," (or a meaningful value) before writing, fill text placeholders per column rather than with ",[184,1837,956],{},", show missing numbers with formatting instead of text, and read files back with nullable dtypes so the round trip keeps its types.",[172,1840,1842],{"id":1841},"frequently-asked-questions","Frequently asked questions",[10,1844,1845,1848],{},[1764,1846,1847],{},"What does to_excel write for NaN?","\nAn empty cell, by default. The same happens for None, pd.NA and NaT, in every column type. Empty cells are ignored by Excel's SUM, AVERAGE and COUNT, which matches how pandas skips missing values.",[10,1850,1851,1854],{},[1764,1852,1853],{},"Why do I see \"inf\" in my spreadsheet?","\nInfinity has no Excel equivalent, so pandas writes it as text using inf_rep, which defaults to \"inf\". It usually comes from dividing by zero. Replace infinities with NaN, or with a value that means something to readers, before writing.",[10,1856,1857,1860],{},[1764,1858,1859],{},"Is na_rep a good way to show \"n\u002Fa\" for missing values?","\nOnly for text columns. na_rep applies to every missing value in every column, so numeric and date columns end up mixing numbers with text, which breaks arithmetic in Excel. Fill placeholders per column instead.",[10,1862,1863,1866],{},[1764,1864,1865],{},"Why did my integer column come back as floats?","\nA blank cell reads back as NaN, and a NumPy integer column cannot hold NaN, so pandas uses float64. Pass dtype={\"col\": \"Int64\"} to read_excel to get the nullable integer type back.",[172,1868,1870],{"id":1869},"related","Related",[1758,1872,1873,1879,1886,1893],{},[1761,1874,1875,1876,1878],{},"Up one level: ",[19,1877,22],{"href":21}," — every to_excel option.",[1761,1880,1881,1885],{},[19,1882,1884],{"href":1883},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002F","Handling Missing Data in Excel Reports with Pandas"," — finding and filling gaps before they reach the report.",[1761,1887,1888,1892],{},[19,1889,1891],{"href":1890},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffill-missing-values-in-excel-with-pandas-fillna\u002F","Fill Missing Values in Excel with Pandas fillna"," — choosing fill values.",[1761,1894,1895,1899],{},[19,1896,1898],{"href":1897},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-dates-without-time-from-pandas-to-excel\u002F","Write Dates Without the Time Part from pandas to Excel"," — the other common surprise in date columns.",[1901,1902,1903],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":182,"searchDepth":230,"depth":230,"links":1905},[1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917],{"id":174,"depth":230,"text":175},{"id":474,"depth":230,"text":475},{"id":570,"depth":230,"text":571},{"id":737,"depth":230,"text":738},{"id":950,"depth":230,"text":951},{"id":1434,"depth":230,"text":1435},{"id":1657,"depth":230,"text":1658},{"id":1755,"depth":230,"text":1756},{"id":1802,"depth":230,"text":1803},{"id":1815,"depth":230,"text":1816},{"id":1841,"depth":230,"text":1842},{"id":1869,"depth":230,"text":1870},"2026-09-25","pandas writes NaN, None, pd.NA and NaT as empty cells and infinity as the text \"inf\". Learn what each choice does to Excel formulas, when na_rep and inf_rep help or hurt, and how to handle division-by-zero results and placeholders column by column.","md",[1922,1924,1926,1928],{"q":1847,"a":1923},"An empty cell, by default. The same happens for None, pd.NA and NaT, in every column type. Empty cells are ignored by Excel's SUM, AVERAGE and COUNT, which matches how pandas skips missing values.",{"q":1853,"a":1925},"Infinity has no Excel equivalent, so pandas writes it as text using inf_rep, which defaults to \"inf\". It usually comes from dividing by zero. Replace infinities with NaN, or with a value that means something to readers, before writing.",{"q":1859,"a":1927},"Only for text columns. na_rep applies to every missing value in every column, so numeric and date columns end up mixing numbers with text, which breaks arithmetic in Excel. Fill placeholders per column instead.",{"q":1865,"a":1929},"A blank cell reads back as NaN, and a NumPy integer column cannot hold NaN, so pandas uses float64. Pass dtype={\"col\": \"Int64\"} to read_excel to get the nullable integer type back.","control how nan none and infinity appear in excel output",null,{"breadcrumb":1933},[1934,1936,1939],{"name":1935,"item":836},"Home",{"name":1937,"item":1938},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":22,"item":21},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output",{"title":1942,"description":1943},"pandas to_excel NaN, None and inf: na_rep, inf_rep and Blank Cells","What pandas writes to Excel for NaN, None, pd.NA, NaT and inf, how na_rep and inf_rep change it, why \"inf\" text breaks formulas, and how to write clean blanks and per-column placeholders.","control-how-nan-and-infinity-appear-in-excel-output","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output\u002Findex",[1947,1948,1949],"writing-excel","missing-data","pandas","how-to","kdz10i0ve541x18R-A5xfXi_2cdhgz9KsRdFmBH5RYM",[1953,1957],{"title":1954,"path":1955,"stem":1956,"children":-1},"Auto-Fit Column Widths When Writing with pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fauto-fit-column-widths-when-writing-with-pandas","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fauto-fit-column-widths-when-writing-with-pandas\u002Findex",{"title":1958,"path":1959,"stem":1960,"children":-1},"Export a Styled pandas DataFrame to Excel","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fexport-a-styled-pandas-dataframe-to-excel","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fexport-a-styled-pandas-dataframe-to-excel\u002Findex",[1962,1964],{"path":1963,"title":1937},"\u002Fgetting-started-with-python-excel-automation",{"path":1965,"title":22},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas",[],[1968,1971,1974,1977,1980,1983],{"path":1969,"title":1970,"kind":1950},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file","Write Multiple DataFrames to One Excel File",{"path":1972,"title":1973,"kind":1950},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter","Write a Formatted Excel Report with xlsxwriter",{"path":1975,"title":1976,"kind":1950},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl","Find and Replace Text in Excel with openpyxl",{"path":1978,"title":1979,"kind":1950},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Finsert-and-delete-rows-and-columns-with-openpyxl","Insert and Delete Rows and Columns with openpyxl",{"path":1981,"title":1982,"kind":1950},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fopenpyxl-append-data-to-existing-excel-sheet","openpyxl: Append Data to an Existing Excel Sheet",{"path":1984,"title":1985,"kind":1950},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Freorder-columns-to-match-a-template-layout-with-pandas","Reorder Columns to Match a Template Layout with pandas",1790972076407]