[{"data":1,"prerenderedAt":2198},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns":2189},{"id":4,"title":5,"body":6,"dateModified":2162,"datePublished":2162,"description":2163,"extension":2164,"faq":2165,"meta":2174,"navigation":228,"path":2182,"seo":2183,"slug":2185,"stem":2186,"type":2187,"__hash__":2188},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns\u002Findex.md","Excel IF Formulas as pandas Conditional Columns",{"type":7,"value":8,"toc":2147},"minimark",[9,28,152,157,187,396,400,525,531,585,588,592,690,696,835,838,841,962,966,972,1099,1109,1114,1118,1121,1234,1240,1244,1247,1314,1320,1324,1335,1459,1466,1477,1481,1484,1708,1719,1723,1844,1848,1921,1935,2041,2050,2054,2072,2076,2083,2089,2095,2101,2105,2143],[10,11,12,13,17,18,21,22,27],"p",{},"A column of nested IFs is the most common thing in a mature spreadsheet and the one that translates\nmost cleanly. pandas separates the two cases Excel bundles together: a single condition becomes\n",[14,15,16],"code",{},"np.where",", and a ladder of them becomes ",[14,19,20],{},"np.select"," — with the useful property that the conditions\nstay readable however many there are. This guide is part of\n",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",".",[29,30,38,39,38,43,38,47,38,54,38,64,38,71,38,79,38,84,38,89,38,94,38,99,38,105,38,109,38,113,38,116,38,118,38,121,38,125,38,130,38,135,38,138,38,140,38,143,38,147],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"0 0 760 264","img",[34,35],"if-pick-t","if-pick-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"Which conditional tool the formula maps onto",[44,45,46],"desc",{"id":35},"A single condition becomes np.where, a ladder of conditions becomes np.select, and thresholds on one numeric column are better expressed as pd.cut with labelled bins.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","760","264","#ffffff",[48,55],{"x":56,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},"230.0","26","300","56","12","#ebebfd","var(--brand,#5b5cf0)","stroke-width:2px",[65,66,70],"text",{"x":67,"y":68,"style":69},"380.0","60","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","How many conditions?",[48,72],{"x":73,"y":74,"width":75,"height":76,"rx":60,"fill":77,"stroke":78,"style":63},"18.0","148","226.7","84","#d9f4f1","var(--teal,#0f9488)",[65,80,16],{"x":81,"y":82,"style":83},"131.35","182","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[65,85,88],{"x":81,"y":86,"style":87},"204","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","condition, true, false",[90,91],"line",{"x1":81,"y1":92,"x2":81,"y2":93,"stroke":62,"style":63},"87","141",[95,96],"polygon",{"points":97,"fill":98},"131.35,141 126.35,132 136.35,132","#5b5cf0",[65,100,104],{"x":101,"y":102,"style":103},"137.35","116","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","one",[48,106],{"x":107,"y":74,"width":75,"height":76,"rx":60,"fill":108,"stroke":62,"style":63},"266.7","#f0f4ff",[65,110,20],{"x":111,"y":82,"style":112},"380.04999999999995","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[65,114,115],{"x":111,"y":86,"style":87},"first match wins",[90,117],{"x1":111,"y1":92,"x2":111,"y2":93,"stroke":62,"style":63},[95,119],{"points":120,"fill":98},"380.04999999999995,141 375.04999999999995,132 385.04999999999995,132",[65,122,124],{"x":123,"y":102,"style":103},"386.04999999999995","several",[48,126],{"x":127,"y":74,"width":75,"height":76,"rx":60,"fill":128,"stroke":129,"style":63},"515.4","#fdefd8","var(--gold,#b4740a)",[65,131,134],{"x":132,"y":82,"style":133},"628.75","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","pd.cut",[65,136,137],{"x":132,"y":86,"style":87},"thresholds stay data",[90,139],{"x1":132,"y1":92,"x2":132,"y2":93,"stroke":62,"style":63},[95,141],{"points":142,"fill":98},"628.75,141 623.75,132 633.75,132",[65,144,146],{"x":145,"y":102,"style":103},"634.75","numeric bands",[65,148,151],{"x":67,"y":149,"style":150},"252","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","the shape of the condition picks the tool",[153,154,156],"h2",{"id":155},"prerequisites","Prerequisites",[158,159,164],"pre",{"className":160,"code":161,"language":162,"meta":163,"style":163},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas numpy openpyxl\n","bash","",[14,165,166],{"__ignoreMap":163},[167,168,170,174,178,181,184],"span",{"class":90,"line":169},1,[167,171,173],{"class":172},"sMTad","pip",[167,175,177],{"class":176},"srMev"," install",[167,179,180],{"class":176}," pandas",[167,182,183],{"class":176}," numpy",[167,185,186],{"class":176}," openpyxl\n",[158,188,192],{"className":189,"code":190,"language":191,"meta":163,"style":163},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\ndeals = pd.DataFrame({\n    \"Deal\": [\"D-1\", \"D-2\", \"D-3\", \"D-4\", \"D-5\", \"D-6\"],\n    \"Revenue\": [24500.0, 9800.0, 15320.0, 3010.0, np.nan, 41200.0],\n    \"Region\": [\"North\", \"South\", \"North\", \"West\", \"South\", \"North\"],\n    \"Days_Open\": [12, 91, 45, 7, 33, 128],\n})\n","python",[14,193,194,210,223,230,242,283,318,353,390],{"__ignoreMap":163},[167,195,196,200,204,207],{"class":90,"line":169},[167,197,199],{"class":198},"s-kum","import",[167,201,203],{"class":202},"skGVy"," numpy ",[167,205,206],{"class":198},"as",[167,208,209],{"class":202}," np\n",[167,211,213,215,218,220],{"class":90,"line":212},2,[167,214,199],{"class":198},[167,216,217],{"class":202}," pandas ",[167,219,206],{"class":198},[167,221,222],{"class":202}," pd\n",[167,224,226],{"class":90,"line":225},3,[167,227,229],{"emptyLinePlaceholder":228},true,"\n",[167,231,233,236,239],{"class":90,"line":232},4,[167,234,235],{"class":202},"deals ",[167,237,238],{"class":198},"=",[167,240,241],{"class":202}," pd.DataFrame({\n",[167,243,245,248,251,254,257,260,262,265,267,270,272,275,277,280],{"class":90,"line":244},5,[167,246,247],{"class":176},"    \"Deal\"",[167,249,250],{"class":202},": [",[167,252,253],{"class":176},"\"D-1\"",[167,255,256],{"class":202},", ",[167,258,259],{"class":176},"\"D-2\"",[167,261,256],{"class":202},[167,263,264],{"class":176},"\"D-3\"",[167,266,256],{"class":202},[167,268,269],{"class":176},"\"D-4\"",[167,271,256],{"class":202},[167,273,274],{"class":176},"\"D-5\"",[167,276,256],{"class":202},[167,278,279],{"class":176},"\"D-6\"",[167,281,282],{"class":202},"],\n",[167,284,286,289,291,295,297,300,302,305,307,310,313,316],{"class":90,"line":285},6,[167,287,288],{"class":176},"    \"Revenue\"",[167,290,250],{"class":202},[167,292,294],{"class":293},"sP0c6","24500.0",[167,296,256],{"class":202},[167,298,299],{"class":293},"9800.0",[167,301,256],{"class":202},[167,303,304],{"class":293},"15320.0",[167,306,256],{"class":202},[167,308,309],{"class":293},"3010.0",[167,311,312],{"class":202},", np.nan, ",[167,314,315],{"class":293},"41200.0",[167,317,282],{"class":202},[167,319,321,324,326,329,331,334,336,338,340,343,345,347,349,351],{"class":90,"line":320},7,[167,322,323],{"class":176},"    \"Region\"",[167,325,250],{"class":202},[167,327,328],{"class":176},"\"North\"",[167,330,256],{"class":202},[167,332,333],{"class":176},"\"South\"",[167,335,256],{"class":202},[167,337,328],{"class":176},[167,339,256],{"class":202},[167,341,342],{"class":176},"\"West\"",[167,344,256],{"class":202},[167,346,333],{"class":176},[167,348,256],{"class":202},[167,350,328],{"class":176},[167,352,282],{"class":202},[167,354,356,359,361,363,365,368,370,373,375,378,380,383,385,388],{"class":90,"line":355},8,[167,357,358],{"class":176},"    \"Days_Open\"",[167,360,250],{"class":202},[167,362,60],{"class":293},[167,364,256],{"class":202},[167,366,367],{"class":293},"91",[167,369,256],{"class":202},[167,371,372],{"class":293},"45",[167,374,256],{"class":202},[167,376,377],{"class":293},"7",[167,379,256],{"class":202},[167,381,382],{"class":293},"33",[167,384,256],{"class":202},[167,386,387],{"class":293},"128",[167,389,282],{"class":202},[167,391,393],{"class":90,"line":392},9,[167,394,395],{"class":202},"})\n",[153,397,399],{"id":398},"one-condition-npwhere","One condition: np.where",[158,401,403],{"className":189,"code":402,"language":191,"meta":163,"style":163},"# =IF(B2>10000, \"Large\", \"Standard\")\ndeals[\"Size\"] = np.where(deals[\"Revenue\"] > 10000, \"Large\", \"Standard\")\n\n# =IF(D2>90, \"Stale\", \"Active\")\ndeals[\"Status\"] = np.where(deals[\"Days_Open\"] > 90, \"Stale\", \"Active\")\nprint(deals[[\"Deal\", \"Revenue\", \"Size\", \"Days_Open\", \"Status\"]])\n",[14,404,405,411,451,455,460,495],{"__ignoreMap":163},[167,406,407],{"class":90,"line":169},[167,408,410],{"class":409},"s-wDw","# =IF(B2>10000, \"Large\", \"Standard\")\n",[167,412,413,416,419,422,424,427,430,432,435,438,440,443,445,448],{"class":90,"line":212},[167,414,415],{"class":202},"deals[",[167,417,418],{"class":176},"\"Size\"",[167,420,421],{"class":202},"] ",[167,423,238],{"class":198},[167,425,426],{"class":202}," np.where(deals[",[167,428,429],{"class":176},"\"Revenue\"",[167,431,421],{"class":202},[167,433,434],{"class":198},">",[167,436,437],{"class":293}," 10000",[167,439,256],{"class":202},[167,441,442],{"class":176},"\"Large\"",[167,444,256],{"class":202},[167,446,447],{"class":176},"\"Standard\"",[167,449,450],{"class":202},")\n",[167,452,453],{"class":90,"line":225},[167,454,229],{"emptyLinePlaceholder":228},[167,456,457],{"class":90,"line":232},[167,458,459],{"class":409},"# =IF(D2>90, \"Stale\", \"Active\")\n",[167,461,462,464,467,469,471,473,476,478,480,483,485,488,490,493],{"class":90,"line":244},[167,463,415],{"class":202},[167,465,466],{"class":176},"\"Status\"",[167,468,421],{"class":202},[167,470,238],{"class":198},[167,472,426],{"class":202},[167,474,475],{"class":176},"\"Days_Open\"",[167,477,421],{"class":202},[167,479,434],{"class":198},[167,481,482],{"class":293}," 90",[167,484,256],{"class":202},[167,486,487],{"class":176},"\"Stale\"",[167,489,256],{"class":202},[167,491,492],{"class":176},"\"Active\"",[167,494,450],{"class":202},[167,496,497,500,503,506,508,510,512,514,516,518,520,522],{"class":90,"line":285},[167,498,499],{"class":293},"print",[167,501,502],{"class":202},"(deals[[",[167,504,505],{"class":176},"\"Deal\"",[167,507,256],{"class":202},[167,509,429],{"class":176},[167,511,256],{"class":202},[167,513,418],{"class":176},[167,515,256],{"class":202},[167,517,475],{"class":176},[167,519,256],{"class":202},[167,521,466],{"class":176},[167,523,524],{"class":202},"]])\n",[10,526,527,530],{},[14,528,529],{},"np.where(condition, if_true, if_false)"," reads in the same order as the Excel function and evaluates\nthe whole column at once. Both branches can be columns rather than constants, which covers the\nformula that picks between two values:",[158,532,534],{"className":189,"code":533,"language":191,"meta":163,"style":163},"# =IF(C2=\"North\", B2*1.1, B2)\ndeals[\"Adjusted\"] = np.where(deals[\"Region\"] == \"North\", deals[\"Revenue\"] * 1.1, deals[\"Revenue\"])\n",[14,535,536,541],{"__ignoreMap":163},[167,537,538],{"class":90,"line":169},[167,539,540],{"class":409},"# =IF(C2=\"North\", B2*1.1, B2)\n",[167,542,543,545,548,550,552,554,557,559,562,565,568,570,572,575,578,580,582],{"class":90,"line":212},[167,544,415],{"class":202},[167,546,547],{"class":176},"\"Adjusted\"",[167,549,421],{"class":202},[167,551,238],{"class":198},[167,553,426],{"class":202},[167,555,556],{"class":176},"\"Region\"",[167,558,421],{"class":202},[167,560,561],{"class":198},"==",[167,563,564],{"class":176}," \"North\"",[167,566,567],{"class":202},", deals[",[167,569,429],{"class":176},[167,571,421],{"class":202},[167,573,574],{"class":198},"*",[167,576,577],{"class":293}," 1.1",[167,579,567],{"class":202},[167,581,429],{"class":176},[167,583,584],{"class":202},"])\n",[10,586,587],{},"One difference from Excel is worth knowing: both branches are computed for every row before the\nselection happens. That is harmless for arithmetic and matters when a branch would raise — dividing\nby a column containing zeros, for instance — in which case guard the operation rather than relying on\nthe condition to skip it.",[153,589,591],{"id":590},"nested-ifs-npselect","Nested IFs: np.select",[29,593,38,598,38,601,38,604,38,607,38,616,38,622,38,628,38,633,38,637,38,641,38,644,38,647,38,651,38,654,38,657,38,660,38,666,38,671,38,676,38,679,38,683,38,686],{"viewBox":594,"role":32,"ariaLabelledBy":595,"xmlns":36,"style":37},"0 0 760 201",[596,597],"if-ladder-t","if-ladder-d",[40,599,600],{"id":596},"A nested IF against a list of conditions",[44,602,603],{"id":597},"A nested IF becomes unreadable by its third level and requires re-nesting to add a band, while np.select keeps the conditions and outcomes as two aligned lists that can be printed and tested.",[48,605],{"x":50,"y":50,"width":51,"height":606,"fill":53},"201",[48,608],{"x":609,"y":610,"width":611,"height":612,"rx":613,"fill":614,"stroke":615,"style":63},"20","28","270.0","139","14","#fee8f2","var(--accent,#d81b73)",[65,617,621],{"x":618,"y":619,"style":620},"155.0","54","font-size:13px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:middle","nested IF",[90,623],{"x1":624,"y1":625,"x2":626,"y2":625,"stroke":615,"style":627},"36","64","274.0","stroke-width:1px",[65,629,632],{"x":618,"y":630,"style":631},"86","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","unreadable at level 3",[65,634,636],{"x":618,"y":635,"style":631},"109","re-nest to add a band",[65,638,640],{"x":618,"y":639,"style":631},"132","cannot be tested",[48,642],{"x":643,"y":610,"width":611,"height":612,"rx":613,"fill":77,"stroke":78,"style":63},"470.0",[65,645,20],{"x":646,"y":619,"style":83},"605.0",[90,648],{"x1":649,"y1":625,"x2":650,"y2":625,"stroke":78,"style":627},"486.0","724.0",[65,652,653],{"x":646,"y":630,"style":631},"two aligned lists",[65,655,656],{"x":646,"y":635,"style":631},"add one entry",[65,658,659],{"x":646,"y":639,"style":631},"printable and testable",[48,661],{"x":662,"y":663,"width":387,"height":664,"rx":665,"fill":61,"stroke":62},"316.0","78.5","38","19",[65,667,670],{"x":67,"y":668,"style":669},"102.5","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","same order",[90,672],{"x1":673,"y1":674,"x2":675,"y2":674,"stroke":62,"style":63},"295.0","97.5","309.0",[95,677],{"points":678,"fill":98},"309.0,97.5 300.0,92.5 300.0,102.5",[90,680],{"x1":681,"y1":674,"x2":682,"y2":674,"stroke":62,"style":63},"449.0","463.0",[95,684],{"points":685,"fill":98},"463.0,97.5 454.0,92.5 454.0,102.5",[65,687,689],{"x":67,"y":688,"style":150},"187","first match wins in both",[10,691,692,693,695],{},"A nested IF ladder is unreadable by its third level and unmaintainable by its fifth. ",[14,694,20],{},"\ntakes the conditions as a list and the outcomes as another, in the same order, and the first match\nwins.",[158,697,699],{"className":189,"code":698,"language":191,"meta":163,"style":163},"# =IFS(B2>40000,\"A\", B2>20000,\"B\", B2>10000,\"C\", TRUE,\"D\")\nconditions = [\n    deals[\"Revenue\"] > 40000,\n    deals[\"Revenue\"] > 20000,\n    deals[\"Revenue\"] > 10000,\n]\ngrades = [\"A\", \"B\", \"C\"]\ndeals[\"Grade\"] = np.select(conditions, grades, default=\"D\")\nprint(deals[[\"Deal\", \"Revenue\", \"Grade\"]])\n",[14,700,701,706,716,733,748,762,767,792,817],{"__ignoreMap":163},[167,702,703],{"class":90,"line":169},[167,704,705],{"class":409},"# =IFS(B2>40000,\"A\", B2>20000,\"B\", B2>10000,\"C\", TRUE,\"D\")\n",[167,707,708,711,713],{"class":90,"line":212},[167,709,710],{"class":202},"conditions ",[167,712,238],{"class":198},[167,714,715],{"class":202}," [\n",[167,717,718,721,723,725,727,730],{"class":90,"line":225},[167,719,720],{"class":202},"    deals[",[167,722,429],{"class":176},[167,724,421],{"class":202},[167,726,434],{"class":198},[167,728,729],{"class":293}," 40000",[167,731,732],{"class":202},",\n",[167,734,735,737,739,741,743,746],{"class":90,"line":232},[167,736,720],{"class":202},[167,738,429],{"class":176},[167,740,421],{"class":202},[167,742,434],{"class":198},[167,744,745],{"class":293}," 20000",[167,747,732],{"class":202},[167,749,750,752,754,756,758,760],{"class":90,"line":244},[167,751,720],{"class":202},[167,753,429],{"class":176},[167,755,421],{"class":202},[167,757,434],{"class":198},[167,759,437],{"class":293},[167,761,732],{"class":202},[167,763,764],{"class":90,"line":285},[167,765,766],{"class":202},"]\n",[167,768,769,772,774,777,780,782,785,787,790],{"class":90,"line":320},[167,770,771],{"class":202},"grades ",[167,773,238],{"class":198},[167,775,776],{"class":202}," [",[167,778,779],{"class":176},"\"A\"",[167,781,256],{"class":202},[167,783,784],{"class":176},"\"B\"",[167,786,256],{"class":202},[167,788,789],{"class":176},"\"C\"",[167,791,766],{"class":202},[167,793,794,796,799,801,803,806,810,812,815],{"class":90,"line":355},[167,795,415],{"class":202},[167,797,798],{"class":176},"\"Grade\"",[167,800,421],{"class":202},[167,802,238],{"class":198},[167,804,805],{"class":202}," np.select(conditions, grades, ",[167,807,809],{"class":808},"sa561","default",[167,811,238],{"class":198},[167,813,814],{"class":176},"\"D\"",[167,816,450],{"class":202},[167,818,819,821,823,825,827,829,831,833],{"class":90,"line":392},[167,820,499],{"class":293},[167,822,502],{"class":202},[167,824,505],{"class":176},[167,826,256],{"class":202},[167,828,429],{"class":176},[167,830,256],{"class":202},[167,832,798],{"class":176},[167,834,524],{"class":202},[10,836,837],{},"Order matters exactly as it does in a nested IF: put the narrowest condition first, or a broader one\nabove it will claim the rows. The advantage over the formula is that the ladder is a list you can\nprint, count and test, and adding a band is one entry in two lists rather than a re-nesting exercise\nin a formula bar.",[10,839,840],{},"Conditions can span columns, which nested IFs make painful:",[158,842,844],{"className":189,"code":843,"language":191,"meta":163,"style":163},"deals[\"Flag\"] = np.select(\n    [\n        deals[\"Revenue\"].isna(),\n        (deals[\"Days_Open\"] > 90) & (deals[\"Revenue\"] > 20000),\n        deals[\"Days_Open\"] > 90,\n    ],\n    [\"Missing revenue\", \"Large and stale\", \"Stale\"],\n    default=\"OK\",\n)\n",[14,845,846,860,865,875,908,922,927,946,958],{"__ignoreMap":163},[167,847,848,850,853,855,857],{"class":90,"line":169},[167,849,415],{"class":202},[167,851,852],{"class":176},"\"Flag\"",[167,854,421],{"class":202},[167,856,238],{"class":198},[167,858,859],{"class":202}," np.select(\n",[167,861,862],{"class":90,"line":212},[167,863,864],{"class":202},"    [\n",[167,866,867,870,872],{"class":90,"line":225},[167,868,869],{"class":202},"        deals[",[167,871,429],{"class":176},[167,873,874],{"class":202},"].isna(),\n",[167,876,877,880,882,884,886,888,891,894,897,899,901,903,905],{"class":90,"line":232},[167,878,879],{"class":202},"        (deals[",[167,881,475],{"class":176},[167,883,421],{"class":202},[167,885,434],{"class":198},[167,887,482],{"class":293},[167,889,890],{"class":202},") ",[167,892,893],{"class":198},"&",[167,895,896],{"class":202}," (deals[",[167,898,429],{"class":176},[167,900,421],{"class":202},[167,902,434],{"class":198},[167,904,745],{"class":293},[167,906,907],{"class":202},"),\n",[167,909,910,912,914,916,918,920],{"class":90,"line":244},[167,911,869],{"class":202},[167,913,475],{"class":176},[167,915,421],{"class":202},[167,917,434],{"class":198},[167,919,482],{"class":293},[167,921,732],{"class":202},[167,923,924],{"class":90,"line":285},[167,925,926],{"class":202},"    ],\n",[167,928,929,932,935,937,940,942,944],{"class":90,"line":320},[167,930,931],{"class":202},"    [",[167,933,934],{"class":176},"\"Missing revenue\"",[167,936,256],{"class":202},[167,938,939],{"class":176},"\"Large and stale\"",[167,941,256],{"class":202},[167,943,487],{"class":176},[167,945,282],{"class":202},[167,947,948,951,953,956],{"class":90,"line":355},[167,949,950],{"class":808},"    default",[167,952,238],{"class":198},[167,954,955],{"class":176},"\"OK\"",[167,957,732],{"class":202},[167,959,960],{"class":90,"line":392},[167,961,450],{"class":202},[153,963,965],{"id":964},"bands-pdcut-instead-of-a-ladder","Bands: pd.cut instead of a ladder",[10,967,968,969,971],{},"When every condition is a numeric threshold on the same column, ",[14,970,134],{}," says so directly and keeps\nthe boundaries as data.",[158,973,975],{"className":189,"code":974,"language":191,"meta":163,"style":163},"deals[\"Band\"] = pd.cut(\n    deals[\"Revenue\"],\n    bins=[0, 10000, 20000, 40000, float(\"inf\")],\n    labels=[\"D\", \"C\", \"B\", \"A\"],\n    right=False,\n)\nprint(deals[[\"Deal\", \"Revenue\", \"Band\"]])\n",[14,976,977,991,999,1040,1065,1077,1081],{"__ignoreMap":163},[167,978,979,981,984,986,988],{"class":90,"line":169},[167,980,415],{"class":202},[167,982,983],{"class":176},"\"Band\"",[167,985,421],{"class":202},[167,987,238],{"class":198},[167,989,990],{"class":202}," pd.cut(\n",[167,992,993,995,997],{"class":90,"line":212},[167,994,720],{"class":202},[167,996,429],{"class":176},[167,998,282],{"class":202},[167,1000,1001,1004,1006,1009,1011,1013,1016,1018,1021,1023,1026,1028,1031,1034,1037],{"class":90,"line":225},[167,1002,1003],{"class":808},"    bins",[167,1005,238],{"class":198},[167,1007,1008],{"class":202},"[",[167,1010,50],{"class":293},[167,1012,256],{"class":202},[167,1014,1015],{"class":293},"10000",[167,1017,256],{"class":202},[167,1019,1020],{"class":293},"20000",[167,1022,256],{"class":202},[167,1024,1025],{"class":293},"40000",[167,1027,256],{"class":202},[167,1029,1030],{"class":293},"float",[167,1032,1033],{"class":202},"(",[167,1035,1036],{"class":176},"\"inf\"",[167,1038,1039],{"class":202},")],\n",[167,1041,1042,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063],{"class":90,"line":232},[167,1043,1044],{"class":808},"    labels",[167,1046,238],{"class":198},[167,1048,1008],{"class":202},[167,1050,814],{"class":176},[167,1052,256],{"class":202},[167,1054,789],{"class":176},[167,1056,256],{"class":202},[167,1058,784],{"class":176},[167,1060,256],{"class":202},[167,1062,779],{"class":176},[167,1064,282],{"class":202},[167,1066,1067,1070,1072,1075],{"class":90,"line":244},[167,1068,1069],{"class":808},"    right",[167,1071,238],{"class":198},[167,1073,1074],{"class":293},"False",[167,1076,732],{"class":202},[167,1078,1079],{"class":90,"line":285},[167,1080,450],{"class":202},[167,1082,1083,1085,1087,1089,1091,1093,1095,1097],{"class":90,"line":320},[167,1084,499],{"class":293},[167,1086,502],{"class":202},[167,1088,505],{"class":176},[167,1090,256],{"class":202},[167,1092,429],{"class":176},[167,1094,256],{"class":202},[167,1096,983],{"class":176},[167,1098,524],{"class":202},[10,1100,1101,1104,1105,1108],{},[14,1102,1103],{},"right=False"," makes each interval half-open — ",[14,1106,1107],{},"[0, 10000)"," — which removes the question of which band\na value exactly on a boundary falls into. That question is the source of most disagreements between\ntwo implementations of the same banding, and stating the answer in the call settles it.",[10,1110,1111,1113],{},[14,1112,134],{}," also returns a categorical with an order, so sorting and grouping by band behave sensibly\nrather than alphabetically. NaN stays NaN rather than falling into the lowest band, which is usually\ncorrect and always explicit.",[153,1115,1117],{"id":1116},"the-iferror-case","The IFERROR case",[10,1119,1120],{},"Excel wraps a formula in IFERROR because a division or a lookup can produce an error value that\npropagates. pandas has no error values — it has NaN, and operations that would error either raise or\nproduce NaN depending on how you ask.",[158,1122,1124],{"className":189,"code":1123,"language":191,"meta":163,"style":163},"# =IFERROR(B2\u002FD2, 0)\ndeals[\"Per_Day\"] = (deals[\"Revenue\"] \u002F deals[\"Days_Open\"]).fillna(0)\n\n# =IFERROR(VALUE(B2), 0) — text that should be numeric\nraw = pd.Series([\"1200\", \"n\u002Fa\", \"980.5\", \"\"])\nnumbers = pd.to_numeric(raw, errors=\"coerce\").fillna(0)\nprint(numbers.tolist())\n",[14,1125,1126,1131,1163,1167,1172,1202,1227],{"__ignoreMap":163},[167,1127,1128],{"class":90,"line":169},[167,1129,1130],{"class":409},"# =IFERROR(B2\u002FD2, 0)\n",[167,1132,1133,1135,1138,1140,1142,1144,1146,1148,1151,1154,1156,1159,1161],{"class":90,"line":212},[167,1134,415],{"class":202},[167,1136,1137],{"class":176},"\"Per_Day\"",[167,1139,421],{"class":202},[167,1141,238],{"class":198},[167,1143,896],{"class":202},[167,1145,429],{"class":176},[167,1147,421],{"class":202},[167,1149,1150],{"class":198},"\u002F",[167,1152,1153],{"class":202}," deals[",[167,1155,475],{"class":176},[167,1157,1158],{"class":202},"]).fillna(",[167,1160,50],{"class":293},[167,1162,450],{"class":202},[167,1164,1165],{"class":90,"line":225},[167,1166,229],{"emptyLinePlaceholder":228},[167,1168,1169],{"class":90,"line":232},[167,1170,1171],{"class":409},"# =IFERROR(VALUE(B2), 0) — text that should be numeric\n",[167,1173,1174,1177,1179,1182,1185,1187,1190,1192,1195,1197,1200],{"class":90,"line":244},[167,1175,1176],{"class":202},"raw ",[167,1178,238],{"class":198},[167,1180,1181],{"class":202}," pd.Series([",[167,1183,1184],{"class":176},"\"1200\"",[167,1186,256],{"class":202},[167,1188,1189],{"class":176},"\"n\u002Fa\"",[167,1191,256],{"class":202},[167,1193,1194],{"class":176},"\"980.5\"",[167,1196,256],{"class":202},[167,1198,1199],{"class":176},"\"\"",[167,1201,584],{"class":202},[167,1203,1204,1207,1209,1212,1215,1217,1220,1223,1225],{"class":90,"line":285},[167,1205,1206],{"class":202},"numbers ",[167,1208,238],{"class":198},[167,1210,1211],{"class":202}," pd.to_numeric(raw, ",[167,1213,1214],{"class":808},"errors",[167,1216,238],{"class":198},[167,1218,1219],{"class":176},"\"coerce\"",[167,1221,1222],{"class":202},").fillna(",[167,1224,50],{"class":293},[167,1226,450],{"class":202},[167,1228,1229,1231],{"class":90,"line":320},[167,1230,499],{"class":293},[167,1232,1233],{"class":202},"(numbers.tolist())\n",[10,1235,1236,1239],{},[14,1237,1238],{},"errors=\"coerce\""," is the closest thing to IFERROR in the library, and it is better than the formula\nbecause the intermediate NaN is visible. Counting them before filling turns \"the report shows zero\"\ninto \"eighteen rows had unparseable amounts\", which is a materially more useful thing to know.",[153,1241,1243],{"id":1242},"conditional-columns-from-a-lookup","Conditional columns from a lookup",[10,1245,1246],{},"An IF ladder that maps exact values — not ranges — is a dictionary in disguise, and writing it as one\nmakes the mapping editable without touching the logic.",[158,1248,1250],{"className":189,"code":1249,"language":191,"meta":163,"style":163},"owners = {\"North\": \"Ana\", \"South\": \"Ben\", \"West\": \"Dev\"}\ndeals[\"Owner\"] = deals[\"Region\"].map(owners).fillna(\"Unassigned\")\n",[14,1251,1252,1291],{"__ignoreMap":163},[167,1253,1254,1257,1259,1262,1264,1267,1270,1272,1274,1276,1279,1281,1283,1285,1288],{"class":90,"line":169},[167,1255,1256],{"class":202},"owners ",[167,1258,238],{"class":198},[167,1260,1261],{"class":202}," {",[167,1263,328],{"class":176},[167,1265,1266],{"class":202},": ",[167,1268,1269],{"class":176},"\"Ana\"",[167,1271,256],{"class":202},[167,1273,333],{"class":176},[167,1275,1266],{"class":202},[167,1277,1278],{"class":176},"\"Ben\"",[167,1280,256],{"class":202},[167,1282,342],{"class":176},[167,1284,1266],{"class":202},[167,1286,1287],{"class":176},"\"Dev\"",[167,1289,1290],{"class":202},"}\n",[167,1292,1293,1295,1298,1300,1302,1304,1306,1309,1312],{"class":90,"line":212},[167,1294,415],{"class":202},[167,1296,1297],{"class":176},"\"Owner\"",[167,1299,421],{"class":202},[167,1301,238],{"class":198},[167,1303,1153],{"class":202},[167,1305,556],{"class":176},[167,1307,1308],{"class":202},"].map(owners).fillna(",[167,1310,1311],{"class":176},"\"Unassigned\"",[167,1313,450],{"class":202},[10,1315,1316,1317,1319],{},"Keeping the mapping in a dictionary — or better, in a small reference sheet read at run time — means\na new region is a data change rather than a code change. That distinction is what stops a reporting\nscript from needing a developer every quarter, and it applies just as much to the thresholds in\n",[14,1318,134],{}," as to the labels here.",[153,1321,1323],{"id":1322},"where-nan-falls-in-a-condition","Where NaN falls in a condition",[10,1325,1326,1327,1330,1331,1334],{},"Missing values deserve their own treatment because they behave differently from anything in Excel.\nEvery comparison against NaN is False — ",[14,1328,1329],{},"NaN > 10000"," is False, and so is ",[14,1332,1333],{},"NaN \u003C= 10000"," — which\nmeans a row with a missing value quietly lands in whichever branch is the fallback.",[158,1336,1338],{"className":189,"code":1337,"language":191,"meta":163,"style":163},"import numpy as np\n\n# Both comparisons are False for the NaN row, so it becomes \"Standard\"\ndeals[\"Size\"] = np.where(deals[\"Revenue\"] > 10000, \"Large\", \"Standard\")\n\n# Better: say what a missing value means, first\ndeals[\"Size\"] = np.select(\n    [deals[\"Revenue\"].isna(), deals[\"Revenue\"] > 10000],\n    [\"Unknown\", \"Large\"],\n    default=\"Standard\",\n)\n",[14,1339,1340,1350,1354,1359,1389,1393,1398,1410,1430,1443,1454],{"__ignoreMap":163},[167,1341,1342,1344,1346,1348],{"class":90,"line":169},[167,1343,199],{"class":198},[167,1345,203],{"class":202},[167,1347,206],{"class":198},[167,1349,209],{"class":202},[167,1351,1352],{"class":90,"line":212},[167,1353,229],{"emptyLinePlaceholder":228},[167,1355,1356],{"class":90,"line":225},[167,1357,1358],{"class":409},"# Both comparisons are False for the NaN row, so it becomes \"Standard\"\n",[167,1360,1361,1363,1365,1367,1369,1371,1373,1375,1377,1379,1381,1383,1385,1387],{"class":90,"line":232},[167,1362,415],{"class":202},[167,1364,418],{"class":176},[167,1366,421],{"class":202},[167,1368,238],{"class":198},[167,1370,426],{"class":202},[167,1372,429],{"class":176},[167,1374,421],{"class":202},[167,1376,434],{"class":198},[167,1378,437],{"class":293},[167,1380,256],{"class":202},[167,1382,442],{"class":176},[167,1384,256],{"class":202},[167,1386,447],{"class":176},[167,1388,450],{"class":202},[167,1390,1391],{"class":90,"line":244},[167,1392,229],{"emptyLinePlaceholder":228},[167,1394,1395],{"class":90,"line":285},[167,1396,1397],{"class":409},"# Better: say what a missing value means, first\n",[167,1399,1400,1402,1404,1406,1408],{"class":90,"line":320},[167,1401,415],{"class":202},[167,1403,418],{"class":176},[167,1405,421],{"class":202},[167,1407,238],{"class":198},[167,1409,859],{"class":202},[167,1411,1412,1415,1417,1420,1422,1424,1426,1428],{"class":90,"line":355},[167,1413,1414],{"class":202},"    [deals[",[167,1416,429],{"class":176},[167,1418,1419],{"class":202},"].isna(), deals[",[167,1421,429],{"class":176},[167,1423,421],{"class":202},[167,1425,434],{"class":198},[167,1427,437],{"class":293},[167,1429,282],{"class":202},[167,1431,1432,1434,1437,1439,1441],{"class":90,"line":392},[167,1433,931],{"class":202},[167,1435,1436],{"class":176},"\"Unknown\"",[167,1438,256],{"class":202},[167,1440,442],{"class":176},[167,1442,282],{"class":202},[167,1444,1446,1448,1450,1452],{"class":90,"line":1445},10,[167,1447,950],{"class":808},[167,1449,238],{"class":198},[167,1451,447],{"class":176},[167,1453,732],{"class":202},[167,1455,1457],{"class":90,"line":1456},11,[167,1458,450],{"class":202},[10,1460,1461,1462,1465],{},"Excel behaves differently here in a way that hides the problem: a blank cell is treated as zero by a\nnumeric comparison, so ",[14,1463,1464],{},"=IF(B2>10000,...)"," puts blanks in the false branch too, but a blank in a SUM\nis skipped entirely. The two conventions coexist in the same workbook, and reproducing them exactly\nis rarely what anybody wants once it is pointed out.",[10,1467,1468,1469,1472,1473,27],{},"Making the missing case explicit as the first condition costs one line and turns a silent\nmisclassification into a visible category. It also makes the count available — how many rows are\n",[14,1470,1471],{},"Unknown"," is a number worth putting in the report, and the reasoning behind it is in\n",[23,1474,1476],{"href":1475},"\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",[153,1478,1480],{"id":1479},"keeping-thresholds-out-of-the-code","Keeping thresholds out of the code",[10,1482,1483],{},"An IF ladder written into a script has the same maintenance problem as one written into a formula:\nthe numbers are buried where only the author can find them. Lifting the thresholds into a small table\n— a dictionary, a config file, or a reference sheet in the workbook — makes them reviewable.",[158,1485,1487],{"className":189,"code":1486,"language":191,"meta":163,"style":163},"import pandas as pd\n\nBANDS = [\n    {\"floor\": 40000, \"label\": \"A\"},\n    {\"floor\": 20000, \"label\": \"B\"},\n    {\"floor\": 10000, \"label\": \"C\"},\n    {\"floor\": 0, \"label\": \"D\"},\n]\n\nbands = pd.DataFrame(BANDS).sort_values(\"floor\")\ndeals[\"Grade\"] = pd.cut(\n    deals[\"Revenue\"],\n    bins=list(bands[\"floor\"]) + [float(\"inf\")],\n    labels=list(bands[\"label\"]),\n    right=False,\n)\n",[14,1488,1489,1499,1503,1513,1537,1557,1577,1597,1601,1605,1624,1636,1645,1676,1692,1703],{"__ignoreMap":163},[167,1490,1491,1493,1495,1497],{"class":90,"line":169},[167,1492,199],{"class":198},[167,1494,217],{"class":202},[167,1496,206],{"class":198},[167,1498,222],{"class":202},[167,1500,1501],{"class":90,"line":212},[167,1502,229],{"emptyLinePlaceholder":228},[167,1504,1505,1508,1511],{"class":90,"line":225},[167,1506,1507],{"class":293},"BANDS",[167,1509,1510],{"class":198}," =",[167,1512,715],{"class":202},[167,1514,1515,1518,1521,1523,1525,1527,1530,1532,1534],{"class":90,"line":232},[167,1516,1517],{"class":202},"    {",[167,1519,1520],{"class":176},"\"floor\"",[167,1522,1266],{"class":202},[167,1524,1025],{"class":293},[167,1526,256],{"class":202},[167,1528,1529],{"class":176},"\"label\"",[167,1531,1266],{"class":202},[167,1533,779],{"class":176},[167,1535,1536],{"class":202},"},\n",[167,1538,1539,1541,1543,1545,1547,1549,1551,1553,1555],{"class":90,"line":244},[167,1540,1517],{"class":202},[167,1542,1520],{"class":176},[167,1544,1266],{"class":202},[167,1546,1020],{"class":293},[167,1548,256],{"class":202},[167,1550,1529],{"class":176},[167,1552,1266],{"class":202},[167,1554,784],{"class":176},[167,1556,1536],{"class":202},[167,1558,1559,1561,1563,1565,1567,1569,1571,1573,1575],{"class":90,"line":285},[167,1560,1517],{"class":202},[167,1562,1520],{"class":176},[167,1564,1266],{"class":202},[167,1566,1015],{"class":293},[167,1568,256],{"class":202},[167,1570,1529],{"class":176},[167,1572,1266],{"class":202},[167,1574,789],{"class":176},[167,1576,1536],{"class":202},[167,1578,1579,1581,1583,1585,1587,1589,1591,1593,1595],{"class":90,"line":320},[167,1580,1517],{"class":202},[167,1582,1520],{"class":176},[167,1584,1266],{"class":202},[167,1586,50],{"class":293},[167,1588,256],{"class":202},[167,1590,1529],{"class":176},[167,1592,1266],{"class":202},[167,1594,814],{"class":176},[167,1596,1536],{"class":202},[167,1598,1599],{"class":90,"line":355},[167,1600,766],{"class":202},[167,1602,1603],{"class":90,"line":392},[167,1604,229],{"emptyLinePlaceholder":228},[167,1606,1607,1610,1612,1615,1617,1620,1622],{"class":90,"line":1445},[167,1608,1609],{"class":202},"bands ",[167,1611,238],{"class":198},[167,1613,1614],{"class":202}," pd.DataFrame(",[167,1616,1507],{"class":293},[167,1618,1619],{"class":202},").sort_values(",[167,1621,1520],{"class":176},[167,1623,450],{"class":202},[167,1625,1626,1628,1630,1632,1634],{"class":90,"line":1456},[167,1627,415],{"class":202},[167,1629,798],{"class":176},[167,1631,421],{"class":202},[167,1633,238],{"class":198},[167,1635,990],{"class":202},[167,1637,1639,1641,1643],{"class":90,"line":1638},12,[167,1640,720],{"class":202},[167,1642,429],{"class":176},[167,1644,282],{"class":202},[167,1646,1648,1650,1652,1655,1658,1660,1663,1666,1668,1670,1672,1674],{"class":90,"line":1647},13,[167,1649,1003],{"class":808},[167,1651,238],{"class":198},[167,1653,1654],{"class":293},"list",[167,1656,1657],{"class":202},"(bands[",[167,1659,1520],{"class":176},[167,1661,1662],{"class":202},"]) ",[167,1664,1665],{"class":198},"+",[167,1667,776],{"class":202},[167,1669,1030],{"class":293},[167,1671,1033],{"class":202},[167,1673,1036],{"class":176},[167,1675,1039],{"class":202},[167,1677,1679,1681,1683,1685,1687,1689],{"class":90,"line":1678},14,[167,1680,1044],{"class":808},[167,1682,238],{"class":198},[167,1684,1654],{"class":293},[167,1686,1657],{"class":202},[167,1688,1529],{"class":176},[167,1690,1691],{"class":202},"]),\n",[167,1693,1695,1697,1699,1701],{"class":90,"line":1694},15,[167,1696,1069],{"class":808},[167,1698,238],{"class":198},[167,1700,1074],{"class":293},[167,1702,732],{"class":202},[167,1704,1706],{"class":90,"line":1705},16,[167,1707,450],{"class":202},[10,1709,1710,1711,1713,1714,1718],{},"Reading ",[14,1712,1507],{}," from a sheet in the source workbook instead of a literal makes the grading editable\nby the people who own the definition, which is usually the finance team rather than whoever maintains\nthe script. That pattern — logic in code, parameters in data — is the same one recommended in\n",[23,1715,1717],{"href":1716},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Fkeep-excel-report-settings-in-a-config-file\u002F","Keep Excel Report Settings in a Config File",",\nand it is what keeps a quarterly threshold change from being a deployment.",[153,1720,1722],{"id":1721},"common-pitfalls","Common pitfalls",[1724,1725,1726,1742],"table",{},[1727,1728,1729],"thead",{},[1730,1731,1732,1736,1739],"tr",{},[1733,1734,1735],"th",{},"Symptom",[1733,1737,1738],{},"Cause",[1733,1740,1741],{},"Fix",[1743,1744,1745,1768,1781,1799,1812,1833],"tbody",{},[1730,1746,1747,1753,1760],{},[1748,1749,1750],"td",{},[14,1751,1752],{},"ValueError: truth value of a Series is ambiguous",[1748,1754,1755,1756,1759],{},"A Python ",[14,1757,1758],{},"if"," used on a column",[1748,1761,1762,1763,1765,1766],{},"Use ",[14,1764,16],{}," or ",[14,1767,20],{},[1730,1769,1770,1773,1776],{},[1748,1771,1772],{},"A band claims too many rows",[1748,1774,1775],{},"Conditions ordered widest-first",[1748,1777,1778,1779],{},"Put the narrowest condition first in ",[14,1780,20],{},[1730,1782,1783,1786,1792],{},[1748,1784,1785],{},"NaN rows get an unexpected label",[1748,1787,1788,1789,1791],{},"Comparisons with NaN are False, so ",[14,1790,809],{}," catches them",[1748,1793,1794,1795,1798],{},"Test ",[14,1796,1797],{},"isna()"," explicitly as the first condition",[1730,1800,1801,1806,1809],{},[1748,1802,1803,1805],{},[14,1804,20],{}," raises about lengths",[1748,1807,1808],{},"Conditions and choices lists differ in length",[1748,1810,1811],{},"They must match element for element",[1730,1813,1814,1817,1820],{},[1748,1815,1816],{},"Numbers on a boundary fall in the wrong band",[1748,1818,1819],{},"Interval closure not stated",[1748,1821,1822,1823,1825,1826,1829,1830,1832],{},"Pass ",[14,1824,1103],{}," (or ",[14,1827,1828],{},"True",") to ",[14,1831,134],{}," deliberately",[1730,1834,1835,1838,1841],{},[1748,1836,1837],{},"A branch raises even though its condition is False",[1748,1839,1840],{},"Both branches are evaluated",[1748,1842,1843],{},"Compute safely first, then select",[153,1845,1847],{"id":1846},"performance-and-scale","Performance and scale",[29,1849,38,1855,38,1858,38,1861,38,1865,38,1869,38,1876,38,1882,38,1888,38,1891,38,1893,38,1896,38,1901,38,1904,38,1906,38,1909,38,1912,38,1916],{"viewBox":1850,"role":32,"ariaLabelledBy":1851,"xmlns":36,"style":1854},"0 0 720 196",[1852,1853],"if-vector-t","if-vector-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[40,1856,1857],{"id":1852},"Applying a conditional to one million rows",[44,1859,1860],{"id":1853},"A vectorised np.where finishes in tens of milliseconds while an apply with a lambda pays a Python function call per row and takes seconds for the same result.",[48,1862],{"x":50,"y":50,"width":1863,"height":1864,"fill":53},"720","196",[65,1866,1868],{"x":609,"y":59,"style":1867},"font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","apply with a lambda",[48,1870],{"x":1871,"y":1872,"width":67,"height":57,"rx":1873,"fill":1874,"stroke":1875},"200","40","6","#e7ebef","var(--line,#cdd5e6)",[48,1877],{"x":606,"y":1878,"width":1879,"height":1880,"rx":1881,"fill":614,"stroke":615},"41","378.0","24","5",[65,1883,1887],{"x":1884,"y":1885,"style":1886},"592.0","58","font-size:12px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:start","a call per row",[65,1889,20],{"x":609,"y":1890,"style":1867},"100",[48,1892],{"x":1871,"y":76,"width":67,"height":57,"rx":1873,"fill":1874,"stroke":1875},[48,1894],{"x":606,"y":1895,"width":610,"height":1880,"rx":1881,"fill":77,"stroke":78},"85",[65,1897,1900],{"x":1884,"y":1898,"style":1899},"102","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","vectorised",[65,1902,16],{"x":609,"y":1903,"style":1867},"144",[48,1905],{"x":1871,"y":387,"width":67,"height":57,"rx":1873,"fill":1874,"stroke":1875},[48,1907],{"x":606,"y":1908,"width":610,"height":1880,"rx":1881,"fill":77,"stroke":78},"129",[65,1910,1900],{"x":1884,"y":1911,"style":1899},"146",[65,1913,1915],{"x":609,"y":609,"style":1914},"font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","relative cost",[65,1917,1920],{"x":1918,"y":1919,"style":150},"360.0","186","the widest gap in the whole translation",[10,1922,1923,1924,1926,1927,1930,1931,1934],{},"The gap between a vectorised conditional and a row loop is the largest in this whole topic. ",[14,1925,16],{},"\nover a million rows runs in the low tens of milliseconds; the same logic in a Python ",[14,1928,1929],{},"for"," loop or an\n",[14,1932,1933],{},"apply"," with a lambda takes seconds, because every row pays the cost of a Python function call.",[158,1936,1938],{"className":189,"code":1937,"language":191,"meta":163,"style":163},"import numpy as np\n\n# Vectorised: one operation over the column\ndeals[\"Size\"] = np.where(deals[\"Revenue\"] > 10000, \"Large\", \"Standard\")\n\n# Avoid: one Python call per row\ndeals[\"Size\"] = deals[\"Revenue\"].apply(lambda v: \"Large\" if v > 10000 else \"Standard\")\n",[14,1939,1940,1950,1954,1959,1989,1993,1998],{"__ignoreMap":163},[167,1941,1942,1944,1946,1948],{"class":90,"line":169},[167,1943,199],{"class":198},[167,1945,203],{"class":202},[167,1947,206],{"class":198},[167,1949,209],{"class":202},[167,1951,1952],{"class":90,"line":212},[167,1953,229],{"emptyLinePlaceholder":228},[167,1955,1956],{"class":90,"line":225},[167,1957,1958],{"class":409},"# Vectorised: one operation over the column\n",[167,1960,1961,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987],{"class":90,"line":232},[167,1962,415],{"class":202},[167,1964,418],{"class":176},[167,1966,421],{"class":202},[167,1968,238],{"class":198},[167,1970,426],{"class":202},[167,1972,429],{"class":176},[167,1974,421],{"class":202},[167,1976,434],{"class":198},[167,1978,437],{"class":293},[167,1980,256],{"class":202},[167,1982,442],{"class":176},[167,1984,256],{"class":202},[167,1986,447],{"class":176},[167,1988,450],{"class":202},[167,1990,1991],{"class":90,"line":244},[167,1992,229],{"emptyLinePlaceholder":228},[167,1994,1995],{"class":90,"line":285},[167,1996,1997],{"class":409},"# Avoid: one Python call per row\n",[167,1999,2000,2002,2004,2006,2008,2010,2012,2015,2018,2021,2023,2026,2029,2031,2033,2036,2039],{"class":90,"line":320},[167,2001,415],{"class":202},[167,2003,418],{"class":176},[167,2005,421],{"class":202},[167,2007,238],{"class":198},[167,2009,1153],{"class":202},[167,2011,429],{"class":176},[167,2013,2014],{"class":202},"].apply(",[167,2016,2017],{"class":198},"lambda",[167,2019,2020],{"class":202}," v: ",[167,2022,442],{"class":176},[167,2024,2025],{"class":198}," if",[167,2027,2028],{"class":202}," v ",[167,2030,434],{"class":198},[167,2032,437],{"class":293},[167,2034,2035],{"class":198}," else",[167,2037,2038],{"class":176}," \"Standard\"",[167,2040,450],{"class":202},[10,2042,2043,2045,2046,2049],{},[14,2044,1933],{}," is not forbidden — it is the right tool when the logic genuinely cannot be expressed as\ncolumn operations — but a conditional on a numeric comparison always can be. When a piece of logic\nresists vectorising, it is usually worth asking whether it is really row-wise or whether it is a\ngroup operation in disguise, which ",[14,2047,2048],{},"transform"," handles at column speed.",[153,2051,2053],{"id":2052},"conclusion","Conclusion",[10,2055,2056,2059,2060,2062,2063,2065,2066,2068,2069,2071],{},[14,2057,2058],{},"IF"," becomes ",[14,2061,16],{},", nested IFs become ",[14,2064,20],{}," with the narrowest condition first, and a\nladder of numeric thresholds is better expressed as ",[14,2067,134],{}," with explicit interval closure. IFERROR\nhas no direct equivalent because there are no error values — use ",[14,2070,1238],{}," and then decide\nwhat the NaN means, which is usually more informative than the blank the formula would have shown.",[153,2073,2075],{"id":2074},"frequently-asked-questions","Frequently asked questions",[10,2077,2078,2082],{},[2079,2080,2081],"strong",{},"Should I use np.where or a Python if statement?","\nnp.where, always, when the condition applies to a column. A Python if evaluates a single truth value and raises on a Series; writing a loop with if works but is hundreds of times slower than the vectorised form.",[10,2084,2085,2088],{},[2079,2086,2087],{},"How many conditions can np.select take?","\nAs many as you like — it takes a list of conditions and a matching list of choices. The first condition that is True for a row wins, which is exactly how a nested IF ladder behaves.",[10,2090,2091,2094],{},[2079,2092,2093],{},"What is the pandas equivalent of IFERROR?","\nThere is no single function because errors do not propagate as values. Guard the operation instead: use errors='coerce' on conversions to get NaN, then fillna to supply the default IFERROR would have shown.",[10,2096,2097,2100],{},[2079,2098,2099],{},"Can I use a lookup table instead of a long IF ladder?","\nUsually yes, and it is the better design. pd.cut maps numeric ranges to labels, and map handles a dictionary of exact values. Both keep the thresholds as data rather than burying them in code.",[153,2102,2104],{"id":2103},"related","Related",[2106,2107,2108,2115,2122,2129,2136],"ul",{},[2109,2110,2111,2112,2114],"li",{},"Up one level: ",[23,2113,26],{"href":25}," — the wider function map.",[2109,2116,2117,2121],{},[23,2118,2120],{"href":2119},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Ffill-missing-values-in-excel-with-pandas-fillna\u002F","Fill Missing Values in Excel with pandas fillna"," — deciding what a NaN should become.",[2109,2123,2124,2128],{},[23,2125,2127],{"href":2126},"\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"," — the coercion behind the IFERROR translation.",[2109,2130,2131,2135],{},[23,2132,2134],{"href":2133},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-above-a-threshold-with-openpyxl\u002F","Highlight Cells Above a Threshold with openpyxl"," — the same conditions, applied as formatting instead.",[2109,2137,2138,2142],{},[23,2139,2141],{"href":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fsumif-and-sumifs-equivalent-in-pandas\u002F","SUMIF and SUMIFS Equivalent in pandas"," — the masks these conditions are built from.",[2144,2145,2146],"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 .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":163,"searchDepth":212,"depth":212,"links":2148},[2149,2150,2151,2152,2153,2154,2155,2156,2157,2158,2159,2160,2161],{"id":155,"depth":212,"text":156},{"id":398,"depth":212,"text":399},{"id":590,"depth":212,"text":591},{"id":964,"depth":212,"text":965},{"id":1116,"depth":212,"text":1117},{"id":1242,"depth":212,"text":1243},{"id":1322,"depth":212,"text":1323},{"id":1479,"depth":212,"text":1480},{"id":1721,"depth":212,"text":1722},{"id":1846,"depth":212,"text":1847},{"id":2052,"depth":212,"text":2053},{"id":2074,"depth":212,"text":2075},{"id":2103,"depth":212,"text":2104},"2026-09-04","IF becomes np.where and nested IFs become np.select, with pd.cut for numeric bands. Includes the IFERROR translation and why apply is the slow way to do it.","md",[2166,2168,2170,2172],{"q":2081,"a":2167},"np.where, always, when the condition applies to a column. A Python if evaluates a single truth value and raises on a Series; writing a loop with if works but is hundreds of times slower than the vectorised form.",{"q":2087,"a":2169},"As many as you like — it takes a list of conditions and a matching list of choices. The first condition that is True for a row wins, which is exactly how a nested IF ladder behaves.",{"q":2093,"a":2171},"There is no single function because errors do not propagate as values. Guard the operation instead: use errors='coerce' on conversions to get NaN, then fillna to supply the default IFERROR would have shown.",{"q":2099,"a":2173},"Usually yes, and it is the better design. pd.cut maps numeric ranges to labels, and map handles a dictionary of exact values. Both keep the thresholds as data rather than burying them in code.",{"breadcrumb":2175},[2176,2178,2181],{"name":2177,"item":1150},"Home",{"name":2179,"item":2180},"Advanced Data Transformation and Cleaning","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns",{"title":5,"description":2184},"Translate IF, IFS and nested IF ladders into pandas with np.where, np.select and pd.cut — plus the IFERROR equivalent, ordering rules and the cost of apply.","excel-if-formulas-as-pandas-conditional-columns","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns\u002Findex","how-to","g0E1ueXLhmdNrYyOs9tq0z37KpbRaxZs9XOlKuU6XJs",[2190,2194],{"title":2191,"path":2192,"stem":2193,"children":-1},"COUNTIF and COUNTIFS Equivalent in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcountif-and-countifs-equivalent-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcountif-and-countifs-equivalent-in-pandas\u002Findex",{"title":2195,"path":2196,"stem":2197,"children":-1},"Excel Text Functions LEFT, RIGHT, MID and CONCAT in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-text-functions-left-right-mid-and-concat-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-text-functions-left-right-mid-and-concat-in-pandas\u002Findex",1788710154434]