[{"data":1,"prerenderedAt":2130},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python":2121},{"id":4,"title":5,"body":6,"dateModified":2092,"datePublished":2092,"description":2093,"extension":2094,"faq":2095,"meta":2104,"navigation":214,"path":2114,"seo":2115,"slug":2117,"stem":2118,"type":2119,"__hash__":2120},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python\u002Findex.md","Convert Excel Formulas to Values with Python",{"type":7,"value":8,"toc":2078},"minimark",[9,19,142,147,172,179,183,186,474,490,494,501,818,821,825,828,1002,1005,1104,1107,1111,1114,1318,1321,1407,1411,1414,1583,1586,1590,1593,1858,1861,1865,1967,1971,1982,1986,1993,1997,2004,2013,2019,2025,2029,2032,2041,2044,2074],[10,11,12,13,18],"p",{},"Sending a workbook full of formulas to someone outside your team is a small act of faith: they may open it in an application that calculates differently, paste it into a system that cannot calculate at all, or change an input and unknowingly rewrite your numbers. Freezing formulas into values removes that risk. This guide, part of ",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", shows the conversion, how to keep the formatting, and what to do when there are no cached results to freeze.",[20,21,29,30,29,34,29,38,29,45,29,54,29,61,29,70,29,75,29,79,29,82,29,92,29,98,29,103,29,107,29,111,29,116,29,119,29,122,29,127,29,131,29,134,29,137],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 740 236","img",[25,26],"fv-flow-t","fv-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:740px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Freezing formulas with two workbook handles",[35,36,37],"desc",{"id":26},"The same file is loaded twice: once with data_only=True to read cached numbers, and once normally to keep formulas, styles and layout. The numbers from the first handle are written into the second, which is then saved as the frozen output file.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","740","236","#ffffff",[39,46],{"x":47,"y":48,"width":49,"height":50,"rx":51,"fill":52,"stroke":53},"270","20","200","44","10","#ebebfd","var(--line,#cdd5e6)",[55,56,60],"text",{"x":57,"y":58,"style":59},"370","47","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","report.xlsx",[62,63],"line",{"x1":64,"y1":65,"x2":66,"y2":67,"stroke":68,"style":69},"320","66","190","98","var(--muted,#5b6780)","stroke-width:1.5px",[71,72],"polygon",{"points":73,"fill":74},"186,100 198,94 196,104","#5b6780",[62,76],{"x1":77,"y1":65,"x2":78,"y2":67,"stroke":68,"style":69},"420","550",[71,80],{"points":81,"fill":74},"554,100 542,94 544,104",[39,83],{"x":84,"y":85,"width":86,"height":87,"rx":88,"fill":89,"stroke":90,"style":91},"30","102","290","84","12","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[55,93,97],{"x":94,"y":95,"style":96},"175","128","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","handle 1 · data_only=True",[55,99,102],{"x":94,"y":100,"style":101},"150","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","supplies the numbers",[55,104,106],{"x":94,"y":105,"style":101},"170","1284.60, 0.183, 42",[39,108],{"x":77,"y":85,"width":86,"height":87,"rx":88,"fill":109,"stroke":110,"style":91},"#f0f4ff","var(--brand,#5b5cf0)",[55,112,115],{"x":113,"y":95,"style":114},"565","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","handle 2 · default",[55,117,118],{"x":113,"y":100,"style":101},"keeps styles, widths, formats",[55,120,121],{"x":113,"y":105,"style":101},"receives the numbers",[62,123],{"x1":124,"y1":125,"x2":126,"y2":125,"stroke":90,"style":91},"324","144","414",[71,128],{"points":129,"fill":130},"418,144 406,138 406,150","#0f766e",[62,132],{"x1":113,"y1":66,"x2":113,"y2":133,"stroke":68,"style":69},"210",[71,135],{"points":136,"fill":74},"565,214 559,202 571,202",[55,138,141],{"x":113,"y":139,"style":140},"230","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","report_frozen.xlsx",[143,144,146],"h2",{"id":145},"prerequisites","Prerequisites",[148,149,154],"pre",{"className":150,"code":151,"language":152,"meta":153,"style":153},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[155,156,157],"code",{"__ignoreMap":153},[158,159,161,165,169],"span",{"class":62,"line":160},1,[158,162,164],{"class":163},"sMTad","pip",[158,166,168],{"class":167},"srMev"," install",[158,170,171],{"class":167}," openpyxl\n",[10,173,174,175,178],{},"The optional recalculation step later uses LibreOffice, which is available as ",[155,176,177],{},"soffice"," on most Linux distributions and inside the standard LibreOffice install on macOS and Windows.",[143,180,182],{"id":181},"the-core-conversion","The core conversion",[10,184,185],{},"Load the file twice, copy cached numbers over formula cells, and save under a new name:",[148,187,191],{"className":188,"code":189,"language":190,"meta":153,"style":153},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nSRC = \"report.xlsx\"\nDST = \"report_frozen.xlsx\"\n\nvalues = load_workbook(SRC, data_only=True)   # cached results\nbook = load_workbook(SRC)                     # formulas, styles, layout\n\nfrozen = 0\nfor sheet in book.sheetnames:\n    v_ws, b_ws = values[sheet], book[sheet]\n    for row in b_ws.iter_rows():\n        for cell in row:\n            if isinstance(cell.value, str) and cell.value.startswith(\"=\"):\n                cell.value = v_ws[cell.coordinate].value\n                frozen += 1\n\nbook.save(DST)\nprint(f\"froze {frozen} formula cell(s) into {DST}\")\n","python",[155,192,193,209,216,229,240,245,278,296,301,312,327,338,352,366,396,407,419,424,435],{"__ignoreMap":153},[158,194,195,199,203,206],{"class":62,"line":160},[158,196,198],{"class":197},"s-kum","from",[158,200,202],{"class":201},"skGVy"," openpyxl ",[158,204,205],{"class":197},"import",[158,207,208],{"class":201}," load_workbook\n",[158,210,212],{"class":62,"line":211},2,[158,213,215],{"emptyLinePlaceholder":214},true,"\n",[158,217,219,223,226],{"class":62,"line":218},3,[158,220,222],{"class":221},"sP0c6","SRC",[158,224,225],{"class":197}," =",[158,227,228],{"class":167}," \"report.xlsx\"\n",[158,230,232,235,237],{"class":62,"line":231},4,[158,233,234],{"class":221},"DST",[158,236,225],{"class":197},[158,238,239],{"class":167}," \"report_frozen.xlsx\"\n",[158,241,243],{"class":62,"line":242},5,[158,244,215],{"emptyLinePlaceholder":214},[158,246,248,251,254,257,259,262,266,268,271,274],{"class":62,"line":247},6,[158,249,250],{"class":201},"values ",[158,252,253],{"class":197},"=",[158,255,256],{"class":201}," load_workbook(",[158,258,222],{"class":221},[158,260,261],{"class":201},", ",[158,263,265],{"class":264},"sa561","data_only",[158,267,253],{"class":197},[158,269,270],{"class":221},"True",[158,272,273],{"class":201},")   ",[158,275,277],{"class":276},"s-wDw","# cached results\n",[158,279,281,284,286,288,290,293],{"class":62,"line":280},7,[158,282,283],{"class":201},"book ",[158,285,253],{"class":197},[158,287,256],{"class":201},[158,289,222],{"class":221},[158,291,292],{"class":201},")                     ",[158,294,295],{"class":276},"# formulas, styles, layout\n",[158,297,299],{"class":62,"line":298},8,[158,300,215],{"emptyLinePlaceholder":214},[158,302,304,307,309],{"class":62,"line":303},9,[158,305,306],{"class":201},"frozen ",[158,308,253],{"class":197},[158,310,311],{"class":221}," 0\n",[158,313,315,318,321,324],{"class":62,"line":314},10,[158,316,317],{"class":197},"for",[158,319,320],{"class":201}," sheet ",[158,322,323],{"class":197},"in",[158,325,326],{"class":201}," book.sheetnames:\n",[158,328,330,333,335],{"class":62,"line":329},11,[158,331,332],{"class":201},"    v_ws, b_ws ",[158,334,253],{"class":197},[158,336,337],{"class":201}," values[sheet], book[sheet]\n",[158,339,341,344,347,349],{"class":62,"line":340},12,[158,342,343],{"class":197},"    for",[158,345,346],{"class":201}," row ",[158,348,323],{"class":197},[158,350,351],{"class":201}," b_ws.iter_rows():\n",[158,353,355,358,361,363],{"class":62,"line":354},13,[158,356,357],{"class":197},"        for",[158,359,360],{"class":201}," cell ",[158,362,323],{"class":197},[158,364,365],{"class":201}," row:\n",[158,367,369,372,375,378,381,384,387,390,393],{"class":62,"line":368},14,[158,370,371],{"class":197},"            if",[158,373,374],{"class":221}," isinstance",[158,376,377],{"class":201},"(cell.value, ",[158,379,380],{"class":221},"str",[158,382,383],{"class":201},") ",[158,385,386],{"class":197},"and",[158,388,389],{"class":201}," cell.value.startswith(",[158,391,392],{"class":167},"\"=\"",[158,394,395],{"class":201},"):\n",[158,397,399,402,404],{"class":62,"line":398},15,[158,400,401],{"class":201},"                cell.value ",[158,403,253],{"class":197},[158,405,406],{"class":201}," v_ws[cell.coordinate].value\n",[158,408,410,413,416],{"class":62,"line":409},16,[158,411,412],{"class":201},"                frozen ",[158,414,415],{"class":197},"+=",[158,417,418],{"class":221}," 1\n",[158,420,422],{"class":62,"line":421},17,[158,423,215],{"emptyLinePlaceholder":214},[158,425,427,430,432],{"class":62,"line":426},18,[158,428,429],{"class":201},"book.save(",[158,431,234],{"class":221},[158,433,434],{"class":201},")\n",[158,436,438,441,444,447,450,454,457,460,463,465,467,469,472],{"class":62,"line":437},19,[158,439,440],{"class":221},"print",[158,442,443],{"class":201},"(",[158,445,446],{"class":197},"f",[158,448,449],{"class":167},"\"froze ",[158,451,453],{"class":452},"sSjpA","{",[158,455,456],{"class":201},"frozen",[158,458,459],{"class":452},"}",[158,461,462],{"class":167}," formula cell(s) into ",[158,464,453],{"class":452},[158,466,234],{"class":221},[158,468,459],{"class":452},[158,470,471],{"class":167},"\"",[158,473,434],{"class":201},[10,475,476,477,480,481,484,485,489],{},"Writing into ",[155,478,479],{},"book"," rather than ",[155,482,483],{},"values"," is what preserves the report's appearance: fills, borders, ",[14,486,488],{"href":487},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","number formats",", column widths and merged cells all live on that handle and are untouched by the value assignment. Saving to a new path keeps the model intact — you almost never want to overwrite the file that still contains the formulas.",[143,491,493],{"id":492},"refuse-to-ship-silently-empty-cells","Refuse to ship silently empty cells",[10,495,496,497,500],{},"If the source has no cached results, the loop above writes ",[155,498,499],{},"None"," everywhere and produces a beautifully formatted blank report. Check first, and fail loudly:",[148,502,504],{"className":188,"code":503,"language":190,"meta":153,"style":153},"from openpyxl import load_workbook\n\ndef freeze(src, dst):\n    values = load_workbook(src, data_only=True)\n    book = load_workbook(src)\n\n    missing = []\n    for sheet in book.sheetnames:\n        v_ws, b_ws = values[sheet], book[sheet]\n        for row in b_ws.iter_rows():\n            for cell in row:\n                if isinstance(cell.value, str) and cell.value.startswith(\"=\"):\n                    cached = v_ws[cell.coordinate].value\n                    if cached is None:\n                        missing.append(f\"{sheet}!{cell.coordinate}\")\n                    else:\n                        cell.value = cached\n\n    if missing:\n        raise ValueError(\n            f\"{len(missing)} formula cell(s) have no cached result \"\n            f\"(first: {missing[0]}). Recalculate the workbook first.\"\n        )\n    book.save(dst)\n    return dst\n\nfreeze(\"report.xlsx\", \"report_frozen.xlsx\")\n",[155,505,506,516,520,532,550,560,564,574,584,593,603,614,635,644,661,691,698,708,712,720,732,753,776,782,788,797,802],{"__ignoreMap":153},[158,507,508,510,512,514],{"class":62,"line":160},[158,509,198],{"class":197},[158,511,202],{"class":201},[158,513,205],{"class":197},[158,515,208],{"class":201},[158,517,518],{"class":62,"line":211},[158,519,215],{"emptyLinePlaceholder":214},[158,521,522,525,529],{"class":62,"line":218},[158,523,524],{"class":197},"def",[158,526,528],{"class":527},"s_Opv"," freeze",[158,530,531],{"class":201},"(src, dst):\n",[158,533,534,537,539,542,544,546,548],{"class":62,"line":231},[158,535,536],{"class":201},"    values ",[158,538,253],{"class":197},[158,540,541],{"class":201}," load_workbook(src, ",[158,543,265],{"class":264},[158,545,253],{"class":197},[158,547,270],{"class":221},[158,549,434],{"class":201},[158,551,552,555,557],{"class":62,"line":242},[158,553,554],{"class":201},"    book ",[158,556,253],{"class":197},[158,558,559],{"class":201}," load_workbook(src)\n",[158,561,562],{"class":62,"line":247},[158,563,215],{"emptyLinePlaceholder":214},[158,565,566,569,571],{"class":62,"line":280},[158,567,568],{"class":201},"    missing ",[158,570,253],{"class":197},[158,572,573],{"class":201}," []\n",[158,575,576,578,580,582],{"class":62,"line":298},[158,577,343],{"class":197},[158,579,320],{"class":201},[158,581,323],{"class":197},[158,583,326],{"class":201},[158,585,586,589,591],{"class":62,"line":303},[158,587,588],{"class":201},"        v_ws, b_ws ",[158,590,253],{"class":197},[158,592,337],{"class":201},[158,594,595,597,599,601],{"class":62,"line":314},[158,596,357],{"class":197},[158,598,346],{"class":201},[158,600,323],{"class":197},[158,602,351],{"class":201},[158,604,605,608,610,612],{"class":62,"line":329},[158,606,607],{"class":197},"            for",[158,609,360],{"class":201},[158,611,323],{"class":197},[158,613,365],{"class":201},[158,615,616,619,621,623,625,627,629,631,633],{"class":62,"line":340},[158,617,618],{"class":197},"                if",[158,620,374],{"class":221},[158,622,377],{"class":201},[158,624,380],{"class":221},[158,626,383],{"class":201},[158,628,386],{"class":197},[158,630,389],{"class":201},[158,632,392],{"class":167},[158,634,395],{"class":201},[158,636,637,640,642],{"class":62,"line":354},[158,638,639],{"class":201},"                    cached ",[158,641,253],{"class":197},[158,643,406],{"class":201},[158,645,646,649,652,655,658],{"class":62,"line":368},[158,647,648],{"class":197},"                    if",[158,650,651],{"class":201}," cached ",[158,653,654],{"class":197},"is",[158,656,657],{"class":221}," None",[158,659,660],{"class":201},":\n",[158,662,663,666,668,670,672,675,677,680,682,685,687,689],{"class":62,"line":398},[158,664,665],{"class":201},"                        missing.append(",[158,667,446],{"class":197},[158,669,471],{"class":167},[158,671,453],{"class":452},[158,673,674],{"class":201},"sheet",[158,676,459],{"class":452},[158,678,679],{"class":167},"!",[158,681,453],{"class":452},[158,683,684],{"class":201},"cell.coordinate",[158,686,459],{"class":452},[158,688,471],{"class":167},[158,690,434],{"class":201},[158,692,693,696],{"class":62,"line":409},[158,694,695],{"class":197},"                    else",[158,697,660],{"class":201},[158,699,700,703,705],{"class":62,"line":421},[158,701,702],{"class":201},"                        cell.value ",[158,704,253],{"class":197},[158,706,707],{"class":201}," cached\n",[158,709,710],{"class":62,"line":426},[158,711,215],{"emptyLinePlaceholder":214},[158,713,714,717],{"class":62,"line":437},[158,715,716],{"class":197},"    if",[158,718,719],{"class":201}," missing:\n",[158,721,723,726,729],{"class":62,"line":722},20,[158,724,725],{"class":197},"        raise",[158,727,728],{"class":221}," ValueError",[158,730,731],{"class":201},"(\n",[158,733,735,738,740,742,745,748,750],{"class":62,"line":734},21,[158,736,737],{"class":197},"            f",[158,739,471],{"class":167},[158,741,453],{"class":452},[158,743,744],{"class":221},"len",[158,746,747],{"class":201},"(missing)",[158,749,459],{"class":452},[158,751,752],{"class":167}," formula cell(s) have no cached result \"\n",[158,754,756,758,761,763,766,768,771,773],{"class":62,"line":755},22,[158,757,737],{"class":197},[158,759,760],{"class":167},"\"(first: ",[158,762,453],{"class":452},[158,764,765],{"class":201},"missing[",[158,767,41],{"class":221},[158,769,770],{"class":201},"]",[158,772,459],{"class":452},[158,774,775],{"class":167},"). Recalculate the workbook first.\"\n",[158,777,779],{"class":62,"line":778},23,[158,780,781],{"class":201},"        )\n",[158,783,785],{"class":62,"line":784},24,[158,786,787],{"class":201},"    book.save(dst)\n",[158,789,791,794],{"class":62,"line":790},25,[158,792,793],{"class":197},"    return",[158,795,796],{"class":201}," dst\n",[158,798,800],{"class":62,"line":799},26,[158,801,215],{"emptyLinePlaceholder":214},[158,803,805,808,811,813,816],{"class":62,"line":804},27,[158,806,807],{"class":201},"freeze(",[158,809,810],{"class":167},"\"report.xlsx\"",[158,812,261],{"class":201},[158,814,815],{"class":167},"\"report_frozen.xlsx\"",[158,817,434],{"class":201},[10,819,820],{},"Raising rather than warning is the right default here, because the failure is invisible downstream: a client receives a report where every total reads zero and nothing in the file says why.",[143,822,824],{"id":823},"recalculate-headlessly-when-there-is-no-cache","Recalculate headlessly when there is no cache",[10,826,827],{},"A workbook your own pipeline generated has never been calculated, so there is nothing to freeze. LibreOffice can do the calculation on a server, without a display:",[148,829,831],{"className":188,"code":830,"language":190,"meta":153,"style":153},"import subprocess\nfrom pathlib import Path\n\ndef recalculate(path, outdir=\"recalc\"):\n    \"\"\"Round-trip a workbook through LibreOffice so formulas get cached results.\"\"\"\n    Path(outdir).mkdir(exist_ok=True)\n    subprocess.run(\n        [\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", outdir, str(path)],\n        check=True,\n        timeout=180,\n    )\n    return Path(outdir) \u002F Path(path).name\n\nrecalculated = recalculate(\"report.xlsx\")\nfreeze(recalculated, \"report_frozen.xlsx\")\n",[155,832,833,840,852,856,873,878,892,897,933,945,957,962,975,979,993],{"__ignoreMap":153},[158,834,835,837],{"class":62,"line":160},[158,836,205],{"class":197},[158,838,839],{"class":201}," subprocess\n",[158,841,842,844,847,849],{"class":62,"line":211},[158,843,198],{"class":197},[158,845,846],{"class":201}," pathlib ",[158,848,205],{"class":197},[158,850,851],{"class":201}," Path\n",[158,853,854],{"class":62,"line":218},[158,855,215],{"emptyLinePlaceholder":214},[158,857,858,860,863,866,868,871],{"class":62,"line":231},[158,859,524],{"class":197},[158,861,862],{"class":527}," recalculate",[158,864,865],{"class":201},"(path, outdir",[158,867,253],{"class":197},[158,869,870],{"class":167},"\"recalc\"",[158,872,395],{"class":201},[158,874,875],{"class":62,"line":242},[158,876,877],{"class":167},"    \"\"\"Round-trip a workbook through LibreOffice so formulas get cached results.\"\"\"\n",[158,879,880,883,886,888,890],{"class":62,"line":247},[158,881,882],{"class":201},"    Path(outdir).mkdir(",[158,884,885],{"class":264},"exist_ok",[158,887,253],{"class":197},[158,889,270],{"class":221},[158,891,434],{"class":201},[158,893,894],{"class":62,"line":280},[158,895,896],{"class":201},"    subprocess.run(\n",[158,898,899,902,905,907,910,912,915,917,920,922,925,928,930],{"class":62,"line":298},[158,900,901],{"class":201},"        [",[158,903,904],{"class":167},"\"soffice\"",[158,906,261],{"class":201},[158,908,909],{"class":167},"\"--headless\"",[158,911,261],{"class":201},[158,913,914],{"class":167},"\"--convert-to\"",[158,916,261],{"class":201},[158,918,919],{"class":167},"\"xlsx\"",[158,921,261],{"class":201},[158,923,924],{"class":167},"\"--outdir\"",[158,926,927],{"class":201},", outdir, ",[158,929,380],{"class":221},[158,931,932],{"class":201},"(path)],\n",[158,934,935,938,940,942],{"class":62,"line":303},[158,936,937],{"class":264},"        check",[158,939,253],{"class":197},[158,941,270],{"class":221},[158,943,944],{"class":201},",\n",[158,946,947,950,952,955],{"class":62,"line":314},[158,948,949],{"class":264},"        timeout",[158,951,253],{"class":197},[158,953,954],{"class":221},"180",[158,956,944],{"class":201},[158,958,959],{"class":62,"line":329},[158,960,961],{"class":201},"    )\n",[158,963,964,966,969,972],{"class":62,"line":340},[158,965,793],{"class":197},[158,967,968],{"class":201}," Path(outdir) ",[158,970,971],{"class":197},"\u002F",[158,973,974],{"class":201}," Path(path).name\n",[158,976,977],{"class":62,"line":354},[158,978,215],{"emptyLinePlaceholder":214},[158,980,981,984,986,989,991],{"class":62,"line":368},[158,982,983],{"class":201},"recalculated ",[158,985,253],{"class":197},[158,987,988],{"class":201}," recalculate(",[158,990,810],{"class":167},[158,992,434],{"class":201},[158,994,995,998,1000],{"class":62,"line":398},[158,996,997],{"class":201},"freeze(recalculated, ",[158,999,815],{"class":167},[158,1001,434],{"class":201},[10,1003,1004],{},"LibreOffice evaluates the formulas as it converts and writes the results into the output file, which then behaves exactly like a workbook a person saved from Excel. Two caveats are worth knowing before you build a pipeline on it: functions added in recent Excel versions may not be supported, and LibreOffice must not already be running as a desktop session for the same user profile, or the headless call exits immediately without converting.",[20,1006,29,1011,29,1014,29,1017,29,1020,29,1025,29,1029,29,1033,29,1036,29,1042,29,1047,29,1051,29,1056,29,1061,29,1066,29,1070,29,1074,29,1079,29,1082,29,1087,29,1090,29,1094,29,1099,29,1101],{"viewBox":1007,"role":23,"ariaLabelledBy":1008,"xmlns":27,"style":28},"0 0 740 250",[1009,1010],"fv-path-t","fv-path-d",[31,1012,1013],{"id":1009},"Choosing a route to frozen values",[35,1015,1016],{"id":1010},"A decision tree. If the workbook already carries cached results, freeze directly. If not, either recalculate it with LibreOffice and then freeze, or skip formulas entirely and compute the numbers in pandas before writing them.",[39,1018],{"x":41,"y":41,"width":42,"height":1019,"fill":44},"250",[39,1021],{"x":1019,"y":1022,"width":1023,"height":1024,"rx":88,"fill":52,"stroke":110,"style":91},"16","240","48",[55,1026,1028],{"x":57,"y":1027,"style":114},"46","Does the file have cached results?",[62,1030],{"x1":1031,"y1":65,"x2":105,"y2":1032,"stroke":90,"style":69},"300","104",[71,1034],{"points":1035,"fill":130},"166,106 178,100 176,110",[55,1037,1041],{"x":1038,"y":1039,"style":1040},"212","86","font-size:11.5px;font-weight:700;fill:var(--teal,#0f9488)","yes",[62,1043],{"x1":1044,"y1":65,"x2":1045,"y2":1032,"stroke":1046,"style":69},"440","560","var(--gold,#b4740a)",[71,1048],{"points":1049,"fill":1050},"564,106 552,100 554,110","#8a5808",[55,1052,1055],{"x":1053,"y":1039,"style":1054},"520","font-size:11.5px;font-weight:700;fill:var(--gold,#b4740a)","no",[39,1057],{"x":1058,"y":1059,"width":86,"height":1060,"rx":88,"fill":89,"stroke":90},"24","108","60",[55,1062,1065],{"x":1063,"y":1064,"style":96},"169","133","freeze directly",[55,1067,1069],{"x":1063,"y":1068,"style":101},"155","two handles, copy, save",[39,1071],{"x":1072,"y":1059,"width":86,"height":1060,"rx":88,"fill":1073,"stroke":1046},"424","#fdefd8",[55,1075,1078],{"x":1076,"y":1064,"style":1077},"569","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","recalculate first",[55,1080,1081],{"x":1076,"y":1068,"style":101},"soffice --headless --convert-to",[62,1083],{"x1":1076,"y1":1084,"x2":1076,"y2":1085,"stroke":68,"style":1086},"172","196","stroke-width:1.5px;stroke-dasharray:4 3",[71,1088],{"points":1089,"fill":74},"569,200 563,188 575,188",[39,1091],{"x":954,"y":49,"width":1092,"height":1093,"rx":51,"fill":109,"stroke":53},"380","42",[55,1095,1098],{"x":57,"y":1096,"style":1097},"226","font-size:12px;fill:var(--text,#172033);text-anchor:middle","Own the maths? Compute in pandas and skip formulas entirely.",[62,1100],{"x1":1063,"y1":1084,"x2":1063,"y2":1085,"stroke":68,"style":1086},[71,1102],{"points":1103,"fill":74},"169,200 163,188 175,188",[10,1105,1106],{},"The third box is the one to prefer when your code owns the calculation. Producing values in pandas and writing literals means there is never a cache to manage, no LibreOffice dependency, and no possibility of a reader's Excel version disagreeing with yours.",[143,1108,1110],{"id":1109},"keep-an-audit-trail-of-what-was-frozen","Keep an audit trail of what was frozen",[10,1112,1113],{},"Once the formula is gone, nobody can see how a number was produced. Copy the formula text into a hidden column or a comment before overwriting, so the derivation survives:",[148,1115,1117],{"className":188,"code":1116,"language":190,"meta":153,"style":153},"from openpyxl import load_workbook\nfrom openpyxl.comments import Comment\n\nvalues = load_workbook(\"report.xlsx\", data_only=True)\nbook = load_workbook(\"report.xlsx\")\n\nfor sheet in book.sheetnames:\n    v_ws, b_ws = values[sheet], book[sheet]\n    for row in b_ws.iter_rows():\n        for cell in row:\n            if isinstance(cell.value, str) and cell.value.startswith(\"=\"):\n                formula, cached = cell.value, v_ws[cell.coordinate].value\n                if cached is None:\n                    continue\n                cell.value = cached\n                cell.comment = Comment(f\"Was: {formula}\", \"report job\")\n\nbook.save(\"report_frozen_audited.xlsx\")\n",[155,1118,1119,1129,1141,1145,1165,1177,1181,1191,1199,1209,1219,1239,1249,1261,1266,1274,1305,1309],{"__ignoreMap":153},[158,1120,1121,1123,1125,1127],{"class":62,"line":160},[158,1122,198],{"class":197},[158,1124,202],{"class":201},[158,1126,205],{"class":197},[158,1128,208],{"class":201},[158,1130,1131,1133,1136,1138],{"class":62,"line":211},[158,1132,198],{"class":197},[158,1134,1135],{"class":201}," openpyxl.comments ",[158,1137,205],{"class":197},[158,1139,1140],{"class":201}," Comment\n",[158,1142,1143],{"class":62,"line":218},[158,1144,215],{"emptyLinePlaceholder":214},[158,1146,1147,1149,1151,1153,1155,1157,1159,1161,1163],{"class":62,"line":231},[158,1148,250],{"class":201},[158,1150,253],{"class":197},[158,1152,256],{"class":201},[158,1154,810],{"class":167},[158,1156,261],{"class":201},[158,1158,265],{"class":264},[158,1160,253],{"class":197},[158,1162,270],{"class":221},[158,1164,434],{"class":201},[158,1166,1167,1169,1171,1173,1175],{"class":62,"line":242},[158,1168,283],{"class":201},[158,1170,253],{"class":197},[158,1172,256],{"class":201},[158,1174,810],{"class":167},[158,1176,434],{"class":201},[158,1178,1179],{"class":62,"line":247},[158,1180,215],{"emptyLinePlaceholder":214},[158,1182,1183,1185,1187,1189],{"class":62,"line":280},[158,1184,317],{"class":197},[158,1186,320],{"class":201},[158,1188,323],{"class":197},[158,1190,326],{"class":201},[158,1192,1193,1195,1197],{"class":62,"line":298},[158,1194,332],{"class":201},[158,1196,253],{"class":197},[158,1198,337],{"class":201},[158,1200,1201,1203,1205,1207],{"class":62,"line":303},[158,1202,343],{"class":197},[158,1204,346],{"class":201},[158,1206,323],{"class":197},[158,1208,351],{"class":201},[158,1210,1211,1213,1215,1217],{"class":62,"line":314},[158,1212,357],{"class":197},[158,1214,360],{"class":201},[158,1216,323],{"class":197},[158,1218,365],{"class":201},[158,1220,1221,1223,1225,1227,1229,1231,1233,1235,1237],{"class":62,"line":329},[158,1222,371],{"class":197},[158,1224,374],{"class":221},[158,1226,377],{"class":201},[158,1228,380],{"class":221},[158,1230,383],{"class":201},[158,1232,386],{"class":197},[158,1234,389],{"class":201},[158,1236,392],{"class":167},[158,1238,395],{"class":201},[158,1240,1241,1244,1246],{"class":62,"line":340},[158,1242,1243],{"class":201},"                formula, cached ",[158,1245,253],{"class":197},[158,1247,1248],{"class":201}," cell.value, v_ws[cell.coordinate].value\n",[158,1250,1251,1253,1255,1257,1259],{"class":62,"line":354},[158,1252,618],{"class":197},[158,1254,651],{"class":201},[158,1256,654],{"class":197},[158,1258,657],{"class":221},[158,1260,660],{"class":201},[158,1262,1263],{"class":62,"line":368},[158,1264,1265],{"class":197},"                    continue\n",[158,1267,1268,1270,1272],{"class":62,"line":398},[158,1269,401],{"class":201},[158,1271,253],{"class":197},[158,1273,707],{"class":201},[158,1275,1276,1279,1281,1284,1286,1289,1291,1294,1296,1298,1300,1303],{"class":62,"line":409},[158,1277,1278],{"class":201},"                cell.comment ",[158,1280,253],{"class":197},[158,1282,1283],{"class":201}," Comment(",[158,1285,446],{"class":197},[158,1287,1288],{"class":167},"\"Was: ",[158,1290,453],{"class":452},[158,1292,1293],{"class":201},"formula",[158,1295,459],{"class":452},[158,1297,471],{"class":167},[158,1299,261],{"class":201},[158,1301,1302],{"class":167},"\"report job\"",[158,1304,434],{"class":201},[158,1306,1307],{"class":62,"line":421},[158,1308,215],{"emptyLinePlaceholder":214},[158,1310,1311,1313,1316],{"class":62,"line":426},[158,1312,429],{"class":201},[158,1314,1315],{"class":167},"\"report_frozen_audited.xlsx\"",[158,1317,434],{"class":201},[10,1319,1320],{},"Comments travel with the cell and stay out of the way visually. For a regulated report, writing the formula inventory to a separate log file alongside the delivered workbook is usually the better record — it is diffable, and it does not bloat the file a reader downloads.",[20,1322,29,1328,29,1331,29,1334,29,1337,29,1341,29,1345,29,1350,29,1353,29,1357,29,1362,29,1365,29,1370,29,1374,29,1377,29,1382,29,1386,29,1392,29,1398,29,1402],{"viewBox":1323,"role":23,"ariaLabelledBy":1324,"xmlns":27,"style":1327},"0 0 720 196",[1325,1326],"fv-audit-t","fv-audit-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1329,1330],{"id":1325},"A frozen cell that still carries its derivation",[35,1332,1333],{"id":1326},"Before freezing, the cell shows the formula SUM D2 to D9. After freezing it shows the number 1284.60 with a cell comment reading was SUM D2 to D9, so the derivation is still available to a reader.",[39,1335],{"x":41,"y":41,"width":1336,"height":1085,"fill":44},"720",[55,1338,1340],{"x":105,"y":84,"style":1339},"font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[39,1342],{"x":1343,"y":50,"width":1344,"height":1060,"rx":51,"fill":52,"stroke":110,"style":91},"40","260",[55,1346,1349],{"x":105,"y":1347,"style":1348},"80","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,Menlo,monospace","=SUM(D2:D9)",[55,1351,1352],{"x":105,"y":95,"style":101},"the reader sees a number,",[55,1354,1356],{"x":105,"y":1355,"style":101},"146","the file holds an instruction",[62,1358],{"x1":1359,"y1":1360,"x2":1361,"y2":1360,"stroke":90,"style":91},"308","74","356",[71,1363],{"points":1364,"fill":130},"360,74 348,68 348,80",[55,1366,1369],{"x":1367,"y":1060,"style":1368},"334","font-size:11px;fill:var(--teal,#0f9488);text-anchor:middle","freeze",[55,1371,1373],{"x":1372,"y":84,"style":1339},"530","after",[39,1375],{"x":1376,"y":50,"width":1031,"height":1060,"rx":51,"fill":89,"stroke":90,"style":91},"366",[55,1378,1381],{"x":1379,"y":1347,"style":1380},"516","font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle;font-family:ui-monospace,Menlo,monospace","1284.60",[71,1383],{"points":1384,"fill":1385},"666,44 666,58 652,44","#d81b73",[39,1387],{"x":1388,"y":1389,"width":1019,"height":1390,"rx":1391,"fill":1073,"stroke":1046},"416","116","56","8",[55,1393,1397],{"x":1394,"y":1395,"style":1396},"541","140","font-size:11.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","comment on the cell",[55,1399,1401],{"x":1394,"y":1400,"style":101},"160","\"Was: =SUM(D2:D9)\"",[62,1403],{"x1":1404,"y1":1032,"x2":1405,"y2":1406,"stroke":1046,"style":69},"655","600","114",[143,1408,1410],{"id":1409},"freeze-only-part-of-a-workbook","Freeze only part of a workbook",[10,1412,1413],{},"Freezing everything is rarely required. A common pattern keeps the interactive model on one sheet and freezes the sheet that people copy into presentations:",[148,1415,1417],{"className":188,"code":1416,"language":190,"meta":153,"style":153},"from openpyxl import load_workbook\n\nFREEZE_SHEETS = {\"Summary\", \"Board Pack\"}\n\nvalues = load_workbook(\"report.xlsx\", data_only=True)\nbook = load_workbook(\"report.xlsx\")\n\nfor sheet in FREEZE_SHEETS & set(book.sheetnames):\n    v_ws, b_ws = values[sheet], book[sheet]\n    for row in b_ws.iter_rows():\n        for cell in row:\n            if isinstance(cell.value, str) and cell.value.startswith(\"=\"):\n                cell.value = v_ws[cell.coordinate].value\n\nbook.save(\"report_partial_freeze.xlsx\")\n",[155,1418,1419,1429,1433,1454,1458,1478,1490,1494,1514,1522,1532,1542,1562,1570,1574],{"__ignoreMap":153},[158,1420,1421,1423,1425,1427],{"class":62,"line":160},[158,1422,198],{"class":197},[158,1424,202],{"class":201},[158,1426,205],{"class":197},[158,1428,208],{"class":201},[158,1430,1431],{"class":62,"line":211},[158,1432,215],{"emptyLinePlaceholder":214},[158,1434,1435,1438,1440,1443,1446,1448,1451],{"class":62,"line":218},[158,1436,1437],{"class":221},"FREEZE_SHEETS",[158,1439,225],{"class":197},[158,1441,1442],{"class":201}," {",[158,1444,1445],{"class":167},"\"Summary\"",[158,1447,261],{"class":201},[158,1449,1450],{"class":167},"\"Board Pack\"",[158,1452,1453],{"class":201},"}\n",[158,1455,1456],{"class":62,"line":231},[158,1457,215],{"emptyLinePlaceholder":214},[158,1459,1460,1462,1464,1466,1468,1470,1472,1474,1476],{"class":62,"line":242},[158,1461,250],{"class":201},[158,1463,253],{"class":197},[158,1465,256],{"class":201},[158,1467,810],{"class":167},[158,1469,261],{"class":201},[158,1471,265],{"class":264},[158,1473,253],{"class":197},[158,1475,270],{"class":221},[158,1477,434],{"class":201},[158,1479,1480,1482,1484,1486,1488],{"class":62,"line":247},[158,1481,283],{"class":201},[158,1483,253],{"class":197},[158,1485,256],{"class":201},[158,1487,810],{"class":167},[158,1489,434],{"class":201},[158,1491,1492],{"class":62,"line":280},[158,1493,215],{"emptyLinePlaceholder":214},[158,1495,1496,1498,1500,1502,1505,1508,1511],{"class":62,"line":298},[158,1497,317],{"class":197},[158,1499,320],{"class":201},[158,1501,323],{"class":197},[158,1503,1504],{"class":221}," FREEZE_SHEETS",[158,1506,1507],{"class":197}," &",[158,1509,1510],{"class":221}," set",[158,1512,1513],{"class":201},"(book.sheetnames):\n",[158,1515,1516,1518,1520],{"class":62,"line":303},[158,1517,332],{"class":201},[158,1519,253],{"class":197},[158,1521,337],{"class":201},[158,1523,1524,1526,1528,1530],{"class":62,"line":314},[158,1525,343],{"class":197},[158,1527,346],{"class":201},[158,1529,323],{"class":197},[158,1531,351],{"class":201},[158,1533,1534,1536,1538,1540],{"class":62,"line":329},[158,1535,357],{"class":197},[158,1537,360],{"class":201},[158,1539,323],{"class":197},[158,1541,365],{"class":201},[158,1543,1544,1546,1548,1550,1552,1554,1556,1558,1560],{"class":62,"line":340},[158,1545,371],{"class":197},[158,1547,374],{"class":221},[158,1549,377],{"class":201},[158,1551,380],{"class":221},[158,1553,383],{"class":201},[158,1555,386],{"class":197},[158,1557,389],{"class":201},[158,1559,392],{"class":167},[158,1561,395],{"class":201},[158,1563,1564,1566,1568],{"class":62,"line":354},[158,1565,401],{"class":201},[158,1567,253],{"class":197},[158,1569,406],{"class":201},[158,1571,1572],{"class":62,"line":368},[158,1573,215],{"emptyLinePlaceholder":214},[158,1575,1576,1578,1581],{"class":62,"line":398},[158,1577,429],{"class":201},[158,1579,1580],{"class":167},"\"report_partial_freeze.xlsx\"",[158,1582,434],{"class":201},[10,1584,1585],{},"Because the detail sheets keep their formulas, a reader can still trace a figure by following the summary's original layout — while the summary itself can no longer drift.",[143,1587,1589],{"id":1588},"verify-the-frozen-file-before-you-send-it","Verify the frozen file before you send it",[10,1591,1592],{},"A frozen report should contain no formulas at all, and every number that mattered should still be there. Two assertions catch both mistakes:",[148,1594,1596],{"className":188,"code":1595,"language":190,"meta":153,"style":153},"from openpyxl import load_workbook\n\nfrozen = load_workbook(\"report_frozen.xlsx\")\nleftovers = [\n    f\"{ws.title}!{cell.coordinate}\"\n    for ws in frozen.worksheets\n    for row in ws.iter_rows()\n    for cell in row\n    if isinstance(cell.value, str) and cell.value.startswith(\"=\")\n]\nassert not leftovers, f\"still holds formulas: {leftovers[:5]}\"\n\noriginal = load_workbook(\"report.xlsx\", data_only=True)\nfor ws in frozen.worksheets:\n    src = original[ws.title]\n    for row in ws.iter_rows():\n        for cell in row:\n            if cell.value is not None:\n                assert cell.value == src[cell.coordinate].value, cell.coordinate\n\nprint(\"frozen file matches the cached values it came from\")\n",[155,1597,1598,1608,1612,1624,1634,1659,1671,1682,1693,1713,1718,1748,1752,1773,1784,1794,1805,1815,1830,1843,1847],{"__ignoreMap":153},[158,1599,1600,1602,1604,1606],{"class":62,"line":160},[158,1601,198],{"class":197},[158,1603,202],{"class":201},[158,1605,205],{"class":197},[158,1607,208],{"class":201},[158,1609,1610],{"class":62,"line":211},[158,1611,215],{"emptyLinePlaceholder":214},[158,1613,1614,1616,1618,1620,1622],{"class":62,"line":218},[158,1615,306],{"class":201},[158,1617,253],{"class":197},[158,1619,256],{"class":201},[158,1621,815],{"class":167},[158,1623,434],{"class":201},[158,1625,1626,1629,1631],{"class":62,"line":231},[158,1627,1628],{"class":201},"leftovers ",[158,1630,253],{"class":197},[158,1632,1633],{"class":201}," [\n",[158,1635,1636,1639,1641,1643,1646,1648,1650,1652,1654,1656],{"class":62,"line":242},[158,1637,1638],{"class":197},"    f",[158,1640,471],{"class":167},[158,1642,453],{"class":452},[158,1644,1645],{"class":201},"ws.title",[158,1647,459],{"class":452},[158,1649,679],{"class":167},[158,1651,453],{"class":452},[158,1653,684],{"class":201},[158,1655,459],{"class":452},[158,1657,1658],{"class":167},"\"\n",[158,1660,1661,1663,1666,1668],{"class":62,"line":247},[158,1662,343],{"class":197},[158,1664,1665],{"class":201}," ws ",[158,1667,323],{"class":197},[158,1669,1670],{"class":201}," frozen.worksheets\n",[158,1672,1673,1675,1677,1679],{"class":62,"line":280},[158,1674,343],{"class":197},[158,1676,346],{"class":201},[158,1678,323],{"class":197},[158,1680,1681],{"class":201}," ws.iter_rows()\n",[158,1683,1684,1686,1688,1690],{"class":62,"line":298},[158,1685,343],{"class":197},[158,1687,360],{"class":201},[158,1689,323],{"class":197},[158,1691,1692],{"class":201}," row\n",[158,1694,1695,1697,1699,1701,1703,1705,1707,1709,1711],{"class":62,"line":303},[158,1696,716],{"class":197},[158,1698,374],{"class":221},[158,1700,377],{"class":201},[158,1702,380],{"class":221},[158,1704,383],{"class":201},[158,1706,386],{"class":197},[158,1708,389],{"class":201},[158,1710,392],{"class":167},[158,1712,434],{"class":201},[158,1714,1715],{"class":62,"line":314},[158,1716,1717],{"class":201},"]\n",[158,1719,1720,1723,1726,1729,1731,1734,1736,1739,1742,1744,1746],{"class":62,"line":329},[158,1721,1722],{"class":197},"assert",[158,1724,1725],{"class":197}," not",[158,1727,1728],{"class":201}," leftovers, ",[158,1730,446],{"class":197},[158,1732,1733],{"class":167},"\"still holds formulas: ",[158,1735,453],{"class":452},[158,1737,1738],{"class":201},"leftovers[:",[158,1740,1741],{"class":221},"5",[158,1743,770],{"class":201},[158,1745,459],{"class":452},[158,1747,1658],{"class":167},[158,1749,1750],{"class":62,"line":340},[158,1751,215],{"emptyLinePlaceholder":214},[158,1753,1754,1757,1759,1761,1763,1765,1767,1769,1771],{"class":62,"line":354},[158,1755,1756],{"class":201},"original ",[158,1758,253],{"class":197},[158,1760,256],{"class":201},[158,1762,810],{"class":167},[158,1764,261],{"class":201},[158,1766,265],{"class":264},[158,1768,253],{"class":197},[158,1770,270],{"class":221},[158,1772,434],{"class":201},[158,1774,1775,1777,1779,1781],{"class":62,"line":368},[158,1776,317],{"class":197},[158,1778,1665],{"class":201},[158,1780,323],{"class":197},[158,1782,1783],{"class":201}," frozen.worksheets:\n",[158,1785,1786,1789,1791],{"class":62,"line":398},[158,1787,1788],{"class":201},"    src ",[158,1790,253],{"class":197},[158,1792,1793],{"class":201}," original[ws.title]\n",[158,1795,1796,1798,1800,1802],{"class":62,"line":409},[158,1797,343],{"class":197},[158,1799,346],{"class":201},[158,1801,323],{"class":197},[158,1803,1804],{"class":201}," ws.iter_rows():\n",[158,1806,1807,1809,1811,1813],{"class":62,"line":421},[158,1808,357],{"class":197},[158,1810,360],{"class":201},[158,1812,323],{"class":197},[158,1814,365],{"class":201},[158,1816,1817,1819,1822,1824,1826,1828],{"class":62,"line":426},[158,1818,371],{"class":197},[158,1820,1821],{"class":201}," cell.value ",[158,1823,654],{"class":197},[158,1825,1725],{"class":197},[158,1827,657],{"class":221},[158,1829,660],{"class":201},[158,1831,1832,1835,1837,1840],{"class":62,"line":437},[158,1833,1834],{"class":197},"                assert",[158,1836,1821],{"class":201},[158,1838,1839],{"class":197},"==",[158,1841,1842],{"class":201}," src[cell.coordinate].value, cell.coordinate\n",[158,1844,1845],{"class":62,"line":722},[158,1846,215],{"emptyLinePlaceholder":214},[158,1848,1849,1851,1853,1856],{"class":62,"line":734},[158,1850,440],{"class":221},[158,1852,443],{"class":201},[158,1854,1855],{"class":167},"\"frozen file matches the cached values it came from\"",[158,1857,434],{"class":201},[10,1859,1860],{},"Run this as the final step of the job that produces the file. It takes seconds, it fails loudly when a sheet was skipped, and it turns \"the freeze worked on my machine\" into something the pipeline can prove on every run.",[143,1862,1864],{"id":1863},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1866,1867,1868,1884],"table",{},[1869,1870,1871],"thead",{},[1872,1873,1874,1878,1881],"tr",{},[1875,1876,1877],"th",{},"Symptom",[1875,1879,1880],{},"Cause",[1875,1882,1883],{},"Fix",[1885,1886,1887,1899,1913,1928,1945,1956],"tbody",{},[1872,1888,1889,1893,1896],{},[1890,1891,1892],"td",{},"Frozen cells are blank",[1890,1894,1895],{},"No cached results in the source",[1890,1897,1898],{},"Recalculate with Excel or LibreOffice first",[1872,1900,1901,1904,1910],{},[1890,1902,1903],{},"Styling lost",[1890,1905,1906,1907,1909],{},"Values were written into the ",[155,1908,265],{}," handle and saved without the second load",[1890,1911,1912],{},"Write into the default handle instead",[1872,1914,1915,1918,1921],{},[1890,1916,1917],{},"Numbers frozen from stale inputs",[1890,1919,1920],{},"Cache predates the last edit to source cells",[1890,1922,1923,1924,1927],{},"Recalculate, or set ",[155,1925,1926],{},"fullCalcOnLoad"," before the round trip",[1872,1929,1930,1935,1938],{},[1890,1931,1932,1934],{},[155,1933,177],{}," returns instantly, no output",[1890,1936,1937],{},"A desktop LibreOffice session is using the profile",[1890,1939,1940,1941,1944],{},"Close it, or pass a dedicated ",[155,1942,1943],{},"-env:UserInstallation"," profile",[1872,1946,1947,1950,1953],{},[1890,1948,1949],{},"Dates become numbers",[1890,1951,1952],{},"The cached value is a serial and the cell's format was General",[1890,1954,1955],{},"Assign the date format after freezing",[1872,1957,1958,1961,1964],{},[1890,1959,1960],{},"Original file overwritten",[1890,1962,1963],{},"Wrote back to the source path",[1890,1965,1966],{},"Always save the frozen copy to a new filename",[143,1968,1970],{"id":1969},"performance-and-scale-notes","Performance and scale notes",[10,1972,1973,1974,1977,1978,1981],{},"The conversion is two full parses plus one write, so on a 50 MB workbook expect it to be dominated by I\u002FO rather than by the loop. Iterating with ",[155,1975,1976],{},"ws.iter_rows()"," is already the efficient access pattern; avoid ",[155,1979,1980],{},"ws.cell(...)"," lookups in a nested range loop, which re-resolve coordinates one at a time. Read-only mode cannot be used for the writable handle, so peak memory is roughly the size of the workbook in object form — for very large files, freeze sheet by sheet and save between sheets, or generate the frozen report from the source data instead of converting a finished workbook.",[143,1983,1985],{"id":1984},"conclusion","Conclusion",[10,1987,1988,1989,1992],{},"Freezing formulas is a copy from one workbook handle to another: ",[155,1990,1991],{},"data_only=True"," supplies the numbers, the ordinary handle supplies everything else, and saving to a new file keeps the model available. The whole technique rests on there being cached results to copy, so check for them and either recalculate through LibreOffice or, better, compute the figures in pandas and never depend on a cache at all.",[143,1994,1996],{"id":1995},"frequently-asked-questions","Frequently asked questions",[10,1998,1999,2003],{},[2000,2001,2002],"strong",{},"Why do my frozen cells come out empty?","\nThe workbook had no cached results to freeze. Recalculate it once — in Excel, or headlessly with LibreOffice — before running the conversion.",[10,2005,2006,2009,2010,2012],{},[2000,2007,2008],{},"Does freezing lose my formatting?","\nNot if you load the file twice and write the values into the ordinary handle. Saving a workbook loaded with ",[155,2011,1991],{}," keeps styles too, but gives you no chance to check for missing values first.",[10,2014,2015,2018],{},[2000,2016,2017],{},"Can I freeze only one sheet?","\nYes. Iterate the sheets you want and leave the others untouched — the conversion is per cell, so any subset works.",[10,2020,2021,2024],{},[2000,2022,2023],{},"Is there a way to keep the formula visible for reference?","\nWrite the formula text into a neighbouring column or a cell comment before overwriting the cell, so auditors can still see how the number was derived.",[143,2026,2028],{"id":2027},"related","Related",[10,2030,2031],{},"Up to the parent guide:",[2033,2034,2035],"ul",{},[2036,2037,2038,2040],"li",{},[14,2039,17],{"href":16}," — the full formula workflow.",[10,2042,2043],{},"Related guides:",[2033,2045,2046,2053,2060,2067],{},[2036,2047,2048,2052],{},[14,2049,2051],{"href":2050},"\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"," — the cache this conversion depends on.",[2036,2054,2055,2059],{},[14,2056,2058],{"href":2057},"\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"," — putting the formulas there in the first place.",[2036,2061,2062,2066],{},[14,2063,2065],{"href":2064},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python\u002F","Convert an Excel File to PDF with Python"," — the other common way to send an uneditable report.",[2036,2068,2069,2073],{},[14,2070,2072],{"href":2071},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fpopulate-excel-template-without-losing-formatting\u002F","Populate an Excel Template Without Losing Formatting"," — keeping styles through a scripted edit.",[2075,2076,2077],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":153,"searchDepth":211,"depth":211,"links":2079},[2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091],{"id":145,"depth":211,"text":146},{"id":181,"depth":211,"text":182},{"id":492,"depth":211,"text":493},{"id":823,"depth":211,"text":824},{"id":1109,"depth":211,"text":1110},{"id":1409,"depth":211,"text":1410},{"id":1588,"depth":211,"text":1589},{"id":1863,"depth":211,"text":1864},{"id":1969,"depth":211,"text":1970},{"id":1984,"depth":211,"text":1985},{"id":1995,"depth":211,"text":1996},{"id":2027,"depth":211,"text":2028},"2026-08-01","Freeze a workbook's formulas into static values with openpyxl while keeping every style, plus the LibreOffice recalculation step for files Excel has never opened.","md",[2096,2098,2100,2102],{"q":2002,"a":2097},"The workbook had no cached results to freeze. Recalculate it once — in Excel, or headlessly with LibreOffice — before running the conversion.",{"q":2008,"a":2099},"Not if you load the file twice and write the values into the ordinary handle. Saving a workbook loaded with data_only=True keeps styles too, but gives you no chance to check for missing values first.",{"q":2017,"a":2101},"Yes. Iterate the sheets you want and leave the others untouched — the conversion is per cell, so any subset works.",{"q":2023,"a":2103},"Write the formula text into a neighbouring column or a cell comment before overwriting the cell, so auditors can still see how the number was derived.",{"breadcrumb":2105},[2106,2108,2111,2112],{"name":2107,"item":971},"Home",{"name":2109,"item":2110},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},{"name":5,"item":2113},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python\u002F","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python",{"title":5,"description":2116},"Replace formulas with their results using openpyxl's data_only cache, preserve formatting with a two-handle load, and recalculate headlessly with LibreOffice when no cache exists.","convert-excel-formulas-to-values-with-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python\u002Findex","how-to","Bk0WioRB7b29-Tu-k3pa8t2VinlY7aei9988K4zkBJE",[2122,2126],{"title":2123,"path":2124,"stem":2125,"children":-1},"Add SUM and AVERAGE Formulas with Python","\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",{"title":2127,"path":2128,"stem":2129,"children":-1},"Copy a Formula Down a Column in openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl\u002Findex",1785584462714]