[{"data":1,"prerenderedAt":2330},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl":2295,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl":2304,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl":2309,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl":2310},{"id":4,"title":5,"body":6,"dateModified":2260,"datePublished":2260,"description":2261,"extension":2262,"faq":2263,"keyword":2272,"kind":2273,"meta":2274,"navigation":119,"path":2283,"seo":2284,"slug":2287,"stem":2288,"topics":2289,"type":2293,"__hash__":2294},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl\u002Findex.md","Write Lookup Formulas (XLOOKUP, VLOOKUP, INDEX\u002FMATCH) with openpyxl",{"type":7,"value":8,"toc":2245},"minimark",[9,28,33,61,87,91,277,304,308,311,398,408,557,561,576,721,748,862,866,877,1050,1061,1065,1071,1383,1396,1675,1679,1685,1864,1876,1880,1883,2096,2103,2107,2110,2114,2158,2162,2180,2184,2190,2196,2202,2208,2212,2241],[10,11,12,13,17,18,21,22,27],"p",{},"When a report is computed in pandas, a lookup is a ",[14,15,16],"code",{},"merge"," and the workbook only needs the result. But some workbooks are meant to stay alive: an order form where the price appears when someone types a product code, a forecast where changing a rate on the assumptions sheet updates every line. Those need real lookup formulas in the cells. Writing them from Python is mostly string building, with three things to get right: anchoring the lookup range so the formula can be repeated down a column, deciding what a missing key should show, and — for XLOOKUP — a file-format detail that makes the difference between a working formula and ",[14,19,20],{},"#NAME?",". This page belongs to ",[23,24,26],"a",{"href":25},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",".",[29,30,32],"h2",{"id":31},"prerequisites","Prerequisites",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[14,41,42],{"__ignoreMap":39},[43,44,47,51,55,58],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sMTad","pip",[43,52,54],{"class":53},"srMev"," install",[43,56,57],{"class":53}," openpyxl",[43,59,60],{"class":53}," pandas\n",[10,62,63,66,67,70,71,74,75,78,79,82,83,86],{},[14,64,65],{},"order_form.xlsx"," has an ",[14,68,69],{},"Orders"," sheet with SKUs and quantities (one SKU, ",[14,72,73],{},"Z-99",", is not in the price list), a ",[14,76,77],{},"Prices"," sheet, and a ",[14,80,81],{},"Discounts"," sheet of quantity tiers. LibreOffice (",[14,84,85],{},"soffice",") recalculates the result in Step 5.",[29,88,90],{"id":89},"step-1-vlookup-with-an-exact-match","Step 1 — VLOOKUP with an exact match",[34,92,96],{"className":93,"code":94,"language":95,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nwb = load_workbook(\"order_form.xlsx\")\nws = wb[\"Orders\"]\nws[\"C1\"] = \"Price (VLOOKUP)\"\nlast = ws.max_row\nfor r in range(2, last + 1):\n    ws[f\"C{r}\"] = f'=IFERROR(VLOOKUP(A{r},Prices!$A$2:$C$5,3,FALSE),\"not found\")'\nprint(ws[\"C2\"].value)\n","python",[14,97,98,114,121,139,156,173,184,218,262],{"__ignoreMap":39},[43,99,100,104,108,111],{"class":45,"line":46},[43,101,103],{"class":102},"s-kum","from",[43,105,107],{"class":106},"skGVy"," openpyxl ",[43,109,110],{"class":102},"import",[43,112,113],{"class":106}," load_workbook\n",[43,115,117],{"class":45,"line":116},2,[43,118,120],{"emptyLinePlaceholder":119},true,"\n",[43,122,124,127,130,133,136],{"class":45,"line":123},3,[43,125,126],{"class":106},"wb ",[43,128,129],{"class":102},"=",[43,131,132],{"class":106}," load_workbook(",[43,134,135],{"class":53},"\"order_form.xlsx\"",[43,137,138],{"class":106},")\n",[43,140,142,145,147,150,153],{"class":45,"line":141},4,[43,143,144],{"class":106},"ws ",[43,146,129],{"class":102},[43,148,149],{"class":106}," wb[",[43,151,152],{"class":53},"\"Orders\"",[43,154,155],{"class":106},"]\n",[43,157,159,162,165,168,170],{"class":45,"line":158},5,[43,160,161],{"class":106},"ws[",[43,163,164],{"class":53},"\"C1\"",[43,166,167],{"class":106},"] ",[43,169,129],{"class":102},[43,171,172],{"class":53}," \"Price (VLOOKUP)\"\n",[43,174,176,179,181],{"class":45,"line":175},6,[43,177,178],{"class":106},"last ",[43,180,129],{"class":102},[43,182,183],{"class":106}," ws.max_row\n",[43,185,187,190,193,196,200,203,206,209,212,215],{"class":45,"line":186},7,[43,188,189],{"class":102},"for",[43,191,192],{"class":106}," r ",[43,194,195],{"class":102},"in",[43,197,199],{"class":198},"sP0c6"," range",[43,201,202],{"class":106},"(",[43,204,205],{"class":198},"2",[43,207,208],{"class":106},", last ",[43,210,211],{"class":102},"+",[43,213,214],{"class":198}," 1",[43,216,217],{"class":106},"):\n",[43,219,221,224,227,230,234,237,240,243,245,247,250,253,255,257,259],{"class":45,"line":220},8,[43,222,223],{"class":106},"    ws[",[43,225,226],{"class":102},"f",[43,228,229],{"class":53},"\"C",[43,231,233],{"class":232},"sSjpA","{",[43,235,236],{"class":106},"r",[43,238,239],{"class":232},"}",[43,241,242],{"class":53},"\"",[43,244,167],{"class":106},[43,246,129],{"class":102},[43,248,249],{"class":102}," f",[43,251,252],{"class":53},"'=IFERROR(VLOOKUP(A",[43,254,233],{"class":232},[43,256,236],{"class":106},[43,258,239],{"class":232},[43,260,261],{"class":53},",Prices!$A$2:$C$5,3,FALSE),\"not found\")'\n",[43,263,265,268,271,274],{"class":45,"line":264},9,[43,266,267],{"class":198},"print",[43,269,270],{"class":106},"(ws[",[43,272,273],{"class":53},"\"C2\"",[43,275,276],{"class":106},"].value)\n",[10,278,279,280,283,284,287,288,291,292,295,296,299,300,303],{},"Two details make this formula safe to repeat down the column. The ",[14,281,282],{},"$"," signs anchor the lookup range — ",[14,285,286],{},"Prices!$A$2:$C$5"," stays the same on every row while ",[14,289,290],{},"A{r}"," moves — exactly what Excel's F4 key does. And the fourth argument, ",[14,293,294],{},"FALSE",", asks for an exact match; leaving it out makes VLOOKUP assume a sorted table and return the nearest smaller key, which for product codes is quietly wrong. ",[14,297,298],{},"IFERROR"," replaces the ",[14,301,302],{},"#N\u002FA"," of a missing SKU with readable text.",[29,305,307],{"id":306},"step-2-indexmatch","Step 2 — INDEX\u002FMATCH",[10,309,310],{},"VLOOKUP can only return columns to the right of the key and breaks when someone inserts a column inside the table. INDEX\u002FMATCH names the result column directly:",[34,312,314],{"className":93,"code":313,"language":95,"meta":39,"style":39},"ws[\"D1\"] = \"Product (INDEX\u002FMATCH)\"\nfor r in range(2, last + 1):\n    ws[f\"D{r}\"] = f'=IFERROR(INDEX(Prices!$B$2:$B$5,MATCH(A{r},Prices!$A$2:$A$5,0)),\"not found\")'\nprint(ws[\"D2\"].value)\n",[14,315,316,330,352,387],{"__ignoreMap":39},[43,317,318,320,323,325,327],{"class":45,"line":46},[43,319,161],{"class":106},[43,321,322],{"class":53},"\"D1\"",[43,324,167],{"class":106},[43,326,129],{"class":102},[43,328,329],{"class":53}," \"Product (INDEX\u002FMATCH)\"\n",[43,331,332,334,336,338,340,342,344,346,348,350],{"class":45,"line":116},[43,333,189],{"class":102},[43,335,192],{"class":106},[43,337,195],{"class":102},[43,339,199],{"class":198},[43,341,202],{"class":106},[43,343,205],{"class":198},[43,345,208],{"class":106},[43,347,211],{"class":102},[43,349,214],{"class":198},[43,351,217],{"class":106},[43,353,354,356,358,361,363,365,367,369,371,373,375,378,380,382,384],{"class":45,"line":123},[43,355,223],{"class":106},[43,357,226],{"class":102},[43,359,360],{"class":53},"\"D",[43,362,233],{"class":232},[43,364,236],{"class":106},[43,366,239],{"class":232},[43,368,242],{"class":53},[43,370,167],{"class":106},[43,372,129],{"class":102},[43,374,249],{"class":102},[43,376,377],{"class":53},"'=IFERROR(INDEX(Prices!$B$2:$B$5,MATCH(A",[43,379,233],{"class":232},[43,381,236],{"class":106},[43,383,239],{"class":232},[43,385,386],{"class":53},",Prices!$A$2:$A$5,0)),\"not found\")'\n",[43,388,389,391,393,396],{"class":45,"line":141},[43,390,267],{"class":198},[43,392,270],{"class":106},[43,394,395],{"class":53},"\"D2\"",[43,397,276],{"class":106},[10,399,400,403,404,407],{},[14,401,402],{},"MATCH(…, 0)"," finds the row of an exact match, and ",[14,405,406],{},"INDEX"," takes that row from the product column. It works in every Excel version and in LibreOffice, which makes it the safest choice for files with an unknown audience.",[409,410,418,419,418,423,418,427,418,435,418,445,418,456,418,463,418,468,418,474,418,479,418,483,418,490,418,495,418,499,418,503,418,507,418,510,418,513,418,519,418,523,418,527,418,530,418,534,418,537,418,542,418,549,418,554],"svg",{"viewBox":411,"role":412,"ariaLabelledBy":413,"xmlns":416,"style":417},"4 0 752 142","img",[414,415],"lk-index-t","lk-index-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  ",[420,421,422],"title",{"id":414},"How INDEX\u002FMATCH finds the product for one order row",[424,425,426],"desc",{"id":415},"The SKU S-02 in the order row is matched against the SKU column of the Prices sheet with MATCH, which returns position 2. INDEX then takes the second value of the Product column, Saw.",[428,429],"rect",{"x":430,"y":431,"width":432,"height":433,"fill":434},"4","0","752","142","#ffffff",[428,436],{"x":437,"y":438,"width":439,"height":440,"rx":441,"fill":442,"stroke":443,"style":444},"20.0","16.0","153.0","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[446,447,449],"g",{"transform":448},"translate(84.5,37.8) scale(1.000)",[450,451],"path",{"d":452,"fill":453,"stroke":454,"style":455},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[457,458,462],"text",{"x":459,"y":460,"style":461},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Orders!A2 = S-02",[457,464,467],{"x":459,"y":465,"style":466},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","the key",[45,469],{"x1":470,"y1":471,"x2":472,"y2":471,"stroke":473,"style":444},"178.0","71.2","200.0","var(--brand,#5b5cf0)",[475,476],"polygon",{"points":477,"fill":478},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[428,480],{"x":481,"y":438,"width":439,"height":440,"rx":441,"fill":482,"stroke":473,"style":444},"209.0","#f0f4ff",[446,484,486],{"transform":485},"translate(273.5,29.0) scale(1.000)",[450,487],{"d":488,"fill":453,"stroke":489,"style":455},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--brand-strong,#4338ca)",[457,491,402],{"x":492,"y":493,"style":494},"285.5","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[457,496,498],{"x":492,"y":497,"style":466},"95.0","position 2 in",[457,500,502],{"x":492,"y":501,"style":466},"112.5","Prices!A2:A5",[45,504],{"x1":505,"y1":471,"x2":506,"y2":471,"stroke":473,"style":444},"367.0","389.0",[475,508],{"points":509,"fill":478},"393.0,71.2 384.0,76.2 384.0,66.2",[428,511],{"x":512,"y":438,"width":439,"height":440,"rx":441,"fill":482,"stroke":473,"style":444},"398.0",[446,514,516],{"transform":515},"translate(462.5,29.0) scale(1.000)",[450,517],{"d":518,"fill":453,"stroke":489,"style":455},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[457,520,522],{"x":521,"y":493,"style":494},"474.5","INDEX(Prices!B2:B5,",[457,524,526],{"x":521,"y":525,"style":494},"93.0","2)",[457,528,529],{"x":521,"y":501,"style":466},"second product",[45,531],{"x1":532,"y1":471,"x2":533,"y2":471,"stroke":473,"style":444},"556.0","578.0",[475,535],{"points":536,"fill":478},"582.0,71.2 573.0,76.2 573.0,66.2",[428,538],{"x":539,"y":438,"width":439,"height":440,"rx":441,"fill":540,"stroke":541,"style":444},"587.0","#d9f4f1","var(--teal,#0f9488)",[446,543,545],{"transform":544},"translate(651.5,37.8) scale(1.000)",[450,546],{"d":547,"fill":453,"stroke":548,"style":455},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[457,550,553],{"x":551,"y":460,"style":552},"663.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Saw",[457,555,556],{"x":551,"y":465,"style":466},"shown in Orders!D2",[29,558,560],{"id":559},"step-3-xlookup-needs-the-_xlfn-prefix","Step 3 — XLOOKUP needs the _xlfn prefix",[10,562,563,564,567,568,571,572,575],{},"XLOOKUP combines both and has a built-in not-found argument. But it was added to Excel in 2019, and functions added since Excel 2010 are stored in the file with a prefix, ",[14,565,566],{},"_xlfn.",". Excel shows ",[14,569,570],{},"XLOOKUP"," to the user; the file contains ",[14,573,574],{},"_xlfn.XLOOKUP",". openpyxl writes exactly the text you give it, so write the stored form:",[34,577,579],{"className":93,"code":578,"language":95,"meta":39,"style":39},"ws[\"E1\"] = \"Price (XLOOKUP)\"\nws[\"F1\"] = \"Price (XLOOKUP, no prefix)\"\nfor r in range(2, last + 1):\n    ws[f\"E{r}\"] = f'=_xlfn.XLOOKUP(A{r},Prices!$A$2:$A$5,Prices!$C$2:$C$5,\"not found\")'\n    ws[f\"F{r}\"] = f'=XLOOKUP(A{r},Prices!$A$2:$A$5,Prices!$C$2:$C$5,\"not found\")'\nwb.save(\"order_form_lookups.xlsx\")\nprint(ws[\"E2\"].value)\n",[14,580,581,595,609,631,666,700,710],{"__ignoreMap":39},[43,582,583,585,588,590,592],{"class":45,"line":46},[43,584,161],{"class":106},[43,586,587],{"class":53},"\"E1\"",[43,589,167],{"class":106},[43,591,129],{"class":102},[43,593,594],{"class":53}," \"Price (XLOOKUP)\"\n",[43,596,597,599,602,604,606],{"class":45,"line":116},[43,598,161],{"class":106},[43,600,601],{"class":53},"\"F1\"",[43,603,167],{"class":106},[43,605,129],{"class":102},[43,607,608],{"class":53}," \"Price (XLOOKUP, no prefix)\"\n",[43,610,611,613,615,617,619,621,623,625,627,629],{"class":45,"line":123},[43,612,189],{"class":102},[43,614,192],{"class":106},[43,616,195],{"class":102},[43,618,199],{"class":198},[43,620,202],{"class":106},[43,622,205],{"class":198},[43,624,208],{"class":106},[43,626,211],{"class":102},[43,628,214],{"class":198},[43,630,217],{"class":106},[43,632,633,635,637,640,642,644,646,648,650,652,654,657,659,661,663],{"class":45,"line":141},[43,634,223],{"class":106},[43,636,226],{"class":102},[43,638,639],{"class":53},"\"E",[43,641,233],{"class":232},[43,643,236],{"class":106},[43,645,239],{"class":232},[43,647,242],{"class":53},[43,649,167],{"class":106},[43,651,129],{"class":102},[43,653,249],{"class":102},[43,655,656],{"class":53},"'=_xlfn.XLOOKUP(A",[43,658,233],{"class":232},[43,660,236],{"class":106},[43,662,239],{"class":232},[43,664,665],{"class":53},",Prices!$A$2:$A$5,Prices!$C$2:$C$5,\"not found\")'\n",[43,667,668,670,672,675,677,679,681,683,685,687,689,692,694,696,698],{"class":45,"line":158},[43,669,223],{"class":106},[43,671,226],{"class":102},[43,673,674],{"class":53},"\"F",[43,676,233],{"class":232},[43,678,236],{"class":106},[43,680,239],{"class":232},[43,682,242],{"class":53},[43,684,167],{"class":106},[43,686,129],{"class":102},[43,688,249],{"class":102},[43,690,691],{"class":53},"'=XLOOKUP(A",[43,693,233],{"class":232},[43,695,236],{"class":106},[43,697,239],{"class":232},[43,699,665],{"class":53},[43,701,702,705,708],{"class":45,"line":175},[43,703,704],{"class":106},"wb.save(",[43,706,707],{"class":53},"\"order_form_lookups.xlsx\"",[43,709,138],{"class":106},[43,711,712,714,716,719],{"class":45,"line":186},[43,713,267],{"class":198},[43,715,270],{"class":106},[43,717,718],{"class":53},"\"E2\"",[43,720,276],{"class":106},[10,722,723,724,726,727,730,731,730,734,730,737,730,740,743,744,27],{},"Column F is the mistake on purpose: without the prefix, the spreadsheet treats ",[14,725,570],{}," as an unknown name. The same rule applies to ",[14,728,729],{},"FILTER",", ",[14,732,733],{},"UNIQUE",[14,735,736],{},"LET",[14,738,739],{},"IFS",[14,741,742],{},"TEXTJOIN"," and the other newer functions; the full list and fix are in ",[23,745,747],{"href":746},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-name-error-for-new-excel-functions-written-by-python\u002F","Fix #NAME? Errors for New Excel Functions Written by Python",[409,749,418,754,418,757,418,760,418,763,418,768,418,774,418,780,418,786,418,792,418,798,418,802,418,807,418,811,418,816,418,820,418,827,418,832,418,836,418,840,418,846,418,849,418,854,418,857],{"viewBox":750,"role":412,"ariaLabelledBy":751,"xmlns":416,"style":417},"4 0 752 207",[752,753],"lk-prefix-t","lk-prefix-d",[420,755,756],{"id":752},"What is stored versus what Excel shows",[424,758,759],{"id":753},"Written as _xlfn.XLOOKUP, the formula is recognised and Excel displays XLOOKUP with the right price. Written as plain XLOOKUP, the stored name is unknown and the cell shows",[428,761],{"x":430,"y":431,"width":432,"height":762,"fill":434},"207",[428,764],{"x":437,"y":438,"width":765,"height":766,"rx":767,"fill":540,"stroke":541,"style":444},"351.0","175.0","14",[446,769,771],{"transform":770},"translate(182.5,29.0) scale(1.083)",[450,772],{"d":547,"fill":453,"stroke":548,"style":773},"stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[457,775,779],{"x":776,"y":777,"style":778},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","=_xlfn.XLOOKUP(…)",[45,781],{"x1":782,"y1":783,"x2":784,"y2":783,"stroke":541,"style":785},"36.0","91.0","355.0","stroke-width:1px",[457,787,791],{"x":788,"y":789,"style":790},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","stored form of a post-2010 function",[446,793,795],{"transform":794},"translate(38.5,99.5) scale(0.625)",[450,796],{"d":547,"fill":453,"stroke":541,"style":797},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[457,799,801],{"x":788,"y":800,"style":790},"137.0","Excel displays =XLOOKUP(…)",[446,803,805],{"transform":804},"translate(38.5,125.5) scale(0.625)",[450,806],{"d":547,"fill":453,"stroke":541,"style":797},[457,808,810],{"x":788,"y":809,"style":790},"163.0","result 45.5",[446,812,814],{"transform":813},"translate(38.5,151.5) scale(0.625)",[450,815],{"d":547,"fill":453,"stroke":541,"style":797},[428,817],{"x":506,"y":438,"width":765,"height":766,"rx":767,"fill":818,"stroke":819,"style":444},"#fce9e9","var(--danger,#dc2626)",[446,821,823],{"transform":822},"translate(551.5,29.0) scale(1.083)",[450,824],{"d":825,"fill":453,"stroke":826,"style":773},"M6 6l12 12 M18 6L6 18","var(--accent-ink,#be185d)",[457,828,831],{"x":829,"y":777,"style":830},"564.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","=XLOOKUP(…)",[45,833],{"x1":834,"y1":783,"x2":835,"y2":783,"stroke":819,"style":785},"405.0","724.0",[457,837,839],{"x":838,"y":789,"style":790},"429.0","not a known stored name",[446,841,843],{"transform":842},"translate(407.5,99.5) scale(0.625)",[450,844],{"d":825,"fill":453,"stroke":845,"style":797},"var(--accent,#f43f8f)",[457,847,848],{"x":838,"y":800,"style":790},"cell shows #NAME?",[446,850,852],{"transform":851},"translate(407.5,125.5) scale(0.625)",[450,853],{"d":825,"fill":453,"stroke":845,"style":797},[457,855,856],{"x":838,"y":809,"style":790},"easy to miss in testing",[446,858,860],{"transform":859},"translate(407.5,151.5) scale(0.625)",[450,861],{"d":825,"fill":453,"stroke":845,"style":797},[29,863,865],{"id":864},"step-4-tiered-lookups-with-an-approximate-match","Step 4 — Tiered lookups with an approximate match",[10,867,868,869,872,873,876],{},"Discount tiers are the case where approximate matching is exactly right: a quantity of 12 should get the 10-and-over discount. The tier table must be sorted ascending; then VLOOKUP with ",[14,870,871],{},"TRUE"," (or XLOOKUP with match mode ",[14,874,875],{},"-1",", \"exact or next smaller\") returns the tier the quantity falls into:",[34,878,880],{"className":93,"code":879,"language":95,"meta":39,"style":39},"ws[\"G1\"], ws[\"H1\"] = \"Discount\", \"Discount (XLOOKUP)\"\nfor r in range(2, last + 1):\n    ws[f\"G{r}\"] = f\"=VLOOKUP(B{r},Discounts!$A$2:$B$5,2,TRUE)\"\n    ws[f\"H{r}\"] = f\"=_xlfn.XLOOKUP(B{r},Discounts!$A$2:$A$5,Discounts!$B$2:$B$5,0,-1)\"\n    ws[f\"G{r}\"].number_format = ws[f\"H{r}\"].number_format = \"0%\"\nwb.save(\"order_form_lookups.xlsx\")\n",[14,881,882,907,929,964,999,1042],{"__ignoreMap":39},[43,883,884,886,889,892,895,897,899,902,904],{"class":45,"line":46},[43,885,161],{"class":106},[43,887,888],{"class":53},"\"G1\"",[43,890,891],{"class":106},"], ws[",[43,893,894],{"class":53},"\"H1\"",[43,896,167],{"class":106},[43,898,129],{"class":102},[43,900,901],{"class":53}," \"Discount\"",[43,903,730],{"class":106},[43,905,906],{"class":53},"\"Discount (XLOOKUP)\"\n",[43,908,909,911,913,915,917,919,921,923,925,927],{"class":45,"line":116},[43,910,189],{"class":102},[43,912,192],{"class":106},[43,914,195],{"class":102},[43,916,199],{"class":198},[43,918,202],{"class":106},[43,920,205],{"class":198},[43,922,208],{"class":106},[43,924,211],{"class":102},[43,926,214],{"class":198},[43,928,217],{"class":106},[43,930,931,933,935,938,940,942,944,946,948,950,952,955,957,959,961],{"class":45,"line":123},[43,932,223],{"class":106},[43,934,226],{"class":102},[43,936,937],{"class":53},"\"G",[43,939,233],{"class":232},[43,941,236],{"class":106},[43,943,239],{"class":232},[43,945,242],{"class":53},[43,947,167],{"class":106},[43,949,129],{"class":102},[43,951,249],{"class":102},[43,953,954],{"class":53},"\"=VLOOKUP(B",[43,956,233],{"class":232},[43,958,236],{"class":106},[43,960,239],{"class":232},[43,962,963],{"class":53},",Discounts!$A$2:$B$5,2,TRUE)\"\n",[43,965,966,968,970,973,975,977,979,981,983,985,987,990,992,994,996],{"class":45,"line":141},[43,967,223],{"class":106},[43,969,226],{"class":102},[43,971,972],{"class":53},"\"H",[43,974,233],{"class":232},[43,976,236],{"class":106},[43,978,239],{"class":232},[43,980,242],{"class":53},[43,982,167],{"class":106},[43,984,129],{"class":102},[43,986,249],{"class":102},[43,988,989],{"class":53},"\"=_xlfn.XLOOKUP(B",[43,991,233],{"class":232},[43,993,236],{"class":106},[43,995,239],{"class":232},[43,997,998],{"class":53},",Discounts!$A$2:$A$5,Discounts!$B$2:$B$5,0,-1)\"\n",[43,1000,1001,1003,1005,1007,1009,1011,1013,1015,1018,1020,1023,1025,1027,1029,1031,1033,1035,1037,1039],{"class":45,"line":158},[43,1002,223],{"class":106},[43,1004,226],{"class":102},[43,1006,937],{"class":53},[43,1008,233],{"class":232},[43,1010,236],{"class":106},[43,1012,239],{"class":232},[43,1014,242],{"class":53},[43,1016,1017],{"class":106},"].number_format ",[43,1019,129],{"class":102},[43,1021,1022],{"class":106}," ws[",[43,1024,226],{"class":102},[43,1026,972],{"class":53},[43,1028,233],{"class":232},[43,1030,236],{"class":106},[43,1032,239],{"class":232},[43,1034,242],{"class":53},[43,1036,1017],{"class":106},[43,1038,129],{"class":102},[43,1040,1041],{"class":53}," \"0%\"\n",[43,1043,1044,1046,1048],{"class":45,"line":175},[43,1045,704],{"class":106},[43,1047,707],{"class":53},[43,1049,138],{"class":106},[10,1051,1052,1053,1056,1057,27],{},"The same idea in pandas is ",[14,1054,1055],{},"merge_asof",", covered in ",[23,1058,1060],{"href":1059},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fapproximate-match-vlookup-in-pandas\u002F","Approximate Match VLOOKUP in pandas",[29,1062,1064],{"id":1063},"step-5-recalculate-and-check-against-pandas","Step 5 — Recalculate and check against pandas",[10,1066,1067,1068,1070],{},"A wrong column index or a missing ",[14,1069,282],{}," produces numbers, not errors, so compare the formulas' results with an independent pandas calculation:",[34,1072,1074],{"className":93,"code":1073,"language":95,"meta":39,"style":39},"import subprocess\nimport pandas as pd\n\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"recalc\",\n                \"order_form_lookups.xlsx\"], check=True, capture_output=True)\ngot = pd.read_excel(\"recalc\u002Forder_form_lookups.xlsx\", sheet_name=\"Orders\")\nprint(got.to_string(index=False))\n\nprice_list = pd.read_excel(\"order_form.xlsx\", sheet_name=\"Prices\")\nexpected = pd.read_excel(\"order_form.xlsx\", sheet_name=\"Orders\").merge(price_list, on=\"SKU\", how=\"left\")\nfound = expected[\"Price\"].notna()\nassert (got.loc[found, \"Price (VLOOKUP)\"].astype(float) == expected.loc[found, \"Price\"]).all()\nassert (got.loc[found, \"Price (XLOOKUP)\"].astype(float) == expected.loc[found, \"Price\"]).all()\nprint(\"formulas agree with pandas for\", int(found.sum()), \"orders; not found:\", got.loc[~found, \"SKU\"].tolist())\n",[14,1075,1076,1083,1096,1100,1136,1164,1188,1206,1210,1232,1275,1292,1324,1348],{"__ignoreMap":39},[43,1077,1078,1080],{"class":45,"line":46},[43,1079,110],{"class":102},[43,1081,1082],{"class":106}," subprocess\n",[43,1084,1085,1087,1090,1093],{"class":45,"line":116},[43,1086,110],{"class":102},[43,1088,1089],{"class":106}," pandas ",[43,1091,1092],{"class":102},"as",[43,1094,1095],{"class":106}," pd\n",[43,1097,1098],{"class":45,"line":123},[43,1099,120],{"emptyLinePlaceholder":119},[43,1101,1102,1105,1108,1110,1113,1115,1118,1120,1123,1125,1128,1130,1133],{"class":45,"line":141},[43,1103,1104],{"class":106},"subprocess.run([",[43,1106,1107],{"class":53},"\"soffice\"",[43,1109,730],{"class":106},[43,1111,1112],{"class":53},"\"--headless\"",[43,1114,730],{"class":106},[43,1116,1117],{"class":53},"\"--convert-to\"",[43,1119,730],{"class":106},[43,1121,1122],{"class":53},"\"xlsx\"",[43,1124,730],{"class":106},[43,1126,1127],{"class":53},"\"--outdir\"",[43,1129,730],{"class":106},[43,1131,1132],{"class":53},"\"recalc\"",[43,1134,1135],{"class":106},",\n",[43,1137,1138,1141,1144,1148,1150,1153,1155,1158,1160,1162],{"class":45,"line":158},[43,1139,1140],{"class":53},"                \"order_form_lookups.xlsx\"",[43,1142,1143],{"class":106},"], ",[43,1145,1147],{"class":1146},"sa561","check",[43,1149,129],{"class":102},[43,1151,1152],{"class":198},"True",[43,1154,730],{"class":106},[43,1156,1157],{"class":1146},"capture_output",[43,1159,129],{"class":102},[43,1161,1152],{"class":198},[43,1163,138],{"class":106},[43,1165,1166,1169,1171,1174,1177,1179,1182,1184,1186],{"class":45,"line":175},[43,1167,1168],{"class":106},"got ",[43,1170,129],{"class":102},[43,1172,1173],{"class":106}," pd.read_excel(",[43,1175,1176],{"class":53},"\"recalc\u002Forder_form_lookups.xlsx\"",[43,1178,730],{"class":106},[43,1180,1181],{"class":1146},"sheet_name",[43,1183,129],{"class":102},[43,1185,152],{"class":53},[43,1187,138],{"class":106},[43,1189,1190,1192,1195,1198,1200,1203],{"class":45,"line":186},[43,1191,267],{"class":198},[43,1193,1194],{"class":106},"(got.to_string(",[43,1196,1197],{"class":1146},"index",[43,1199,129],{"class":102},[43,1201,1202],{"class":198},"False",[43,1204,1205],{"class":106},"))\n",[43,1207,1208],{"class":45,"line":220},[43,1209,120],{"emptyLinePlaceholder":119},[43,1211,1212,1215,1217,1219,1221,1223,1225,1227,1230],{"class":45,"line":264},[43,1213,1214],{"class":106},"price_list ",[43,1216,129],{"class":102},[43,1218,1173],{"class":106},[43,1220,135],{"class":53},[43,1222,730],{"class":106},[43,1224,1181],{"class":1146},[43,1226,129],{"class":102},[43,1228,1229],{"class":53},"\"Prices\"",[43,1231,138],{"class":106},[43,1233,1235,1238,1240,1242,1244,1246,1248,1250,1252,1255,1258,1260,1263,1265,1268,1270,1273],{"class":45,"line":1234},10,[43,1236,1237],{"class":106},"expected ",[43,1239,129],{"class":102},[43,1241,1173],{"class":106},[43,1243,135],{"class":53},[43,1245,730],{"class":106},[43,1247,1181],{"class":1146},[43,1249,129],{"class":102},[43,1251,152],{"class":53},[43,1253,1254],{"class":106},").merge(price_list, ",[43,1256,1257],{"class":1146},"on",[43,1259,129],{"class":102},[43,1261,1262],{"class":53},"\"SKU\"",[43,1264,730],{"class":106},[43,1266,1267],{"class":1146},"how",[43,1269,129],{"class":102},[43,1271,1272],{"class":53},"\"left\"",[43,1274,138],{"class":106},[43,1276,1278,1281,1283,1286,1289],{"class":45,"line":1277},11,[43,1279,1280],{"class":106},"found ",[43,1282,129],{"class":102},[43,1284,1285],{"class":106}," expected[",[43,1287,1288],{"class":53},"\"Price\"",[43,1290,1291],{"class":106},"].notna()\n",[43,1293,1295,1298,1301,1304,1307,1310,1313,1316,1319,1321],{"class":45,"line":1294},12,[43,1296,1297],{"class":102},"assert",[43,1299,1300],{"class":106}," (got.loc[found, ",[43,1302,1303],{"class":53},"\"Price (VLOOKUP)\"",[43,1305,1306],{"class":106},"].astype(",[43,1308,1309],{"class":198},"float",[43,1311,1312],{"class":106},") ",[43,1314,1315],{"class":102},"==",[43,1317,1318],{"class":106}," expected.loc[found, ",[43,1320,1288],{"class":53},[43,1322,1323],{"class":106},"]).all()\n",[43,1325,1327,1329,1331,1334,1336,1338,1340,1342,1344,1346],{"class":45,"line":1326},13,[43,1328,1297],{"class":102},[43,1330,1300],{"class":106},[43,1332,1333],{"class":53},"\"Price (XLOOKUP)\"",[43,1335,1306],{"class":106},[43,1337,1309],{"class":198},[43,1339,1312],{"class":106},[43,1341,1315],{"class":102},[43,1343,1318],{"class":106},[43,1345,1288],{"class":53},[43,1347,1323],{"class":106},[43,1349,1351,1353,1355,1358,1360,1363,1366,1369,1372,1375,1378,1380],{"class":45,"line":1350},14,[43,1352,267],{"class":198},[43,1354,202],{"class":106},[43,1356,1357],{"class":53},"\"formulas agree with pandas for\"",[43,1359,730],{"class":106},[43,1361,1362],{"class":198},"int",[43,1364,1365],{"class":106},"(found.sum()), ",[43,1367,1368],{"class":53},"\"orders; not found:\"",[43,1370,1371],{"class":106},", got.loc[",[43,1373,1374],{"class":102},"~",[43,1376,1377],{"class":106},"found, ",[43,1379,1262],{"class":53},[43,1381,1382],{"class":106},"].tolist())\n",[10,1384,1385,1386,1388,1389,1391,1392,1395],{},"The VLOOKUP, INDEX\u002FMATCH and prefixed XLOOKUP columns agree with the pandas merge, the unknown ",[14,1387,73],{}," shows \"not found\" in all three, the unprefixed column holds ",[14,1390,20],{}," in every cell (pandas reads error values as ",[14,1393,1394],{},"NaN",", so it prints blank), and the discount columns give 5% for 5 units and 10% for 12. Running this comparison in the report's tests turns a whole class of silent formula errors into failing tests.",[409,1397,418,1403,418,1406,418,1409,418,1414,418,1419,418,1423,418,1429,418,1433,418,1437,418,1441,418,1445,418,1449,418,1453,418,1457,418,1461,418,1465,418,1469,418,1473,418,1477,418,1481,418,1486,418,1489,418,1493,418,1495,418,1498,418,1500,418,1503,418,1505,418,1508,418,1510,418,1512,418,1514,418,1517,418,1519,418,1522,418,1525,418,1528,418,1530,418,1534,418,1536,418,1539,418,1541,418,1544,418,1546,418,1548,418,1550,418,1552,418,1554,418,1557,418,1559,418,1562,418,1565,418,1569,418,1571,418,1574,418,1576,418,1578,418,1580,418,1582,418,1584,418,1587,418,1589,418,1591,418,1593,418,1595,418,1597,418,1600,418,1603,418,1606,418,1608,418,1611,418,1613,418,1615,418,1617,418,1620,418,1622,418,1625,418,1627,418,1629,418,1631,418,1633,418,1635,418,1638,418,1641,418,1643,418,1645,418,1647,418,1649,418,1651,418,1655,418,1659,418,1661,418,1663,418,1665,418,1667,418,1669,418,1671,418,1673],{"viewBox":1398,"role":412,"ariaLabelledBy":1399,"xmlns":416,"style":1402},"96 0 567 196",[1400,1401],"lk-result-t","lk-result-d","width:100%;max-width:567px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[420,1404,1405],{"id":1400},"The order sheet after recalculation",[424,1407,1408],{"id":1401},"The Orders sheet with SKU, Qty, the VLOOKUP price, the product from INDEX\u002FMATCH, the prefixed XLOOKUP price, the unprefixed XLOOKUP showing",[428,1410],{"x":1411,"y":431,"width":1412,"height":1413,"fill":434},"96","567","196",[428,1415],{"x":1416,"y":438,"width":1417,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"113.0","32.0","24.0",[428,1420],{"x":1421,"y":438,"width":1422,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"145.0","51.9",[457,1424,1428],{"x":1425,"y":1426,"style":1427},"171.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[428,1430],{"x":1431,"y":438,"width":1432,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"196.9","44.0",[457,1434,1436],{"x":1435,"y":1426,"style":1427},"218.9","B",[428,1438],{"x":1439,"y":438,"width":1440,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"240.9","78.6",[457,1442,1444],{"x":1443,"y":1426,"style":1427},"280.2","C",[428,1446],{"x":1447,"y":438,"width":1448,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"319.5","104.8",[457,1450,1452],{"x":1451,"y":1426,"style":1427},"371.9","D",[428,1454],{"x":1455,"y":438,"width":1456,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"424.4","78.5",[457,1458,1460],{"x":1459,"y":1426,"style":1427},"463.6","E",[428,1462],{"x":1463,"y":438,"width":1464,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"502.9","72.0",[457,1466,1468],{"x":1467,"y":1426,"style":1427},"538.9","F",[428,1470],{"x":1471,"y":438,"width":1472,"height":1418,"rx":431,"fill":442,"stroke":443,"style":785},"574.9","72.1",[457,1474,1476],{"x":1475,"y":1426,"style":1427},"611.0","G",[428,1478],{"x":1416,"y":1479,"width":1417,"height":1480,"rx":431,"fill":442,"stroke":443,"style":785},"40.0","28.0",[457,1482,1485],{"x":1483,"y":1484,"style":1427},"129.0","59.0","1",[428,1487],{"x":1421,"y":1479,"width":1422,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},"stroke-width:1.5px",[457,1490,1492],{"x":1425,"y":1484,"style":1491},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","SKU",[428,1494],{"x":1431,"y":1479,"width":1432,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1496,1497],{"x":1435,"y":1484,"style":1491},"Qty",[428,1499],{"x":1439,"y":1479,"width":1440,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1501,1502],{"x":1443,"y":1484,"style":1491},"VLOOKUP",[428,1504],{"x":1447,"y":1479,"width":1448,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1506,1507],{"x":1451,"y":1484,"style":1491},"INDEX\u002FMATCH",[428,1509],{"x":1455,"y":1479,"width":1456,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1511,570],{"x":1459,"y":1484,"style":1491},[428,1513],{"x":1463,"y":1479,"width":1464,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1515,1516],{"x":1467,"y":1484,"style":1491},"no prefix",[428,1518],{"x":1471,"y":1479,"width":1472,"height":1480,"rx":431,"fill":482,"stroke":473,"style":1488},[457,1520,1521],{"x":1475,"y":1484,"style":1491},"Discount",[428,1523],{"x":1416,"y":1524,"width":1417,"height":1480,"rx":431,"fill":442,"stroke":443,"style":785},"68.0",[457,1526,205],{"x":1483,"y":1527,"style":1427},"87.0",[428,1529],{"x":1421,"y":1524,"width":1422,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1531,1533],{"x":1425,"y":1527,"style":1532},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","S-02",[428,1535],{"x":1431,"y":1524,"width":1432,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1537,1538],{"x":1435,"y":1527,"style":1532},"5",[428,1540],{"x":1439,"y":1524,"width":1440,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1542,1543],{"x":1443,"y":1527,"style":1532},"45.5",[428,1545],{"x":1447,"y":1524,"width":1448,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1547,553],{"x":1451,"y":1527,"style":1532},[428,1549],{"x":1455,"y":1524,"width":1456,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1551,1543],{"x":1459,"y":1527,"style":1532},[428,1553],{"x":1463,"y":1524,"width":1464,"height":1480,"rx":431,"fill":818,"stroke":819,"style":1488},[457,1555,20],{"x":1467,"y":1527,"style":1556},"font-size:10.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[428,1558],{"x":1471,"y":1524,"width":1472,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1560,1561],{"x":1475,"y":1527,"style":1532},"5%",[428,1563],{"x":1416,"y":1564,"width":1417,"height":1480,"rx":431,"fill":442,"stroke":443,"style":785},"96.0",[457,1566,1568],{"x":1483,"y":1567,"style":1427},"115.0","3",[428,1570],{"x":1421,"y":1564,"width":1422,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1572,1573],{"x":1425,"y":1567,"style":1532},"H-03",[428,1575],{"x":1431,"y":1564,"width":1432,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1577,441],{"x":1435,"y":1567,"style":1532},[428,1579],{"x":1439,"y":1564,"width":1440,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1581,767],{"x":1443,"y":1567,"style":1532},[428,1583],{"x":1447,"y":1564,"width":1448,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1585,1586],{"x":1451,"y":1567,"style":1532},"Hose",[428,1588],{"x":1455,"y":1564,"width":1456,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1590,767],{"x":1459,"y":1567,"style":1532},[428,1592],{"x":1463,"y":1564,"width":1464,"height":1480,"rx":431,"fill":818,"stroke":819,"style":1488},[457,1594,20],{"x":1467,"y":1567,"style":1556},[428,1596],{"x":1471,"y":1564,"width":1472,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1598,1599],{"x":1475,"y":1567,"style":1532},"10%",[428,1601],{"x":1416,"y":1602,"width":1417,"height":1480,"rx":431,"fill":442,"stroke":443,"style":785},"124.0",[457,1604,430],{"x":1483,"y":1605,"style":1427},"143.0",[428,1607],{"x":1421,"y":1602,"width":1422,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1609,1610],{"x":1425,"y":1605,"style":1532},"D-01",[428,1612],{"x":1431,"y":1602,"width":1432,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1614,1568],{"x":1435,"y":1605,"style":1532},[428,1616],{"x":1439,"y":1602,"width":1440,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1618,1619],{"x":1443,"y":1605,"style":1532},"89",[428,1621],{"x":1447,"y":1602,"width":1448,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1623,1624],{"x":1451,"y":1605,"style":1532},"Drill",[428,1626],{"x":1455,"y":1602,"width":1456,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1628,1619],{"x":1459,"y":1605,"style":1532},[428,1630],{"x":1463,"y":1602,"width":1464,"height":1480,"rx":431,"fill":818,"stroke":819,"style":1488},[457,1632,20],{"x":1467,"y":1605,"style":1556},[428,1634],{"x":1471,"y":1602,"width":1472,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1636,1637],{"x":1475,"y":1605,"style":1532},"0%",[428,1639],{"x":1416,"y":1640,"width":1417,"height":1480,"rx":431,"fill":442,"stroke":443,"style":785},"152.0",[457,1642,1538],{"x":1483,"y":1425,"style":1427},[428,1644],{"x":1421,"y":1640,"width":1422,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1646,73],{"x":1425,"y":1425,"style":1532},[428,1648],{"x":1431,"y":1640,"width":1432,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1650,1485],{"x":1435,"y":1425,"style":1532},[428,1652],{"x":1439,"y":1640,"width":1440,"height":1480,"rx":431,"fill":1653,"stroke":1654,"style":1488},"#fdefd8","var(--gold,#b4740a)",[457,1656,1658],{"x":1443,"y":1425,"style":1657},"font-size:10.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","not found",[428,1660],{"x":1447,"y":1640,"width":1448,"height":1480,"rx":431,"fill":1653,"stroke":1654,"style":1488},[457,1662,1658],{"x":1451,"y":1425,"style":1657},[428,1664],{"x":1455,"y":1640,"width":1456,"height":1480,"rx":431,"fill":1653,"stroke":1654,"style":1488},[457,1666,1658],{"x":1459,"y":1425,"style":1657},[428,1668],{"x":1463,"y":1640,"width":1464,"height":1480,"rx":431,"fill":818,"stroke":819,"style":1488},[457,1670,20],{"x":1467,"y":1425,"style":1556},[428,1672],{"x":1471,"y":1640,"width":1472,"height":1480,"rx":431,"fill":434,"stroke":443,"style":785},[457,1674,1637],{"x":1475,"y":1425,"style":1532},[29,1676,1678],{"id":1677},"step-6-name-the-lookup-range","Step 6 — Name the lookup range",[10,1680,1681,1682,1684],{},"Hard-coded ranges like ",[14,1683,286],{}," stop at row 5; add a product and the formulas miss it. A defined name gives the range one place to change, and makes formulas readable:",[34,1686,1688],{"className":93,"code":1687,"language":95,"meta":39,"style":39},"from openpyxl.workbook.defined_name import DefinedName\n\nwb = load_workbook(\"order_form.xlsx\")\nwb.defined_names[\"PriceTable\"] = DefinedName(\"PriceTable\", attr_text=\"Prices!$A$2:$C$1000\")\nws = wb[\"Orders\"]\nws[\"C1\"] = \"Price\"\nfor r in range(2, ws.max_row + 1):\n    ws[f\"C{r}\"] = f'=IFERROR(VLOOKUP(A{r},PriceTable,3,FALSE),\"not found\")'\nwb.save(\"order_form_named.xlsx\")\nprint(ws[\"C2\"].value, \"|\", load_workbook(\"order_form_named.xlsx\").defined_names[\"PriceTable\"].attr_text)\n",[14,1689,1690,1702,1706,1718,1747,1759,1772,1795,1828,1837],{"__ignoreMap":39},[43,1691,1692,1694,1697,1699],{"class":45,"line":46},[43,1693,103],{"class":102},[43,1695,1696],{"class":106}," openpyxl.workbook.defined_name ",[43,1698,110],{"class":102},[43,1700,1701],{"class":106}," DefinedName\n",[43,1703,1704],{"class":45,"line":116},[43,1705,120],{"emptyLinePlaceholder":119},[43,1707,1708,1710,1712,1714,1716],{"class":45,"line":123},[43,1709,126],{"class":106},[43,1711,129],{"class":102},[43,1713,132],{"class":106},[43,1715,135],{"class":53},[43,1717,138],{"class":106},[43,1719,1720,1723,1726,1728,1730,1733,1735,1737,1740,1742,1745],{"class":45,"line":141},[43,1721,1722],{"class":106},"wb.defined_names[",[43,1724,1725],{"class":53},"\"PriceTable\"",[43,1727,167],{"class":106},[43,1729,129],{"class":102},[43,1731,1732],{"class":106}," DefinedName(",[43,1734,1725],{"class":53},[43,1736,730],{"class":106},[43,1738,1739],{"class":1146},"attr_text",[43,1741,129],{"class":102},[43,1743,1744],{"class":53},"\"Prices!$A$2:$C$1000\"",[43,1746,138],{"class":106},[43,1748,1749,1751,1753,1755,1757],{"class":45,"line":158},[43,1750,144],{"class":106},[43,1752,129],{"class":102},[43,1754,149],{"class":106},[43,1756,152],{"class":53},[43,1758,155],{"class":106},[43,1760,1761,1763,1765,1767,1769],{"class":45,"line":175},[43,1762,161],{"class":106},[43,1764,164],{"class":53},[43,1766,167],{"class":106},[43,1768,129],{"class":102},[43,1770,1771],{"class":53}," \"Price\"\n",[43,1773,1774,1776,1778,1780,1782,1784,1786,1789,1791,1793],{"class":45,"line":186},[43,1775,189],{"class":102},[43,1777,192],{"class":106},[43,1779,195],{"class":102},[43,1781,199],{"class":198},[43,1783,202],{"class":106},[43,1785,205],{"class":198},[43,1787,1788],{"class":106},", ws.max_row ",[43,1790,211],{"class":102},[43,1792,214],{"class":198},[43,1794,217],{"class":106},[43,1796,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819,1821,1823,1825],{"class":45,"line":220},[43,1798,223],{"class":106},[43,1800,226],{"class":102},[43,1802,229],{"class":53},[43,1804,233],{"class":232},[43,1806,236],{"class":106},[43,1808,239],{"class":232},[43,1810,242],{"class":53},[43,1812,167],{"class":106},[43,1814,129],{"class":102},[43,1816,249],{"class":102},[43,1818,252],{"class":53},[43,1820,233],{"class":232},[43,1822,236],{"class":106},[43,1824,239],{"class":232},[43,1826,1827],{"class":53},",PriceTable,3,FALSE),\"not found\")'\n",[43,1829,1830,1832,1835],{"class":45,"line":264},[43,1831,704],{"class":106},[43,1833,1834],{"class":53},"\"order_form_named.xlsx\"",[43,1836,138],{"class":106},[43,1838,1839,1841,1843,1845,1848,1851,1854,1856,1859,1861],{"class":45,"line":1234},[43,1840,267],{"class":198},[43,1842,270],{"class":106},[43,1844,273],{"class":53},[43,1846,1847],{"class":106},"].value, ",[43,1849,1850],{"class":53},"\"|\"",[43,1852,1853],{"class":106},", load_workbook(",[43,1855,1834],{"class":53},[43,1857,1858],{"class":106},").defined_names[",[43,1860,1725],{"class":53},[43,1862,1863],{"class":106},"].attr_text)\n",[10,1865,1866,1867,1871,1872,27],{},"The name covers rows up to 1,000, so new products are picked up without touching any formula. An Excel table with structured references is the other common solution, described in ",[23,1868,1870],{"href":1869},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fuse-structured-table-references-in-formulas-from-python\u002F","Use Structured Table References in Formulas from Python",", and defined names in general in ",[23,1873,1875],{"href":1874},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002F","Create a Named Range in Excel with openpyxl",[29,1877,1879],{"id":1878},"step-7-protect-the-lookup-table","Step 7 — Protect the lookup table",[10,1881,1882],{},"A live lookup is only as good as its table, and the easiest way to break an order form is to edit or sort the price list by accident. Hide the lookup sheets from casual view and protect them, while leaving the input cells on the order sheet editable:",[34,1884,1886],{"className":93,"code":1885,"language":95,"meta":39,"style":39},"from openpyxl.styles import Protection\n\nwb = load_workbook(\"order_form_named.xlsx\")\nfor name in (\"Prices\", \"Discounts\"):\n    wb[name].protection.sheet = True\n    wb[name].sheet_state = \"hidden\"\norders = wb[\"Orders\"]\nfor r in range(2, 101):\n    orders[f\"A{r}\"].protection = Protection(locked=False)\n    orders[f\"B{r}\"].protection = Protection(locked=False)\norders.protection.sheet = True\nwb.save(\"order_form_protected.xlsx\")\nprint([(ws.title, ws.sheet_state, ws.protection.sheet) for ws in load_workbook(\"order_form_protected.xlsx\")])\n",[14,1887,1888,1900,1904,1916,1937,1947,1957,1970,1991,2026,2057,2066,2075],{"__ignoreMap":39},[43,1889,1890,1892,1895,1897],{"class":45,"line":46},[43,1891,103],{"class":102},[43,1893,1894],{"class":106}," openpyxl.styles ",[43,1896,110],{"class":102},[43,1898,1899],{"class":106}," Protection\n",[43,1901,1902],{"class":45,"line":116},[43,1903,120],{"emptyLinePlaceholder":119},[43,1905,1906,1908,1910,1912,1914],{"class":45,"line":123},[43,1907,126],{"class":106},[43,1909,129],{"class":102},[43,1911,132],{"class":106},[43,1913,1834],{"class":53},[43,1915,138],{"class":106},[43,1917,1918,1920,1923,1925,1928,1930,1932,1935],{"class":45,"line":141},[43,1919,189],{"class":102},[43,1921,1922],{"class":106}," name ",[43,1924,195],{"class":102},[43,1926,1927],{"class":106}," (",[43,1929,1229],{"class":53},[43,1931,730],{"class":106},[43,1933,1934],{"class":53},"\"Discounts\"",[43,1936,217],{"class":106},[43,1938,1939,1942,1944],{"class":45,"line":158},[43,1940,1941],{"class":106},"    wb[name].protection.sheet ",[43,1943,129],{"class":102},[43,1945,1946],{"class":198}," True\n",[43,1948,1949,1952,1954],{"class":45,"line":175},[43,1950,1951],{"class":106},"    wb[name].sheet_state ",[43,1953,129],{"class":102},[43,1955,1956],{"class":53}," \"hidden\"\n",[43,1958,1959,1962,1964,1966,1968],{"class":45,"line":186},[43,1960,1961],{"class":106},"orders ",[43,1963,129],{"class":102},[43,1965,149],{"class":106},[43,1967,152],{"class":53},[43,1969,155],{"class":106},[43,1971,1972,1974,1976,1978,1980,1982,1984,1986,1989],{"class":45,"line":220},[43,1973,189],{"class":102},[43,1975,192],{"class":106},[43,1977,195],{"class":102},[43,1979,199],{"class":198},[43,1981,202],{"class":106},[43,1983,205],{"class":198},[43,1985,730],{"class":106},[43,1987,1988],{"class":198},"101",[43,1990,217],{"class":106},[43,1992,1993,1996,1998,2001,2003,2005,2007,2009,2012,2014,2017,2020,2022,2024],{"class":45,"line":264},[43,1994,1995],{"class":106},"    orders[",[43,1997,226],{"class":102},[43,1999,2000],{"class":53},"\"A",[43,2002,233],{"class":232},[43,2004,236],{"class":106},[43,2006,239],{"class":232},[43,2008,242],{"class":53},[43,2010,2011],{"class":106},"].protection ",[43,2013,129],{"class":102},[43,2015,2016],{"class":106}," Protection(",[43,2018,2019],{"class":1146},"locked",[43,2021,129],{"class":102},[43,2023,1202],{"class":198},[43,2025,138],{"class":106},[43,2027,2028,2030,2032,2035,2037,2039,2041,2043,2045,2047,2049,2051,2053,2055],{"class":45,"line":1234},[43,2029,1995],{"class":106},[43,2031,226],{"class":102},[43,2033,2034],{"class":53},"\"B",[43,2036,233],{"class":232},[43,2038,236],{"class":106},[43,2040,239],{"class":232},[43,2042,242],{"class":53},[43,2044,2011],{"class":106},[43,2046,129],{"class":102},[43,2048,2016],{"class":106},[43,2050,2019],{"class":1146},[43,2052,129],{"class":102},[43,2054,1202],{"class":198},[43,2056,138],{"class":106},[43,2058,2059,2062,2064],{"class":45,"line":1277},[43,2060,2061],{"class":106},"orders.protection.sheet ",[43,2063,129],{"class":102},[43,2065,1946],{"class":198},[43,2067,2068,2070,2073],{"class":45,"line":1294},[43,2069,704],{"class":106},[43,2071,2072],{"class":53},"\"order_form_protected.xlsx\"",[43,2074,138],{"class":106},[43,2076,2077,2079,2082,2084,2087,2089,2091,2093],{"class":45,"line":1326},[43,2078,267],{"class":198},[43,2080,2081],{"class":106},"([(ws.title, ws.sheet_state, ws.protection.sheet) ",[43,2083,189],{"class":102},[43,2085,2086],{"class":106}," ws ",[43,2088,195],{"class":102},[43,2090,132],{"class":106},[43,2092,2072],{"class":53},[43,2094,2095],{"class":106},")])\n",[10,2097,2098,2099,27],{},"The SKU and quantity columns stay editable for a hundred rows; everything else — including the formula columns — is locked, so nobody can overwrite a formula with a typed price. Sheet protection without a password is a guard against accidents, not security; the details are in ",[23,2100,2102],{"href":2101},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl\u002F","Lock Cells and Protect a Sheet with openpyxl",[29,2104,2106],{"id":2105},"performance-notes","Performance notes",[10,2108,2109],{},"Lookups are cheap until they are numerous and wide. A few thousand VLOOKUPs against a few hundred rows recalculate instantly; tens of thousands against a 100,000-row table, each scanning for an exact match, make every edit pause. If the workbook is large, keep lookup ranges tight (a defined name sized to the data rather than whole columns), prefer INDEX\u002FMATCH or XLOOKUP over repeated VLOOKUPs that return several columns from the same row, and ask whether the lookup needs to be live at all.",[29,2111,2113],{"id":2112},"common-pitfalls","Common pitfalls",[2115,2116,2117,2128,2136,2146,2152],"ul",{},[2118,2119,2120,2127],"li",{},[2121,2122,2123,2124,2126],"strong",{},"Missing ",[14,2125,282],{}," on the lookup range."," Without anchors, a formula copied down shifts its table too.",[2118,2129,2130,2135],{},[2121,2131,2132,2133,27],{},"VLOOKUP without ",[14,2134,294],{}," It defaults to approximate matching and returns wrong keys from unsorted tables.",[2118,2137,2138,2143,2144,27],{},[2121,2139,2140,2141,27],{},"XLOOKUP without ",[14,2142,566],{}," The cell shows ",[14,2145,20],{},[2118,2147,2148,2151],{},[2121,2149,2150],{},"Approximate matches on unsorted tiers."," Sort the tier table ascending, or the result is undefined.",[2118,2153,2154,2157],{},[2121,2155,2156],{},"Thousands of lookups over big ranges."," Workbooks slow down noticeably; if nobody edits the keys, do the lookup in pandas and write values.",[29,2159,2161],{"id":2160},"conclusion","Conclusion",[10,2163,2164,2165,2167,2168,2170,2171,2173,2174,2176,2177,2179],{},"Lookup formulas from Python are strings with a few rules: anchor the lookup range with ",[14,2166,282],{},", ask for exact matches (",[14,2169,294],{}," in VLOOKUP, ",[14,2172,431],{}," in MATCH), handle missing keys with ",[14,2175,298],{}," or XLOOKUP's not-found argument, and write XLOOKUP as ",[14,2178,574],{},". Use approximate matching only for sorted tier tables, give the lookup range a name so it can grow, and recalculate in LibreOffice to check the formulas against the same lookup in pandas.",[29,2181,2183],{"id":2182},"frequently-asked-questions","Frequently asked questions",[10,2185,2186,2189],{},[2121,2187,2188],{},"How do I write a VLOOKUP formula with openpyxl?","\nAssign the formula text to the cell as a string that starts with an equals sign, with the lookup range anchored by $ signs and FALSE as the last argument for an exact match, exactly as you would type it in Excel.",[10,2191,2192,2195],{},[2121,2193,2194],{},"Why does XLOOKUP written by openpyxl show #NAME?","\nFunctions added after Excel 2010 are stored in the file with an _xlfn. prefix. Write =_xlfn.XLOOKUP(...) and Excel will display and evaluate it as XLOOKUP.",[10,2197,2198,2201],{},[2121,2199,2200],{},"Should I use VLOOKUP, INDEX\u002FMATCH or XLOOKUP in generated workbooks?","\nINDEX\u002FMATCH works in every Excel version and LibreOffice and survives inserted columns. XLOOKUP is clearer but needs Excel 2021 or 365 and the _xlfn. prefix. VLOOKUP is simplest but can only return columns to the right of the key.",[10,2203,2204,2207],{},[2121,2205,2206],{},"How do I check that generated lookup formulas are correct?","\nRecalculate the workbook with LibreOffice (soffice --headless --convert-to xlsx), read the results with pandas, and compare them with a pandas merge of the same tables.",[29,2209,2211],{"id":2210},"related","Related",[2115,2213,2214,2220,2227,2234],{},[2118,2215,2216,2217,2219],{},"Up one level: ",[23,2218,26],{"href":25}," — writing and checking formulas.",[2118,2221,2222,2226],{},[23,2223,2225],{"href":2224},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fxlookup-equivalent-in-pandas\u002F","XLOOKUP Equivalent in pandas"," — the same lookup without formulas.",[2118,2228,2229,2233],{},[23,2230,2232],{"href":2231},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl\u002F","Write Formulas That Reference Other Sheets with openpyxl"," — quoting and anchoring sheet references.",[2118,2235,2236,2240],{},[23,2237,2239],{"href":2238},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcreate-dependent-dropdown-lists-in-excel-with-python\u002F","Create Dependent Dropdown Lists in Excel with Python"," — pairing lookups with input validation.",[2242,2243,2244],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":39,"searchDepth":116,"depth":116,"links":2246},[2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259],{"id":31,"depth":116,"text":32},{"id":89,"depth":116,"text":90},{"id":306,"depth":116,"text":307},{"id":559,"depth":116,"text":560},{"id":864,"depth":116,"text":865},{"id":1063,"depth":116,"text":1064},{"id":1677,"depth":116,"text":1678},{"id":1878,"depth":116,"text":1879},{"id":2105,"depth":116,"text":2106},{"id":2112,"depth":116,"text":2113},{"id":2160,"depth":116,"text":2161},{"id":2182,"depth":116,"text":2183},{"id":2210,"depth":116,"text":2211},"2026-10-02","Generate live lookup formulas that pull prices, names or rates from another sheet: VLOOKUP with an exact match, INDEX\u002FMATCH, and XLOOKUP — which must be written with the _xlfn prefix or it shows #NAME? — plus absolute ranges that survive copying, not-found handling, tiered approximate matches, a named lookup range, and a LibreOffice check that every formula returns what pandas expects.","md",[2264,2266,2268,2270],{"q":2188,"a":2265},"Assign the formula text to the cell as a string that starts with an equals sign, with the lookup range anchored by $ signs and FALSE as the last argument for an exact match, exactly as you would type it in Excel.",{"q":2194,"a":2267},"Functions added after Excel 2010 are stored in the file with an _xlfn. prefix. Write =_xlfn.XLOOKUP(...) and Excel will display and evaluate it as XLOOKUP.",{"q":2200,"a":2269},"INDEX\u002FMATCH works in every Excel version and LibreOffice and survives inserted columns. XLOOKUP is clearer but needs Excel 2021 or 365 and the _xlfn. prefix. VLOOKUP is simplest but can only return columns to the right of the key.",{"q":2206,"a":2271},"Recalculate the workbook with LibreOffice (soffice --headless --convert-to xlsx), read the results with pandas, and compare them with a pandas merge of the same tables.","write lookup formulas with openpyxl",null,{"breadcrumb":2275},[2276,2279,2282],{"name":2277,"item":2278},"Home","\u002F",{"name":2280,"item":2281},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":26,"item":25},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl",{"title":2285,"description":2286},"Write XLOOKUP, VLOOKUP and INDEX\u002FMATCH Formulas with openpyxl","Write lookup formulas from Python with openpyxl: VLOOKUP FALSE, INDEX\u002FMATCH, _xlfn.XLOOKUP (not plain XLOOKUP, which gives #NAME?), $-anchored ranges, IFERROR and if_not_found, tier lookups, defined names, and recalculating to verify.","write-lookup-formulas-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-lookup-formulas-with-openpyxl\u002Findex",[2290,2291,2292],"excel-formulas","joins-and-lookups","openpyxl","how-to","-jYL9ptbEvHOYplgHSfYJ4J9UY8V0SZvoa7vKRa75zk",[2296,2300],{"title":2297,"path":2298,"stem":2299,"children":-1},"Write LET and LAMBDA Formulas from Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-let-and-lambda-formulas-from-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-let-and-lambda-formulas-from-python\u002Findex",{"title":2301,"path":2302,"stem":2303,"children":-1},"Working with Multiple Excel Sheets in Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Findex",[2305,2307],{"path":2306,"title":2280},"\u002Fgetting-started-with-python-excel-automation",{"path":2308,"title":26},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python",[],[2311,2314,2318,2321,2324,2327],{"path":2312,"title":2313,"kind":2293},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fstore-formula-results-with-xlsxwriter","Store Formula Results with xlsxwriter So Every Viewer Shows Values",{"path":2315,"title":2316,"kind":2317},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-with-formula-errors-using-conditional-formatting","Highlight Cells with Formula Errors Using Conditional Formatting","fix",{"path":2319,"title":2320,"kind":2293},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgoal-seek-in-python-instead-of-excel","Goal Seek in Python Instead of Excel",{"path":2322,"title":2323,"kind":2293},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-that-totals-in-a-report-add-up","Check That Totals in a Report Add Up",{"path":2325,"title":2326,"kind":2293},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","Call Excel Worksheet Functions Through Microsoft Graph",{"path":2328,"title":2329,"kind":2293},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions","Recalculate Excel Online Workbooks with Graph Sessions",1790972076007]