[{"data":1,"prerenderedAt":2165},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data":2133,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data":2141,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data":2146,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data":2147},{"id":4,"title":5,"body":6,"dateModified":2097,"datePublished":2097,"description":2098,"extension":2099,"faq":2100,"keyword":2109,"kind":2110,"meta":2111,"navigation":154,"path":2121,"seo":2122,"slug":2125,"stem":2126,"topics":2127,"type":2131,"__hash__":2132},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data\u002Findex.md","Tell Blank Cells, Empty Strings and Zeros Apart in Excel Data",{"type":7,"value":8,"toc":2084},"minimark",[9,32,37,65,122,126,224,232,259,523,527,536,601,638,642,645,1175,1181,1201,1400,1404,1407,1635,1667,1733,1737,1754,1883,1890,1894,1973,1977,1991,1995,2015,2019,2029,2035,2041,2047,2051,2080],[10,11,12,13,17,18,21,22,25,26,31],"p",{},"In a stock-count sheet, a blank in the \"Counted\" column means nobody counted that shelf, and a zero means somebody counted it and found nothing. Those are opposite facts, and a report that confuses them either orders stock that is already there or misses a stock-out. The trouble is that \"looks blank\" covers several different cell contents in Excel — a truly empty cell, a formula that returns ",[14,15,16],"code",{},"\"\"",", a cell holding a single space, a zero hidden by a custom number format — and ",[14,19,20],{},"pd.read_excel"," folds most of them into the same ",[14,23,24],{},"NaN",". This page shows what each case really contains, what pandas and openpyxl return for it, and how to classify cells so the distinction survives into your report. It belongs to ",[27,28,30],"a",{"href":29},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002F","Handling Missing Data in Excel Reports",".",[33,34,36],"h2",{"id":35},"prerequisites","Prerequisites",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,45,46],{"__ignoreMap":43},[47,48,51,55,59,62],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"sMTad","pip",[47,56,58],{"class":57},"srMev"," install",[47,60,61],{"class":57}," pandas",[47,63,64],{"class":57}," openpyxl\n",[10,66,67,70,71,74,75,78,79,82,83,86,87,90,91,94,95,98,99,102,103,106,107,102,110,113,114,117,118,121],{},[14,68,69],{},"stock_count.xlsx"," has one row per item with a ",[14,72,73],{},"Counted"," column. In Excel, four of the nine counts look empty and two more show a placeholder. Behind them: ",[14,76,77],{},"Bolts M8"," is truly empty; ",[14,80,81],{},"Washers"," holds a formula that returns an empty string (the kind a failed lookup wrapped in ",[14,84,85],{},"IFERROR(…, \"\")"," produces); ",[14,88,89],{},"Nuts M6"," holds a single space; ",[14,92,93],{},"Brackets"," holds a real zero formatted with ",[14,96,97],{},"0;-0;;@",", whose empty third section hides zeros; and ",[14,100,101],{},"Hinges"," and ",[14,104,105],{},"Clamps"," hold the placeholders ",[14,108,109],{},"-",[14,111,112],{},"N\u002FA",". ",[14,115,116],{},"Nuts M8"," is a genuine visible zero and ",[14,119,120],{},"Rivets"," a zero typed as text.",[33,123,125],{"id":124},"step-1-see-what-the-default-read-returns","Step 1 — See what the default read returns",[38,127,131],{"className":128,"code":129,"language":130,"meta":43,"style":43},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndf = pd.read_excel(\"stock_count.xlsx\")\ndf[\"repr\"] = df[\"Counted\"].map(repr)\nprint(df.to_string(index=False))\n","python",[14,132,133,149,156,174,203],{"__ignoreMap":43},[47,134,135,139,143,146],{"class":49,"line":50},[47,136,138],{"class":137},"s-kum","import",[47,140,142],{"class":141},"skGVy"," pandas ",[47,144,145],{"class":137},"as",[47,147,148],{"class":141}," pd\n",[47,150,152],{"class":49,"line":151},2,[47,153,155],{"emptyLinePlaceholder":154},true,"\n",[47,157,159,162,165,168,171],{"class":49,"line":158},3,[47,160,161],{"class":141},"df ",[47,163,164],{"class":137},"=",[47,166,167],{"class":141}," pd.read_excel(",[47,169,170],{"class":57},"\"stock_count.xlsx\"",[47,172,173],{"class":141},")\n",[47,175,177,180,183,186,188,191,194,197,201],{"class":49,"line":176},4,[47,178,179],{"class":141},"df[",[47,181,182],{"class":57},"\"repr\"",[47,184,185],{"class":141},"] ",[47,187,164],{"class":137},[47,189,190],{"class":141}," df[",[47,192,193],{"class":57},"\"Counted\"",[47,195,196],{"class":141},"].map(",[47,198,200],{"class":199},"sP0c6","repr",[47,202,173],{"class":141},[47,204,206,209,212,216,218,221],{"class":49,"line":205},5,[47,207,208],{"class":199},"print",[47,210,211],{"class":141},"(df.to_string(",[47,213,215],{"class":214},"sa561","index",[47,217,164],{"class":137},[47,219,220],{"class":199},"False",[47,222,223],{"class":141},"))\n",[38,225,230],{"className":226,"code":228,"language":229,"meta":43},[227],"language-text","    Item Counted repr\nBolts M6      40   40\nBolts M8     NaN  nan\n Washers     NaN  nan\n Nuts M6          ' '\n Nuts M8       0    0\n  Rivets       0  '0'\nBrackets       0    0\n  Hinges       -  '-'\n  Clamps     NaN  nan\n","text",[14,231,228],{"__ignoreMap":43},[10,233,234,235,113,237,239,240,242,243,247,248,251,252,254,255,258],{},"Three different situations — never counted, a lookup that found nothing, and an explicit \"not applicable\" — all became ",[14,236,24],{},[14,238,112],{}," is on pandas' default list of missing-value markers, so it disappears without trace. Meanwhile the space survived as a one-character string, ",[14,241,120],{}," is the ",[244,245,246],"em",{},"string"," ",[14,249,250],{},"'0'"," sitting in the same column as the numbers, and the hidden zero on ",[14,253,93],{}," is an ordinary ",[14,256,257],{},"0",", because number formats only change what Excel displays.",[260,261,269,270,269,274,269,278,269,285,269,294,269,298,269,304,269,308,269,312,269,316,269,320,269,324,269,329,269,334,269,338,269,340,269,342,269,344,269,347,269,350,269,354,269,356,269,360,269,362,269,365,269,367,269,370,269,373,269,377,269,379,269,381,269,385,269,387,269,390,269,393,269,397,269,399,269,401,269,403,269,405,269,408,269,411,269,415,269,417,269,419,269,421,269,423,269,426,269,429,269,433,269,435,269,437,269,439,269,441,269,443,269,446,269,449,269,453,269,455,269,457,269,459,269,461,269,463,269,466,269,469,269,473,269,475,269,477,269,479,269,481,269,484,269,487,269,490,269,492,269,494,269,496,269,498,269,500,269,503,269,506,269,510,269,512,269,514,269,516,269,518,269,520],"svg",{"viewBox":262,"role":263,"ariaLabelledBy":264,"xmlns":267,"style":268},"160 0 439 336","img",[265,266],"bz-sheet-t","bz-sheet-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:439px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[271,272,273],"title",{"id":265},"Nine cells, four of which look empty in Excel",[275,276,277],"desc",{"id":266},"The stock count sheet as Excel displays it. Rows for Bolts M8, Washers, Nuts M6 and Brackets look empty, and Hinges and Clamps show a dash and N\u002FA. Notes name what each cell really holds.",[279,280],"rect",{"x":281,"y":257,"width":282,"height":283,"fill":284},"160","439","336","#ffffff",[279,286],{"x":287,"y":288,"width":289,"height":290,"rx":257,"fill":291,"stroke":292,"style":293},"176.6","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[279,295],{"x":296,"y":288,"width":297,"height":290,"rx":257,"fill":291,"stroke":292,"style":293},"208.6","74.4",[229,299,303],{"x":300,"y":301,"style":302},"245.8","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[279,305],{"x":306,"y":288,"width":307,"height":290,"rx":257,"fill":291,"stroke":292,"style":293},"283.0","72.4",[229,309,311],{"x":310,"y":301,"style":302},"319.2","B",[279,313],{"x":314,"y":288,"width":315,"height":290,"rx":257,"fill":291,"stroke":292,"style":293},"355.4","227.9",[229,317,319],{"x":318,"y":301,"style":302},"469.4","C",[279,321],{"x":287,"y":322,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"40.0","28.0",[229,325,328],{"x":326,"y":327,"style":302},"192.6","59.0","1",[279,330],{"x":296,"y":322,"width":297,"height":323,"rx":257,"fill":331,"stroke":332,"style":333},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[229,335,337],{"x":300,"y":327,"style":336},"font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Item",[279,339],{"x":306,"y":322,"width":307,"height":323,"rx":257,"fill":331,"stroke":332,"style":333},[229,341,73],{"x":310,"y":327,"style":336},[279,343],{"x":314,"y":322,"width":315,"height":323,"rx":257,"fill":331,"stroke":332,"style":333},[229,345,346],{"x":318,"y":327,"style":336},"Really holds",[279,348],{"x":287,"y":349,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"68.0",[229,351,353],{"x":326,"y":352,"style":302},"87.0","2",[279,355],{"x":296,"y":349,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,357,359],{"x":300,"y":352,"style":358},"font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Bolts M6",[279,361],{"x":306,"y":349,"width":307,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,363,364],{"x":310,"y":352,"style":358},"40",[279,366],{"x":314,"y":349,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,368,369],{"x":318,"y":352,"style":358},"number 40",[279,371],{"x":287,"y":372,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"96.0",[229,374,376],{"x":326,"y":375,"style":302},"115.0","3",[279,378],{"x":296,"y":372,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,380,77],{"x":300,"y":375,"style":358},[279,382],{"x":306,"y":372,"width":307,"height":323,"rx":257,"fill":383,"stroke":384,"style":333},"#fdefd8","var(--gold,#b4740a)",[279,386],{"x":314,"y":372,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,388,389],{"x":318,"y":375,"style":358},"nothing",[279,391],{"x":287,"y":392,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"124.0",[229,394,396],{"x":326,"y":395,"style":302},"143.0","4",[279,398],{"x":296,"y":392,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,400,81],{"x":300,"y":395,"style":358},[279,402],{"x":306,"y":392,"width":307,"height":323,"rx":257,"fill":383,"stroke":384,"style":333},[279,404],{"x":314,"y":392,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,406,407],{"x":318,"y":395,"style":358},"formula returning \"\"",[279,409],{"x":287,"y":410,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"152.0",[229,412,414],{"x":326,"y":413,"style":302},"171.0","5",[279,416],{"x":296,"y":410,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,418,89],{"x":300,"y":413,"style":358},[279,420],{"x":306,"y":410,"width":307,"height":323,"rx":257,"fill":383,"stroke":384,"style":333},[279,422],{"x":314,"y":410,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,424,425],{"x":318,"y":413,"style":358},"one space",[279,427],{"x":287,"y":428,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"180.0",[229,430,432],{"x":326,"y":431,"style":302},"199.0","6",[279,434],{"x":296,"y":428,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,436,116],{"x":300,"y":431,"style":358},[279,438],{"x":306,"y":428,"width":307,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,440,257],{"x":310,"y":431,"style":358},[279,442],{"x":314,"y":428,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,444,445],{"x":318,"y":431,"style":358},"number 0",[279,447],{"x":287,"y":448,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"208.0",[229,450,452],{"x":326,"y":451,"style":302},"227.0","7",[279,454],{"x":296,"y":448,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,456,120],{"x":300,"y":451,"style":358},[279,458],{"x":306,"y":448,"width":307,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,460,257],{"x":310,"y":451,"style":358},[279,462],{"x":314,"y":448,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,464,465],{"x":318,"y":451,"style":358},"text \"0\"",[279,467],{"x":287,"y":468,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"236.0",[229,470,472],{"x":326,"y":471,"style":302},"255.0","8",[279,474],{"x":296,"y":468,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,476,93],{"x":300,"y":471,"style":358},[279,478],{"x":306,"y":468,"width":307,"height":323,"rx":257,"fill":383,"stroke":384,"style":333},[279,480],{"x":314,"y":468,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,482,483],{"x":318,"y":471,"style":358},"number 0 with a format hiding zeros",[279,485],{"x":287,"y":486,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"264.0",[229,488,489],{"x":326,"y":306,"style":302},"9",[279,491],{"x":296,"y":486,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,493,101],{"x":300,"y":306,"style":358},[279,495],{"x":306,"y":486,"width":307,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,497,109],{"x":310,"y":306,"style":358},[279,499],{"x":314,"y":486,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,501,502],{"x":318,"y":306,"style":358},"text \"-\"",[279,504],{"x":287,"y":505,"width":289,"height":323,"rx":257,"fill":291,"stroke":292,"style":293},"292.0",[229,507,509],{"x":326,"y":508,"style":302},"311.0","10",[279,511],{"x":296,"y":505,"width":297,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,513,105],{"x":300,"y":508,"style":358},[279,515],{"x":306,"y":505,"width":307,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,517,112],{"x":310,"y":508,"style":358},[279,519],{"x":314,"y":505,"width":315,"height":323,"rx":257,"fill":284,"stroke":292,"style":293},[229,521,522],{"x":318,"y":508,"style":358},"text \"N\u002FA\"",[33,524,526],{"id":525},"step-2-turn-off-the-automatic-na-conversion","Step 2 — Turn off the automatic NA conversion",[10,528,529,532,533,535],{},[14,530,531],{},"keep_default_na=False"," stops pandas from replacing marker strings and blanks with ",[14,534,24],{},", so every cell comes through as what it is:",[38,537,539],{"className":128,"code":538,"language":130,"meta":43,"style":43},"raw = pd.read_excel(\"stock_count.xlsx\", keep_default_na=False)\nraw[\"repr\"] = raw[\"Counted\"].map(repr)\nprint(raw.to_string(index=False))\n",[14,540,541,564,586],{"__ignoreMap":43},[47,542,543,546,548,550,552,555,558,560,562],{"class":49,"line":50},[47,544,545],{"class":141},"raw ",[47,547,164],{"class":137},[47,549,167],{"class":141},[47,551,170],{"class":57},[47,553,554],{"class":141},", ",[47,556,557],{"class":214},"keep_default_na",[47,559,164],{"class":137},[47,561,220],{"class":199},[47,563,173],{"class":141},[47,565,566,569,571,573,575,578,580,582,584],{"class":49,"line":151},[47,567,568],{"class":141},"raw[",[47,570,182],{"class":57},[47,572,185],{"class":141},[47,574,164],{"class":137},[47,576,577],{"class":141}," raw[",[47,579,193],{"class":57},[47,581,196],{"class":141},[47,583,200],{"class":199},[47,585,173],{"class":141},[47,587,588,590,593,595,597,599],{"class":49,"line":158},[47,589,208],{"class":199},[47,591,592],{"class":141},"(raw.to_string(",[47,594,215],{"class":214},[47,596,164],{"class":137},[47,598,220],{"class":199},[47,600,223],{"class":141},[10,602,603,604,102,606,608,609,612,613,615,616,618,619,247,622,624,625,628,629,631,632,634,635,637],{},"Now ",[14,605,112],{},[14,607,109],{}," are visible as text, and the two empty-looking cells come back as ",[14,610,611],{},"''",". That is progress, but notice that the truly empty ",[14,614,77],{}," and the formula-returning-empty ",[14,617,81],{}," are ",[244,620,621],{},"both",[14,623,611],{}," — pandas cannot tell them apart, because neither has a value to read. Do not combine this with ",[14,626,627],{},"dtype=str"," while investigating: that turns the numeric zeros into ",[14,630,250],{}," too, and the difference between ",[14,633,116],{}," (a number) and ",[14,636,120],{}," (text) is lost.",[33,639,641],{"id":640},"step-3-ask-openpyxl-what-each-cell-holds","Step 3 — Ask openpyxl what each cell holds",[10,643,644],{},"openpyxl exposes the information pandas discards: whether a cell has a formula, its stored data type, and its number format. Load the workbook twice — once for formulas, once for the cached results — and classify each cell:",[38,646,648],{"className":128,"code":647,"language":130,"meta":43,"style":43},"from openpyxl import load_workbook\n\nformulas = load_workbook(\"stock_count.xlsx\")[\"Count\"]\nvalues = load_workbook(\"stock_count.xlsx\", data_only=True)[\"Count\"]\nPLACEHOLDERS = {\"-\", \"n\u002Fa\", \"na\", \"none\", \"null\", \"tbc\"}\n\ndef classify(f_cell, v_cell):\n    v = v_cell.value\n    if isinstance(f_cell.value, str) and f_cell.value.startswith(\"=\"):\n        return \"formula, empty result\" if v in (None, \"\") else \"formula\"\n    if v is None:\n        return \"empty\"\n    if isinstance(v, str):\n        if v.strip() == \"\":\n            return \"whitespace only\"\n        if v.strip().lower() in PLACEHOLDERS:\n            return \"placeholder text\"\n        if v.strip().lstrip(\"-\").replace(\".\", \"\", 1).isdigit():\n            return \"number stored as text\"\n        return \"text\"\n    if v == 0 and v_cell.number_format.split(\";\")[2:3] == [\"\"]:\n        return \"zero, hidden by format\"\n    return \"zero\" if v == 0 else \"number\"\n\nreport = [(f[0].value, classify(f[1], v[1]))\n          for f, v in zip(formulas.iter_rows(min_row=2), values.iter_rows(min_row=2))]\nfor item, kind in report:\n    print(f\"{item:10} {kind}\")\n",[14,649,650,663,667,688,715,757,762,775,786,816,852,868,876,890,907,916,931,939,966,974,982,1024,1032,1055,1060,1086,1122,1136],{"__ignoreMap":43},[47,651,652,655,658,660],{"class":49,"line":50},[47,653,654],{"class":137},"from",[47,656,657],{"class":141}," openpyxl ",[47,659,138],{"class":137},[47,661,662],{"class":141}," load_workbook\n",[47,664,665],{"class":49,"line":151},[47,666,155],{"emptyLinePlaceholder":154},[47,668,669,672,674,677,679,682,685],{"class":49,"line":158},[47,670,671],{"class":141},"formulas ",[47,673,164],{"class":137},[47,675,676],{"class":141}," load_workbook(",[47,678,170],{"class":57},[47,680,681],{"class":141},")[",[47,683,684],{"class":57},"\"Count\"",[47,686,687],{"class":141},"]\n",[47,689,690,693,695,697,699,701,704,706,709,711,713],{"class":49,"line":176},[47,691,692],{"class":141},"values ",[47,694,164],{"class":137},[47,696,676],{"class":141},[47,698,170],{"class":57},[47,700,554],{"class":141},[47,702,703],{"class":214},"data_only",[47,705,164],{"class":137},[47,707,708],{"class":199},"True",[47,710,681],{"class":141},[47,712,684],{"class":57},[47,714,687],{"class":141},[47,716,717,720,723,726,729,731,734,736,739,741,744,746,749,751,754],{"class":49,"line":205},[47,718,719],{"class":199},"PLACEHOLDERS",[47,721,722],{"class":137}," =",[47,724,725],{"class":141}," {",[47,727,728],{"class":57},"\"-\"",[47,730,554],{"class":141},[47,732,733],{"class":57},"\"n\u002Fa\"",[47,735,554],{"class":141},[47,737,738],{"class":57},"\"na\"",[47,740,554],{"class":141},[47,742,743],{"class":57},"\"none\"",[47,745,554],{"class":141},[47,747,748],{"class":57},"\"null\"",[47,750,554],{"class":141},[47,752,753],{"class":57},"\"tbc\"",[47,755,756],{"class":141},"}\n",[47,758,760],{"class":49,"line":759},6,[47,761,155],{"emptyLinePlaceholder":154},[47,763,765,768,772],{"class":49,"line":764},7,[47,766,767],{"class":137},"def",[47,769,771],{"class":770},"s_Opv"," classify",[47,773,774],{"class":141},"(f_cell, v_cell):\n",[47,776,778,781,783],{"class":49,"line":777},8,[47,779,780],{"class":141},"    v ",[47,782,164],{"class":137},[47,784,785],{"class":141}," v_cell.value\n",[47,787,789,792,795,798,801,804,807,810,813],{"class":49,"line":788},9,[47,790,791],{"class":137},"    if",[47,793,794],{"class":199}," isinstance",[47,796,797],{"class":141},"(f_cell.value, ",[47,799,800],{"class":199},"str",[47,802,803],{"class":141},") ",[47,805,806],{"class":137},"and",[47,808,809],{"class":141}," f_cell.value.startswith(",[47,811,812],{"class":57},"\"=\"",[47,814,815],{"class":141},"):\n",[47,817,819,822,825,828,831,834,837,840,842,844,846,849],{"class":49,"line":818},10,[47,820,821],{"class":137},"        return",[47,823,824],{"class":57}," \"formula, empty result\"",[47,826,827],{"class":137}," if",[47,829,830],{"class":141}," v ",[47,832,833],{"class":137},"in",[47,835,836],{"class":141}," (",[47,838,839],{"class":199},"None",[47,841,554],{"class":141},[47,843,16],{"class":57},[47,845,803],{"class":141},[47,847,848],{"class":137},"else",[47,850,851],{"class":57}," \"formula\"\n",[47,853,855,857,859,862,865],{"class":49,"line":854},11,[47,856,791],{"class":137},[47,858,830],{"class":141},[47,860,861],{"class":137},"is",[47,863,864],{"class":199}," None",[47,866,867],{"class":141},":\n",[47,869,871,873],{"class":49,"line":870},12,[47,872,821],{"class":137},[47,874,875],{"class":57}," \"empty\"\n",[47,877,879,881,883,886,888],{"class":49,"line":878},13,[47,880,791],{"class":137},[47,882,794],{"class":199},[47,884,885],{"class":141},"(v, ",[47,887,800],{"class":199},[47,889,815],{"class":141},[47,891,893,896,899,902,905],{"class":49,"line":892},14,[47,894,895],{"class":137},"        if",[47,897,898],{"class":141}," v.strip() ",[47,900,901],{"class":137},"==",[47,903,904],{"class":57}," \"\"",[47,906,867],{"class":141},[47,908,910,913],{"class":49,"line":909},15,[47,911,912],{"class":137},"            return",[47,914,915],{"class":57}," \"whitespace only\"\n",[47,917,919,921,924,926,929],{"class":49,"line":918},16,[47,920,895],{"class":137},[47,922,923],{"class":141}," v.strip().lower() ",[47,925,833],{"class":137},[47,927,928],{"class":199}," PLACEHOLDERS",[47,930,867],{"class":141},[47,932,934,936],{"class":49,"line":933},17,[47,935,912],{"class":137},[47,937,938],{"class":57}," \"placeholder text\"\n",[47,940,942,944,947,949,952,955,957,959,961,963],{"class":49,"line":941},18,[47,943,895],{"class":137},[47,945,946],{"class":141}," v.strip().lstrip(",[47,948,728],{"class":57},[47,950,951],{"class":141},").replace(",[47,953,954],{"class":57},"\".\"",[47,956,554],{"class":141},[47,958,16],{"class":57},[47,960,554],{"class":141},[47,962,328],{"class":199},[47,964,965],{"class":141},").isdigit():\n",[47,967,969,971],{"class":49,"line":968},19,[47,970,912],{"class":137},[47,972,973],{"class":57}," \"number stored as text\"\n",[47,975,977,979],{"class":49,"line":976},20,[47,978,821],{"class":137},[47,980,981],{"class":57}," \"text\"\n",[47,983,985,987,989,991,994,997,1000,1003,1005,1007,1010,1012,1014,1016,1019,1021],{"class":49,"line":984},21,[47,986,791],{"class":137},[47,988,830],{"class":141},[47,990,901],{"class":137},[47,992,993],{"class":199}," 0",[47,995,996],{"class":137}," and",[47,998,999],{"class":141}," v_cell.number_format.split(",[47,1001,1002],{"class":57},"\";\"",[47,1004,681],{"class":141},[47,1006,353],{"class":199},[47,1008,1009],{"class":141},":",[47,1011,376],{"class":199},[47,1013,185],{"class":141},[47,1015,901],{"class":137},[47,1017,1018],{"class":141}," [",[47,1020,16],{"class":57},[47,1022,1023],{"class":141},"]:\n",[47,1025,1027,1029],{"class":49,"line":1026},22,[47,1028,821],{"class":137},[47,1030,1031],{"class":57}," \"zero, hidden by format\"\n",[47,1033,1035,1038,1041,1043,1045,1047,1049,1052],{"class":49,"line":1034},23,[47,1036,1037],{"class":137},"    return",[47,1039,1040],{"class":57}," \"zero\"",[47,1042,827],{"class":137},[47,1044,830],{"class":141},[47,1046,901],{"class":137},[47,1048,993],{"class":199},[47,1050,1051],{"class":137}," else",[47,1053,1054],{"class":57}," \"number\"\n",[47,1056,1058],{"class":49,"line":1057},24,[47,1059,155],{"emptyLinePlaceholder":154},[47,1061,1063,1066,1068,1071,1073,1076,1078,1081,1083],{"class":49,"line":1062},25,[47,1064,1065],{"class":141},"report ",[47,1067,164],{"class":137},[47,1069,1070],{"class":141}," [(f[",[47,1072,257],{"class":199},[47,1074,1075],{"class":141},"].value, classify(f[",[47,1077,328],{"class":199},[47,1079,1080],{"class":141},"], v[",[47,1082,328],{"class":199},[47,1084,1085],{"class":141},"]))\n",[47,1087,1089,1092,1095,1097,1100,1103,1106,1108,1110,1113,1115,1117,1119],{"class":49,"line":1088},26,[47,1090,1091],{"class":137},"          for",[47,1093,1094],{"class":141}," f, v ",[47,1096,833],{"class":137},[47,1098,1099],{"class":199}," zip",[47,1101,1102],{"class":141},"(formulas.iter_rows(",[47,1104,1105],{"class":214},"min_row",[47,1107,164],{"class":137},[47,1109,353],{"class":199},[47,1111,1112],{"class":141},"), values.iter_rows(",[47,1114,1105],{"class":214},[47,1116,164],{"class":137},[47,1118,353],{"class":199},[47,1120,1121],{"class":141},"))]\n",[47,1123,1125,1128,1131,1133],{"class":49,"line":1124},27,[47,1126,1127],{"class":137},"for",[47,1129,1130],{"class":141}," item, kind ",[47,1132,833],{"class":137},[47,1134,1135],{"class":141}," report:\n",[47,1137,1139,1142,1145,1148,1151,1155,1158,1161,1164,1166,1169,1171,1173],{"class":49,"line":1138},28,[47,1140,1141],{"class":199},"    print",[47,1143,1144],{"class":141},"(",[47,1146,1147],{"class":137},"f",[47,1149,1150],{"class":57},"\"",[47,1152,1154],{"class":1153},"sSjpA","{",[47,1156,1157],{"class":141},"item",[47,1159,1160],{"class":137},":10",[47,1162,1163],{"class":1153},"}",[47,1165,725],{"class":1153},[47,1167,1168],{"class":141},"kind",[47,1170,1163],{"class":1153},[47,1172,1150],{"class":57},[47,1174,173],{"class":141},[38,1176,1179],{"className":1177,"code":1178,"language":229,"meta":43},[227],"Bolts M6   number\nBolts M8   empty\nWashers    formula, empty result\nNuts M6    whitespace only\nNuts M8    zero\nRivets     number stored as text\nBrackets   zero, hidden by format\nHinges     placeholder text\nClamps     placeholder text\n",[14,1180,1178],{"__ignoreMap":43},[10,1182,1183,1184,1187,1188,1190,1191,1193,1194,1196,1197,31],{},"The formula check needs the first workbook because the ",[14,1185,1186],{},"data_only=True"," load only sees the cached result, and for ",[14,1189,81],{}," that result is ",[14,1192,839],{},", indistinguishable from an empty cell. The hidden-zero check looks at the third section of the number format, the one Excel uses for zero values; an empty section means zeros display as nothing. If the workbook was saved by a tool that does not store formula results, the second load sees ",[14,1195,839],{}," for every formula — see ",[27,1198,1200],{"href":1199},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Read Formula Results with openpyxl data_only",[260,1202,269,1208,269,1211,269,1214,269,1218,269,1225,269,1236,269,1241,269,1245,269,1249,269,1252,269,1257,269,1264,269,1269,269,1275,269,1281,269,1285,269,1290,269,1294,269,1297,269,1300,269,1303,269,1306,269,1309,269,1316,269,1321,269,1325,269,1328,269,1331,269,1335,269,1337,269,1340,269,1347,269,1352,269,1355,269,1358,269,1362,269,1364,269,1367,269,1371,269,1374,269,1379,269,1386,269,1390,269,1394,269,1397],{"viewBox":1203,"role":263,"ariaLabelledBy":1204,"xmlns":267,"style":1207},"4 0 752 308",[1205,1206],"bz-classify-t","bz-classify-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[271,1209,1210],{"id":1205},"How the classifier sorts an empty-looking cell",[275,1212,1213],{"id":1206},"A cell first splits on whether it has a formula, then on whether its value is None, text or a number. Formulas with empty results, truly empty cells, whitespace and placeholder text, and zeros hidden by a number format each get their own label.",[279,1215],{"x":396,"y":257,"width":1216,"height":1217,"fill":284},"752","308",[279,1219],{"x":1220,"y":288,"width":1221,"height":1222,"rx":1223,"fill":331,"stroke":332,"style":1224},"240.0","280.0","75.5","12","stroke-width:2px",[1226,1227,1229],"g",{"transform":1228},"translate(368.0,29.0) scale(1.000)",[1230,1231],"path",{"d":1232,"fill":1233,"stroke":1234,"style":1235},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[229,1237,1240],{"x":1238,"y":1222,"style":1239},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What is in the cell?",[49,1242],{"x1":1238,"y1":1243,"x2":1238,"y2":1244,"stroke":332,"style":1224},"91.5","111.5",[49,1246],{"x1":1247,"y1":1244,"x2":1248,"y2":1244,"stroke":332,"style":1224},"104.0","656.0",[49,1250],{"x1":1247,"y1":1244,"x2":1247,"y2":1251,"stroke":332,"style":1224},"157.5",[1253,1254],"polygon",{"points":1255,"fill":1256},"104.0,161.5 99.0,152.5 109.0,152.5","#5b5cf0",[279,1258],{"x":1259,"y":1260,"width":1261,"height":1262,"rx":509,"fill":284,"stroke":292,"style":1263},"67.1","121.5","73.8","21.0","stroke-width:1.2px",[229,1265,1268],{"x":1247,"y":1266,"style":1267},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","a formula",[279,1270],{"x":1271,"y":1272,"width":1273,"height":1274,"rx":1223,"fill":331,"stroke":332,"style":1224},"20.0","163.5","168.0","128.0",[1226,1276,1278],{"transform":1277},"translate(92.0,185.2) scale(1.000)",[1230,1279],{"d":1280,"fill":1233,"stroke":1234,"style":1235},"M9 8l-4 4 4 4 M15 8l4 4-4 4",[229,1282,1284],{"x":1247,"y":1283,"style":1239},"231.8","Formula",[229,1286,1289],{"x":1247,"y":1287,"style":1288},"251.2","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","empty result if the",[229,1291,1293],{"x":1247,"y":1292,"style":1288},"268.8","cached value is None",[49,1295],{"x1":1296,"y1":1244,"x2":1296,"y2":1251,"stroke":332,"style":1224},"288.0",[1253,1298],{"points":1299,"fill":1256},"288.0,161.5 283.0,152.5 293.0,152.5",[279,1301],{"x":1302,"y":1260,"width":349,"height":1262,"rx":509,"fill":284,"stroke":292,"style":1263},"254.0",[229,1304,1305],{"x":1296,"y":1266,"style":1267},"no value",[279,1307],{"x":1308,"y":1272,"width":1273,"height":1274,"rx":1223,"fill":291,"stroke":292,"style":1224},"204.0",[1226,1310,1312],{"transform":1311},"translate(276.0,194.0) scale(1.000)",[1230,1313],{"d":1314,"fill":1233,"stroke":1315,"style":1235},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--text,#172033)",[229,1317,1320],{"x":1296,"y":1318,"style":1319},"240.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Empty",[229,1322,1324],{"x":1296,"y":1323,"style":1288},"260.0","never filled in",[49,1326],{"x1":1327,"y1":1244,"x2":1327,"y2":1251,"stroke":332,"style":1224},"472.0",[1253,1329],{"points":1330,"fill":1256},"472.0,161.5 467.0,152.5 477.0,152.5",[279,1332],{"x":1333,"y":1260,"width":1334,"height":1262,"rx":509,"fill":284,"stroke":292,"style":1263},"451.5","41.0",[229,1336,229],{"x":1327,"y":1266,"style":1267},[279,1338],{"x":1339,"y":1272,"width":1273,"height":1274,"rx":1223,"fill":383,"stroke":384,"style":1224},"388.0",[1226,1341,1343],{"transform":1342},"translate(460.0,176.5) scale(1.000)",[1230,1344],{"d":1345,"fill":1233,"stroke":1346,"style":1235},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[229,1348,1351],{"x":1327,"y":1349,"style":1350},"223.0","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Whitespace or",[229,1353,1354],{"x":1327,"y":1318,"style":1350},"placeholder",[229,1356,1357],{"x":1327,"y":1323,"style":1288},"' ', '-', 'N\u002FA', or a number",[229,1359,1361],{"x":1327,"y":1360,"style":1288},"277.5","as text",[49,1363],{"x1":1248,"y1":1244,"x2":1248,"y2":1251,"stroke":332,"style":1224},[1253,1365],{"points":1366,"fill":1256},"656.0,161.5 651.0,152.5 661.0,152.5",[279,1368],{"x":1369,"y":1260,"width":1370,"height":1262,"rx":509,"fill":284,"stroke":292,"style":1263},"619.2","73.7",[229,1372,1373],{"x":1248,"y":1266,"style":1267},"a number",[279,1375],{"x":1376,"y":1272,"width":1273,"height":1274,"rx":1223,"fill":1377,"stroke":1378,"style":1224},"572.0","#d9f4f1","var(--teal,#0f9488)",[1226,1380,1382],{"transform":1381},"translate(644.0,176.5) scale(1.000)",[1230,1383],{"d":1384,"fill":1233,"stroke":1385,"style":1235},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--teal-ink,#0b6157)",[229,1387,1389],{"x":1248,"y":1349,"style":1388},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Zero or number",[229,1391,1393],{"x":1248,"y":1392,"style":1288},"242.5","zero hidden if the",[229,1395,1396],{"x":1248,"y":1323,"style":1288},"format's third section is",[229,1398,1399],{"x":1248,"y":1360,"style":1288},"empty",[33,1401,1403],{"id":1402},"step-4-decide-what-each-case-means-and-apply-it","Step 4 — Decide what each case means and apply it",[10,1405,1406],{},"Classification is the evidence; the policy is a business decision. For a stock count, a sensible policy is: a real or text zero is a count of zero; empty, whitespace and empty formula results mean \"not counted\"; placeholders mean \"not applicable\"; a hidden zero is a zero that someone wanted to look tidy. Apply it as a mapping from class to status, then build a clean numeric column:",[38,1408,1410],{"className":128,"code":1409,"language":130,"meta":43,"style":43},"policy = {\n    \"number\": \"counted\", \"zero\": \"counted\", \"zero, hidden by format\": \"counted\",\n    \"number stored as text\": \"counted\",\n    \"empty\": \"not counted\", \"whitespace only\": \"not counted\", \"formula, empty result\": \"not counted\",\n    \"placeholder text\": \"not applicable\",\n}\nstatus = pd.Series([policy[k] for _, k in report], name=\"Status\")\nclean = raw[[\"Item\"]].assign(\n    Status=status,\n    Counted=pd.to_numeric(raw[\"Counted\"].astype(str).str.strip(), errors=\"coerce\").astype(\"Int64\"),\n)\nprint(clean.to_string(index=False))\nprint(clean[\"Status\"].value_counts().to_dict())\n",[14,1411,1412,1422,1454,1465,1495,1507,1511,1541,1557,1567,1604,1608,1623],{"__ignoreMap":43},[47,1413,1414,1417,1419],{"class":49,"line":50},[47,1415,1416],{"class":141},"policy ",[47,1418,164],{"class":137},[47,1420,1421],{"class":141}," {\n",[47,1423,1424,1427,1430,1433,1435,1438,1440,1442,1444,1447,1449,1451],{"class":49,"line":151},[47,1425,1426],{"class":57},"    \"number\"",[47,1428,1429],{"class":141},": ",[47,1431,1432],{"class":57},"\"counted\"",[47,1434,554],{"class":141},[47,1436,1437],{"class":57},"\"zero\"",[47,1439,1429],{"class":141},[47,1441,1432],{"class":57},[47,1443,554],{"class":141},[47,1445,1446],{"class":57},"\"zero, hidden by format\"",[47,1448,1429],{"class":141},[47,1450,1432],{"class":57},[47,1452,1453],{"class":141},",\n",[47,1455,1456,1459,1461,1463],{"class":49,"line":158},[47,1457,1458],{"class":57},"    \"number stored as text\"",[47,1460,1429],{"class":141},[47,1462,1432],{"class":57},[47,1464,1453],{"class":141},[47,1466,1467,1470,1472,1475,1477,1480,1482,1484,1486,1489,1491,1493],{"class":49,"line":176},[47,1468,1469],{"class":57},"    \"empty\"",[47,1471,1429],{"class":141},[47,1473,1474],{"class":57},"\"not counted\"",[47,1476,554],{"class":141},[47,1478,1479],{"class":57},"\"whitespace only\"",[47,1481,1429],{"class":141},[47,1483,1474],{"class":57},[47,1485,554],{"class":141},[47,1487,1488],{"class":57},"\"formula, empty result\"",[47,1490,1429],{"class":141},[47,1492,1474],{"class":57},[47,1494,1453],{"class":141},[47,1496,1497,1500,1502,1505],{"class":49,"line":205},[47,1498,1499],{"class":57},"    \"placeholder text\"",[47,1501,1429],{"class":141},[47,1503,1504],{"class":57},"\"not applicable\"",[47,1506,1453],{"class":141},[47,1508,1509],{"class":49,"line":759},[47,1510,756],{"class":141},[47,1512,1513,1516,1518,1521,1523,1526,1528,1531,1534,1536,1539],{"class":49,"line":764},[47,1514,1515],{"class":141},"status ",[47,1517,164],{"class":137},[47,1519,1520],{"class":141}," pd.Series([policy[k] ",[47,1522,1127],{"class":137},[47,1524,1525],{"class":141}," _, k ",[47,1527,833],{"class":137},[47,1529,1530],{"class":141}," report], ",[47,1532,1533],{"class":214},"name",[47,1535,164],{"class":137},[47,1537,1538],{"class":57},"\"Status\"",[47,1540,173],{"class":141},[47,1542,1543,1546,1548,1551,1554],{"class":49,"line":777},[47,1544,1545],{"class":141},"clean ",[47,1547,164],{"class":137},[47,1549,1550],{"class":141}," raw[[",[47,1552,1553],{"class":57},"\"Item\"",[47,1555,1556],{"class":141},"]].assign(\n",[47,1558,1559,1562,1564],{"class":49,"line":788},[47,1560,1561],{"class":214},"    Status",[47,1563,164],{"class":137},[47,1565,1566],{"class":141},"status,\n",[47,1568,1569,1572,1574,1577,1579,1582,1584,1587,1590,1592,1595,1598,1601],{"class":49,"line":818},[47,1570,1571],{"class":214},"    Counted",[47,1573,164],{"class":137},[47,1575,1576],{"class":141},"pd.to_numeric(raw[",[47,1578,193],{"class":57},[47,1580,1581],{"class":141},"].astype(",[47,1583,800],{"class":199},[47,1585,1586],{"class":141},").str.strip(), ",[47,1588,1589],{"class":214},"errors",[47,1591,164],{"class":137},[47,1593,1594],{"class":57},"\"coerce\"",[47,1596,1597],{"class":141},").astype(",[47,1599,1600],{"class":57},"\"Int64\"",[47,1602,1603],{"class":141},"),\n",[47,1605,1606],{"class":49,"line":854},[47,1607,173],{"class":141},[47,1609,1610,1612,1615,1617,1619,1621],{"class":49,"line":870},[47,1611,208],{"class":199},[47,1613,1614],{"class":141},"(clean.to_string(",[47,1616,215],{"class":214},[47,1618,164],{"class":137},[47,1620,220],{"class":199},[47,1622,223],{"class":141},[47,1624,1625,1627,1630,1632],{"class":49,"line":878},[47,1626,208],{"class":199},[47,1628,1629],{"class":141},"(clean[",[47,1631,1538],{"class":57},[47,1633,1634],{"class":141},"].value_counts().to_dict())\n",[10,1636,1637,1638,1640,1641,1643,1644,554,1646,102,1648,1650,1651,1654,1655,1658,1659,1662,1663,31],{},"The ",[14,1639,73],{}," column is now nullable integer: numbers where a count exists — including ",[14,1642,257],{}," for ",[14,1645,116],{},[14,1647,120],{},[14,1649,93],{}," — and ",[14,1652,1653],{},"\u003CNA>"," everywhere else, while ",[14,1656,1657],{},"Status"," records ",[244,1660,1661],{},"why"," a value is missing. The report can now say \"three items not counted, two not applicable\" instead of \"three blanks, a space and a dash\". Nullable integers are covered in ",[27,1664,1666],{"href":1665},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fuse-nullable-dtypes-when-reading-excel-with-pandas\u002F","Use Nullable Data Types When Reading Excel with pandas",[260,1668,269,1674,269,1677,269,1680,269,1684,269,1687,269,1693,269,1697,269,1701,269,1704,269,1708,269,1711,269,1715,269,1717,269,1721,269,1724,269,1728],{"viewBox":1669,"role":263,"ariaLabelledBy":1670,"xmlns":267,"style":1673},"4 0 744 166",[1671,1672],"bz-status-t","bz-status-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[271,1675,1676],{"id":1671},"The nine counts after applying the policy",[275,1678,1679],{"id":1672},"Four items have a count, three of them zero. Three were not counted and two are not applicable, where the default read would have reported three NaN values and hidden the reasons.",[279,1681],{"x":396,"y":257,"width":1682,"height":1683,"fill":284},"744","166",[279,1685],{"x":1271,"y":288,"width":1686,"height":323,"rx":452,"fill":291,"stroke":292,"style":293},"186.6",[229,1688,1692],{"x":1689,"y":1690,"style":1691},"113.3","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","counted (3 of them zero)",[279,1694],{"x":1695,"y":288,"width":1696,"height":323,"rx":452,"fill":1377,"stroke":1378,"style":333},"216.6","498.9",[229,1698,396],{"x":1699,"y":1690,"style":1700},"723.5","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start",[279,1702],{"x":1271,"y":1703,"width":1686,"height":323,"rx":452,"fill":291,"stroke":292,"style":293},"56.0",[229,1705,1707],{"x":1689,"y":1706,"style":1691},"74.5","not counted",[279,1709],{"x":1695,"y":1703,"width":1710,"height":323,"rx":452,"fill":383,"stroke":384,"style":333},"374.2",[229,1712,376],{"x":1713,"y":1706,"style":1714},"598.8","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start",[279,1716],{"x":1271,"y":372,"width":1686,"height":323,"rx":452,"fill":291,"stroke":292,"style":293},[229,1718,1720],{"x":1689,"y":1719,"style":1691},"114.5","not applicable",[279,1722],{"x":1695,"y":372,"width":1723,"height":323,"rx":452,"fill":291,"stroke":292,"style":333},"249.4",[229,1725,353],{"x":1726,"y":1719,"style":1727},"474.0","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[229,1729,1732],{"x":1238,"y":1730,"style":1731},"146.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","The default read showed three NaN, a space, a dash and three zeros, one of them text.",[33,1734,1736],{"id":1735},"step-5-count-blanks-the-way-excel-does-or-deliberately-differently","Step 5 — Count blanks the way Excel does, or deliberately differently",[10,1738,1739,1740,1743,1744,1746,1747,1749,1750,1753],{},"If a report must match a figure someone produced in Excel, know that Excel's own functions disagree with each other. ",[14,1741,1742],{},"COUNTBLANK"," counts truly empty cells ",[244,1745,806],{}," cells whose formula returns ",[14,1748,16],{},"; ",[14,1751,1752],{},"ISBLANK"," is TRUE only for truly empty cells; neither treats a single space as blank. Reproducing each in pandas is straightforward once the classification exists:",[38,1755,1757],{"className":128,"code":1756,"language":130,"meta":43,"style":43},"kinds = pd.Series([k for _, k in report])\ncountblank = kinds.isin([\"empty\", \"formula, empty result\"]).sum()\nisblank = (kinds == \"empty\").sum()\nlooks_blank = kinds.isin([\"empty\", \"formula, empty result\", \"whitespace only\", \"zero, hidden by format\"]).sum()\nprint(f\"COUNTBLANK={countblank}  ISBLANK count={isblank}  looks blank on screen={looks_blank}\")\n",[14,1758,1759,1778,1798,1816,1841],{"__ignoreMap":43},[47,1760,1761,1764,1766,1769,1771,1773,1775],{"class":49,"line":50},[47,1762,1763],{"class":141},"kinds ",[47,1765,164],{"class":137},[47,1767,1768],{"class":141}," pd.Series([k ",[47,1770,1127],{"class":137},[47,1772,1525],{"class":141},[47,1774,833],{"class":137},[47,1776,1777],{"class":141}," report])\n",[47,1779,1780,1783,1785,1788,1791,1793,1795],{"class":49,"line":151},[47,1781,1782],{"class":141},"countblank ",[47,1784,164],{"class":137},[47,1786,1787],{"class":141}," kinds.isin([",[47,1789,1790],{"class":57},"\"empty\"",[47,1792,554],{"class":141},[47,1794,1488],{"class":57},[47,1796,1797],{"class":141},"]).sum()\n",[47,1799,1800,1803,1805,1808,1810,1813],{"class":49,"line":158},[47,1801,1802],{"class":141},"isblank ",[47,1804,164],{"class":137},[47,1806,1807],{"class":141}," (kinds ",[47,1809,901],{"class":137},[47,1811,1812],{"class":57}," \"empty\"",[47,1814,1815],{"class":141},").sum()\n",[47,1817,1818,1821,1823,1825,1827,1829,1831,1833,1835,1837,1839],{"class":49,"line":176},[47,1819,1820],{"class":141},"looks_blank ",[47,1822,164],{"class":137},[47,1824,1787],{"class":141},[47,1826,1790],{"class":57},[47,1828,554],{"class":141},[47,1830,1488],{"class":57},[47,1832,554],{"class":141},[47,1834,1479],{"class":57},[47,1836,554],{"class":141},[47,1838,1446],{"class":57},[47,1840,1797],{"class":141},[47,1842,1843,1845,1847,1849,1852,1854,1857,1859,1862,1864,1867,1869,1872,1874,1877,1879,1881],{"class":49,"line":205},[47,1844,208],{"class":199},[47,1846,1144],{"class":141},[47,1848,1147],{"class":137},[47,1850,1851],{"class":57},"\"COUNTBLANK=",[47,1853,1154],{"class":1153},[47,1855,1856],{"class":141},"countblank",[47,1858,1163],{"class":1153},[47,1860,1861],{"class":57},"  ISBLANK count=",[47,1863,1154],{"class":1153},[47,1865,1866],{"class":141},"isblank",[47,1868,1163],{"class":1153},[47,1870,1871],{"class":57},"  looks blank on screen=",[47,1873,1154],{"class":1153},[47,1875,1876],{"class":141},"looks_blank",[47,1878,1163],{"class":1153},[47,1880,1150],{"class":57},[47,1882,173],{"class":141},[10,1884,1885,1886,31],{},"The three numbers come out as 2, 1 and 4 for the same column. Whichever you report, say which definition it uses. For a full missing-value audit across every column, combine this with ",[27,1887,1889],{"href":1888},"\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",[33,1891,1893],{"id":1892},"common-pitfalls","Common pitfalls",[1895,1896,1897,1921,1934,1950,1964],"ul",{},[1898,1899,1900,247,1904,554,1906,554,1909,554,1912,1914,1915,1917,1918,1920],"li",{},[1901,1902,1903],"strong",{},"Relying on the default NA list.",[14,1905,112],{},[14,1907,1908],{},"NA",[14,1910,1911],{},"null",[14,1913,24],{}," and several others become ",[14,1916,24],{}," silently; pass ",[14,1919,531],{}," while you investigate, then map the markers yourself.",[1898,1922,1923,1926,1927,1929,1930,1933],{},[1901,1924,1925],{},"Expecting pandas to see formulas."," A formula returning ",[14,1928,16],{}," and an empty cell are identical to ",[14,1931,1932],{},"read_excel","; only openpyxl's formula load can separate them.",[1898,1935,1936,1939,1940,102,1942,1945,1946,1949],{},[1901,1937,1938],{},"Trusting what Excel displays."," Custom formats such as ",[14,1941,97],{},[14,1943,1944],{},";;;"," hide real values; check ",[14,1947,1948],{},"number_format"," before calling a cell blank.",[1898,1951,1952,247,1955,102,1957,1959,1960,1963],{},[1901,1953,1954],{},"Mixed numbers and text in one column.",[14,1956,250],{},[14,1958,257],{}," print the same but compare differently: ",[14,1961,1962],{},"df[\"Counted\"] == 0"," misses the text zero.",[1898,1965,1966,1969,1970,1972],{},[1901,1967,1968],{},"Writing blanks back."," openpyxl does not store empty strings at all, so ",[14,1971,611],{}," written to a cell becomes a truly empty cell on reload. If the distinction must survive, write the status column alongside the numbers.",[33,1974,1976],{"id":1975},"performance-notes","Performance notes",[10,1978,1979,1980,1983,1984,1987,1988,1990],{},"Loading a workbook twice with openpyxl doubles the parse time, which is acceptable for sheets of a few thousand rows. For larger files, only classify the columns where blanks carry meaning: read them in ",[14,1981,1982],{},"read_only=True"," mode with ",[14,1985,1986],{},"iter_rows(min_col=…, max_col=…)",", and leave the rest to ",[14,1989,1932],{},". The pandas reads in Steps 1 and 2 cost the same as any normal read.",[33,1992,1994],{"id":1993},"conclusion","Conclusion",[10,1996,1997,1998,2000,2001,2003,2004,2006,2007,2009,2010,102,2012,2014],{},"\"Blank\" in Excel covers truly empty cells, formulas returning ",[14,1999,16],{},", whitespace, zeros hidden by number formats and placeholder text, and the default ",[14,2002,1932],{}," merges most of them into ",[14,2005,24],{},". Read with ",[14,2008,531],{}," to see the raw values, use openpyxl's formula load, cached values and number formats to classify each cell, and turn the classes into an explicit status column plus a nullable numeric column. Then counts of \"missing\" mean something — and when they must match Excel, remember that ",[14,2011,1742],{},[14,2013,1752],{}," use different definitions.",[33,2016,2018],{"id":2017},"frequently-asked-questions","Frequently asked questions",[10,2020,2021,2024,2025,2028],{},[1901,2022,2023],{},"Why does pandas read \"N\u002FA\" and empty cells as the same NaN?","\nread_excel converts truly empty cells and a default list of marker strings, including N\u002FA, NA, null and NaN, to NaN. Pass keep_default_na=False to keep the strings, and na_values=",[47,2026,2027],{},"..."," to choose your own markers.",[10,2030,2031,2034],{},[1901,2032,2033],{},"How can I tell a formula that returns \"\" from an empty cell in Python?","\nLoad the workbook with openpyxl without data_only: the formula cell's value is a string starting with \"=\". With data_only=True, or with pandas, both cells look the same because the cached result of the formula is empty.",[10,2036,2037,2040],{},[1901,2038,2039],{},"How do I detect zeros hidden by a number format?","\nRead the cell with openpyxl and inspect cell.number_format. A format whose third section, after the second semicolon, is empty — such as 0;-0;;@ — displays zero values as nothing even though the cell holds 0.",[10,2042,2043,2046],{},[1901,2044,2045],{},"Does Excel COUNTBLANK count cells with a space?","\nNo. COUNTBLANK counts truly empty cells and cells whose formula returns an empty string, but a cell containing a space is text and is not counted. ISBLANK is stricter still and returns TRUE only for truly empty cells.",[33,2048,2050],{"id":2049},"related","Related",[1895,2052,2053,2059,2066,2073],{},[1898,2054,2055,2056,2058],{},"Up one level: ",[27,2057,30],{"href":29}," — finding, filling and reporting gaps.",[1898,2060,2061,2065],{},[27,2062,2064],{"href":2063},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Freplace-placeholder-text-with-nan-in-pandas\u002F","Replace Placeholder Text with NaN in pandas"," — mapping dashes and N\u002FA once you have decided what they mean.",[1898,2067,2068,2072],{},[27,2069,2071],{"href":2070},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-blank-cells-with-openpyxl\u002F","Highlight Blank Cells in Excel with openpyxl"," — showing the gaps to the people who fill them in.",[1898,2074,2075,2079],{},[27,2076,2078],{"href":2077},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fread-cell-values-as-displayed-in-excel\u002F","Read Cell Values as Displayed in Excel"," — applying number formats in Python.",[2081,2082,2083],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":43,"searchDepth":151,"depth":151,"links":2085},[2086,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096],{"id":35,"depth":151,"text":36},{"id":124,"depth":151,"text":125},{"id":525,"depth":151,"text":526},{"id":640,"depth":151,"text":641},{"id":1402,"depth":151,"text":1403},{"id":1735,"depth":151,"text":1736},{"id":1892,"depth":151,"text":1893},{"id":1975,"depth":151,"text":1976},{"id":1993,"depth":151,"text":1994},{"id":2017,"depth":151,"text":2018},{"id":2049,"depth":151,"text":2050},"2026-10-02","A cell that looks empty in Excel can be truly empty, a formula returning \"\", a typed space, a zero hidden by its number format, or a placeholder like N\u002FA — and pandas collapses most of them into NaN. See what read_excel and openpyxl actually return for each, classify every cell, and choose a policy before you count or fill blanks.","md",[2101,2103,2105,2107],{"q":2023,"a":2102},"read_excel converts truly empty cells and a default list of marker strings, including N\u002FA, NA, null and NaN, to NaN. Pass keep_default_na=False to keep the strings, and na_values=[...] to choose your own markers.",{"q":2033,"a":2104},"Load the workbook with openpyxl without data_only: the formula cell's value is a string starting with \"=\". With data_only=True, or with pandas, both cells look the same because the cached result of the formula is empty.",{"q":2039,"a":2106},"Read the cell with openpyxl and inspect cell.number_format. A format whose third section, after the second semicolon, is empty — such as 0;-0;;@ — displays zero values as nothing even though the cell holds 0.",{"q":2045,"a":2108},"No. COUNTBLANK counts truly empty cells and cells whose formula returns an empty string, but a cell containing a space is text and is not counted. ISBLANK is stricter still and returns TRUE only for truly empty cells.","tell blank cells empty strings and zeros apart in excel",null,{"breadcrumb":2112},[2113,2116,2119],{"name":2114,"item":2115},"Home","\u002F",{"name":2117,"item":2118},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":2120,"item":29},"Handling Missing Data in Excel Reports with Pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data",{"title":2123,"description":2124},"Blank vs Empty String vs Zero in Excel Data with Python","What pandas and openpyxl return for truly empty cells, formulas returning \"\", spaces, zeros hidden by number formats and N\u002FA text — classify each cell, why keep_default_na matters, and how Excel COUNTBLANK differs from ISBLANK.","tell-blank-cells-empty-strings-and-zeros-apart-in-excel-data","advanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ftell-blank-cells-empty-strings-and-zeros-apart-in-excel-data\u002Findex",[2128,2129,2130],"missing-data","reading-excel","pandas","how-to","FK8eDJ4aemBzAkNF2V7cu3Uu6oHJOtBGiBRQfNer_YE",[2134,2137],{"title":2064,"path":2135,"stem":2136,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Freplace-placeholder-text-with-nan-in-pandas","advanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Freplace-placeholder-text-with-nan-in-pandas\u002Findex",{"title":2138,"path":2139,"stem":2140,"children":-1},"Visualise Missing Data Patterns in an Excel File","\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Fvisualise-missing-data-patterns-in-an-excel-file","advanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Fvisualise-missing-data-patterns-in-an-excel-file\u002Findex",[2142,2144],{"path":2143,"title":2120},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports",{"path":2145,"title":2117},"\u002Fadvanced-data-transformation-and-cleaning",[],[2148,2150,2153,2156,2159,2162],{"path":2149,"title":2071,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-blank-cells-with-openpyxl",{"path":2151,"title":2152,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fcoalesce-several-excel-columns-into-one-with-pandas","Coalesce Several Excel Columns into One with pandas",{"path":2154,"title":2155,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas","IFERROR, IFNA, ISNA and ISNUMBER Equivalents in pandas",{"path":2157,"title":2158,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Fdrop-rows-with-missing-required-fields-in-pandas","Drop Rows with Missing Required Fields in pandas",{"path":2160,"title":2161,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffill-gaps-in-per-item-time-series-with-a-staleness-limit","Fill Gaps in Per-Item Time Series with a Staleness Limit",{"path":2163,"title":2164,"kind":2131},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffill-missing-values-in-excel-with-pandas-fillna","Fill Missing Values in Excel with Pandas fillna",1790972096098]