[{"data":1,"prerenderedAt":3987},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python":3979},{"id":4,"title":5,"body":6,"dateModified":3947,"datePublished":3947,"description":3948,"extension":3949,"faq":3950,"meta":3964,"navigation":216,"path":3972,"seo":3973,"slug":3975,"stem":3976,"type":3977,"__hash__":3978},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Findex.md","Working with Excel Formulas in Python",{"type":7,"value":8,"toc":3932},"minimark",[9,24,172,177,184,434,452,471,475,482,1045,1064,1130,1141,1145,1151,1261,1276,1433,1436,1485,1568,1578,1582,1593,1824,1839,2012,2124,2140,2144,2147,2375,2392,2396,2410,2842,2849,2853,2864,3205,3227,3231,3324,3327,3331,3465,3469,3478,3733,3741,3745,3777,3781,3788,3802,3808,3818,3834,3842,3846,3849,3856,3859,3903,3906,3928],[10,11,12,13,17,18,23],"p",{},"A spreadsheet that only carries numbers is a report; a spreadsheet that carries formulas is a model someone can prod. Python can produce either. ",[14,15,16],"code",{},"openpyxl"," writes formulas as plain strings and reads them back the same way — what it never does is calculate them, and that single fact explains most of the surprises in this area. This guide, part of ",[19,20,22],"a",{"href":21},"\u002Fgetting-started-with-python-excel-automation\u002F","Getting Started with Python Excel Automation",", covers the full round trip: writing a formula, generating references that survive a growing dataset, reading results back, naming ranges, and freezing formulas into values before a file leaves your hands.",[25,26,34,35,34,39,34,43,34,50,34,59,34,66,34,71,34,76,34,84,34,88,34,94,34,100,34,105,34,110,34,115,34,119,34,123,34,128,34,131,34,134,34,139,34,143,34,150,34,156,34,160,34,164,34,168],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"0 0 760 268","img",[30,31],"fx-round-t","fx-round-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  ",[36,37,38],"title",{"id":30},"The formula round trip between Python and Excel",[40,41,42],"desc",{"id":31},"Python writes a formula string into a cell and saves the file. openpyxl stores the text only. When Excel opens the workbook it evaluates the formula and stores a cached result alongside it on save. Reading with data_only=True returns that cached result, which is None until a calculating application has saved the file.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","268","#ffffff",[44,51],{"x":52,"y":53,"width":54,"height":55,"rx":56,"fill":57,"stroke":58},"24","40","200","92","14","#5b5cf0","var(--brand-strong,#4338ca)",[60,61,65],"text",{"x":62,"y":63,"style":64},"124","74","font-size:15px;font-weight:700;fill:#ffffff;text-anchor:middle","Python",[60,67,70],{"x":62,"y":68,"style":69},"98","font-size:12px;fill:rgba(255,255,255,0.88);text-anchor:middle","ws[\"D2\"] = \"=B2*C2\"",[60,72,75],{"x":62,"y":73,"style":74},"118","font-size:11.5px;fill:rgba(255,255,255,0.88);text-anchor:middle","writes text, never a number",[77,78],"line",{"x1":79,"y1":80,"x2":81,"y2":80,"stroke":82,"style":83},"228","86","286","var(--brand,#5b5cf0)","stroke-width:2px",[85,86],"polygon",{"points":87,"fill":57},"290,86 278,80 278,92",[44,89],{"x":90,"y":53,"width":91,"height":55,"rx":56,"fill":92,"stroke":93},"292","176","#f0f4ff","var(--line,#cdd5e6)",[60,95,99],{"x":96,"y":97,"style":98},"380","72","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","report.xlsx",[60,101,104],{"x":96,"y":102,"style":103},"94","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","formula: =B2*C2",[60,106,109],{"x":96,"y":107,"style":108},"114","font-size:11.5px;fill:var(--danger,#dc2626);text-anchor:middle","cached value: none yet",[77,111],{"x1":112,"y1":80,"x2":113,"y2":80,"stroke":114,"style":83},"472","530","var(--teal,#0f9488)",[85,116],{"points":117,"fill":118},"534,86 522,80 522,92","#0f766e",[44,120],{"x":121,"y":53,"width":54,"height":55,"rx":56,"fill":122,"stroke":114,"style":83},"536","#d9f4f1",[60,124,127],{"x":125,"y":97,"style":126},"636","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Excel opens it",[60,129,130],{"x":125,"y":102,"style":103},"evaluates the formula",[60,132,133],{"x":125,"y":107,"style":103},"caches 249.90 on save",[77,135],{"x1":125,"y1":136,"x2":125,"y2":91,"stroke":137,"style":138},"136","var(--muted,#5b6780)","stroke-width:1.5px",[85,140],{"points":141,"fill":142},"636,180 630,168 642,168","#5b6780",[44,144],{"x":90,"y":145,"width":146,"height":147,"rx":148,"fill":149,"stroke":93},"180","444","60","12","#ebebfd",[60,151,155],{"x":152,"y":153,"style":154},"514","204","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","load_workbook(path, data_only=True)",[60,157,159],{"x":152,"y":158,"style":103},"226","reads the cached result — only after a calculating app has saved",[77,161],{"x1":62,"y1":136,"x2":62,"y2":162,"stroke":137,"style":163},"210","stroke-width:1.5px;stroke-dasharray:4 4",[60,165,167],{"x":62,"y":166,"style":103},"196","re-open in Python:",[60,169,171],{"x":62,"y":158,"style":170},"font-size:11.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","formula text comes back",[173,174,176],"h2",{"id":175},"write-your-first-formula","Write your first formula",[10,178,179,180,183],{},"A formula is just a string that starts with ",[14,181,182],{},"=",". Assign it like any other value and save:",[185,186,191],"pre",{"className":187,"code":188,"language":189,"meta":190,"style":190},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Orders\"\n\nws.append([\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\"])\nws.append([\"A-100\", 4, 19.99, \"=B2*C2\"])\nws.append([\"B-200\", 2, 49.50, \"=B3*C3\"])\nws.append([\"C-300\", 7, 12.25, \"=B4*C4\"])\n\nws[\"C6\"] = \"Order total\"\nws[\"D6\"] = \"=SUM(D2:D4)\"\n\nwb.save(\"orders.xlsx\")\nprint(ws[\"D2\"].value)      # '=B2*C2' — the text, not 79.96\n","python","",[14,192,193,211,218,229,240,252,257,285,311,336,361,366,383,398,403,415],{"__ignoreMap":190},[194,195,197,201,205,208],"span",{"class":77,"line":196},1,[194,198,200],{"class":199},"s-kum","from",[194,202,204],{"class":203},"skGVy"," openpyxl ",[194,206,207],{"class":199},"import",[194,209,210],{"class":203}," Workbook\n",[194,212,214],{"class":77,"line":213},2,[194,215,217],{"emptyLinePlaceholder":216},true,"\n",[194,219,221,224,226],{"class":77,"line":220},3,[194,222,223],{"class":203},"wb ",[194,225,182],{"class":199},[194,227,228],{"class":203}," Workbook()\n",[194,230,232,235,237],{"class":77,"line":231},4,[194,233,234],{"class":203},"ws ",[194,236,182],{"class":199},[194,238,239],{"class":203}," wb.active\n",[194,241,243,246,248],{"class":77,"line":242},5,[194,244,245],{"class":203},"ws.title ",[194,247,182],{"class":199},[194,249,251],{"class":250},"srMev"," \"Orders\"\n",[194,253,255],{"class":77,"line":254},6,[194,256,217],{"emptyLinePlaceholder":216},[194,258,260,263,266,269,272,274,277,279,282],{"class":77,"line":259},7,[194,261,262],{"class":203},"ws.append([",[194,264,265],{"class":250},"\"SKU\"",[194,267,268],{"class":203},", ",[194,270,271],{"class":250},"\"Quantity\"",[194,273,268],{"class":203},[194,275,276],{"class":250},"\"Unit_Price\"",[194,278,268],{"class":203},[194,280,281],{"class":250},"\"Line_Total\"",[194,283,284],{"class":203},"])\n",[194,286,288,290,293,295,299,301,304,306,309],{"class":77,"line":287},8,[194,289,262],{"class":203},[194,291,292],{"class":250},"\"A-100\"",[194,294,268],{"class":203},[194,296,298],{"class":297},"sP0c6","4",[194,300,268],{"class":203},[194,302,303],{"class":297},"19.99",[194,305,268],{"class":203},[194,307,308],{"class":250},"\"=B2*C2\"",[194,310,284],{"class":203},[194,312,314,316,319,321,324,326,329,331,334],{"class":77,"line":313},9,[194,315,262],{"class":203},[194,317,318],{"class":250},"\"B-200\"",[194,320,268],{"class":203},[194,322,323],{"class":297},"2",[194,325,268],{"class":203},[194,327,328],{"class":297},"49.50",[194,330,268],{"class":203},[194,332,333],{"class":250},"\"=B3*C3\"",[194,335,284],{"class":203},[194,337,339,341,344,346,349,351,354,356,359],{"class":77,"line":338},10,[194,340,262],{"class":203},[194,342,343],{"class":250},"\"C-300\"",[194,345,268],{"class":203},[194,347,348],{"class":297},"7",[194,350,268],{"class":203},[194,352,353],{"class":297},"12.25",[194,355,268],{"class":203},[194,357,358],{"class":250},"\"=B4*C4\"",[194,360,284],{"class":203},[194,362,364],{"class":77,"line":363},11,[194,365,217],{"emptyLinePlaceholder":216},[194,367,369,372,375,378,380],{"class":77,"line":368},12,[194,370,371],{"class":203},"ws[",[194,373,374],{"class":250},"\"C6\"",[194,376,377],{"class":203},"] ",[194,379,182],{"class":199},[194,381,382],{"class":250}," \"Order total\"\n",[194,384,386,388,391,393,395],{"class":77,"line":385},13,[194,387,371],{"class":203},[194,389,390],{"class":250},"\"D6\"",[194,392,377],{"class":203},[194,394,182],{"class":199},[194,396,397],{"class":250}," \"=SUM(D2:D4)\"\n",[194,399,401],{"class":77,"line":400},14,[194,402,217],{"emptyLinePlaceholder":216},[194,404,406,409,412],{"class":77,"line":405},15,[194,407,408],{"class":203},"wb.save(",[194,410,411],{"class":250},"\"orders.xlsx\"",[194,413,414],{"class":203},")\n",[194,416,418,421,424,427,430],{"class":77,"line":417},16,[194,419,420],{"class":297},"print",[194,422,423],{"class":203},"(ws[",[194,425,426],{"class":250},"\"D2\"",[194,428,429],{"class":203},"].value)      ",[194,431,433],{"class":432},"s-wDw","# '=B2*C2' — the text, not 79.96\n",[10,435,436,437,439,440,443,444,447,448,451],{},"The ",[14,438,420],{}," is the important line. Python holds ",[14,441,442],{},"'=B2*C2'",", not ",[14,445,446],{},"79.96",", because nothing has calculated anything. Open ",[14,449,450],{},"orders.xlsx"," in Excel or LibreOffice and the numbers appear immediately; the file on disk is complete and correct, it simply carries instructions rather than results.",[10,453,454,455,462,463,466,467,470],{},"That is also why a workbook full of formulas can look empty to a downstream script. If another job reads this file with ",[19,456,458,459],{"href":457},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002F","pandas ",[14,460,461],{},"read_excel",", the ",[14,464,465],{},"Line_Total"," column arrives as ",[14,468,469],{},"NaN"," until someone has opened and saved the workbook in a spreadsheet application.",[173,472,474],{"id":473},"generate-references-that-follow-your-data","Generate references that follow your data",[10,476,477,478,481],{},"Hardcoded row numbers survive exactly one dataset. Build them from the row you are writing, and let ",[14,479,480],{},"get_column_letter"," translate column indexes so a re-ordered layout does not silently point at the wrong column:",[185,483,485],{"className":187,"code":484,"language":189,"meta":190,"style":190},"from openpyxl import Workbook\nfrom openpyxl.utils import get_column_letter\n\nrows = [\n    (\"A-100\", 4, 19.99),\n    (\"B-200\", 2, 49.50),\n    (\"C-300\", 7, 12.25),\n    (\"D-400\", 5, 8.75),\n]\n\nwb = Workbook()\nws = wb.active\nws.append([\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\", \"Share\"])\n\nqty_col = get_column_letter(2)      # 'B'\nprice_col = get_column_letter(3)    # 'C'\ntotal_col = get_column_letter(4)    # 'D'\n\nfor sku, qty, price in rows:\n    ws.append([sku, qty, price])\n    r = ws.max_row                                  # the row just written\n    ws[f\"{total_col}{r}\"] = f\"={qty_col}{r}*{price_col}{r}\"\n\nlast = ws.max_row\ntotal_row = last + 2\nws[f\"C{total_row}\"] = \"Order total\"\nws[f\"{total_col}{total_row}\"] = f\"=SUM({total_col}2:{total_col}{last})\"\n\n# Each line's share of the order — the divisor must not move, so anchor it\nfor r in range(2, last + 1):\n    ws[f\"E{r}\"] = f\"={total_col}{r}\u002F$D${total_row}\"\n    ws[f\"E{r}\"].number_format = \"0.0%\"\n\nwb.save(\"orders_generated.xlsx\")\n",[14,486,487,497,509,513,523,541,557,573,592,597,601,609,617,642,646,664,682,699,704,719,725,739,807,812,823,840,865,918,923,929,958,1005,1030,1035],{"__ignoreMap":190},[194,488,489,491,493,495],{"class":77,"line":196},[194,490,200],{"class":199},[194,492,204],{"class":203},[194,494,207],{"class":199},[194,496,210],{"class":203},[194,498,499,501,504,506],{"class":77,"line":213},[194,500,200],{"class":199},[194,502,503],{"class":203}," openpyxl.utils ",[194,505,207],{"class":199},[194,507,508],{"class":203}," get_column_letter\n",[194,510,511],{"class":77,"line":220},[194,512,217],{"emptyLinePlaceholder":216},[194,514,515,518,520],{"class":77,"line":231},[194,516,517],{"class":203},"rows ",[194,519,182],{"class":199},[194,521,522],{"class":203}," [\n",[194,524,525,528,530,532,534,536,538],{"class":77,"line":242},[194,526,527],{"class":203},"    (",[194,529,292],{"class":250},[194,531,268],{"class":203},[194,533,298],{"class":297},[194,535,268],{"class":203},[194,537,303],{"class":297},[194,539,540],{"class":203},"),\n",[194,542,543,545,547,549,551,553,555],{"class":77,"line":254},[194,544,527],{"class":203},[194,546,318],{"class":250},[194,548,268],{"class":203},[194,550,323],{"class":297},[194,552,268],{"class":203},[194,554,328],{"class":297},[194,556,540],{"class":203},[194,558,559,561,563,565,567,569,571],{"class":77,"line":259},[194,560,527],{"class":203},[194,562,343],{"class":250},[194,564,268],{"class":203},[194,566,348],{"class":297},[194,568,268],{"class":203},[194,570,353],{"class":297},[194,572,540],{"class":203},[194,574,575,577,580,582,585,587,590],{"class":77,"line":287},[194,576,527],{"class":203},[194,578,579],{"class":250},"\"D-400\"",[194,581,268],{"class":203},[194,583,584],{"class":297},"5",[194,586,268],{"class":203},[194,588,589],{"class":297},"8.75",[194,591,540],{"class":203},[194,593,594],{"class":77,"line":313},[194,595,596],{"class":203},"]\n",[194,598,599],{"class":77,"line":338},[194,600,217],{"emptyLinePlaceholder":216},[194,602,603,605,607],{"class":77,"line":363},[194,604,223],{"class":203},[194,606,182],{"class":199},[194,608,228],{"class":203},[194,610,611,613,615],{"class":77,"line":368},[194,612,234],{"class":203},[194,614,182],{"class":199},[194,616,239],{"class":203},[194,618,619,621,623,625,627,629,631,633,635,637,640],{"class":77,"line":385},[194,620,262],{"class":203},[194,622,265],{"class":250},[194,624,268],{"class":203},[194,626,271],{"class":250},[194,628,268],{"class":203},[194,630,276],{"class":250},[194,632,268],{"class":203},[194,634,281],{"class":250},[194,636,268],{"class":203},[194,638,639],{"class":250},"\"Share\"",[194,641,284],{"class":203},[194,643,644],{"class":77,"line":400},[194,645,217],{"emptyLinePlaceholder":216},[194,647,648,651,653,656,658,661],{"class":77,"line":405},[194,649,650],{"class":203},"qty_col ",[194,652,182],{"class":199},[194,654,655],{"class":203}," get_column_letter(",[194,657,323],{"class":297},[194,659,660],{"class":203},")      ",[194,662,663],{"class":432},"# 'B'\n",[194,665,666,669,671,673,676,679],{"class":77,"line":417},[194,667,668],{"class":203},"price_col ",[194,670,182],{"class":199},[194,672,655],{"class":203},[194,674,675],{"class":297},"3",[194,677,678],{"class":203},")    ",[194,680,681],{"class":432},"# 'C'\n",[194,683,685,688,690,692,694,696],{"class":77,"line":684},17,[194,686,687],{"class":203},"total_col ",[194,689,182],{"class":199},[194,691,655],{"class":203},[194,693,298],{"class":297},[194,695,678],{"class":203},[194,697,698],{"class":432},"# 'D'\n",[194,700,702],{"class":77,"line":701},18,[194,703,217],{"emptyLinePlaceholder":216},[194,705,707,710,713,716],{"class":77,"line":706},19,[194,708,709],{"class":199},"for",[194,711,712],{"class":203}," sku, qty, price ",[194,714,715],{"class":199},"in",[194,717,718],{"class":203}," rows:\n",[194,720,722],{"class":77,"line":721},20,[194,723,724],{"class":203},"    ws.append([sku, qty, price])\n",[194,726,728,731,733,736],{"class":77,"line":727},21,[194,729,730],{"class":203},"    r ",[194,732,182],{"class":199},[194,734,735],{"class":203}," ws.max_row                                  ",[194,737,738],{"class":432},"# the row just written\n",[194,740,742,745,748,751,755,758,761,764,767,769,771,773,776,779,781,784,786,788,790,793,795,798,800,802,804],{"class":77,"line":741},22,[194,743,744],{"class":203},"    ws[",[194,746,747],{"class":199},"f",[194,749,750],{"class":250},"\"",[194,752,754],{"class":753},"sSjpA","{",[194,756,757],{"class":203},"total_col",[194,759,760],{"class":753},"}{",[194,762,763],{"class":203},"r",[194,765,766],{"class":753},"}",[194,768,750],{"class":250},[194,770,377],{"class":203},[194,772,182],{"class":199},[194,774,775],{"class":199}," f",[194,777,778],{"class":250},"\"=",[194,780,754],{"class":753},[194,782,783],{"class":203},"qty_col",[194,785,760],{"class":753},[194,787,763],{"class":203},[194,789,766],{"class":753},[194,791,792],{"class":250},"*",[194,794,754],{"class":753},[194,796,797],{"class":203},"price_col",[194,799,760],{"class":753},[194,801,763],{"class":203},[194,803,766],{"class":753},[194,805,806],{"class":250},"\"\n",[194,808,810],{"class":77,"line":809},23,[194,811,217],{"emptyLinePlaceholder":216},[194,813,815,818,820],{"class":77,"line":814},24,[194,816,817],{"class":203},"last ",[194,819,182],{"class":199},[194,821,822],{"class":203}," ws.max_row\n",[194,824,826,829,831,834,837],{"class":77,"line":825},25,[194,827,828],{"class":203},"total_row ",[194,830,182],{"class":199},[194,832,833],{"class":203}," last ",[194,835,836],{"class":199},"+",[194,838,839],{"class":297}," 2\n",[194,841,843,845,847,850,852,855,857,859,861,863],{"class":77,"line":842},26,[194,844,371],{"class":203},[194,846,747],{"class":199},[194,848,849],{"class":250},"\"C",[194,851,754],{"class":753},[194,853,854],{"class":203},"total_row",[194,856,766],{"class":753},[194,858,750],{"class":250},[194,860,377],{"class":203},[194,862,182],{"class":199},[194,864,382],{"class":250},[194,866,868,870,872,874,876,878,880,882,884,886,888,890,892,895,897,899,901,904,906,908,910,913,915],{"class":77,"line":867},27,[194,869,371],{"class":203},[194,871,747],{"class":199},[194,873,750],{"class":250},[194,875,754],{"class":753},[194,877,757],{"class":203},[194,879,760],{"class":753},[194,881,854],{"class":203},[194,883,766],{"class":753},[194,885,750],{"class":250},[194,887,377],{"class":203},[194,889,182],{"class":199},[194,891,775],{"class":199},[194,893,894],{"class":250},"\"=SUM(",[194,896,754],{"class":753},[194,898,757],{"class":203},[194,900,766],{"class":753},[194,902,903],{"class":250},"2:",[194,905,754],{"class":753},[194,907,757],{"class":203},[194,909,760],{"class":753},[194,911,912],{"class":203},"last",[194,914,766],{"class":753},[194,916,917],{"class":250},")\"\n",[194,919,921],{"class":77,"line":920},28,[194,922,217],{"emptyLinePlaceholder":216},[194,924,926],{"class":77,"line":925},29,[194,927,928],{"class":432},"# Each line's share of the order — the divisor must not move, so anchor it\n",[194,930,932,934,937,939,942,945,947,950,952,955],{"class":77,"line":931},30,[194,933,709],{"class":199},[194,935,936],{"class":203}," r ",[194,938,715],{"class":199},[194,940,941],{"class":297}," range",[194,943,944],{"class":203},"(",[194,946,323],{"class":297},[194,948,949],{"class":203},", last ",[194,951,836],{"class":199},[194,953,954],{"class":297}," 1",[194,956,957],{"class":203},"):\n",[194,959,961,963,965,968,970,972,974,976,978,980,982,984,986,988,990,992,994,997,999,1001,1003],{"class":77,"line":960},31,[194,962,744],{"class":203},[194,964,747],{"class":199},[194,966,967],{"class":250},"\"E",[194,969,754],{"class":753},[194,971,763],{"class":203},[194,973,766],{"class":753},[194,975,750],{"class":250},[194,977,377],{"class":203},[194,979,182],{"class":199},[194,981,775],{"class":199},[194,983,778],{"class":250},[194,985,754],{"class":753},[194,987,757],{"class":203},[194,989,760],{"class":753},[194,991,763],{"class":203},[194,993,766],{"class":753},[194,995,996],{"class":250},"\u002F$D$",[194,998,754],{"class":753},[194,1000,854],{"class":203},[194,1002,766],{"class":753},[194,1004,806],{"class":250},[194,1006,1008,1010,1012,1014,1016,1018,1020,1022,1025,1027],{"class":77,"line":1007},32,[194,1009,744],{"class":203},[194,1011,747],{"class":199},[194,1013,967],{"class":250},[194,1015,754],{"class":753},[194,1017,763],{"class":203},[194,1019,766],{"class":753},[194,1021,750],{"class":250},[194,1023,1024],{"class":203},"].number_format ",[194,1026,182],{"class":199},[194,1028,1029],{"class":250}," \"0.0%\"\n",[194,1031,1033],{"class":77,"line":1032},33,[194,1034,217],{"emptyLinePlaceholder":216},[194,1036,1038,1040,1043],{"class":77,"line":1037},34,[194,1039,408],{"class":203},[194,1041,1042],{"class":250},"\"orders_generated.xlsx\"",[194,1044,414],{"class":203},[10,1046,1047,1048,1051,1052,1055,1056,1059,1060,1063],{},"Two habits are doing the work here. ",[14,1049,1050],{},"ws.max_row"," gives the row that was just appended, so the formula and its data can never drift apart. And the ",[14,1053,1054],{},"$D$14","-style anchor on the divisor means every ",[14,1057,1058],{},"Share"," formula points at the one total cell — without the dollar signs, a reader who sorts or inserts a row gets a column of ",[14,1061,1062],{},"#DIV\u002F0!",".",[25,1065,34,1070,34,1073,34,1076,34,1079,34,1084,34,1089,34,1094,34,1098,34,1102,34,1107,34,1111,34,1115,34,1119,34,1124,34,1127],{"viewBox":1066,"role":28,"ariaLabelledBy":1067,"xmlns":32,"style":33},"0 0 760 232",[1068,1069],"fx-ref-t","fx-ref-d",[36,1071,1072],{"id":1068},"Which parts of a generated reference move and which are anchored",[40,1074,1075],{"id":1069},"The formula equals D3 divided by dollar D dollar 14 is broken into parts: D3 is relative, so both its column and row shift when the formula is copied; dollar D dollar 14 is absolute, so the column and row stay pinned to the order total cell.",[44,1077],{"x":46,"y":46,"width":47,"height":1078,"fill":49},"232",[44,1080],{"x":145,"y":1081,"width":1082,"height":1083,"rx":148,"fill":149,"stroke":82,"style":83},"28","400","52",[60,1085,1088],{"x":96,"y":1086,"style":1087},"61","font-size:19px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,monospace","=D3 \u002F $D$14",[77,1090],{"x1":81,"y1":1091,"x2":1092,"y2":1093,"stroke":137,"style":138},"84","240","120",[77,1095],{"x1":1096,"y1":1091,"x2":1097,"y2":1093,"stroke":137,"style":138},"470","520",[44,1099],{"x":147,"y":62,"width":1100,"height":1101,"rx":148,"fill":92,"stroke":93},"300","82",[60,1103,1106],{"x":162,"y":1104,"style":1105},"150","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","D3 — relative",[60,1108,1110],{"x":162,"y":1109,"style":103},"172","shifts with the row it lives on",[60,1112,1114],{"x":162,"y":1113,"style":103},"192","generated per row from ws.max_row",[44,1116],{"x":1082,"y":62,"width":1100,"height":1101,"rx":148,"fill":1117,"stroke":1118},"#fdefd8","var(--gold,#b4740a)",[60,1120,1123],{"x":1121,"y":1104,"style":1122},"550","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","$D$14 — absolute",[60,1125,1126],{"x":1121,"y":1109,"style":103},"pinned column and row",[60,1128,1129],{"x":1121,"y":1113,"style":103},"survives sorting and inserted rows",[10,1131,1132,1133,1136,1137,1140],{},"Mixed anchors are legal and useful: ",[14,1134,1135],{},"$D14"," pins the column while letting the row move, and ",[14,1138,1139],{},"D$14"," does the opposite. When you generate a grid of formulas — a cross-tab where every cell divides by its column header — mixed anchors are what let one generated pattern fill the whole block.",[173,1142,1144],{"id":1143},"read-a-formula-back-text-or-cached-result","Read a formula back: text or cached result",[10,1146,1147,1150],{},[14,1148,1149],{},"load_workbook"," has two personalities, and picking the wrong one is the most common formula bug in Python code:",[185,1152,1154],{"className":187,"code":1153,"language":189,"meta":190,"style":190},"from openpyxl import load_workbook\n\n# Personality 1 — the default: formulas come back as text\nwb_text = load_workbook(\"orders.xlsx\")\nprint(wb_text[\"Orders\"][\"D2\"].value)      # '=B2*C2'\n\n# Personality 2 — cached results written by the last calculating application\nwb_vals = load_workbook(\"orders.xlsx\", data_only=True)\nprint(wb_vals[\"Orders\"][\"D2\"].value)      # None if Excel has never saved this file\n",[14,1155,1156,1167,1171,1176,1190,1210,1214,1219,1243],{"__ignoreMap":190},[194,1157,1158,1160,1162,1164],{"class":77,"line":196},[194,1159,200],{"class":199},[194,1161,204],{"class":203},[194,1163,207],{"class":199},[194,1165,1166],{"class":203}," load_workbook\n",[194,1168,1169],{"class":77,"line":213},[194,1170,217],{"emptyLinePlaceholder":216},[194,1172,1173],{"class":77,"line":220},[194,1174,1175],{"class":432},"# Personality 1 — the default: formulas come back as text\n",[194,1177,1178,1181,1183,1186,1188],{"class":77,"line":231},[194,1179,1180],{"class":203},"wb_text ",[194,1182,182],{"class":199},[194,1184,1185],{"class":203}," load_workbook(",[194,1187,411],{"class":250},[194,1189,414],{"class":203},[194,1191,1192,1194,1197,1200,1203,1205,1207],{"class":77,"line":242},[194,1193,420],{"class":297},[194,1195,1196],{"class":203},"(wb_text[",[194,1198,1199],{"class":250},"\"Orders\"",[194,1201,1202],{"class":203},"][",[194,1204,426],{"class":250},[194,1206,429],{"class":203},[194,1208,1209],{"class":432},"# '=B2*C2'\n",[194,1211,1212],{"class":77,"line":254},[194,1213,217],{"emptyLinePlaceholder":216},[194,1215,1216],{"class":77,"line":259},[194,1217,1218],{"class":432},"# Personality 2 — cached results written by the last calculating application\n",[194,1220,1221,1224,1226,1228,1230,1232,1236,1238,1241],{"class":77,"line":287},[194,1222,1223],{"class":203},"wb_vals ",[194,1225,182],{"class":199},[194,1227,1185],{"class":203},[194,1229,411],{"class":250},[194,1231,268],{"class":203},[194,1233,1235],{"class":1234},"sa561","data_only",[194,1237,182],{"class":199},[194,1239,1240],{"class":297},"True",[194,1242,414],{"class":203},[194,1244,1245,1247,1250,1252,1254,1256,1258],{"class":77,"line":313},[194,1246,420],{"class":297},[194,1248,1249],{"class":203},"(wb_vals[",[194,1251,1199],{"class":250},[194,1253,1202],{"class":203},[194,1255,426],{"class":250},[194,1257,429],{"class":203},[194,1259,1260],{"class":432},"# None if Excel has never saved this file\n",[10,1262,1263,1266,1267,1271,1272,1275],{},[14,1264,1265],{},"data_only=True"," does not calculate. It reads a value Excel stored next to the formula the last time ",[1268,1269,1270],"em",{},"Excel"," saved the workbook. For a file Python just generated, that slot is empty and you get ",[14,1273,1274],{},"None"," — a fact worth asserting loudly rather than discovering in a report:",[185,1277,1279],{"className":187,"code":1278,"language":189,"meta":190,"style":190},"from openpyxl import load_workbook\n\nwb = load_workbook(\"orders.xlsx\", data_only=True)\nws = wb[\"Orders\"]\n\nmissing = [c.coordinate for c in ws[\"D\"] if c.row > 1 and c.value is None]\nif missing:\n    raise SystemExit(\n        f\"No cached values in {missing[:5]} — open and save the file in Excel, \"\n        \"or compute the totals in Python instead of writing formulas.\"\n    )\n",[14,1280,1281,1291,1295,1315,1328,1332,1382,1389,1400,1423,1428],{"__ignoreMap":190},[194,1282,1283,1285,1287,1289],{"class":77,"line":196},[194,1284,200],{"class":199},[194,1286,204],{"class":203},[194,1288,207],{"class":199},[194,1290,1166],{"class":203},[194,1292,1293],{"class":77,"line":213},[194,1294,217],{"emptyLinePlaceholder":216},[194,1296,1297,1299,1301,1303,1305,1307,1309,1311,1313],{"class":77,"line":220},[194,1298,223],{"class":203},[194,1300,182],{"class":199},[194,1302,1185],{"class":203},[194,1304,411],{"class":250},[194,1306,268],{"class":203},[194,1308,1235],{"class":1234},[194,1310,182],{"class":199},[194,1312,1240],{"class":297},[194,1314,414],{"class":203},[194,1316,1317,1319,1321,1324,1326],{"class":77,"line":231},[194,1318,234],{"class":203},[194,1320,182],{"class":199},[194,1322,1323],{"class":203}," wb[",[194,1325,1199],{"class":250},[194,1327,596],{"class":203},[194,1329,1330],{"class":77,"line":242},[194,1331,217],{"emptyLinePlaceholder":216},[194,1333,1334,1337,1339,1342,1344,1347,1349,1352,1355,1357,1360,1363,1366,1368,1371,1374,1377,1380],{"class":77,"line":254},[194,1335,1336],{"class":203},"missing ",[194,1338,182],{"class":199},[194,1340,1341],{"class":203}," [c.coordinate ",[194,1343,709],{"class":199},[194,1345,1346],{"class":203}," c ",[194,1348,715],{"class":199},[194,1350,1351],{"class":203}," ws[",[194,1353,1354],{"class":250},"\"D\"",[194,1356,377],{"class":203},[194,1358,1359],{"class":199},"if",[194,1361,1362],{"class":203}," c.row ",[194,1364,1365],{"class":199},">",[194,1367,954],{"class":297},[194,1369,1370],{"class":199}," and",[194,1372,1373],{"class":203}," c.value ",[194,1375,1376],{"class":199},"is",[194,1378,1379],{"class":297}," None",[194,1381,596],{"class":203},[194,1383,1384,1386],{"class":77,"line":259},[194,1385,1359],{"class":199},[194,1387,1388],{"class":203}," missing:\n",[194,1390,1391,1394,1397],{"class":77,"line":287},[194,1392,1393],{"class":199},"    raise",[194,1395,1396],{"class":297}," SystemExit",[194,1398,1399],{"class":203},"(\n",[194,1401,1402,1405,1408,1410,1413,1415,1418,1420],{"class":77,"line":313},[194,1403,1404],{"class":199},"        f",[194,1406,1407],{"class":250},"\"No cached values in ",[194,1409,754],{"class":753},[194,1411,1412],{"class":203},"missing[:",[194,1414,584],{"class":297},[194,1416,1417],{"class":203},"]",[194,1419,766],{"class":753},[194,1421,1422],{"class":250}," — open and save the file in Excel, \"\n",[194,1424,1425],{"class":77,"line":338},[194,1426,1427],{"class":250},"        \"or compute the totals in Python instead of writing formulas.\"\n",[194,1429,1430],{"class":77,"line":363},[194,1431,1432],{"class":203},"    )\n",[10,1434,1435],{},"There is also a halfway house. Setting the workbook's recalculation flag asks the reading application to recompute everything on open, which is useful when you have rewritten input cells under formulas that Excel might otherwise consider unchanged:",[185,1437,1439],{"className":187,"code":1438,"language":189,"meta":190,"style":190},"from openpyxl import load_workbook\n\nwb = load_workbook(\"orders.xlsx\")\nwb.calculation.fullCalcOnLoad = True\nwb.save(\"orders.xlsx\")\n",[14,1440,1441,1451,1455,1467,1477],{"__ignoreMap":190},[194,1442,1443,1445,1447,1449],{"class":77,"line":196},[194,1444,200],{"class":199},[194,1446,204],{"class":203},[194,1448,207],{"class":199},[194,1450,1166],{"class":203},[194,1452,1453],{"class":77,"line":213},[194,1454,217],{"emptyLinePlaceholder":216},[194,1456,1457,1459,1461,1463,1465],{"class":77,"line":220},[194,1458,223],{"class":203},[194,1460,182],{"class":199},[194,1462,1185],{"class":203},[194,1464,411],{"class":250},[194,1466,414],{"class":203},[194,1468,1469,1472,1474],{"class":77,"line":231},[194,1470,1471],{"class":203},"wb.calculation.fullCalcOnLoad ",[194,1473,182],{"class":199},[194,1475,1476],{"class":297}," True\n",[194,1478,1479,1481,1483],{"class":77,"line":242},[194,1480,408],{"class":203},[194,1482,411],{"class":250},[194,1484,414],{"class":203},[25,1486,34,1491,34,1494,34,1497,34,1500,34,1506,34,1510,34,1515,34,1518,34,1522,34,1525,34,1530,34,1535,34,1540,34,1543,34,1546,34,1550,34,1552,34,1556,34,1559,34,1562,34,1565],{"viewBox":1487,"role":28,"ariaLabelledBy":1488,"xmlns":32,"style":33},"0 0 760 250",[1489,1490],"fx-modes-t","fx-modes-d",[36,1492,1493],{"id":1489},"What each load_workbook mode returns for a formula cell",[40,1495,1496],{"id":1490},"A decision path: opening with the default mode returns the formula text and lets you edit the formula. Opening with data_only=True returns the cached number, or None when no calculating application has saved the workbook, and any save from that mode discards the formulas.",[44,1498],{"x":46,"y":46,"width":47,"height":1499,"fill":49},"250",[44,1501],{"x":1502,"y":1503,"width":1504,"height":1505,"rx":148,"fill":149,"stroke":93},"270","16","220","44",[60,1507,1509],{"x":96,"y":1505,"style":1508},"font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","load_workbook(path, …)",[77,1511],{"x1":1512,"y1":1513,"x2":54,"y2":1514,"stroke":137,"style":138},"330","62","96",[85,1516],{"points":1517,"fill":142},"196,98 208,92 206,102",[77,1519],{"x1":1520,"y1":1513,"x2":1521,"y2":1514,"stroke":137,"style":138},"430","560",[85,1523],{"points":1524,"fill":142},"564,98 552,92 554,102",[44,1526],{"x":1527,"y":1528,"width":1512,"height":1529,"rx":56,"fill":92,"stroke":82,"style":83},"30","100","132",[60,1531,1534],{"x":1532,"y":1533,"style":1508},"195","126","default",[60,1536,1539],{"x":1532,"y":1537,"style":1538},"152","font-size:12.5px;fill:var(--text,#172033);text-anchor:middle","cell.value → \"=B2*C2\"",[60,1541,1542],{"x":1532,"y":91,"style":103},"edit and re-save formulas safely",[60,1544,1545],{"x":1532,"y":54,"style":103},"no numbers available",[60,1547,1549],{"x":1532,"y":1504,"style":1548},"font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","use when editing the model",[44,1551],{"x":1082,"y":1528,"width":1512,"height":1529,"rx":56,"fill":122,"stroke":114,"style":83},[60,1553,1265],{"x":1554,"y":1533,"style":1555},"565","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[60,1557,1558],{"x":1554,"y":1537,"style":1538},"cell.value → 79.96 or None",[60,1560,1561],{"x":1554,"y":91,"style":103},"None until Excel has saved the file",[60,1563,1564],{"x":1554,"y":54,"style":108},"saving from here drops the formulas",[60,1566,1567],{"x":1554,"y":1504,"style":1548},"use when harvesting results",[10,1569,1570,1571,1573,1574,1577],{},"The warning in the right-hand box deserves its own sentence: if you load with ",[14,1572,1265],{},", change one cell and call ",[14,1575,1576],{},"save()",", every formula in the workbook is replaced by whatever cached number was sitting in it. That is occasionally exactly what you want — see freezing values below — and otherwise it is a silent data loss.",[173,1579,1581],{"id":1580},"name-a-range-so-formulas-read-like-english","Name a range so formulas read like English",[10,1583,1584,1585,1588,1589,1592],{},"A defined name turns ",[14,1586,1587],{},"'Rates'!$B$1"," into ",[14,1590,1591],{},"TaxRate",". Formulas written from Python become far easier to review, and a reader can find the input cell without hunting:",[185,1594,1596],{"className":187,"code":1595,"language":189,"meta":190,"style":190},"from openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nwb = Workbook()\nrates = wb.active\nrates.title = \"Rates\"\nrates[\"A1\"] = \"VAT rate\"\nrates[\"B1\"] = 0.20\n\norders = wb.create_sheet(\"Orders\")\norders.append([\"SKU\", \"Net\", \"VAT\", \"Gross\"])\norders.append([\"A-100\", 79.96])\n\n# Workbook-wide: usable from any sheet\nwb.defined_names.add(DefinedName(\"TaxRate\", attr_text=\"'Rates'!$B$1\"))\n\norders[\"C2\"] = \"=B2*TaxRate\"\norders[\"D2\"] = \"=B2+C2\"\n\nwb.save(\"named.xlsx\")\nprint(list(wb.defined_names))          # ['TaxRate']\n",[14,1597,1598,1608,1620,1624,1632,1641,1651,1666,1680,1684,1698,1722,1734,1738,1743,1764,1768,1783,1796,1800,1809],{"__ignoreMap":190},[194,1599,1600,1602,1604,1606],{"class":77,"line":196},[194,1601,200],{"class":199},[194,1603,204],{"class":203},[194,1605,207],{"class":199},[194,1607,210],{"class":203},[194,1609,1610,1612,1615,1617],{"class":77,"line":213},[194,1611,200],{"class":199},[194,1613,1614],{"class":203}," openpyxl.workbook.defined_name ",[194,1616,207],{"class":199},[194,1618,1619],{"class":203}," DefinedName\n",[194,1621,1622],{"class":77,"line":220},[194,1623,217],{"emptyLinePlaceholder":216},[194,1625,1626,1628,1630],{"class":77,"line":231},[194,1627,223],{"class":203},[194,1629,182],{"class":199},[194,1631,228],{"class":203},[194,1633,1634,1637,1639],{"class":77,"line":242},[194,1635,1636],{"class":203},"rates ",[194,1638,182],{"class":199},[194,1640,239],{"class":203},[194,1642,1643,1646,1648],{"class":77,"line":254},[194,1644,1645],{"class":203},"rates.title ",[194,1647,182],{"class":199},[194,1649,1650],{"class":250}," \"Rates\"\n",[194,1652,1653,1656,1659,1661,1663],{"class":77,"line":259},[194,1654,1655],{"class":203},"rates[",[194,1657,1658],{"class":250},"\"A1\"",[194,1660,377],{"class":203},[194,1662,182],{"class":199},[194,1664,1665],{"class":250}," \"VAT rate\"\n",[194,1667,1668,1670,1673,1675,1677],{"class":77,"line":287},[194,1669,1655],{"class":203},[194,1671,1672],{"class":250},"\"B1\"",[194,1674,377],{"class":203},[194,1676,182],{"class":199},[194,1678,1679],{"class":297}," 0.20\n",[194,1681,1682],{"class":77,"line":313},[194,1683,217],{"emptyLinePlaceholder":216},[194,1685,1686,1689,1691,1694,1696],{"class":77,"line":338},[194,1687,1688],{"class":203},"orders ",[194,1690,182],{"class":199},[194,1692,1693],{"class":203}," wb.create_sheet(",[194,1695,1199],{"class":250},[194,1697,414],{"class":203},[194,1699,1700,1703,1705,1707,1710,1712,1715,1717,1720],{"class":77,"line":363},[194,1701,1702],{"class":203},"orders.append([",[194,1704,265],{"class":250},[194,1706,268],{"class":203},[194,1708,1709],{"class":250},"\"Net\"",[194,1711,268],{"class":203},[194,1713,1714],{"class":250},"\"VAT\"",[194,1716,268],{"class":203},[194,1718,1719],{"class":250},"\"Gross\"",[194,1721,284],{"class":203},[194,1723,1724,1726,1728,1730,1732],{"class":77,"line":368},[194,1725,1702],{"class":203},[194,1727,292],{"class":250},[194,1729,268],{"class":203},[194,1731,446],{"class":297},[194,1733,284],{"class":203},[194,1735,1736],{"class":77,"line":385},[194,1737,217],{"emptyLinePlaceholder":216},[194,1739,1740],{"class":77,"line":400},[194,1741,1742],{"class":432},"# Workbook-wide: usable from any sheet\n",[194,1744,1745,1748,1751,1753,1756,1758,1761],{"class":77,"line":405},[194,1746,1747],{"class":203},"wb.defined_names.add(DefinedName(",[194,1749,1750],{"class":250},"\"TaxRate\"",[194,1752,268],{"class":203},[194,1754,1755],{"class":1234},"attr_text",[194,1757,182],{"class":199},[194,1759,1760],{"class":250},"\"'Rates'!$B$1\"",[194,1762,1763],{"class":203},"))\n",[194,1765,1766],{"class":77,"line":417},[194,1767,217],{"emptyLinePlaceholder":216},[194,1769,1770,1773,1776,1778,1780],{"class":77,"line":684},[194,1771,1772],{"class":203},"orders[",[194,1774,1775],{"class":250},"\"C2\"",[194,1777,377],{"class":203},[194,1779,182],{"class":199},[194,1781,1782],{"class":250}," \"=B2*TaxRate\"\n",[194,1784,1785,1787,1789,1791,1793],{"class":77,"line":701},[194,1786,1772],{"class":203},[194,1788,426],{"class":250},[194,1790,377],{"class":203},[194,1792,182],{"class":199},[194,1794,1795],{"class":250}," \"=B2+C2\"\n",[194,1797,1798],{"class":77,"line":706},[194,1799,217],{"emptyLinePlaceholder":216},[194,1801,1802,1804,1807],{"class":77,"line":721},[194,1803,408],{"class":203},[194,1805,1806],{"class":250},"\"named.xlsx\"",[194,1808,414],{"class":203},[194,1810,1811,1813,1815,1818,1821],{"class":77,"line":727},[194,1812,420],{"class":297},[194,1814,944],{"class":203},[194,1816,1817],{"class":297},"list",[194,1819,1820],{"class":203},"(wb.defined_names))          ",[194,1822,1823],{"class":432},"# ['TaxRate']\n",[10,1825,1826,1827,1830,1831,1834,1835,1838],{},"Names come in two scopes. A name added to ",[14,1828,1829],{},"wb.defined_names"," is visible everywhere; a name added to ",[14,1832,1833],{},"ws.defined_names"," belongs to that sheet only, which is how you give five regional tabs their own ",[14,1836,1837],{},"Target"," cell without collisions:",[185,1840,1842],{"className":187,"code":1841,"language":189,"meta":190,"style":190},"from openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nwb = Workbook()\nfor region in (\"North\", \"South\", \"West\"):\n    ws = wb.create_sheet(region)\n    ws[\"A1\"] = \"Target\"\n    ws[\"B1\"] = 100000\n    ws.defined_names.add(DefinedName(\"Target\", attr_text=f\"'{region}'!$B$1\"))\n    ws[\"B3\"] = \"=B2\u002FTarget\"          # resolves to this sheet's own Target\n\ndel wb[\"Sheet\"]\nwb.save(\"scoped_names.xlsx\")\n",[14,1843,1844,1854,1864,1868,1876,1903,1913,1926,1939,1970,1987,1991,2003],{"__ignoreMap":190},[194,1845,1846,1848,1850,1852],{"class":77,"line":196},[194,1847,200],{"class":199},[194,1849,204],{"class":203},[194,1851,207],{"class":199},[194,1853,210],{"class":203},[194,1855,1856,1858,1860,1862],{"class":77,"line":213},[194,1857,200],{"class":199},[194,1859,1614],{"class":203},[194,1861,207],{"class":199},[194,1863,1619],{"class":203},[194,1865,1866],{"class":77,"line":220},[194,1867,217],{"emptyLinePlaceholder":216},[194,1869,1870,1872,1874],{"class":77,"line":231},[194,1871,223],{"class":203},[194,1873,182],{"class":199},[194,1875,228],{"class":203},[194,1877,1878,1880,1883,1885,1888,1891,1893,1896,1898,1901],{"class":77,"line":242},[194,1879,709],{"class":199},[194,1881,1882],{"class":203}," region ",[194,1884,715],{"class":199},[194,1886,1887],{"class":203}," (",[194,1889,1890],{"class":250},"\"North\"",[194,1892,268],{"class":203},[194,1894,1895],{"class":250},"\"South\"",[194,1897,268],{"class":203},[194,1899,1900],{"class":250},"\"West\"",[194,1902,957],{"class":203},[194,1904,1905,1908,1910],{"class":77,"line":254},[194,1906,1907],{"class":203},"    ws ",[194,1909,182],{"class":199},[194,1911,1912],{"class":203}," wb.create_sheet(region)\n",[194,1914,1915,1917,1919,1921,1923],{"class":77,"line":259},[194,1916,744],{"class":203},[194,1918,1658],{"class":250},[194,1920,377],{"class":203},[194,1922,182],{"class":199},[194,1924,1925],{"class":250}," \"Target\"\n",[194,1927,1928,1930,1932,1934,1936],{"class":77,"line":287},[194,1929,744],{"class":203},[194,1931,1672],{"class":250},[194,1933,377],{"class":203},[194,1935,182],{"class":199},[194,1937,1938],{"class":297}," 100000\n",[194,1940,1941,1944,1947,1949,1951,1953,1955,1958,1960,1963,1965,1968],{"class":77,"line":313},[194,1942,1943],{"class":203},"    ws.defined_names.add(DefinedName(",[194,1945,1946],{"class":250},"\"Target\"",[194,1948,268],{"class":203},[194,1950,1755],{"class":1234},[194,1952,182],{"class":199},[194,1954,747],{"class":199},[194,1956,1957],{"class":250},"\"'",[194,1959,754],{"class":753},[194,1961,1962],{"class":203},"region",[194,1964,766],{"class":753},[194,1966,1967],{"class":250},"'!$B$1\"",[194,1969,1763],{"class":203},[194,1971,1972,1974,1977,1979,1981,1984],{"class":77,"line":338},[194,1973,744],{"class":203},[194,1975,1976],{"class":250},"\"B3\"",[194,1978,377],{"class":203},[194,1980,182],{"class":199},[194,1982,1983],{"class":250}," \"=B2\u002FTarget\"",[194,1985,1986],{"class":432},"          # resolves to this sheet's own Target\n",[194,1988,1989],{"class":77,"line":363},[194,1990,217],{"emptyLinePlaceholder":216},[194,1992,1993,1996,1998,2001],{"class":77,"line":368},[194,1994,1995],{"class":199},"del",[194,1997,1323],{"class":203},[194,1999,2000],{"class":250},"\"Sheet\"",[194,2002,596],{"class":203},[194,2004,2005,2007,2010],{"class":77,"line":385},[194,2006,408],{"class":203},[194,2008,2009],{"class":250},"\"scoped_names.xlsx\"",[194,2011,414],{"class":203},[25,2013,34,2018,34,2021,34,2024,34,2027,34,2029,34,2033,34,2039,34,2043,34,2047,34,2053,34,2056,34,2059,34,2062,34,2066,34,2069,34,2072,34,2075,34,2079,34,2082,34,2086,34,2090,34,2094,34,2096,34,2100,34,2102,34,2105,34,2108,34,2112,34,2114,34,2117,34,2120],{"viewBox":2014,"role":28,"ariaLabelledBy":2015,"xmlns":32,"style":33},"0 0 760 246",[2016,2017],"fx-scope-t","fx-scope-d",[36,2019,2020],{"id":2016},"Workbook-wide names versus sheet-local names",[40,2022,2023],{"id":2017},"TaxRate is added to the workbook, so formulas on every sheet resolve it to the same Rates cell. Target is added to each worksheet separately, so the North, South and West sheets each resolve Target to their own cell without colliding.",[44,2025],{"x":46,"y":46,"width":47,"height":2026,"fill":49},"246",[44,2028],{"x":52,"y":52,"width":1512,"height":54,"rx":56,"fill":92,"stroke":82,"style":83},[60,2030,1829],{"x":2031,"y":2032,"style":1508},"189","50",[44,2034],{"x":2035,"y":2036,"width":81,"height":2037,"rx":2038,"fill":57},"46","64","34","8",[60,2040,2042],{"x":2031,"y":80,"style":2041},"font-size:12.5px;font-weight:700;fill:#ffffff;text-anchor:middle","TaxRate → 'Rates'!$B$1",[44,2044],{"x":2035,"y":2045,"width":2046,"height":53,"rx":2038,"fill":149,"stroke":93},"112","88",[60,2048,2052],{"x":2049,"y":2050,"style":2051},"90","137","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","Orders",[44,2054],{"x":2055,"y":2045,"width":2046,"height":53,"rx":2038,"fill":149,"stroke":93},"145",[60,2057,2058],{"x":2031,"y":2050,"style":2051},"Invoices",[44,2060],{"x":2061,"y":2045,"width":2046,"height":53,"rx":2038,"fill":149,"stroke":93},"244",[60,2063,2065],{"x":2064,"y":2050,"style":2051},"288","Summary",[60,2067,2068],{"x":2031,"y":145,"style":103},"every sheet resolves the same cell",[60,2070,2071],{"x":2031,"y":153,"style":1548},"one rate, one place to change it",[44,2073],{"x":2074,"y":52,"width":1512,"height":54,"rx":56,"fill":1117,"stroke":1118,"style":83},"406",[60,2076,1833],{"x":2077,"y":2032,"style":2078},"571","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[44,2080],{"x":2081,"y":2036,"width":55,"height":2046,"rx":2038,"fill":49,"stroke":93},"428",[60,2083,2085],{"x":2084,"y":2046,"style":170},"474","North",[60,2087,2089],{"x":2084,"y":2045,"style":2088},"font-size:11px;fill:var(--muted,#5b6780);text-anchor:middle","Target →",[60,2091,2093],{"x":2084,"y":2092,"style":2088},"130","'North'!$B$1",[44,2095],{"x":113,"y":2036,"width":55,"height":2046,"rx":2038,"fill":49,"stroke":93},[60,2097,2099],{"x":2098,"y":2046,"style":170},"576","South",[60,2101,2089],{"x":2098,"y":2045,"style":2088},[60,2103,2104],{"x":2098,"y":2092,"style":2088},"'South'!$B$1",[44,2106],{"x":2107,"y":2036,"width":55,"height":2046,"rx":2038,"fill":49,"stroke":93},"632",[60,2109,2111],{"x":2110,"y":2046,"style":170},"678","West",[60,2113,2089],{"x":2110,"y":2045,"style":2088},[60,2115,2116],{"x":2110,"y":2092,"style":2088},"'West'!$B$1",[60,2118,2119],{"x":2077,"y":145,"style":103},"same name, three different cells",[60,2121,2123],{"x":2077,"y":153,"style":2122},"font-size:11.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","no collision between sheets",[10,2125,2126,2127,2131,2132,2135,2136,2139],{},"Names are also the cleanest way to hand a range to a chart or a validation list. When you later ",[19,2128,2130],{"href":2129},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fadd-dropdown-data-validation-to-excel-with-openpyxl\u002F","add a dropdown from a named list",", the formula ",[14,2133,2134],{},"=Regions"," is far more robust than ",[14,2137,2138],{},"='Lookups'!$A$2:$A$12",", because the name can be repointed in one place.",[173,2141,2143],{"id":2142},"freeze-formulas-into-values-before-shipping","Freeze formulas into values before shipping",[10,2145,2146],{},"Some recipients should never see a formula: a file sent to a client, an archive that must read the same in five years, or a sheet that feeds a system without a calculation engine. Freezing means reading the cached results and writing them back as literals — which requires that the workbook has been opened and saved by Excel or LibreOffice at least once, so that cached values exist:",[185,2148,2150],{"className":187,"code":2149,"language":189,"meta":190,"style":190},"from openpyxl import load_workbook\n\nsrc = load_workbook(\"orders.xlsx\", data_only=True)   # cached results\ndst = load_workbook(\"orders.xlsx\")                   # formulas + all formatting\n\nfor name in src.sheetnames:\n    s, d = src[name], dst[name]\n    for row in s.iter_rows():\n        for cell in row:\n            target = d[cell.coordinate]\n            if isinstance(target.value, str) and target.value.startswith(\"=\"):\n                if cell.value is None:\n                    raise SystemExit(f\"{name}!{cell.coordinate} has no cached value\")\n                target.value = cell.value            # replace formula with result\n\ndst.save(\"orders_frozen.xlsx\")\n",[14,2151,2152,2162,2166,2191,2208,2212,2224,2234,2247,2260,2270,2298,2313,2348,2361,2365],{"__ignoreMap":190},[194,2153,2154,2156,2158,2160],{"class":77,"line":196},[194,2155,200],{"class":199},[194,2157,204],{"class":203},[194,2159,207],{"class":199},[194,2161,1166],{"class":203},[194,2163,2164],{"class":77,"line":213},[194,2165,217],{"emptyLinePlaceholder":216},[194,2167,2168,2171,2173,2175,2177,2179,2181,2183,2185,2188],{"class":77,"line":220},[194,2169,2170],{"class":203},"src ",[194,2172,182],{"class":199},[194,2174,1185],{"class":203},[194,2176,411],{"class":250},[194,2178,268],{"class":203},[194,2180,1235],{"class":1234},[194,2182,182],{"class":199},[194,2184,1240],{"class":297},[194,2186,2187],{"class":203},")   ",[194,2189,2190],{"class":432},"# cached results\n",[194,2192,2193,2196,2198,2200,2202,2205],{"class":77,"line":231},[194,2194,2195],{"class":203},"dst ",[194,2197,182],{"class":199},[194,2199,1185],{"class":203},[194,2201,411],{"class":250},[194,2203,2204],{"class":203},")                   ",[194,2206,2207],{"class":432},"# formulas + all formatting\n",[194,2209,2210],{"class":77,"line":242},[194,2211,217],{"emptyLinePlaceholder":216},[194,2213,2214,2216,2219,2221],{"class":77,"line":254},[194,2215,709],{"class":199},[194,2217,2218],{"class":203}," name ",[194,2220,715],{"class":199},[194,2222,2223],{"class":203}," src.sheetnames:\n",[194,2225,2226,2229,2231],{"class":77,"line":259},[194,2227,2228],{"class":203},"    s, d ",[194,2230,182],{"class":199},[194,2232,2233],{"class":203}," src[name], dst[name]\n",[194,2235,2236,2239,2242,2244],{"class":77,"line":287},[194,2237,2238],{"class":199},"    for",[194,2240,2241],{"class":203}," row ",[194,2243,715],{"class":199},[194,2245,2246],{"class":203}," s.iter_rows():\n",[194,2248,2249,2252,2255,2257],{"class":77,"line":313},[194,2250,2251],{"class":199},"        for",[194,2253,2254],{"class":203}," cell ",[194,2256,715],{"class":199},[194,2258,2259],{"class":203}," row:\n",[194,2261,2262,2265,2267],{"class":77,"line":338},[194,2263,2264],{"class":203},"            target ",[194,2266,182],{"class":199},[194,2268,2269],{"class":203}," d[cell.coordinate]\n",[194,2271,2272,2275,2278,2281,2284,2287,2290,2293,2296],{"class":77,"line":363},[194,2273,2274],{"class":199},"            if",[194,2276,2277],{"class":297}," isinstance",[194,2279,2280],{"class":203},"(target.value, ",[194,2282,2283],{"class":297},"str",[194,2285,2286],{"class":203},") ",[194,2288,2289],{"class":199},"and",[194,2291,2292],{"class":203}," target.value.startswith(",[194,2294,2295],{"class":250},"\"=\"",[194,2297,957],{"class":203},[194,2299,2300,2303,2306,2308,2310],{"class":77,"line":368},[194,2301,2302],{"class":199},"                if",[194,2304,2305],{"class":203}," cell.value ",[194,2307,1376],{"class":199},[194,2309,1379],{"class":297},[194,2311,2312],{"class":203},":\n",[194,2314,2315,2318,2320,2322,2324,2326,2328,2331,2333,2336,2338,2341,2343,2346],{"class":77,"line":385},[194,2316,2317],{"class":199},"                    raise",[194,2319,1396],{"class":297},[194,2321,944],{"class":203},[194,2323,747],{"class":199},[194,2325,750],{"class":250},[194,2327,754],{"class":753},[194,2329,2330],{"class":203},"name",[194,2332,766],{"class":753},[194,2334,2335],{"class":250},"!",[194,2337,754],{"class":753},[194,2339,2340],{"class":203},"cell.coordinate",[194,2342,766],{"class":753},[194,2344,2345],{"class":250}," has no cached value\"",[194,2347,414],{"class":203},[194,2349,2350,2353,2355,2358],{"class":77,"line":400},[194,2351,2352],{"class":203},"                target.value ",[194,2354,182],{"class":199},[194,2356,2357],{"class":203}," cell.value            ",[194,2359,2360],{"class":432},"# replace formula with result\n",[194,2362,2363],{"class":77,"line":405},[194,2364,217],{"emptyLinePlaceholder":216},[194,2366,2367,2370,2373],{"class":77,"line":417},[194,2368,2369],{"class":203},"dst.save(",[194,2371,2372],{"class":250},"\"orders_frozen.xlsx\"",[194,2374,414],{"class":203},[10,2376,2377,2378,2380,2381,2385,2386,2388,2389,2391],{},"Loading the file twice looks wasteful, but it is what keeps the styling. The ",[14,2379,1235],{}," copy is only a source of numbers; every fill, border and ",[19,2382,2384],{"href":2383},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","number format"," is preserved because the file being saved is the ordinary copy. Running the same freeze with a single ",[14,2387,1265],{}," load and a ",[14,2390,1576],{}," also works, and quietly strips nothing else — but it gives you no chance to check for missing cached values first.",[173,2393,2395],{"id":2394},"cross-sheet-references-and-the-quoting-rule","Cross-sheet references and the quoting rule",[10,2397,2398,2399,2401,2402,2405,2406,2409],{},"A formula on the ",[14,2400,2065],{}," tab that reads the ",[14,2403,2404],{},"Q1 Sales"," tab needs the sheet name in front of the reference, and that name must be wrapped in single quotes whenever it contains a space, a hyphen or anything other than letters, digits and underscores. Getting this wrong produces a ",[14,2407,2408],{},"#REF!"," that only shows up when someone opens the file, so it is worth a helper:",[185,2411,2413],{"className":187,"code":2412,"language":189,"meta":190,"style":190},"import re\nfrom openpyxl import Workbook\n\ndef sheet_ref(sheet_name, ref):\n    \"\"\"Return a formula-safe reference like 'Q1 Sales'!$B$2 or Summary!$B$2.\"\"\"\n    safe = re.fullmatch(r\"[A-Za-z_][A-Za-z0-9_]*\", sheet_name)\n    quoted = sheet_name if safe else \"'{}'\".format(sheet_name.replace(\"'\", \"''\"))\n    return f\"{quoted}!{ref}\"\n\nwb = Workbook()\nq1 = wb.active\nq1.title = \"Q1 Sales\"\nq1.append([\"Region\", \"Revenue\"])\nfor region, revenue in ((\"North\", 26700), (\"South\", 20900), (\"West\", 7600)):\n    q1.append([region, revenue])\n\nsummary = wb.create_sheet(\"Summary\")\nsummary[\"A1\"] = \"Q1 revenue\"\nsummary[\"B1\"] = f\"=SUM({sheet_ref('Q1 Sales', '$B$2:$B$4')})\"\nsummary[\"A2\"] = \"Best region\"\nsummary[\"B2\"] = (\n    f\"=INDEX({sheet_ref('Q1 Sales', '$A$2:$A$4')},\"\n    f\"MATCH(MAX({sheet_ref('Q1 Sales', '$B$2:$B$4')}),\"\n    f\"{sheet_ref('Q1 Sales', '$B$2:$B$4')},0))\"\n)\n\nwb.save(\"cross_sheet.xlsx\")\nprint(summary[\"B1\"].value)\n",[14,2414,2415,2422,2432,2436,2448,2453,2487,2527,2554,2558,2566,2575,2585,2600,2641,2646,2650,2664,2678,2712,2726,2740,2766,2790,2813,2817,2821,2830],{"__ignoreMap":190},[194,2416,2417,2419],{"class":77,"line":196},[194,2418,207],{"class":199},[194,2420,2421],{"class":203}," re\n",[194,2423,2424,2426,2428,2430],{"class":77,"line":213},[194,2425,200],{"class":199},[194,2427,204],{"class":203},[194,2429,207],{"class":199},[194,2431,210],{"class":203},[194,2433,2434],{"class":77,"line":220},[194,2435,217],{"emptyLinePlaceholder":216},[194,2437,2438,2441,2445],{"class":77,"line":231},[194,2439,2440],{"class":199},"def",[194,2442,2444],{"class":2443},"s_Opv"," sheet_ref",[194,2446,2447],{"class":203},"(sheet_name, ref):\n",[194,2449,2450],{"class":77,"line":242},[194,2451,2452],{"class":250},"    \"\"\"Return a formula-safe reference like 'Q1 Sales'!$B$2 or Summary!$B$2.\"\"\"\n",[194,2454,2455,2458,2460,2463,2465,2467,2470,2473,2475,2478,2480,2482,2484],{"class":77,"line":254},[194,2456,2457],{"class":203},"    safe ",[194,2459,182],{"class":199},[194,2461,2462],{"class":203}," re.fullmatch(",[194,2464,763],{"class":199},[194,2466,750],{"class":250},[194,2468,2469],{"class":297},"[",[194,2471,2472],{"class":753},"A-Za-z_",[194,2474,1202],{"class":297},[194,2476,2477],{"class":753},"A-Za-z0-9_",[194,2479,1417],{"class":297},[194,2481,792],{"class":199},[194,2483,750],{"class":250},[194,2485,2486],{"class":203},", sheet_name)\n",[194,2488,2489,2492,2494,2497,2499,2502,2505,2508,2511,2514,2517,2520,2522,2525],{"class":77,"line":259},[194,2490,2491],{"class":203},"    quoted ",[194,2493,182],{"class":199},[194,2495,2496],{"class":203}," sheet_name ",[194,2498,1359],{"class":199},[194,2500,2501],{"class":203}," safe ",[194,2503,2504],{"class":199},"else",[194,2506,2507],{"class":250}," \"'",[194,2509,2510],{"class":753},"{}",[194,2512,2513],{"class":250},"'\"",[194,2515,2516],{"class":203},".format(sheet_name.replace(",[194,2518,2519],{"class":250},"\"'\"",[194,2521,268],{"class":203},[194,2523,2524],{"class":250},"\"''\"",[194,2526,1763],{"class":203},[194,2528,2529,2532,2534,2536,2538,2541,2543,2545,2547,2550,2552],{"class":77,"line":287},[194,2530,2531],{"class":199},"    return",[194,2533,775],{"class":199},[194,2535,750],{"class":250},[194,2537,754],{"class":753},[194,2539,2540],{"class":203},"quoted",[194,2542,766],{"class":753},[194,2544,2335],{"class":250},[194,2546,754],{"class":753},[194,2548,2549],{"class":203},"ref",[194,2551,766],{"class":753},[194,2553,806],{"class":250},[194,2555,2556],{"class":77,"line":313},[194,2557,217],{"emptyLinePlaceholder":216},[194,2559,2560,2562,2564],{"class":77,"line":338},[194,2561,223],{"class":203},[194,2563,182],{"class":199},[194,2565,228],{"class":203},[194,2567,2568,2571,2573],{"class":77,"line":363},[194,2569,2570],{"class":203},"q1 ",[194,2572,182],{"class":199},[194,2574,239],{"class":203},[194,2576,2577,2580,2582],{"class":77,"line":368},[194,2578,2579],{"class":203},"q1.title ",[194,2581,182],{"class":199},[194,2583,2584],{"class":250}," \"Q1 Sales\"\n",[194,2586,2587,2590,2593,2595,2598],{"class":77,"line":385},[194,2588,2589],{"class":203},"q1.append([",[194,2591,2592],{"class":250},"\"Region\"",[194,2594,268],{"class":203},[194,2596,2597],{"class":250},"\"Revenue\"",[194,2599,284],{"class":203},[194,2601,2602,2604,2607,2609,2612,2614,2616,2619,2622,2624,2626,2629,2631,2633,2635,2638],{"class":77,"line":400},[194,2603,709],{"class":199},[194,2605,2606],{"class":203}," region, revenue ",[194,2608,715],{"class":199},[194,2610,2611],{"class":203}," ((",[194,2613,1890],{"class":250},[194,2615,268],{"class":203},[194,2617,2618],{"class":297},"26700",[194,2620,2621],{"class":203},"), (",[194,2623,1895],{"class":250},[194,2625,268],{"class":203},[194,2627,2628],{"class":297},"20900",[194,2630,2621],{"class":203},[194,2632,1900],{"class":250},[194,2634,268],{"class":203},[194,2636,2637],{"class":297},"7600",[194,2639,2640],{"class":203},")):\n",[194,2642,2643],{"class":77,"line":405},[194,2644,2645],{"class":203},"    q1.append([region, revenue])\n",[194,2647,2648],{"class":77,"line":417},[194,2649,217],{"emptyLinePlaceholder":216},[194,2651,2652,2655,2657,2659,2662],{"class":77,"line":684},[194,2653,2654],{"class":203},"summary ",[194,2656,182],{"class":199},[194,2658,1693],{"class":203},[194,2660,2661],{"class":250},"\"Summary\"",[194,2663,414],{"class":203},[194,2665,2666,2669,2671,2673,2675],{"class":77,"line":701},[194,2667,2668],{"class":203},"summary[",[194,2670,1658],{"class":250},[194,2672,377],{"class":203},[194,2674,182],{"class":199},[194,2676,2677],{"class":250}," \"Q1 revenue\"\n",[194,2679,2680,2682,2684,2686,2688,2690,2692,2694,2697,2700,2702,2705,2708,2710],{"class":77,"line":706},[194,2681,2668],{"class":203},[194,2683,1672],{"class":250},[194,2685,377],{"class":203},[194,2687,182],{"class":199},[194,2689,775],{"class":199},[194,2691,894],{"class":250},[194,2693,754],{"class":753},[194,2695,2696],{"class":203},"sheet_ref(",[194,2698,2699],{"class":250},"'Q1 Sales'",[194,2701,268],{"class":203},[194,2703,2704],{"class":250},"'$B$2:$B$4'",[194,2706,2707],{"class":203},")",[194,2709,766],{"class":753},[194,2711,917],{"class":250},[194,2713,2714,2716,2719,2721,2723],{"class":77,"line":721},[194,2715,2668],{"class":203},[194,2717,2718],{"class":250},"\"A2\"",[194,2720,377],{"class":203},[194,2722,182],{"class":199},[194,2724,2725],{"class":250}," \"Best region\"\n",[194,2727,2728,2730,2733,2735,2737],{"class":77,"line":727},[194,2729,2668],{"class":203},[194,2731,2732],{"class":250},"\"B2\"",[194,2734,377],{"class":203},[194,2736,182],{"class":199},[194,2738,2739],{"class":203}," (\n",[194,2741,2742,2745,2748,2750,2752,2754,2756,2759,2761,2763],{"class":77,"line":741},[194,2743,2744],{"class":199},"    f",[194,2746,2747],{"class":250},"\"=INDEX(",[194,2749,754],{"class":753},[194,2751,2696],{"class":203},[194,2753,2699],{"class":250},[194,2755,268],{"class":203},[194,2757,2758],{"class":250},"'$A$2:$A$4'",[194,2760,2707],{"class":203},[194,2762,766],{"class":753},[194,2764,2765],{"class":250},",\"\n",[194,2767,2768,2770,2773,2775,2777,2779,2781,2783,2785,2787],{"class":77,"line":809},[194,2769,2744],{"class":199},[194,2771,2772],{"class":250},"\"MATCH(MAX(",[194,2774,754],{"class":753},[194,2776,2696],{"class":203},[194,2778,2699],{"class":250},[194,2780,268],{"class":203},[194,2782,2704],{"class":250},[194,2784,2707],{"class":203},[194,2786,766],{"class":753},[194,2788,2789],{"class":250},"),\"\n",[194,2791,2792,2794,2796,2798,2800,2802,2804,2806,2808,2810],{"class":77,"line":814},[194,2793,2744],{"class":199},[194,2795,750],{"class":250},[194,2797,754],{"class":753},[194,2799,2696],{"class":203},[194,2801,2699],{"class":250},[194,2803,268],{"class":203},[194,2805,2704],{"class":250},[194,2807,2707],{"class":203},[194,2809,766],{"class":753},[194,2811,2812],{"class":250},",0))\"\n",[194,2814,2815],{"class":77,"line":825},[194,2816,414],{"class":203},[194,2818,2819],{"class":77,"line":842},[194,2820,217],{"emptyLinePlaceholder":216},[194,2822,2823,2825,2828],{"class":77,"line":867},[194,2824,408],{"class":203},[194,2826,2827],{"class":250},"\"cross_sheet.xlsx\"",[194,2829,414],{"class":203},[194,2831,2832,2834,2837,2839],{"class":77,"line":920},[194,2833,420],{"class":297},[194,2835,2836],{"class":203},"(summary[",[194,2838,1672],{"class":250},[194,2840,2841],{"class":203},"].value)\n",[10,2843,2844,2845,2848],{},"Two details save time later. Sheet names that a user might rename are exactly the ones worth replacing with a defined name, because a rename updates the name's target but not a hardcoded string you generated. And a sheet name containing an apostrophe needs that apostrophe doubled inside the quotes — rare, but it happens with names like ",[14,2846,2847],{},"O'Brien Region",", and the helper above handles it.",[173,2850,2852],{"id":2851},"writing-lookup-formulas-from-python","Writing lookup formulas from Python",[10,2854,2855,2856,2859,2860,2863],{},"Lookups are where generated formulas earn their keep: the reader can add a row to the price list and every line recalculates. ",[14,2857,2858],{},"VLOOKUP"," remains the most portable choice, while ",[14,2861,2862],{},"XLOOKUP"," is cleaner but only resolves in recent Excel versions:",[185,2865,2867],{"className":187,"code":2866,"language":189,"meta":190,"style":190},"from openpyxl import Workbook\n\nwb = Workbook()\n\nprices = wb.create_sheet(\"Prices\")\nprices.append([\"SKU\", \"Unit_Price\"])\nfor sku, price in ((\"A-100\", 19.99), (\"B-200\", 49.50), (\"C-300\", 12.25)):\n    prices.append([sku, price])\nlast_price_row = prices.max_row\n\norders = wb.create_sheet(\"Orders\")\norders.append([\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\"])\nfor sku, qty in ((\"A-100\", 4), (\"C-300\", 7), (\"Z-999\", 2)):\n    orders.append([sku, qty])\n    r = orders.max_row\n    lookup_range = f\"Prices!$A$2:$B${last_price_row}\"\n    # IFERROR keeps an unknown SKU from poisoning the totals below\n    orders[f\"C{r}\"] = f'=IFERROR(VLOOKUP(A{r},{lookup_range},2,FALSE),\"unpriced\")'\n    orders[f\"D{r}\"] = f'=IF(ISNUMBER(C{r}),B{r}*C{r},0)'\n\ndel wb[\"Sheet\"]\nwb.save(\"lookups.xlsx\")\n",[14,2868,2869,2879,2883,2891,2895,2909,2922,2957,2962,2972,2976,2988,3008,3044,3049,3058,3079,3084,3129,3182,3186,3196],{"__ignoreMap":190},[194,2870,2871,2873,2875,2877],{"class":77,"line":196},[194,2872,200],{"class":199},[194,2874,204],{"class":203},[194,2876,207],{"class":199},[194,2878,210],{"class":203},[194,2880,2881],{"class":77,"line":213},[194,2882,217],{"emptyLinePlaceholder":216},[194,2884,2885,2887,2889],{"class":77,"line":220},[194,2886,223],{"class":203},[194,2888,182],{"class":199},[194,2890,228],{"class":203},[194,2892,2893],{"class":77,"line":231},[194,2894,217],{"emptyLinePlaceholder":216},[194,2896,2897,2900,2902,2904,2907],{"class":77,"line":242},[194,2898,2899],{"class":203},"prices ",[194,2901,182],{"class":199},[194,2903,1693],{"class":203},[194,2905,2906],{"class":250},"\"Prices\"",[194,2908,414],{"class":203},[194,2910,2911,2914,2916,2918,2920],{"class":77,"line":254},[194,2912,2913],{"class":203},"prices.append([",[194,2915,265],{"class":250},[194,2917,268],{"class":203},[194,2919,276],{"class":250},[194,2921,284],{"class":203},[194,2923,2924,2926,2929,2931,2933,2935,2937,2939,2941,2943,2945,2947,2949,2951,2953,2955],{"class":77,"line":259},[194,2925,709],{"class":199},[194,2927,2928],{"class":203}," sku, price ",[194,2930,715],{"class":199},[194,2932,2611],{"class":203},[194,2934,292],{"class":250},[194,2936,268],{"class":203},[194,2938,303],{"class":297},[194,2940,2621],{"class":203},[194,2942,318],{"class":250},[194,2944,268],{"class":203},[194,2946,328],{"class":297},[194,2948,2621],{"class":203},[194,2950,343],{"class":250},[194,2952,268],{"class":203},[194,2954,353],{"class":297},[194,2956,2640],{"class":203},[194,2958,2959],{"class":77,"line":287},[194,2960,2961],{"class":203},"    prices.append([sku, price])\n",[194,2963,2964,2967,2969],{"class":77,"line":313},[194,2965,2966],{"class":203},"last_price_row ",[194,2968,182],{"class":199},[194,2970,2971],{"class":203}," prices.max_row\n",[194,2973,2974],{"class":77,"line":338},[194,2975,217],{"emptyLinePlaceholder":216},[194,2977,2978,2980,2982,2984,2986],{"class":77,"line":363},[194,2979,1688],{"class":203},[194,2981,182],{"class":199},[194,2983,1693],{"class":203},[194,2985,1199],{"class":250},[194,2987,414],{"class":203},[194,2989,2990,2992,2994,2996,2998,3000,3002,3004,3006],{"class":77,"line":368},[194,2991,1702],{"class":203},[194,2993,265],{"class":250},[194,2995,268],{"class":203},[194,2997,271],{"class":250},[194,2999,268],{"class":203},[194,3001,276],{"class":250},[194,3003,268],{"class":203},[194,3005,281],{"class":250},[194,3007,284],{"class":203},[194,3009,3010,3012,3015,3017,3019,3021,3023,3025,3027,3029,3031,3033,3035,3038,3040,3042],{"class":77,"line":385},[194,3011,709],{"class":199},[194,3013,3014],{"class":203}," sku, qty ",[194,3016,715],{"class":199},[194,3018,2611],{"class":203},[194,3020,292],{"class":250},[194,3022,268],{"class":203},[194,3024,298],{"class":297},[194,3026,2621],{"class":203},[194,3028,343],{"class":250},[194,3030,268],{"class":203},[194,3032,348],{"class":297},[194,3034,2621],{"class":203},[194,3036,3037],{"class":250},"\"Z-999\"",[194,3039,268],{"class":203},[194,3041,323],{"class":297},[194,3043,2640],{"class":203},[194,3045,3046],{"class":77,"line":400},[194,3047,3048],{"class":203},"    orders.append([sku, qty])\n",[194,3050,3051,3053,3055],{"class":77,"line":405},[194,3052,730],{"class":203},[194,3054,182],{"class":199},[194,3056,3057],{"class":203}," orders.max_row\n",[194,3059,3060,3063,3065,3067,3070,3072,3075,3077],{"class":77,"line":417},[194,3061,3062],{"class":203},"    lookup_range ",[194,3064,182],{"class":199},[194,3066,775],{"class":199},[194,3068,3069],{"class":250},"\"Prices!$A$2:$B$",[194,3071,754],{"class":753},[194,3073,3074],{"class":203},"last_price_row",[194,3076,766],{"class":753},[194,3078,806],{"class":250},[194,3080,3081],{"class":77,"line":684},[194,3082,3083],{"class":432},"    # IFERROR keeps an unknown SKU from poisoning the totals below\n",[194,3085,3086,3089,3091,3093,3095,3097,3099,3101,3103,3105,3107,3110,3112,3114,3116,3119,3121,3124,3126],{"class":77,"line":701},[194,3087,3088],{"class":203},"    orders[",[194,3090,747],{"class":199},[194,3092,849],{"class":250},[194,3094,754],{"class":753},[194,3096,763],{"class":203},[194,3098,766],{"class":753},[194,3100,750],{"class":250},[194,3102,377],{"class":203},[194,3104,182],{"class":199},[194,3106,775],{"class":199},[194,3108,3109],{"class":250},"'=IFERROR(VLOOKUP(A",[194,3111,754],{"class":753},[194,3113,763],{"class":203},[194,3115,766],{"class":753},[194,3117,3118],{"class":250},",",[194,3120,754],{"class":753},[194,3122,3123],{"class":203},"lookup_range",[194,3125,766],{"class":753},[194,3127,3128],{"class":250},",2,FALSE),\"unpriced\")'\n",[194,3130,3131,3133,3135,3138,3140,3142,3144,3146,3148,3150,3152,3155,3157,3159,3161,3164,3166,3168,3170,3173,3175,3177,3179],{"class":77,"line":706},[194,3132,3088],{"class":203},[194,3134,747],{"class":199},[194,3136,3137],{"class":250},"\"D",[194,3139,754],{"class":753},[194,3141,763],{"class":203},[194,3143,766],{"class":753},[194,3145,750],{"class":250},[194,3147,377],{"class":203},[194,3149,182],{"class":199},[194,3151,775],{"class":199},[194,3153,3154],{"class":250},"'=IF(ISNUMBER(C",[194,3156,754],{"class":753},[194,3158,763],{"class":203},[194,3160,766],{"class":753},[194,3162,3163],{"class":250},"),B",[194,3165,754],{"class":753},[194,3167,763],{"class":203},[194,3169,766],{"class":753},[194,3171,3172],{"class":250},"*C",[194,3174,754],{"class":753},[194,3176,763],{"class":203},[194,3178,766],{"class":753},[194,3180,3181],{"class":250},",0)'\n",[194,3183,3184],{"class":77,"line":721},[194,3185,217],{"emptyLinePlaceholder":216},[194,3187,3188,3190,3192,3194],{"class":77,"line":727},[194,3189,1995],{"class":199},[194,3191,1323],{"class":203},[194,3193,2000],{"class":250},[194,3195,596],{"class":203},[194,3197,3198,3200,3203],{"class":77,"line":741},[194,3199,408],{"class":203},[194,3201,3202],{"class":250},"\"lookups.xlsx\"",[194,3204,414],{"class":203},[10,3206,436,3207,3210,3211,3214,3215,3218,3219,3222,3223,3226],{},[14,3208,3209],{},"FALSE"," fourth argument forces an exact match, which is almost always what a SKU lookup wants — the default of ",[14,3212,3213],{},"TRUE"," silently returns the nearest lower match on unsorted data, producing wrong prices rather than an error. Wrapping the call in ",[14,3216,3217],{},"IFERROR"," turns a missing SKU into a readable label instead of ",[14,3220,3221],{},"#N\u002FA",", and the ",[14,3224,3225],{},"ISNUMBER"," guard on the line total stops one unpriced row from breaking the grand total underneath.",[173,3228,3230],{"id":3229},"formula-or-pandas-a-quick-decision","Formula or pandas? A quick decision",[3232,3233,3234,3250],"table",{},[3235,3236,3237],"thead",{},[3238,3239,3240,3244,3247],"tr",{},[3241,3242,3243],"th",{},"Situation",[3241,3245,3246],{},"Better choice",[3241,3248,3249],{},"Why",[3251,3252,3253,3265,3276,3286,3300,3310],"tbody",{},[3238,3254,3255,3259,3262],{},[3256,3257,3258],"td",{},"Reader changes an input and expects totals to update",[3256,3260,3261],{},"Excel formula",[3256,3263,3264],{},"The model has to live in the file",[3238,3266,3267,3270,3273],{},[3256,3268,3269],{},"The number must be identical for every recipient",[3256,3271,3272],{},"Compute in pandas",[3256,3274,3275],{},"No dependence on a calculation engine",[3238,3277,3278,3281,3283],{},[3256,3279,3280],{},"Output is also exported to CSV",[3256,3282,3272],{},[3256,3284,3285],{},"CSV keeps values, not formulas",[3238,3287,3288,3291,3293],{},[3256,3289,3290],{},"Aggregations across 200k rows",[3256,3292,3272],{},[3256,3294,3295,3296,3299],{},"Thousands of ",[14,3297,3298],{},"SUMIFS"," calls make a workbook crawl",[3238,3301,3302,3305,3307],{},[3256,3303,3304],{},"Audit trail showing how a figure was derived",[3256,3306,3261],{},[3256,3308,3309],{},"The derivation is visible in the cell",[3238,3311,3312,3315,3317],{},[3256,3313,3314],{},"A chart you generate from Python needs the numbers",[3256,3316,3272],{},[3256,3318,3319,3323],{},[19,3320,3322],{"href":3321},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Chart series"," read values, not uncalculated formulas",[10,3325,3326],{},"The mixed approach is usually the best one for recurring reports: compute the figures in pandas so the numbers are fixed and correct, then add a handful of formulas — a grand total, a variance against target — where you want the reader to be able to play.",[173,3328,3330],{"id":3329},"common-errors-and-fixes","Common errors and fixes",[3232,3332,3333,3346],{},[3235,3334,3335],{},[3238,3336,3337,3340,3343],{},[3241,3338,3339],{},"Symptom",[3241,3341,3342],{},"Cause",[3241,3344,3345],{},"Fix",[3251,3347,3348,3367,3382,3399,3418,3436,3454],{},[3238,3349,3350,3357,3360],{},[3256,3351,3352,3353,3356],{},"Cell shows ",[14,3354,3355],{},"=B2*C2"," as text in Excel",[3256,3358,3359],{},"The cell's number format is Text",[3256,3361,3362,3363,3366],{},"Set ",[14,3364,3365],{},"cell.number_format = \"General\""," before assigning",[3238,3368,3369,3376,3379],{},[3256,3370,3371,3373,3374],{},[14,3372,1274],{}," from ",[14,3375,1265],{},[3256,3377,3378],{},"No calculating application has saved the file",[3256,3380,3381],{},"Open and save in Excel, or compute the value in Python",[3238,3383,3384,3387,3393],{},[3256,3385,3386],{},"Formulas vanished after a script ran",[3256,3388,3389,3390,3392],{},"Workbook was loaded with ",[14,3391,1265],{}," and saved",[3256,3394,3395,3396,3398],{},"Load without ",[14,3397,1235],{}," when you intend to keep formulas",[3238,3400,3401,3406,3409],{},[3256,3402,3403,3405],{},[14,3404,2408],{}," after rows were inserted",[3256,3407,3408],{},"Reference was built with hardcoded row numbers",[3256,3410,3411,3412,3414,3415],{},"Generate references from ",[14,3413,1050],{},", anchor fixed cells with ",[14,3416,3417],{},"$",[3238,3419,3420,3425,3428],{},[3256,3421,3422,3424],{},[14,3423,1062],{}," down a whole column",[3256,3426,3427],{},"The divisor moved with each row",[3256,3429,3430,3431,443,3433],{},"Anchor it: ",[14,3432,1054],{},[14,3434,3435],{},"D14",[3238,3437,3438,3444,3447],{},[3256,3439,3440,3443],{},[14,3441,3442],{},"#NAME?"," in Excel",[3256,3445,3446],{},"The defined name was never added, or is misspelled",[3256,3448,3449,3450,3453],{},"Check ",[14,3451,3452],{},"list(wb.defined_names)"," and the exact spelling in the formula",[3238,3455,3456,3459,3462],{},[3256,3457,3458],{},"Formula works in Excel but breaks in LibreOffice",[3256,3460,3461],{},"A function specific to one application",[3256,3463,3464],{},"Stick to the common function set, or compute in pandas",[173,3466,3468],{"id":3467},"audit-a-delivered-workbook-for-broken-formulas","Audit a delivered workbook for broken formulas",[10,3470,3471,3472,3474,3475,3477],{},"Once a file has been round-tripped through Excel, the cached values tell you whether the model still works. Excel stores error results as strings such as ",[14,3473,2408],{}," and ",[14,3476,1062],{},", so a short scan catches a broken report before a reader does:",[185,3479,3481],{"className":187,"code":3480,"language":189,"meta":190,"style":190},"from openpyxl import load_workbook\n\nERRORS = {\"#REF!\", \"#DIV\u002F0!\", \"#VALUE!\", \"#N\u002FA\", \"#NAME?\", \"#NULL!\", \"#NUM!\"}\n\nwb = load_workbook(\"orders_frozen.xlsx\", data_only=True)\nbroken = [\n    f\"{ws.title}!{cell.coordinate} = {cell.value}\"\n    for ws in wb.worksheets\n    for row in ws.iter_rows()\n    for cell in row\n    if isinstance(cell.value, str) and cell.value in ERRORS\n]\n\nif broken:\n    print(\"Formula errors found:\")\n    print(\"\\n\".join(broken[:20]))\nelse:\n    print(\"No formula errors cached in this workbook.\")\n",[14,3482,3483,3493,3497,3544,3548,3568,3577,3610,3622,3633,3644,3667,3671,3675,3682,3694,3716,3722],{"__ignoreMap":190},[194,3484,3485,3487,3489,3491],{"class":77,"line":196},[194,3486,200],{"class":199},[194,3488,204],{"class":203},[194,3490,207],{"class":199},[194,3492,1166],{"class":203},[194,3494,3495],{"class":77,"line":213},[194,3496,217],{"emptyLinePlaceholder":216},[194,3498,3499,3502,3505,3508,3511,3513,3516,3518,3521,3523,3526,3528,3531,3533,3536,3538,3541],{"class":77,"line":220},[194,3500,3501],{"class":297},"ERRORS",[194,3503,3504],{"class":199}," =",[194,3506,3507],{"class":203}," {",[194,3509,3510],{"class":250},"\"#REF!\"",[194,3512,268],{"class":203},[194,3514,3515],{"class":250},"\"#DIV\u002F0!\"",[194,3517,268],{"class":203},[194,3519,3520],{"class":250},"\"#VALUE!\"",[194,3522,268],{"class":203},[194,3524,3525],{"class":250},"\"#N\u002FA\"",[194,3527,268],{"class":203},[194,3529,3530],{"class":250},"\"#NAME?\"",[194,3532,268],{"class":203},[194,3534,3535],{"class":250},"\"#NULL!\"",[194,3537,268],{"class":203},[194,3539,3540],{"class":250},"\"#NUM!\"",[194,3542,3543],{"class":203},"}\n",[194,3545,3546],{"class":77,"line":231},[194,3547,217],{"emptyLinePlaceholder":216},[194,3549,3550,3552,3554,3556,3558,3560,3562,3564,3566],{"class":77,"line":242},[194,3551,223],{"class":203},[194,3553,182],{"class":199},[194,3555,1185],{"class":203},[194,3557,2372],{"class":250},[194,3559,268],{"class":203},[194,3561,1235],{"class":1234},[194,3563,182],{"class":199},[194,3565,1240],{"class":297},[194,3567,414],{"class":203},[194,3569,3570,3573,3575],{"class":77,"line":254},[194,3571,3572],{"class":203},"broken ",[194,3574,182],{"class":199},[194,3576,522],{"class":203},[194,3578,3579,3581,3583,3585,3588,3590,3592,3594,3596,3598,3601,3603,3606,3608],{"class":77,"line":259},[194,3580,2744],{"class":199},[194,3582,750],{"class":250},[194,3584,754],{"class":753},[194,3586,3587],{"class":203},"ws.title",[194,3589,766],{"class":753},[194,3591,2335],{"class":250},[194,3593,754],{"class":753},[194,3595,2340],{"class":203},[194,3597,766],{"class":753},[194,3599,3600],{"class":250}," = ",[194,3602,754],{"class":753},[194,3604,3605],{"class":203},"cell.value",[194,3607,766],{"class":753},[194,3609,806],{"class":250},[194,3611,3612,3614,3617,3619],{"class":77,"line":287},[194,3613,2238],{"class":199},[194,3615,3616],{"class":203}," ws ",[194,3618,715],{"class":199},[194,3620,3621],{"class":203}," wb.worksheets\n",[194,3623,3624,3626,3628,3630],{"class":77,"line":313},[194,3625,2238],{"class":199},[194,3627,2241],{"class":203},[194,3629,715],{"class":199},[194,3631,3632],{"class":203}," ws.iter_rows()\n",[194,3634,3635,3637,3639,3641],{"class":77,"line":338},[194,3636,2238],{"class":199},[194,3638,2254],{"class":203},[194,3640,715],{"class":199},[194,3642,3643],{"class":203}," row\n",[194,3645,3646,3649,3651,3654,3656,3658,3660,3662,3664],{"class":77,"line":363},[194,3647,3648],{"class":199},"    if",[194,3650,2277],{"class":297},[194,3652,3653],{"class":203},"(cell.value, ",[194,3655,2283],{"class":297},[194,3657,2286],{"class":203},[194,3659,2289],{"class":199},[194,3661,2305],{"class":203},[194,3663,715],{"class":199},[194,3665,3666],{"class":297}," ERRORS\n",[194,3668,3669],{"class":77,"line":368},[194,3670,596],{"class":203},[194,3672,3673],{"class":77,"line":385},[194,3674,217],{"emptyLinePlaceholder":216},[194,3676,3677,3679],{"class":77,"line":400},[194,3678,1359],{"class":199},[194,3680,3681],{"class":203}," broken:\n",[194,3683,3684,3687,3689,3692],{"class":77,"line":405},[194,3685,3686],{"class":297},"    print",[194,3688,944],{"class":203},[194,3690,3691],{"class":250},"\"Formula errors found:\"",[194,3693,414],{"class":203},[194,3695,3696,3698,3700,3702,3705,3707,3710,3713],{"class":77,"line":417},[194,3697,3686],{"class":297},[194,3699,944],{"class":203},[194,3701,750],{"class":250},[194,3703,3704],{"class":753},"\\n",[194,3706,750],{"class":250},[194,3708,3709],{"class":203},".join(broken[:",[194,3711,3712],{"class":297},"20",[194,3714,3715],{"class":203},"]))\n",[194,3717,3718,3720],{"class":77,"line":684},[194,3719,2504],{"class":199},[194,3721,2312],{"class":203},[194,3723,3724,3726,3728,3731],{"class":77,"line":701},[194,3725,3686],{"class":297},[194,3727,944],{"class":203},[194,3729,3730],{"class":250},"\"No formula errors cached in this workbook.\"",[194,3732,414],{"class":203},[10,3734,3735,3736,3740],{},"Run this as the last step of a report job and the failure lands in your logs rather than in someone's inbox. It pairs naturally with the wider checks in ",[19,3737,3739],{"href":3738},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002F","validating Excel data with Python",", which cover the input side of the same problem.",[173,3742,3744],{"id":3743},"key-takeaways","Key takeaways",[3746,3747,3748,3752,3760,3766,3771,3774],"ul",{},[3749,3750,3751],"li",{},"openpyxl stores formulas as text and never calculates them; numbers appear when a spreadsheet application opens the file.",[3749,3753,3754,3756,3757,3759],{},[14,3755,1265],{}," returns cached results, not fresh calculations — and returns ",[14,3758,1274],{}," for a file Python just created.",[3749,3761,3762,3763,3765],{},"Saving a workbook that was loaded with ",[14,3764,1265],{}," permanently replaces its formulas with numbers.",[3749,3767,3768,3769,1063],{},"Generate references from the row you are writing, and anchor anything that must not move with ",[14,3770,3417],{},[3749,3772,3773],{},"Defined names make generated formulas readable, and sheet-local names let repeated tabs reuse one name safely.",[3749,3775,3776],{},"When the number matters more than the model, compute it in pandas and write a literal.",[173,3778,3780],{"id":3779},"frequently-asked-questions","Frequently asked questions",[10,3782,3783,3787],{},[3784,3785,3786],"strong",{},"Does openpyxl calculate my formulas?","\nNo. openpyxl writes and reads the formula text; it has no calculation engine. The numbers appear when Excel, LibreOffice or another spreadsheet application opens the file and evaluates it.",[10,3789,3790,3799,3801],{},[3784,3791,3792,3793,3795,3796,3798],{},"Why does a formula cell read back as ",[14,3794,1274],{}," with ",[14,3797,1265],{},"?",[14,3800,1265],{}," returns the value Excel cached the last time it saved the file. A workbook that Python just created has never been opened by a calculating application, so there is no cached result yet.",[10,3803,3804,3807],{},[3784,3805,3806],{},"Should I write a formula or compute the number in pandas?","\nWrite a formula when the reader is expected to change inputs and see the result update. Compute in pandas when the number must be identical for everyone, must survive a CSV export, or feeds a chart you are generating.",[10,3809,3810,3813,3814,3817],{},[3784,3811,3812],{},"How do I stop my generated formula breaking when rows shift?","\nAnchor the parts that must not move with dollar signs, for example ",[14,3815,3816],{},"$B$1"," for a single rate cell, and build the moving parts from the row number you are on rather than hardcoding them.",[10,3819,3820,3823,3824,3827,3828,3830,3831,3833],{},[3784,3821,3822],{},"Can I use named ranges written by Python inside formulas?","\nYes. Add a ",[14,3825,3826],{},"DefinedName"," to ",[14,3829,1829],{}," for a workbook-wide name or to ",[14,3832,1833],{}," for a sheet-local one, then reference the name in any formula string you write.",[10,3835,3836,3839,3840,1063],{},[3784,3837,3838],{},"Why does Excel show my formula as text instead of a result?","\nThe cell was written while its number format was Text, or the string does not start with an equals sign. Set the format to General before assigning, and always begin the string with ",[14,3841,182],{},[173,3843,3845],{"id":3844},"related","Related",[10,3847,3848],{},"Up to the parent guide:",[3746,3850,3851],{},[3749,3852,3853,3855],{},[19,3854,22],{"href":21}," — the full introduction to the libraries and their jobs.",[10,3857,3858],{},"Go deeper here:",[3746,3860,3861,3868,3875,3882,3889,3896],{},[3749,3862,3863,3867],{},[19,3864,3866],{"href":3865},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002F","Write a Formula to an Excel Cell with openpyxl"," — the minimal write, including number-format traps.",[3749,3869,3870,3874],{},[19,3871,3873],{"href":3872},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Read Formula Results with openpyxl data_only"," — why results are missing and what to do about it.",[3749,3876,3877,3881],{},[19,3878,3880],{"href":3879},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002F","Add SUM and AVERAGE Formulas with Python"," — total rows that grow with the data.",[3749,3883,3884,3888],{},[19,3885,3887],{"href":3886},"\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"," — workbook and sheet scope in practice.",[3749,3890,3891,3895],{},[19,3892,3894],{"href":3893},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python\u002F","Convert Excel Formulas to Values with Python"," — freeze a model before sending it out.",[3749,3897,3898,3902],{},[19,3899,3901],{"href":3900},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl\u002F","Copy a Formula Down a Column in openpyxl"," — fill patterns without Excel's fill handle.",[10,3904,3905],{},"Related areas:",[3746,3907,3908,3915,3922],{},[3749,3909,3910,3914],{},[19,3911,3913],{"href":3912},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation"," — the object model these formulas live in.",[3749,3916,3917,3921],{},[19,3918,3920],{"href":3919},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with Pandas"," — export first, then add formulas on top.",[3749,3923,3924,3927],{},[19,3925,3926],{"href":3738},"Validating Excel Data with Python"," — dropdowns and rules that reference named ranges.",[3929,3930,3931],"style",{},"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 .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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 .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 .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}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":190,"searchDepth":213,"depth":213,"links":3933},[3934,3935,3936,3937,3938,3939,3940,3941,3942,3943,3944,3945,3946],{"id":175,"depth":213,"text":176},{"id":473,"depth":213,"text":474},{"id":1143,"depth":213,"text":1144},{"id":1580,"depth":213,"text":1581},{"id":2142,"depth":213,"text":2143},{"id":2394,"depth":213,"text":2395},{"id":2851,"depth":213,"text":2852},{"id":3229,"depth":213,"text":3230},{"id":3329,"depth":213,"text":3330},{"id":3467,"depth":213,"text":3468},{"id":3743,"depth":213,"text":3744},{"id":3779,"depth":213,"text":3780},{"id":3844,"depth":213,"text":3845},"2026-08-01","Write, generate, read back and freeze Excel formulas from Python with openpyxl — including cached values, absolute references, named ranges, and when to calculate in pandas instead.","md",[3951,3953,3956,3958,3960,3962],{"q":3786,"a":3952},"No. openpyxl writes and reads the formula text; it has no calculation engine. The numbers appear when Excel, LibreOffice or another spreadsheet application opens the file and evaluates it.",{"q":3954,"a":3955},"Why does a formula cell read back as None with data_only=True?","data_only=True returns the value Excel cached the last time it saved the file. A workbook that Python just created has never been opened by a calculating application, so there is no cached result yet.",{"q":3806,"a":3957},"Write a formula when the reader is expected to change inputs and see the result update. Compute in pandas when the number must be identical for everyone, must survive a CSV export, or feeds a chart you are generating.",{"q":3812,"a":3959},"Anchor the parts that must not move with dollar signs, for example $B$1 for a single rate cell, and build the moving parts from the row number you are on rather than hardcoding them.",{"q":3822,"a":3961},"Yes. Add a DefinedName to wb.defined_names for a workbook-wide name or to ws.defined_names for a sheet-local one, then reference the name in any formula string you write.",{"q":3838,"a":3963},"The cell was written while its number format was Text, or the string does not start with an equals sign. Set the format to General before assigning, and always begin the string with =.",{"breadcrumb":3965},[3966,3969,3970],{"name":3967,"item":3968},"Home","\u002F",{"name":22,"item":21},{"name":5,"item":3971},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python",{"title":5,"description":3974},"How openpyxl writes formulas as strings, why data_only=True returns None, how to generate references safely, name ranges, and convert formulas back to values.","working-with-excel-formulas-in-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Findex","guide","eukbfX5i9R9xPzleQpDxN0L7oWS8hN_eF35nkFgqHyg",[3980,3984],{"title":3981,"path":3982,"stem":3983,"children":-1},"Read a Cell Value From Excel With openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl\u002Findex",{"title":3880,"path":3985,"stem":3986,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002Findex",1785584462252]