[{"data":1,"prerenderedAt":1993},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas":1960,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas":1969,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas":1974,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas":1975},{"id":4,"title":5,"body":6,"dateModified":1927,"datePublished":1927,"description":1928,"extension":1929,"faq":1930,"keyword":1939,"kind":1940,"meta":1941,"navigation":282,"path":1949,"seo":1950,"slug":1953,"stem":1954,"topics":1955,"type":1958,"__hash__":1959},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas\u002Findex.md","IFERROR, IFNA, ISNA and ISNUMBER Equivalents in pandas",{"type":7,"value":8,"toc":1913},"minimark",[9,47,194,199,227,241,350,359,363,376,439,448,488,499,503,516,653,678,790,794,807,966,989,993,1000,1131,1154,1158,1168,1412,1432,1436,1447,1587,1601,1727,1731,1796,1800,1817,1821,1848,1852,1858,1864,1870,1876,1880,1909],[10,11,12,16,17,20,21,24,25,28,29,32,33,36,37,42,43,46],"p",{},[13,14,15],"code",{},"IFERROR"," is probably the most-used defensive function in Excel: ",[13,18,19],{},"=IFERROR(B2\u002FC2, 0)"," stops a division by zero, ",[13,22,23],{},"=IFERROR(VLOOKUP(...), \"not found\")"," covers missing keys, and ",[13,26,27],{},"=IFERROR(VALUE(D2), \"\")"," swallows text that is not a number. Translating it to pandas is not a matter of finding the matching function, because pandas does not have error values at all. Each of those failures shows up differently — as ",[13,30,31],{},"inf",", as ",[13,34,35],{},"NaN",", or as an exception — and each needs its own handling. That turns out to be an advantage: it forces you to decide what each failure means, instead of hiding them all behind one zero. This page, part of ",[38,39,41],"a",{"href":40},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",", maps each case, and the ",[13,44,45],{},"IS…"," functions that go with them.",[48,49,57,58,57,62,57,66,57,74,57,84,57,95,57,102,57,107,57,115,57,122,57,128,57,133,57,136,57,141,57,146,57,153,57,157,57,161,57,164,57,167,57,172,57,179,57,184,57,188,57,191],"svg",{"viewBox":50,"role":51,"ariaLabelledBy":52,"xmlns":55,"style":56},"4 0 752 332","img",[53,54],"ife-map-t","ife-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  ",[59,60,61],"title",{"id":53},"One IFERROR, three different failures in pandas",[63,64,65],"desc",{"id":54},"An Excel IFERROR fans out to three pandas situations. Division by zero produces inf or NaN instead of #DIV\u002F0!. A lookup that finds no match produces NaN after a merge instead of #N\u002FA. Text that cannot be converted raises an error or becomes NaN with errors set to coerce, instead of #VALUE!. Each needs its own fill.",[67,68],"rect",{"x":69,"y":70,"width":71,"height":72,"fill":73},"4","0","752","332","#ffffff",[67,75],{"x":76,"y":77,"width":78,"height":79,"rx":80,"fill":81,"stroke":82,"style":83},"20.0","119.5","220.0","93.0","12","#fdefd8","var(--gold,#b4740a)","stroke-width:2px",[85,86,88],"g",{"transform":87},"translate(118.0,132.5) scale(1.000)",[89,90],"path",{"d":91,"fill":92,"stroke":93,"style":94},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","none","var(--gold-ink,#7a4e06)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[96,97,101],"text",{"x":98,"y":99,"style":100},"130.0","179.0","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","=IFERROR(x, fallback)",[96,103,106],{"x":98,"y":104,"style":105},"198.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","catches every error",[67,108],{"x":109,"y":110,"width":111,"height":112,"rx":80,"fill":113,"stroke":114,"style":83},"440.0","16.0","300.0","92.0","#f0f4ff","var(--brand,#5b5cf0)",[85,116,118],{"transform":117},"translate(578.0,29.0) scale(1.000)",[89,119],{"d":120,"fill":92,"stroke":121,"style":94},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--brand-strong,#4338ca)",[96,123,127],{"x":124,"y":125,"style":126},"590.0","75.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","#DIV\u002F0! → inf or NaN",[96,129,132],{"x":124,"y":130,"style":131},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","replace([inf, -inf], …) then fillna",[89,134],{"d":135,"fill":92,"stroke":114,"style":83},"M244.0,166.0 C339.0,166.0 339.0,62.0 430.0,62.0",[137,138],"polygon",{"points":139,"fill":140},"434.0,62.0 425.0,67.0 425.0,57.0","#5b5cf0",[67,142],{"x":109,"y":143,"width":111,"height":112,"rx":80,"fill":144,"stroke":145,"style":83},"120.0","#d9f4f1","var(--teal,#0f9488)",[85,147,149],{"transform":148},"translate(578.0,133.0) scale(1.000)",[89,150],{"d":151,"fill":92,"stroke":152,"style":94},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--teal-ink,#0b6157)",[96,154,156],{"x":124,"y":99,"style":155},"font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","#N\u002FA → NaN after merge",[96,158,160],{"x":124,"y":159,"style":131},"198.0","fillna on the looked-up column",[89,162],{"d":163,"fill":92,"stroke":114,"style":83},"M244.0,166.0 C339.0,166.0 339.0,166.0 430.0,166.0",[137,165],{"points":166,"fill":140},"434.0,166.0 425.0,171.0 425.0,161.0",[67,168],{"x":109,"y":169,"width":111,"height":112,"rx":80,"fill":170,"stroke":171,"style":83},"224.0","#fee8f2","var(--accent,#f43f8f)",[85,173,175],{"transform":174},"translate(578.0,237.0) scale(1.000)",[89,176],{"d":177,"fill":92,"stroke":178,"style":94},"M9 8l-4 4 4 4 M15 8l4 4-4 4","var(--accent-ink,#be185d)",[96,180,183],{"x":124,"y":181,"style":182},"283.0","font-size:13px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","#VALUE! → exception or NaN",[96,185,187],{"x":124,"y":186,"style":131},"302.0","to_numeric(errors=\"coerce\")",[89,189],{"d":190,"fill":92,"stroke":114,"style":83},"M244.0,166.0 C339.0,166.0 339.0,270.0 430.0,270.0",[137,192],{"points":193,"fill":140},"434.0,270.0 425.0,275.0 425.0,265.0",[195,196,198],"h2",{"id":197},"prerequisites","Prerequisites",[200,201,206],"pre",{"className":202,"code":203,"language":204,"meta":205,"style":205},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[13,207,208],{"__ignoreMap":205},[209,210,213,217,221,224],"span",{"class":211,"line":212},"line",1,[209,214,216],{"class":215},"sMTad","pip",[209,218,220],{"class":219},"srMev"," install",[209,222,223],{"class":219}," pandas",[209,225,226],{"class":219}," openpyxl\n",[10,228,229,232,233,236,237,240],{},[13,230,231],{},"items.xlsx"," has five products with revenue and units — two with zero units — and a quantity column typed as text, with ",[13,234,235],{},"n\u002Fa",", padding and a word. ",[13,238,239],{},"prices.xlsx"," is a price list that is missing two of the products.",[200,242,246],{"className":243,"code":244,"language":245,"meta":205,"style":205},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\nitems = pd.read_excel(\"items.xlsx\", dtype={\"qty_text\": str}, keep_default_na=False)\nprices = pd.read_excel(\"prices.xlsx\")\n","python",[13,247,248,264,277,284,335],{"__ignoreMap":205},[209,249,250,254,258,261],{"class":211,"line":212},[209,251,253],{"class":252},"s-kum","import",[209,255,257],{"class":256},"skGVy"," numpy ",[209,259,260],{"class":252},"as",[209,262,263],{"class":256}," np\n",[209,265,267,269,272,274],{"class":211,"line":266},2,[209,268,253],{"class":252},[209,270,271],{"class":256}," pandas ",[209,273,260],{"class":252},[209,275,276],{"class":256}," pd\n",[209,278,280],{"class":211,"line":279},3,[209,281,283],{"emptyLinePlaceholder":282},true,"\n",[209,285,287,290,293,296,299,302,306,308,311,314,317,321,324,327,329,332],{"class":211,"line":286},4,[209,288,289],{"class":256},"items ",[209,291,292],{"class":252},"=",[209,294,295],{"class":256}," pd.read_excel(",[209,297,298],{"class":219},"\"items.xlsx\"",[209,300,301],{"class":256},", ",[209,303,305],{"class":304},"sa561","dtype",[209,307,292],{"class":252},[209,309,310],{"class":256},"{",[209,312,313],{"class":219},"\"qty_text\"",[209,315,316],{"class":256},": ",[209,318,320],{"class":319},"sP0c6","str",[209,322,323],{"class":256},"}, ",[209,325,326],{"class":304},"keep_default_na",[209,328,292],{"class":252},[209,330,331],{"class":319},"False",[209,333,334],{"class":256},")\n",[209,336,338,341,343,345,348],{"class":211,"line":337},5,[209,339,340],{"class":256},"prices ",[209,342,292],{"class":252},[209,344,295],{"class":256},[209,346,347],{"class":219},"\"prices.xlsx\"",[209,349,334],{"class":256},[10,351,352,355,356,358],{},[13,353,354],{},"keep_default_na=False"," keeps the text ",[13,357,235],{}," as text, so the conversion step below has something to fail on — by default pandas would have quietly turned it into a missing value on read.",[195,360,362],{"id":361},"step-1-division-by-zero-iferrorb2c2-0","Step 1 — Division by zero: IFERROR(B2\u002FC2, 0)",[10,364,365,366,368,369,372,373,375],{},"Dividing by zero in pandas does not produce an error. A number divided by zero is ",[13,367,31],{}," (or ",[13,370,371],{},"-inf","), and zero divided by zero is ",[13,374,35],{},":",[200,377,379],{"className":243,"code":378,"language":245,"meta":205,"style":205},"items[\"price_per_unit\"] = items[\"revenue\"] \u002F items[\"units\"]\nprint(items[[\"sku\", \"revenue\", \"units\", \"price_per_unit\"]])\n",[13,380,381,413],{"__ignoreMap":205},[209,382,383,386,389,392,394,397,400,402,405,407,410],{"class":211,"line":212},[209,384,385],{"class":256},"items[",[209,387,388],{"class":219},"\"price_per_unit\"",[209,390,391],{"class":256},"] ",[209,393,292],{"class":252},[209,395,396],{"class":256}," items[",[209,398,399],{"class":219},"\"revenue\"",[209,401,391],{"class":256},[209,403,404],{"class":252},"\u002F",[209,406,396],{"class":256},[209,408,409],{"class":219},"\"units\"",[209,411,412],{"class":256},"]\n",[209,414,415,418,421,424,426,428,430,432,434,436],{"class":211,"line":266},[209,416,417],{"class":319},"print",[209,419,420],{"class":256},"(items[[",[209,422,423],{"class":219},"\"sku\"",[209,425,301],{"class":256},[209,427,399],{"class":219},[209,429,301],{"class":256},[209,431,409],{"class":219},[209,433,301],{"class":256},[209,435,388],{"class":219},[209,437,438],{"class":256},"]])\n",[10,440,441,442,444,445,447],{},"B-2 (0 \u002F 0) is ",[13,443,35],{}," and C-3 (450 \u002F 0) is ",[13,446,31],{},". Decide what they mean, then replace them explicitly. For a price per unit, \"no units sold\" means the price is undefined, which is honestly represented as missing:",[200,449,451],{"className":243,"code":450,"language":245,"meta":205,"style":205},"items[\"price_per_unit\"] = items[\"price_per_unit\"].replace([np.inf, -np.inf], np.nan)\nprint(items[\"price_per_unit\"].tolist())\n",[13,452,453,476],{"__ignoreMap":205},[209,454,455,457,459,461,463,465,467,470,473],{"class":211,"line":212},[209,456,385],{"class":256},[209,458,388],{"class":219},[209,460,391],{"class":256},[209,462,292],{"class":252},[209,464,396],{"class":256},[209,466,388],{"class":219},[209,468,469],{"class":256},"].replace([np.inf, ",[209,471,472],{"class":252},"-",[209,474,475],{"class":256},"np.inf], np.nan)\n",[209,477,478,480,483,485],{"class":211,"line":266},[209,479,417],{"class":319},[209,481,482],{"class":256},"(items[",[209,484,388],{"class":219},[209,486,487],{"class":256},"].tolist())\n",[10,489,490,491,494,495,498],{},"The Excel habit — ",[13,492,493],{},"IFERROR(..., 0)"," — would record a price of 0, which averages and charts will treat as a real, very low price. If a report genuinely needs a zero, fill it as the last step before display, not in the data. A guard that states the rule is often clearer still: ",[13,496,497],{},"np.where(items[\"units\"] > 0, items[\"revenue\"] \u002F items[\"units\"], np.nan)",".",[195,500,502],{"id":501},"step-2-failed-lookups-ifnavlookup-not-found","Step 2 — Failed lookups: IFNA(VLOOKUP(...), \"not found\")",[10,504,505,506,508,509,512,513,375],{},"A lookup is a merge, and a key with no match gets ",[13,507,35],{}," in the looked-up columns. That is the pandas form of ",[13,510,511],{},"#N\u002FA",", and filling it is the pandas form of ",[13,514,515],{},"IFNA",[200,517,519],{"className":243,"code":518,"language":245,"meta":205,"style":205},"looked_up = items.merge(prices, on=\"sku\", how=\"left\", indicator=True)\nprint(looked_up[[\"sku\", \"list_price\", \"_merge\"]])\n\nlooked_up[\"list_price_display\"] = looked_up[\"list_price\"].astype(object).where(looked_up[\"_merge\"] == \"both\", \"not found\")\nprint(looked_up[[\"sku\", \"list_price_display\"]].to_string(index=False))\n",[13,520,521,560,581,585,628],{"__ignoreMap":205},[209,522,523,526,528,531,534,536,538,540,543,545,548,550,553,555,558],{"class":211,"line":212},[209,524,525],{"class":256},"looked_up ",[209,527,292],{"class":252},[209,529,530],{"class":256}," items.merge(prices, ",[209,532,533],{"class":304},"on",[209,535,292],{"class":252},[209,537,423],{"class":219},[209,539,301],{"class":256},[209,541,542],{"class":304},"how",[209,544,292],{"class":252},[209,546,547],{"class":219},"\"left\"",[209,549,301],{"class":256},[209,551,552],{"class":304},"indicator",[209,554,292],{"class":252},[209,556,557],{"class":319},"True",[209,559,334],{"class":256},[209,561,562,564,567,569,571,574,576,579],{"class":211,"line":266},[209,563,417],{"class":319},[209,565,566],{"class":256},"(looked_up[[",[209,568,423],{"class":219},[209,570,301],{"class":256},[209,572,573],{"class":219},"\"list_price\"",[209,575,301],{"class":256},[209,577,578],{"class":219},"\"_merge\"",[209,580,438],{"class":256},[209,582,583],{"class":211,"line":279},[209,584,283],{"emptyLinePlaceholder":282},[209,586,587,590,593,595,597,600,602,605,608,611,613,615,618,621,623,626],{"class":211,"line":286},[209,588,589],{"class":256},"looked_up[",[209,591,592],{"class":219},"\"list_price_display\"",[209,594,391],{"class":256},[209,596,292],{"class":252},[209,598,599],{"class":256}," looked_up[",[209,601,573],{"class":219},[209,603,604],{"class":256},"].astype(",[209,606,607],{"class":319},"object",[209,609,610],{"class":256},").where(looked_up[",[209,612,578],{"class":219},[209,614,391],{"class":256},[209,616,617],{"class":252},"==",[209,619,620],{"class":219}," \"both\"",[209,622,301],{"class":256},[209,624,625],{"class":219},"\"not found\"",[209,627,334],{"class":256},[209,629,630,632,634,636,638,640,643,646,648,650],{"class":211,"line":337},[209,631,417],{"class":319},[209,633,566],{"class":256},[209,635,423],{"class":219},[209,637,301],{"class":256},[209,639,592],{"class":219},[209,641,642],{"class":256},"]].to_string(",[209,644,645],{"class":304},"index",[209,647,292],{"class":252},[209,649,331],{"class":319},[209,651,652],{"class":256},"))\n",[10,654,655,658,659,662,663,665,666,669,670,672,673,677],{},[13,656,657],{},"indicator=True"," adds a ",[13,660,661],{},"_merge"," column saying whether each row found a match, which distinguishes \"no match\" from \"matched a row whose price is blank\" — two cases ",[13,664,515],{}," cannot tell apart. Fill the display column, and keep the numeric ",[13,667,668],{},"list_price"," as ",[13,671,35],{}," so later calculations are not fed a string. ",[38,674,676],{"href":675},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files\u002F","The VLOOKUP Equivalent in pandas for Excel Files"," covers the merge itself.",[48,679,57,684,57,687,57,690,57,693,57,699,57,704,57,709,57,714,57,720,57,722,57,726,57,729,57,733,57,735,57,739,57,742,57,746,57,748,57,752,57,755,57,759,57,761,57,764,57,767,57,771,57,773,57,777,57,780,57,784,57,786],{"viewBox":680,"role":51,"ariaLabelledBy":681,"xmlns":55,"style":56},"4 0 752 276",[682,683],"ife-table-t","ife-table-d",[59,685,686],{"id":682},"Excel error handling and its pandas counterpart",[63,688,689],{"id":683},"A translation table. IFERROR around a division becomes replacing inf and filling NaN. IFNA around VLOOKUP becomes a left merge with fillna, ideally using indicator. IFERROR around VALUE becomes to_numeric with errors coerce. ISNA becomes isna. ISBLANK becomes isna, optionally also testing stripped empty text. ISNUMBER becomes to_numeric with errors coerce and notna. ISTEXT becomes a check that the value is a string.",[67,691],{"x":69,"y":70,"width":71,"height":692,"fill":73},"276",[67,694],{"x":76,"y":110,"width":695,"height":696,"rx":70,"fill":73,"stroke":697,"style":698},"195.0","40.0","var(--line,#cdd5e6)","stroke-width:1px",[67,700],{"x":701,"y":110,"width":702,"height":696,"rx":70,"fill":113,"stroke":114,"style":703},"215.0","525.0","stroke-width:1.2px",[96,705,708],{"x":706,"y":707,"style":126},"477.5","41.0","pandas",[67,710],{"x":76,"y":711,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"56.0","34.0","#e7ebef",[96,715,719],{"x":716,"y":717,"style":718},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","=IFERROR(B2\u002FC2, …)",[67,721],{"x":701,"y":711,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,723,725],{"x":706,"y":724,"style":105},"77.0","b \u002F c, then replace inf; fillna at display",[67,727],{"x":76,"y":728,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"90.0",[96,730,732],{"x":716,"y":731,"style":718},"112.0","=IFNA(VLOOKUP(…), …)",[67,734],{"x":701,"y":728,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,736,738],{"x":706,"y":737,"style":105},"111.0","merge(how=\"left\", indicator=True)",[67,740],{"x":76,"y":741,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"124.0",[96,743,745],{"x":716,"y":744,"style":718},"146.0","=IFERROR(VALUE(D2), …)",[67,747],{"x":701,"y":741,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,749,751],{"x":706,"y":750,"style":105},"145.0","pd.to_numeric(d, errors=\"coerce\")",[67,753],{"x":76,"y":754,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"158.0",[96,756,758],{"x":716,"y":757,"style":718},"180.0","=ISNA(x) \u002F ISBLANK(x)",[67,760],{"x":701,"y":754,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,762,763],{"x":706,"y":99,"style":105},"x.isna() (+ stripped \"\" if wanted)",[67,765],{"x":76,"y":766,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"192.0",[96,768,770],{"x":716,"y":769,"style":718},"214.0","=ISNUMBER(x)",[67,772],{"x":701,"y":766,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,774,776],{"x":706,"y":775,"style":105},"213.0","pd.to_numeric(x, errors=\"coerce\").notna()",[67,778],{"x":76,"y":779,"width":695,"height":712,"rx":70,"fill":713,"stroke":697,"style":698},"226.0",[96,781,783],{"x":716,"y":782,"style":718},"248.0","=ISTEXT(x)",[67,785],{"x":701,"y":779,"width":702,"height":712,"rx":70,"fill":73,"stroke":697,"style":698},[96,787,789],{"x":706,"y":788,"style":105},"247.0","x.map(lambda v: isinstance(v, str))",[195,791,793],{"id":792},"step-3-failed-conversions-iferrorvalued2","Step 3 — Failed conversions: IFERROR(VALUE(D2), \"\")",[10,795,796,797,800,801,803,804,375],{},"Converting text to numbers raises an error on the first value that does not parse. ",[13,798,799],{},"errors=\"coerce\""," turns those values into ",[13,802,35],{}," instead — the pandas version of ",[13,805,806],{},"IFERROR(VALUE(...))",[200,808,810],{"className":243,"code":809,"language":245,"meta":205,"style":205},"try:\n    pd.to_numeric(items[\"qty_text\"])\nexcept ValueError as exc:\n    print(\"to_numeric:\", exc)\n\nitems[\"qty\"] = pd.to_numeric(items[\"qty_text\"].str.strip(), errors=\"coerce\")\nfailed = items[\"qty\"].isna() & items[\"qty_text\"].str.strip().ne(\"\")\nprint(items[[\"sku\", \"qty_text\", \"qty\"]].to_string(index=False))\nprint(\"could not convert:\", items.loc[failed, \"qty_text\"].tolist())\n",[13,811,812,820,830,844,858,862,892,922,949],{"__ignoreMap":205},[209,813,814,817],{"class":211,"line":212},[209,815,816],{"class":252},"try",[209,818,819],{"class":256},":\n",[209,821,822,825,827],{"class":211,"line":266},[209,823,824],{"class":256},"    pd.to_numeric(items[",[209,826,313],{"class":219},[209,828,829],{"class":256},"])\n",[209,831,832,835,838,841],{"class":211,"line":279},[209,833,834],{"class":252},"except",[209,836,837],{"class":319}," ValueError",[209,839,840],{"class":252}," as",[209,842,843],{"class":256}," exc:\n",[209,845,846,849,852,855],{"class":211,"line":286},[209,847,848],{"class":319},"    print",[209,850,851],{"class":256},"(",[209,853,854],{"class":219},"\"to_numeric:\"",[209,856,857],{"class":256},", exc)\n",[209,859,860],{"class":211,"line":337},[209,861,283],{"emptyLinePlaceholder":282},[209,863,865,867,870,872,874,877,879,882,885,887,890],{"class":211,"line":864},6,[209,866,385],{"class":256},[209,868,869],{"class":219},"\"qty\"",[209,871,391],{"class":256},[209,873,292],{"class":252},[209,875,876],{"class":256}," pd.to_numeric(items[",[209,878,313],{"class":219},[209,880,881],{"class":256},"].str.strip(), ",[209,883,884],{"class":304},"errors",[209,886,292],{"class":252},[209,888,889],{"class":219},"\"coerce\"",[209,891,334],{"class":256},[209,893,895,898,900,902,904,907,910,912,914,917,920],{"class":211,"line":894},7,[209,896,897],{"class":256},"failed ",[209,899,292],{"class":252},[209,901,396],{"class":256},[209,903,869],{"class":219},[209,905,906],{"class":256},"].isna() ",[209,908,909],{"class":252},"&",[209,911,396],{"class":256},[209,913,313],{"class":219},[209,915,916],{"class":256},"].str.strip().ne(",[209,918,919],{"class":219},"\"\"",[209,921,334],{"class":256},[209,923,925,927,929,931,933,935,937,939,941,943,945,947],{"class":211,"line":924},8,[209,926,417],{"class":319},[209,928,420],{"class":256},[209,930,423],{"class":219},[209,932,301],{"class":256},[209,934,313],{"class":219},[209,936,301],{"class":256},[209,938,869],{"class":219},[209,940,642],{"class":256},[209,942,645],{"class":304},[209,944,292],{"class":252},[209,946,331],{"class":319},[209,948,652],{"class":256},[209,950,952,954,956,959,962,964],{"class":211,"line":951},9,[209,953,417],{"class":319},[209,955,851],{"class":256},[209,957,958],{"class":219},"\"could not convert:\"",[209,960,961],{"class":256},", items.loc[failed, ",[209,963,313],{"class":219},[209,965,487],{"class":256},[10,967,968,969,972,973,975,976,979,980,982,983,985,986,988],{},"Stripping first rescues ",[13,970,971],{},"\" 12 \"","; ",[13,974,235],{}," and ",[13,977,978],{},"four"," become ",[13,981,35],{},". The last line is the part ",[13,984,15],{}," never does, and the reason to prefer this form: it lists what was swallowed, so ",[13,987,978],{}," gets fixed at the source rather than silently disappearing from the totals.",[195,990,992],{"id":991},"step-4-the-is-functions-as-column-tests","Step 4 — The IS… functions as column tests",[10,994,995,996,999],{},"Excel's ",[13,997,998],{},"IS"," functions test one cell; their pandas equivalents test a whole column and return booleans you can filter or count:",[200,1001,1003],{"className":243,"code":1002,"language":245,"meta":205,"style":205},"tests = pd.DataFrame({\n    \"value\": items[\"qty_text\"],\n    \"ISBLANK\": items[\"qty_text\"].str.strip() == \"\",\n    \"ISNUMBER\": pd.to_numeric(items[\"qty_text\"].str.strip(), errors=\"coerce\").notna(),\n    \"ISTEXT\": items[\"qty_text\"].map(lambda v: isinstance(v, str)),\n})\nprint(tests.to_string(index=False))\nprint(tests[[\"ISNUMBER\"]].sum().to_dict())\n",[13,1004,1005,1015,1028,1048,1069,1098,1103,1118],{"__ignoreMap":205},[209,1006,1007,1010,1012],{"class":211,"line":212},[209,1008,1009],{"class":256},"tests ",[209,1011,292],{"class":252},[209,1013,1014],{"class":256}," pd.DataFrame({\n",[209,1016,1017,1020,1023,1025],{"class":211,"line":266},[209,1018,1019],{"class":219},"    \"value\"",[209,1021,1022],{"class":256},": items[",[209,1024,313],{"class":219},[209,1026,1027],{"class":256},"],\n",[209,1029,1030,1033,1035,1037,1040,1042,1045],{"class":211,"line":279},[209,1031,1032],{"class":219},"    \"ISBLANK\"",[209,1034,1022],{"class":256},[209,1036,313],{"class":219},[209,1038,1039],{"class":256},"].str.strip() ",[209,1041,617],{"class":252},[209,1043,1044],{"class":219}," \"\"",[209,1046,1047],{"class":256},",\n",[209,1049,1050,1053,1056,1058,1060,1062,1064,1066],{"class":211,"line":286},[209,1051,1052],{"class":219},"    \"ISNUMBER\"",[209,1054,1055],{"class":256},": pd.to_numeric(items[",[209,1057,313],{"class":219},[209,1059,881],{"class":256},[209,1061,884],{"class":304},[209,1063,292],{"class":252},[209,1065,889],{"class":219},[209,1067,1068],{"class":256},").notna(),\n",[209,1070,1071,1074,1076,1078,1081,1084,1087,1090,1093,1095],{"class":211,"line":337},[209,1072,1073],{"class":219},"    \"ISTEXT\"",[209,1075,1022],{"class":256},[209,1077,313],{"class":219},[209,1079,1080],{"class":256},"].map(",[209,1082,1083],{"class":252},"lambda",[209,1085,1086],{"class":256}," v: ",[209,1088,1089],{"class":319},"isinstance",[209,1091,1092],{"class":256},"(v, ",[209,1094,320],{"class":319},[209,1096,1097],{"class":256},")),\n",[209,1099,1100],{"class":211,"line":864},[209,1101,1102],{"class":256},"})\n",[209,1104,1105,1107,1110,1112,1114,1116],{"class":211,"line":894},[209,1106,417],{"class":319},[209,1108,1109],{"class":256},"(tests.to_string(",[209,1111,645],{"class":304},[209,1113,292],{"class":252},[209,1115,331],{"class":319},[209,1117,652],{"class":256},[209,1119,1120,1122,1125,1128],{"class":211,"line":924},[209,1121,417],{"class":319},[209,1123,1124],{"class":256},"(tests[[",[209,1126,1127],{"class":219},"\"ISNUMBER\"",[209,1129,1130],{"class":256},"]].sum().to_dict())\n",[10,1132,1133,1134,1138,1139,1142,1143,1145,1146,1149,1150,1153],{},"Two cautions come from how pandas stores data. A column read from Excel may hold numbers as text, so \"is this a number?\" is usually a question about whether the value ",[1135,1136,1137],"em",{},"converts"," — the ",[13,1140,1141],{},"to_numeric"," test — rather than about its Python type. And ",[13,1144,35],{}," is a float, so a strict type test such as ",[13,1147,1148],{},"isinstance(v, (int, float))"," counts missing values as numbers; combine it with ",[13,1151,1152],{},"notna()"," when that matters.",[195,1155,1157],{"id":1156},"when-a-function-raises-a-wrapper-that-records-failures","When a function raises: a wrapper that records failures",[10,1159,1160,1161,1164,1165,1167],{},"Some transformations have no vectorised form — parsing an unusual code, calling a helper that raises on bad input. Applied with ",[13,1162,1163],{},"map",", one bad value stops the whole column. A small wrapper is the closest pandas gets to ",[13,1166,15],{}," around a cell formula, with one improvement: it keeps a list of what failed and why:",[200,1169,1171],{"className":243,"code":1170,"language":245,"meta":205,"style":205},"def safe(func, errors):\n    def wrapped(value):\n        try:\n            return func(value)\n        except Exception as exc:\n            errors.append((value, f\"{type(exc).__name__}: {exc}\"))\n            return np.nan\n    return wrapped\n\ndef parse_sku(code):\n    prefix, number = code.split(\"-\")\n    return int(number) * (100 if prefix in \"AB\" else 1)\n\nproblems = []\nparsed = pd.Series([\"A-1\", \"B-2\", \"C3\", \"D-x\"]).map(safe(parse_sku, problems))\nprint(parsed.tolist())\nprint(problems)\n",[13,1172,1173,1185,1196,1203,1211,1223,1262,1269,1277,1281,1292,1308,1348,1353,1364,1396,1404],{"__ignoreMap":205},[209,1174,1175,1178,1182],{"class":211,"line":212},[209,1176,1177],{"class":252},"def",[209,1179,1181],{"class":1180},"s_Opv"," safe",[209,1183,1184],{"class":256},"(func, errors):\n",[209,1186,1187,1190,1193],{"class":211,"line":266},[209,1188,1189],{"class":252},"    def",[209,1191,1192],{"class":1180}," wrapped",[209,1194,1195],{"class":256},"(value):\n",[209,1197,1198,1201],{"class":211,"line":279},[209,1199,1200],{"class":252},"        try",[209,1202,819],{"class":256},[209,1204,1205,1208],{"class":211,"line":286},[209,1206,1207],{"class":252},"            return",[209,1209,1210],{"class":256}," func(value)\n",[209,1212,1213,1216,1219,1221],{"class":211,"line":337},[209,1214,1215],{"class":252},"        except",[209,1217,1218],{"class":319}," Exception",[209,1220,840],{"class":252},[209,1222,843],{"class":256},[209,1224,1225,1228,1231,1234,1237,1240,1243,1246,1249,1251,1253,1256,1258,1260],{"class":211,"line":864},[209,1226,1227],{"class":256},"            errors.append((value, ",[209,1229,1230],{"class":252},"f",[209,1232,1233],{"class":219},"\"",[209,1235,310],{"class":1236},"sSjpA",[209,1238,1239],{"class":319},"type",[209,1241,1242],{"class":256},"(exc).",[209,1244,1245],{"class":319},"__name__",[209,1247,1248],{"class":1236},"}",[209,1250,316],{"class":219},[209,1252,310],{"class":1236},[209,1254,1255],{"class":256},"exc",[209,1257,1248],{"class":1236},[209,1259,1233],{"class":219},[209,1261,652],{"class":256},[209,1263,1264,1266],{"class":211,"line":894},[209,1265,1207],{"class":252},[209,1267,1268],{"class":256}," np.nan\n",[209,1270,1271,1274],{"class":211,"line":924},[209,1272,1273],{"class":252},"    return",[209,1275,1276],{"class":256}," wrapped\n",[209,1278,1279],{"class":211,"line":951},[209,1280,283],{"emptyLinePlaceholder":282},[209,1282,1284,1286,1289],{"class":211,"line":1283},10,[209,1285,1177],{"class":252},[209,1287,1288],{"class":1180}," parse_sku",[209,1290,1291],{"class":256},"(code):\n",[209,1293,1295,1298,1300,1303,1306],{"class":211,"line":1294},11,[209,1296,1297],{"class":256},"    prefix, number ",[209,1299,292],{"class":252},[209,1301,1302],{"class":256}," code.split(",[209,1304,1305],{"class":219},"\"-\"",[209,1307,334],{"class":256},[209,1309,1311,1313,1316,1319,1322,1325,1328,1331,1334,1337,1340,1343,1346],{"class":211,"line":1310},12,[209,1312,1273],{"class":252},[209,1314,1315],{"class":319}," int",[209,1317,1318],{"class":256},"(number) ",[209,1320,1321],{"class":252},"*",[209,1323,1324],{"class":256}," (",[209,1326,1327],{"class":319},"100",[209,1329,1330],{"class":252}," if",[209,1332,1333],{"class":256}," prefix ",[209,1335,1336],{"class":252},"in",[209,1338,1339],{"class":219}," \"AB\"",[209,1341,1342],{"class":252}," else",[209,1344,1345],{"class":319}," 1",[209,1347,334],{"class":256},[209,1349,1351],{"class":211,"line":1350},13,[209,1352,283],{"emptyLinePlaceholder":282},[209,1354,1356,1359,1361],{"class":211,"line":1355},14,[209,1357,1358],{"class":256},"problems ",[209,1360,292],{"class":252},[209,1362,1363],{"class":256}," []\n",[209,1365,1367,1370,1372,1375,1378,1380,1383,1385,1388,1390,1393],{"class":211,"line":1366},15,[209,1368,1369],{"class":256},"parsed ",[209,1371,292],{"class":252},[209,1373,1374],{"class":256}," pd.Series([",[209,1376,1377],{"class":219},"\"A-1\"",[209,1379,301],{"class":256},[209,1381,1382],{"class":219},"\"B-2\"",[209,1384,301],{"class":256},[209,1386,1387],{"class":219},"\"C3\"",[209,1389,301],{"class":256},[209,1391,1392],{"class":219},"\"D-x\"",[209,1394,1395],{"class":256},"]).map(safe(parse_sku, problems))\n",[209,1397,1399,1401],{"class":211,"line":1398},16,[209,1400,417],{"class":319},[209,1402,1403],{"class":256},"(parsed.tolist())\n",[209,1405,1407,1409],{"class":211,"line":1406},17,[209,1408,417],{"class":319},[209,1410,1411],{"class":256},"(problems)\n",[10,1413,1414,1415,1417,1418,1421,1422,1425,1426,1428,1429,1431],{},"The two good codes parse, and the two bad ones become ",[13,1416,35],{}," with a recorded reason — a missing hyphen and a non-numeric part. Write ",[13,1419,1420],{},"problems"," to the job log or a review sheet. Catching ",[13,1423,1424],{},"Exception"," broadly is acceptable here only because every failure is recorded; a wrapper that returns ",[13,1427,35],{}," and forgets why is ",[13,1430,15],{}," again.",[195,1433,1435],{"id":1434},"step-5-do-not-reproduce-blanket-iferror","Step 5 — Do not reproduce blanket IFERROR",[10,1437,1438,1439,1442,1443,1446],{},"The most important translation is the one not to make. A workbook full of ",[13,1440,1441],{},"=IFERROR(..., 0)"," hides every kind of failure behind the same zero: missing prices, bad text, broken references. A pandas pipeline can do the same with a blanket ",[13,1444,1445],{},"fillna(0)"," at the end, and it is just as bad an idea. Keep each failure visible until you have decided what it means:",[200,1448,1450],{"className":243,"code":1449,"language":245,"meta":205,"style":205},"report = looked_up[[\"sku\", \"revenue\", \"units\", \"list_price\"]].copy()\nreport[\"price_per_unit\"] = (report[\"revenue\"] \u002F report[\"units\"]).replace([np.inf, -np.inf], np.nan)\nissues = {\n    \"no units sold\": int((report[\"units\"] == 0).sum()),\n    \"no list price\": int(report[\"list_price\"].isna().sum()),\n    \"unreadable quantity\": int(failed.sum()),\n}\nprint(issues)\n",[13,1451,1452,1479,1511,1521,1546,1563,1575,1580],{"__ignoreMap":205},[209,1453,1454,1457,1459,1462,1464,1466,1468,1470,1472,1474,1476],{"class":211,"line":212},[209,1455,1456],{"class":256},"report ",[209,1458,292],{"class":252},[209,1460,1461],{"class":256}," looked_up[[",[209,1463,423],{"class":219},[209,1465,301],{"class":256},[209,1467,399],{"class":219},[209,1469,301],{"class":256},[209,1471,409],{"class":219},[209,1473,301],{"class":256},[209,1475,573],{"class":219},[209,1477,1478],{"class":256},"]].copy()\n",[209,1480,1481,1484,1486,1488,1490,1493,1495,1497,1499,1502,1504,1507,1509],{"class":211,"line":266},[209,1482,1483],{"class":256},"report[",[209,1485,388],{"class":219},[209,1487,391],{"class":256},[209,1489,292],{"class":252},[209,1491,1492],{"class":256}," (report[",[209,1494,399],{"class":219},[209,1496,391],{"class":256},[209,1498,404],{"class":252},[209,1500,1501],{"class":256}," report[",[209,1503,409],{"class":219},[209,1505,1506],{"class":256},"]).replace([np.inf, ",[209,1508,472],{"class":252},[209,1510,475],{"class":256},[209,1512,1513,1516,1518],{"class":211,"line":279},[209,1514,1515],{"class":256},"issues ",[209,1517,292],{"class":252},[209,1519,1520],{"class":256}," {\n",[209,1522,1523,1526,1528,1531,1534,1536,1538,1540,1543],{"class":211,"line":286},[209,1524,1525],{"class":219},"    \"no units sold\"",[209,1527,316],{"class":256},[209,1529,1530],{"class":319},"int",[209,1532,1533],{"class":256},"((report[",[209,1535,409],{"class":219},[209,1537,391],{"class":256},[209,1539,617],{"class":252},[209,1541,1542],{"class":319}," 0",[209,1544,1545],{"class":256},").sum()),\n",[209,1547,1548,1551,1553,1555,1558,1560],{"class":211,"line":337},[209,1549,1550],{"class":219},"    \"no list price\"",[209,1552,316],{"class":256},[209,1554,1530],{"class":319},[209,1556,1557],{"class":256},"(report[",[209,1559,573],{"class":219},[209,1561,1562],{"class":256},"].isna().sum()),\n",[209,1564,1565,1568,1570,1572],{"class":211,"line":864},[209,1566,1567],{"class":219},"    \"unreadable quantity\"",[209,1569,316],{"class":256},[209,1571,1530],{"class":319},[209,1573,1574],{"class":256},"(failed.sum()),\n",[209,1576,1577],{"class":211,"line":894},[209,1578,1579],{"class":256},"}\n",[209,1581,1582,1584],{"class":211,"line":924},[209,1583,417],{"class":319},[209,1585,1586],{"class":256},"(issues)\n",[10,1588,1589,1590,1592,1593,1595,1596,1600],{},"Counting each kind of gap separately, and reporting it, is what ",[13,1591,15],{}," cannot do. The final display can still show ",[13,1594,70],{}," or \"n\u002Fa\" where readers expect it — ",[38,1597,1599],{"href":1598},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output\u002F","Control How NaN, None and Infinity Appear in Excel Output"," covers how — but the decision is made per column, at the edge, with the counts in the job log.",[48,1602,57,1607,57,1610,57,1613,57,1616,57,1621,57,1627,57,1632,57,1637,57,1642,57,1649,57,1653,57,1658,57,1662,57,1667,57,1671,57,1676,57,1679,57,1685,57,1690,57,1694,57,1698,57,1703,57,1706,57,1711,57,1714,57,1719,57,1722],{"viewBox":1603,"role":51,"ariaLabelledBy":1604,"xmlns":55,"style":56},"4 0 752 233",[1605,1606],"ife-blanket-t","ife-blanket-d",[59,1608,1609],{"id":1605},"Blanket IFERROR versus handling each failure",[63,1611,1612],{"id":1606},"Two panels. A blanket fill with zero turns missing prices, zero-unit divisions and unreadable quantities all into 0, which averages treat as real values and nobody can distinguish. Handling each failure keeps them as missing values, counts each kind separately, and fills only for display.",[67,1614],{"x":69,"y":70,"width":71,"height":1615,"fill":73},"233",[67,1617],{"x":76,"y":110,"width":1618,"height":1619,"rx":1620,"fill":170,"stroke":171,"style":83},"351.0","201.0","14",[85,1622,1624],{"transform":1623},"translate(182.5,29.0) scale(1.083)",[89,1625],{"d":91,"fill":92,"stroke":178,"style":1626},"stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[96,1628,1631],{"x":1629,"y":717,"style":1630},"195.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","fillna(0) everywhere",[211,1633],{"x1":1634,"y1":1635,"x2":1636,"y2":1635,"stroke":171,"style":698},"36.0","91.0","355.0",[96,1638,1641],{"x":1639,"y":737,"style":1640},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","missing price → 0",[85,1643,1645],{"transform":1644},"translate(38.5,99.5) scale(0.625)",[89,1646],{"d":1647,"fill":92,"stroke":171,"style":1648},"M6 6l12 12 M18 6L6 18","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[96,1650,1652],{"x":1639,"y":1651,"style":1640},"137.0","0 \u002F 0 → 0",[85,1654,1656],{"transform":1655},"translate(38.5,125.5) scale(0.625)",[89,1657],{"d":1647,"fill":92,"stroke":171,"style":1648},[96,1659,1661],{"x":1639,"y":1660,"style":1640},"163.0","\"four\" → 0",[85,1663,1665],{"transform":1664},"translate(38.5,151.5) scale(0.625)",[89,1666],{"d":1647,"fill":92,"stroke":171,"style":1648},[96,1668,1670],{"x":1639,"y":1669,"style":1640},"189.0","all look like real zeros",[85,1672,1674],{"transform":1673},"translate(38.5,177.5) scale(0.625)",[89,1675],{"d":1647,"fill":92,"stroke":171,"style":1648},[67,1677],{"x":1678,"y":110,"width":1618,"height":1619,"rx":1620,"fill":144,"stroke":145,"style":83},"389.0",[85,1680,1682],{"transform":1681},"translate(551.5,29.0) scale(1.083)",[89,1683],{"d":1684,"fill":92,"stroke":152,"style":1626},"M5 12.5l4.5 4.5L19 7",[96,1686,1689],{"x":1687,"y":717,"style":1688},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Handle each case",[211,1691],{"x1":1692,"y1":1635,"x2":1693,"y2":1635,"stroke":145,"style":698},"405.0","724.0",[96,1695,1697],{"x":1696,"y":737,"style":1640},"429.0","inf replaced, NaN kept",[85,1699,1701],{"transform":1700},"translate(407.5,99.5) scale(0.625)",[89,1702],{"d":1684,"fill":92,"stroke":145,"style":1648},[96,1704,1705],{"x":1696,"y":1651,"style":1640},"lookup misses flagged",[85,1707,1709],{"transform":1708},"translate(407.5,125.5) scale(0.625)",[89,1710],{"d":1684,"fill":92,"stroke":145,"style":1648},[96,1712,1713],{"x":1696,"y":1660,"style":1640},"bad text listed",[85,1715,1717],{"transform":1716},"translate(407.5,151.5) scale(0.625)",[89,1718],{"d":1684,"fill":92,"stroke":145,"style":1648},[96,1720,1721],{"x":1696,"y":1669,"style":1640},"fill only for display",[85,1723,1725],{"transform":1724},"translate(407.5,177.5) scale(0.625)",[89,1726],{"d":1684,"fill":92,"stroke":145,"style":1648},[195,1728,1730],{"id":1729},"common-pitfalls","Common pitfalls",[1732,1733,1734,1748,1757,1771],"ul",{},[1735,1736,1737,1741,1742,1744,1745,1747],"li",{},[1738,1739,1740],"strong",{},"Expecting pandas to raise on division by zero."," It returns ",[13,1743,31],{}," or ",[13,1746,35],{},"; replace infinities deliberately.",[1735,1749,1750,1753,1754,1756],{},[1738,1751,1752],{},"Filling lookup misses in numeric columns with text."," The column becomes ",[13,1755,607],{}," and arithmetic breaks; fill a separate display column.",[1735,1758,1759,1764,1765,1767,1768,1770],{},[1738,1760,1761,1763],{},[13,1762,799],{}," without a report."," Values vanish into ",[13,1766,35],{}," just as they would behind ",[13,1769,15],{},"; always list what was coerced, and fail the job when the list contains something new.",[1735,1772,1773,1776,1777,1780,1781,301,1783,975,1786,1789,1790,1792,1793,1795],{},[1738,1774,1775],{},"Default NA strings."," ",[13,1778,1779],{},"read_excel"," turns ",[13,1782,235],{},[13,1784,1785],{},"NA",[13,1787,1788],{},"NULL"," into ",[13,1791,35],{}," before your code sees them; use ",[13,1794,354],{}," when you need to know.",[195,1797,1799],{"id":1798},"performance-notes","Performance notes",[10,1801,1802,1803,301,1806,1808,1809,1812,1813,1816],{},"All of these operations are vectorised: division, ",[13,1804,1805],{},"replace",[13,1807,1141],{}," with coercion and ",[13,1810,1811],{},"isna"," handle millions of values in milliseconds. The type test with ",[13,1814,1815],{},"map(isinstance)"," runs in Python per value and is the slowest; use it only on columns that genuinely mix types.",[195,1818,1820],{"id":1819},"conclusion","Conclusion",[10,1822,1823,1824,1826,1827,975,1829,1831,1832,1834,1835,1837,1838,1840,1841,1843,1844,1847],{},"pandas has no error values, so ",[13,1825,15],{}," becomes three separate decisions: replace the ",[13,1828,31],{},[13,1830,35],{}," that division by zero produces, fill lookup misses after a left merge (using ",[13,1833,657],{}," to tell misses from blank matches), and coerce failed conversions with ",[13,1836,187],{}," while listing what failed. The ",[13,1839,45],{}," functions become column tests with ",[13,1842,1811],{},", stripped-string comparisons and ",[13,1845,1846],{},"to_numeric(...).notna()",". Keep every failure visible and counted, and fill for display only at the end.",[195,1849,1851],{"id":1850},"frequently-asked-questions","Frequently asked questions",[10,1853,1854,1857],{},[1738,1855,1856],{},"What is the pandas equivalent of IFERROR?","\nThere is no single one, because pandas does not produce error values. Division by zero gives inf or NaN, a failed lookup gives NaN, and a failed conversion raises or, with errors=\"coerce\", gives NaN. Handle each case where it arises: replace infinities, fill lookup misses, coerce conversions.",[10,1859,1860,1863],{},[1738,1861,1862],{},"What is the difference between IFERROR and IFNA?","\nIFNA catches only #N\u002FA, the error a lookup returns when it finds no match. IFERROR catches every error, including #DIV\u002F0!, #VALUE! and #REF!. IFNA is the safer choice for lookups because other errors still show.",[10,1865,1866,1869],{},[1738,1867,1868],{},"How do I test whether a value is a number, like ISNUMBER?","\nFor a column of mixed values, pd.to_numeric(col, errors=\"coerce\").notna() is true where the value can be read as a number. To test the Python type strictly, use col.map(lambda v: isinstance(v, (int, float))) and remember that NaN is a float.",[10,1871,1872,1875],{},[1738,1873,1874],{},"Is a cell with spaces blank?","\nISBLANK says no, and so does isna(). If spaces should count as blank, test col.isna() | (col.astype(\"string\").str.strip() == \"\").",[195,1877,1879],{"id":1878},"related","Related",[1732,1881,1882,1888,1895,1902],{},[1735,1883,1884,1885,1887],{},"Up one level: ",[38,1886,41],{"href":40}," — every function, translated.",[1735,1889,1890,1894],{},[38,1891,1893],{"href":1892},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns\u002F","Excel IF Formulas as pandas Conditional Columns"," — the conditions around the error handling.",[1735,1896,1897,1901],{},[38,1898,1900],{"href":1899},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Ffind-formula-errors-in-a-workbook-with-python\u002F","Find #REF!, #DIV\u002F0! and Other Formula Errors in a Workbook"," — errors inside workbooks you receive.",[1735,1903,1904,1908],{},[38,1905,1907],{"href":1906},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas\u002F","Convert Excel Text Columns to Numbers with pandas"," — conversion in depth.",[1910,1911,1912],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_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":205,"searchDepth":266,"depth":266,"links":1914},[1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926],{"id":197,"depth":266,"text":198},{"id":361,"depth":266,"text":362},{"id":501,"depth":266,"text":502},{"id":792,"depth":266,"text":793},{"id":991,"depth":266,"text":992},{"id":1156,"depth":266,"text":1157},{"id":1434,"depth":266,"text":1435},{"id":1729,"depth":266,"text":1730},{"id":1798,"depth":266,"text":1799},{"id":1819,"depth":266,"text":1820},{"id":1850,"depth":266,"text":1851},{"id":1878,"depth":266,"text":1879},"2026-09-25","In pandas, a division by zero is inf, a failed lookup is NaN and bad text fails to_numeric — there are no error cells to catch. Translate IFERROR, IFNA, ISNA, ISBLANK, ISNUMBER and ISTEXT into the pandas operations that match each case, and keep IFERROR's habit of hiding real problems out of your pipeline.","md",[1931,1933,1935,1937],{"q":1856,"a":1932},"There is no single one, because pandas does not produce error values. Division by zero gives inf or NaN, a failed lookup gives NaN, and a failed conversion raises or, with errors=\"coerce\", gives NaN. Handle each case where it arises: replace infinities, fill lookup misses, coerce conversions.",{"q":1862,"a":1934},"IFNA catches only #N\u002FA, the error a lookup returns when it finds no match. IFERROR catches every error, including #DIV\u002F0!, #VALUE! and #REF!. IFNA is the safer choice for lookups because other errors still show.",{"q":1868,"a":1936},"For a column of mixed values, pd.to_numeric(col, errors=\"coerce\").notna() is true where the value can be read as a number. To test the Python type strictly, use col.map(lambda v: isinstance(v, (int, float))) and remember that NaN is a float.",{"q":1874,"a":1938},"ISBLANK says no, and so does isna(). If spaces should count as blank, test col.isna() | (col.astype(\"string\").str.strip() == \"\").","iferror and isna equivalents in pandas",null,{"breadcrumb":1942},[1943,1945,1948],{"name":1944,"item":404},"Home",{"name":1946,"item":1947},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":41,"item":40},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas",{"title":1951,"description":1952},"IFERROR and IFNA in pandas (plus ISNA, ISBLANK, ISNUMBER, ISTEXT)","Excel IFERROR and IFNA equivalents in pandas: division by zero as inf and NaN, failed lookups after merge, to_numeric with errors coerce, and ISNA, ISBLANK, ISNUMBER and ISTEXT as vectorised tests.","iferror-and-isna-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas\u002Findex",[1956,1957,708],"formula-equivalents","missing-data","how-to","mMl8YX8EKqSq3GcZEpRjffGC21tJjW2bh2BkPpqJrAE",[1961,1965],{"title":1962,"path":1963,"stem":1964,"children":-1},"Excel GROUPBY and PIVOTBY Function Equivalents in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas\u002Findex",{"title":1966,"path":1967,"stem":1968,"children":-1},"INDEX MATCH Equivalent in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findex-match-equivalent-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findex-match-equivalent-in-pandas\u002Findex",[1970,1972],{"path":1971,"title":41},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":1973,"title":1946},"\u002Fadvanced-data-transformation-and-cleaning",[],[1976,1978,1981,1984,1987,1990],{"path":1977,"title":676,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files",{"path":1979,"title":1980,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas","Extract Numbers and Codes from Text with pandas",{"path":1982,"title":1983,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fsplit-full-names-into-first-and-last-names-with-pandas","Split Full Names into First and Last Names with pandas",{"path":1985,"title":1986,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":1988,"title":1989,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas","Calculate Percent of Total per Group in pandas",{"path":1991,"title":1992,"kind":1958},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcount-distinct-values-per-group-in-pandas","Count Distinct Values per Group in pandas",1790972088276]