[{"data":1,"prerenderedAt":2485},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python":2452,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python":2459,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python":2464,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python":2465},{"id":4,"title":5,"body":6,"dateModified":2417,"datePublished":2417,"description":2418,"extension":2419,"faq":2420,"keyword":2429,"kind":2430,"meta":2431,"navigation":345,"path":2440,"seo":2441,"slug":2444,"stem":2445,"topics":2446,"type":2450,"__hash__":2451},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python\u002Findex.md","Write Dynamic Array Formulas like FILTER and UNIQUE from Python",{"type":7,"value":8,"toc":2404},"minimark",[9,41,279,284,312,315,496,500,503,675,678,731,899,903,910,1189,1214,1218,1221,1489,1526,1625,1629,1639,1780,1791,1795,1798,2245,2251,2255,2308,2312,2315,2319,2339,2343,2349,2355,2361,2367,2371,2400],[10,11,12,13,17,18,21,22,26,27,30,31,34,35,40],"p",{},"Dynamic array formulas changed how Excel works: ",[14,15,16],"code",{},"=UNIQUE(A2:A500)"," or ",[14,19,20],{},"=FILTER(Data, Data[Amount]>1000)"," in one cell ",[23,24,25],"em",{},"spills"," its results into as many cells as it needs, and the output resizes itself when the data changes. They are ideal for reports that Python builds and people then explore — the script writes the data and one formula, and Excel keeps the derived list current. Writing them from Python has one catch: a formula that is perfectly valid when typed into Excel can open as ",[14,28,29],{},"#NAME?"," or with a stray ",[14,32,33],{},"@"," when written by a library. This guide, part of ",[36,37,39],"a",{"href":38},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", explains why and how to write them so they spill.",[42,43,51,52,51,56,51,60,51,68,51,76,51,80,51,87,51,92,51,97,51,101,51,107,51,111,51,115,51,118,51,122,51,126,51,130,51,133,51,138,51,141,51,145,51,147,51,150,51,152,51,154,51,157,51,160,51,164,51,166,51,170,51,172,51,175,51,177,51,181,51,185,51,188,51,192,51,194,51,197,51,199,51,202,51,204,51,208,51,211,51,214,51,218,51,220,51,222,51,224,51,227,51,229,51,231,51,233,51,236,51,240,51,242,51,244,51,246,51,249,51,251,51,253,51,256,51,260,51,262,51,264,51,266,51,269,51,271,51,273],"svg",{"viewBox":44,"role":45,"ariaLabelledBy":46,"xmlns":49,"style":50},"102 0 555 278","img",[47,48],"dyn-spill-t","dyn-spill-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:555px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[53,54,55],"title",{"id":47},"A UNIQUE formula in one cell spilling into the cells below",[57,58,59],"desc",{"id":48},"The formula =SORT(UNIQUE(A2:A6)) is written only in cell D2. When the workbook opens in Excel, the sorted unique regions East, North and South fill D2, D3 and D4 automatically.",[61,62],"rect",{"x":63,"y":64,"width":65,"height":66,"fill":67},"102","0","555","278","#ffffff",[61,69],{"x":70,"y":71,"width":72,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},"243.0","16.0","274.1","28.0","var(--line,#cdd5e6)","stroke-width:1px",[61,77],{"x":70,"y":71,"width":78,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"40.0","#e7ebef",[81,82,86],"text",{"x":83,"y":84,"style":85},"263.0","35.0","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","fx",[81,88,91],{"x":89,"y":84,"style":90},"293.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=SORT(UNIQUE(A2:A6))",[61,93],{"x":70,"y":94,"width":95,"height":96,"rx":64,"fill":79,"stroke":74,"style":75},"44.0","32.0","24.0",[61,98],{"x":99,"y":94,"width":100,"height":96,"rx":64,"fill":79,"stroke":74,"style":75},"275.0","66.7",[81,102,106],{"x":103,"y":104,"style":105},"308.3","61.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[61,108],{"x":109,"y":94,"width":110,"height":96,"rx":64,"fill":79,"stroke":74,"style":75},"341.7","57.6",[81,112,114],{"x":113,"y":104,"style":105},"370.5","B",[61,116],{"x":117,"y":94,"width":94,"height":96,"rx":64,"fill":79,"stroke":74,"style":75},"399.2",[81,119,121],{"x":120,"y":104,"style":105},"421.2","C",[61,123],{"x":124,"y":94,"width":125,"height":96,"rx":64,"fill":79,"stroke":74,"style":75},"443.2","73.8",[81,127,129],{"x":128,"y":104,"style":105},"480.1","D",[61,131],{"x":70,"y":132,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"68.0",[81,134,137],{"x":135,"y":136,"style":105},"259.0","87.0","1",[61,139],{"x":99,"y":132,"width":100,"height":73,"rx":64,"fill":79,"stroke":74,"style":140},"stroke-width:1.5px",[81,142,144],{"x":103,"y":136,"style":143},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Region",[61,146],{"x":109,"y":132,"width":110,"height":73,"rx":64,"fill":79,"stroke":74,"style":140},[81,148,149],{"x":113,"y":136,"style":143},"Sales",[61,151],{"x":117,"y":132,"width":94,"height":73,"rx":64,"fill":79,"stroke":74,"style":140},[61,153],{"x":124,"y":132,"width":125,"height":73,"rx":64,"fill":79,"stroke":74,"style":140},[81,155,156],{"x":128,"y":136,"style":143},"Regions",[61,158],{"x":70,"y":159,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"96.0",[81,161,163],{"x":135,"y":162,"style":105},"115.0","2",[61,165],{"x":99,"y":159,"width":100,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,167,169],{"x":103,"y":162,"style":168},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","North",[61,171],{"x":109,"y":159,"width":110,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,173,174],{"x":113,"y":162,"style":168},"10",[61,176],{"x":117,"y":159,"width":94,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,178],{"x":124,"y":159,"width":125,"height":73,"rx":64,"fill":179,"stroke":180,"style":140},"#f0f4ff","var(--brand,#5b5cf0)",[81,182,184],{"x":128,"y":162,"style":183},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","East",[61,186],{"x":70,"y":187,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"124.0",[81,189,191],{"x":135,"y":190,"style":105},"143.0","3",[61,193],{"x":99,"y":187,"width":100,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,195,196],{"x":103,"y":190,"style":168},"South",[61,198],{"x":109,"y":187,"width":110,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,200,201],{"x":113,"y":190,"style":168},"20",[61,203],{"x":117,"y":187,"width":94,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,205],{"x":124,"y":187,"width":125,"height":73,"rx":64,"fill":206,"stroke":207,"style":140},"#d9f4f1","var(--teal,#0f9488)",[81,209,169],{"x":128,"y":190,"style":210},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[61,212],{"x":70,"y":213,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"152.0",[81,215,217],{"x":135,"y":216,"style":105},"171.0","4",[61,219],{"x":99,"y":213,"width":100,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,221,169],{"x":103,"y":216,"style":168},[61,223],{"x":109,"y":213,"width":110,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,225,226],{"x":113,"y":216,"style":168},"30",[61,228],{"x":117,"y":213,"width":94,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,230],{"x":124,"y":213,"width":125,"height":73,"rx":64,"fill":206,"stroke":207,"style":140},[81,232,196],{"x":128,"y":216,"style":210},[61,234],{"x":70,"y":235,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"180.0",[81,237,239],{"x":135,"y":238,"style":105},"199.0","5",[61,241],{"x":99,"y":235,"width":100,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,243,184],{"x":103,"y":238,"style":168},[61,245],{"x":109,"y":235,"width":110,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,247,248],{"x":113,"y":238,"style":168},"40",[61,250],{"x":117,"y":235,"width":94,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,252],{"x":124,"y":235,"width":125,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,254],{"x":70,"y":255,"width":95,"height":73,"rx":64,"fill":79,"stroke":74,"style":75},"208.0",[81,257,259],{"x":135,"y":258,"style":105},"227.0","6",[61,261],{"x":99,"y":255,"width":100,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,263,196],{"x":103,"y":258,"style":168},[61,265],{"x":109,"y":255,"width":110,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,267,268],{"x":113,"y":258,"style":168},"15",[61,270],{"x":117,"y":255,"width":94,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[61,272],{"x":124,"y":255,"width":125,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},[81,274,278],{"x":275,"y":276,"style":277},"380.0","258.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","only D2 holds a formula; D3 and D4 are the spill range Excel fills when the file opens",[280,281,283],"h2",{"id":282},"prerequisites","Prerequisites",[285,286,291],"pre",{"className":287,"code":288,"language":289,"meta":290,"style":290},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl xlsxwriter\n","bash","",[14,292,293],{"__ignoreMap":290},[294,295,298,302,306,309],"span",{"class":296,"line":297},"line",1,[294,299,301],{"class":300},"sMTad","pip",[294,303,305],{"class":304},"srMev"," install",[294,307,308],{"class":304}," openpyxl",[294,310,311],{"class":304}," xlsxwriter\n",[10,313,314],{},"The examples inspect the saved files directly, so no copy of Excel is needed to follow along. An .xlsx file is a zip archive, and the formula text and its attributes are plain XML inside it:",[285,316,320],{"className":317,"code":318,"language":319,"meta":290,"style":290},"language-python shiki shiki-themes github-light github-dark-high-contrast","import re\nimport zipfile\n\ndef formula_xml(path: str, sheet: str = \"xl\u002Fworksheets\u002Fsheet1.xml\") -> list[str]:\n    xml = zipfile.ZipFile(path).read(sheet).decode()\n    return re.findall(r'\u003Cc r=\"[A-Z]+\\d+\"[^>]*>(?:(?!\u003C\u002Fc>).)*\u003Cf[^>]*>(?:(?!\u003C\u002Fc>).)*\u003C\u002Fc>', xml)\n","python",[14,321,322,332,340,347,383,395],{"__ignoreMap":290},[294,323,324,328],{"class":296,"line":297},[294,325,327],{"class":326},"s-kum","import",[294,329,331],{"class":330},"skGVy"," re\n",[294,333,335,337],{"class":296,"line":334},2,[294,336,327],{"class":326},[294,338,339],{"class":330}," zipfile\n",[294,341,343],{"class":296,"line":342},3,[294,344,346],{"emptyLinePlaceholder":345},true,"\n",[294,348,350,353,357,360,364,367,369,372,375,378,380],{"class":296,"line":349},4,[294,351,352],{"class":326},"def",[294,354,356],{"class":355},"s_Opv"," formula_xml",[294,358,359],{"class":330},"(path: ",[294,361,363],{"class":362},"sP0c6","str",[294,365,366],{"class":330},", sheet: ",[294,368,363],{"class":362},[294,370,371],{"class":326}," =",[294,373,374],{"class":304}," \"xl\u002Fworksheets\u002Fsheet1.xml\"",[294,376,377],{"class":330},") -> list[",[294,379,363],{"class":362},[294,381,382],{"class":330},"]:\n",[294,384,386,389,392],{"class":296,"line":385},5,[294,387,388],{"class":330},"    xml ",[294,390,391],{"class":326},"=",[294,393,394],{"class":330}," zipfile.ZipFile(path).read(sheet).decode()\n",[294,396,398,401,404,407,410,413,417,420,423,426,428,431,433,436,439,441,444,446,449,452,455,458,461,463,466,468,470,472,474,476,478,480,482,484,486,488,490,493],{"class":296,"line":397},6,[294,399,400],{"class":326},"    return",[294,402,403],{"class":330}," re.findall(",[294,405,406],{"class":326},"r",[294,408,409],{"class":304},"'\u003Cc r=\"",[294,411,412],{"class":362},"[",[294,414,416],{"class":415},"sSjpA","A-Z",[294,418,419],{"class":362},"]",[294,421,422],{"class":326},"+",[294,424,425],{"class":362},"\\d",[294,427,422],{"class":326},[294,429,430],{"class":304},"\"",[294,432,412],{"class":362},[294,434,435],{"class":326},"^",[294,437,438],{"class":415},">",[294,440,419],{"class":362},[294,442,443],{"class":326},"*",[294,445,438],{"class":304},[294,447,448],{"class":362},"(?:",[294,450,451],{"class":326},"(?!",[294,453,454],{"class":304},"\u003C\u002Fc>",[294,456,457],{"class":326},")",[294,459,460],{"class":362},".)",[294,462,443],{"class":326},[294,464,465],{"class":304},"\u003Cf",[294,467,412],{"class":362},[294,469,435],{"class":326},[294,471,438],{"class":415},[294,473,419],{"class":362},[294,475,443],{"class":326},[294,477,438],{"class":304},[294,479,448],{"class":362},[294,481,451],{"class":326},[294,483,454],{"class":304},[294,485,457],{"class":326},[294,487,460],{"class":362},[294,489,443],{"class":326},[294,491,492],{"class":304},"\u003C\u002Fc>'",[294,494,495],{"class":330},", xml)\n",[280,497,499],{"id":498},"step-1-see-what-openpyxl-writes","Step 1 — See what openpyxl writes",[10,501,502],{},"openpyxl stores formula text verbatim. Write a dynamic array formula the way you would type it and look at the result:",[285,504,506],{"className":317,"code":505,"language":319,"meta":290,"style":290},"from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nfor row in [[\"Region\", \"Sales\"], [\"North\", 10], [\"South\", 20], [\"North\", 30], [\"East\", 40], [\"South\", 15]]:\n    ws.append(row)\nws[\"D2\"] = \"=SORT(UNIQUE(A2:A6))\"\nwb.save(\"openpyxl_dynamic.xlsx\")\n\nprint(formula_xml(\"openpyxl_dynamic.xlsx\"))\n# ['\u003Cc r=\"D2\">\u003Cf>SORT(UNIQUE(A2:A6))\u003C\u002Ff>\u003Cv>\u003C\u002Fv>\u003C\u002Fc>']\n",[14,507,508,521,525,535,545,615,620,637,649,654,668],{"__ignoreMap":290},[294,509,510,513,516,518],{"class":296,"line":297},[294,511,512],{"class":326},"from",[294,514,515],{"class":330}," openpyxl ",[294,517,327],{"class":326},[294,519,520],{"class":330}," Workbook\n",[294,522,523],{"class":296,"line":334},[294,524,346],{"emptyLinePlaceholder":345},[294,526,527,530,532],{"class":296,"line":342},[294,528,529],{"class":330},"wb ",[294,531,391],{"class":326},[294,533,534],{"class":330}," Workbook()\n",[294,536,537,540,542],{"class":296,"line":349},[294,538,539],{"class":330},"ws ",[294,541,391],{"class":326},[294,543,544],{"class":330}," wb.active\n",[294,546,547,550,553,556,559,562,565,568,571,574,576,578,580,583,585,587,589,591,593,595,597,600,602,604,606,608,610,612],{"class":296,"line":385},[294,548,549],{"class":326},"for",[294,551,552],{"class":330}," row ",[294,554,555],{"class":326},"in",[294,557,558],{"class":330}," [[",[294,560,561],{"class":304},"\"Region\"",[294,563,564],{"class":330},", ",[294,566,567],{"class":304},"\"Sales\"",[294,569,570],{"class":330},"], [",[294,572,573],{"class":304},"\"North\"",[294,575,564],{"class":330},[294,577,174],{"class":362},[294,579,570],{"class":330},[294,581,582],{"class":304},"\"South\"",[294,584,564],{"class":330},[294,586,201],{"class":362},[294,588,570],{"class":330},[294,590,573],{"class":304},[294,592,564],{"class":330},[294,594,226],{"class":362},[294,596,570],{"class":330},[294,598,599],{"class":304},"\"East\"",[294,601,564],{"class":330},[294,603,248],{"class":362},[294,605,570],{"class":330},[294,607,582],{"class":304},[294,609,564],{"class":330},[294,611,268],{"class":362},[294,613,614],{"class":330},"]]:\n",[294,616,617],{"class":296,"line":397},[294,618,619],{"class":330},"    ws.append(row)\n",[294,621,623,626,629,632,634],{"class":296,"line":622},7,[294,624,625],{"class":330},"ws[",[294,627,628],{"class":304},"\"D2\"",[294,630,631],{"class":330},"] ",[294,633,391],{"class":326},[294,635,636],{"class":304}," \"=SORT(UNIQUE(A2:A6))\"\n",[294,638,640,643,646],{"class":296,"line":639},8,[294,641,642],{"class":330},"wb.save(",[294,644,645],{"class":304},"\"openpyxl_dynamic.xlsx\"",[294,647,648],{"class":330},")\n",[294,650,652],{"class":296,"line":651},9,[294,653,346],{"emptyLinePlaceholder":345},[294,655,657,660,663,665],{"class":296,"line":656},10,[294,658,659],{"class":362},"print",[294,661,662],{"class":330},"(formula_xml(",[294,664,645],{"class":304},[294,666,667],{"class":330},"))\n",[294,669,671],{"class":296,"line":670},11,[294,672,674],{"class":673},"s-wDw","# ['\u003Cc r=\"D2\">\u003Cf>SORT(UNIQUE(A2:A6))\u003C\u002Ff>\u003Cv>\u003C\u002Fv>\u003C\u002Fc>']\n",[10,676,677],{},"Two things are missing, and each causes a different symptom when Excel opens the file:",[679,680,681,714],"ul",{},[682,683,684,692,693,564,696,564,699,702,703,706,707,710,711,713],"li",{},[685,686,687,688,691],"strong",{},"The ",[14,689,690],{},"_xlfn"," prefix."," Functions added after Excel 2010 are stored in the file format with a prefix — ",[14,694,695],{},"_xlfn.UNIQUE",[14,697,698],{},"_xlfn._xlws.SORT",[14,700,701],{},"_xlfn._xlws.FILTER",". Without it, ",[14,704,705],{},"SORT"," and ",[14,708,709],{},"UNIQUE"," are unknown names and the cell shows ",[14,712,29],{},".",[682,715,716,719,720,723,724,727,728,730],{},[685,717,718],{},"The dynamic array metadata."," A spilling formula is marked with ",[14,721,722],{},"cm=\"1\""," on the cell and a ",[14,725,726],{},"metadata.xml"," part in the package. Without it, Excel treats the formula as a pre-2019 formula, inserts the implicit intersection operator ",[14,729,33],{},", and returns a single value instead of spilling.",[42,732,51,738,51,741,51,744,51,748,51,757,51,768,51,774,51,779,51,785,51,791,51,795,51,800,51,804,51,809,51,814,51,821,51,825,51,829,51,832,51,838,51,840,51,845,51,848,51,853,51,856,51,859,51,865,51,870,51,874,51,878,51,883,51,886,51,891,51,894],{"viewBox":733,"role":45,"ariaLabelledBy":734,"xmlns":49,"style":737},"4 0 752 224",[735,736],"dyn-parts-t","dyn-parts-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[53,739,740],{"id":735},"What Excel needs in the file for a spilling formula",[57,742,743],{"id":736},"A formula written with openpyxl has neither the _xlfn prefix nor the dynamic array metadata, so Excel shows",[61,745],{"x":217,"y":64,"width":746,"height":747,"fill":67},"752","224",[61,749],{"x":750,"y":71,"width":751,"height":752,"rx":753,"fill":754,"stroke":755,"style":756},"20.0","228.0","192.0","14","#fee8f2","var(--accent,#f43f8f)","stroke-width:2px",[758,759,761],"g",{"transform":760},"translate(121.0,29.0) scale(1.083)",[762,763],"path",{"d":764,"fill":765,"stroke":766,"style":767},"M6 6l12 12 M18 6L6 18","none","var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[81,769,773],{"x":770,"y":771,"style":772},"134.0","78.0","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Plain text (openpyxl)",[296,775],{"x1":776,"y1":777,"x2":778,"y2":777,"stroke":755,"style":75},"36.0","91.0","232.0",[81,780,784],{"x":781,"y":782,"style":783},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","no _xlfn prefix",[758,786,788],{"transform":787},"translate(38.5,99.5) scale(0.625)",[762,789],{"d":764,"fill":765,"stroke":755,"style":790},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[81,792,794],{"x":781,"y":793,"style":783},"137.0","no cm metadata",[758,796,798],{"transform":797},"translate(38.5,125.5) scale(0.625)",[762,799],{"d":764,"fill":765,"stroke":755,"style":790},[81,801,803],{"x":781,"y":802,"style":783},"163.0","opens as #NAME?",[758,805,807],{"transform":806},"translate(38.5,151.5) scale(0.625)",[762,808],{"d":764,"fill":765,"stroke":755,"style":790},[61,810],{"x":811,"y":71,"width":751,"height":752,"rx":753,"fill":812,"stroke":813,"style":756},"266.0","#fdefd8","var(--gold,#b4740a)",[758,815,817],{"transform":816},"translate(367.0,29.0) scale(1.083)",[762,818],{"d":819,"fill":765,"stroke":820,"style":767},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[81,822,824],{"x":275,"y":771,"style":823},"font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Prefix only",[296,826],{"x1":827,"y1":777,"x2":828,"y2":777,"stroke":813,"style":75},"282.0","478.0",[81,830,698],{"x":831,"y":782,"style":783},"306.0",[758,833,835],{"transform":834},"translate(284.5,99.5) scale(0.625)",[762,836],{"d":837,"fill":765,"stroke":207,"style":790},"M5 12.5l4.5 4.5L19 7",[81,839,794],{"x":831,"y":793,"style":783},[758,841,843],{"transform":842},"translate(284.5,125.5) scale(0.625)",[762,844],{"d":764,"fill":765,"stroke":755,"style":790},[81,846,847],{"x":831,"y":802,"style":783},"opens as =@SORT(…), one",[758,849,851],{"transform":850},"translate(284.5,151.5) scale(0.625)",[762,852],{"d":819,"fill":765,"stroke":813,"style":790},[81,854,855],{"x":831,"y":235,"style":783},"value",[61,857],{"x":858,"y":71,"width":751,"height":752,"rx":753,"fill":206,"stroke":207,"style":756},"512.0",[758,860,862],{"transform":861},"translate(613.0,29.0) scale(1.083)",[762,863],{"d":837,"fill":765,"stroke":864,"style":767},"var(--teal-ink,#0b6157)",[81,866,869],{"x":867,"y":771,"style":868},"626.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Prefix + metadata",[296,871],{"x1":872,"y1":777,"x2":873,"y2":777,"stroke":207,"style":75},"528.0","724.0",[81,875,877],{"x":876,"y":782,"style":783},"552.0","_xlfn prefixes",[758,879,881],{"transform":880},"translate(530.5,99.5) scale(0.625)",[762,882],{"d":837,"fill":765,"stroke":207,"style":790},[81,884,885],{"x":876,"y":793,"style":783},"cm=\"1\" and metadata.xml",[758,887,889],{"transform":888},"translate(530.5,125.5) scale(0.625)",[762,890],{"d":837,"fill":765,"stroke":207,"style":790},[81,892,893],{"x":876,"y":802,"style":783},"spills when opened",[758,895,897],{"transform":896},"translate(530.5,151.5) scale(0.625)",[762,898],{"d":837,"fill":765,"stroke":207,"style":790},[280,900,902],{"id":901},"step-2-write-it-properly-with-xlsxwriter","Step 2 — Write it properly with xlsxwriter",[10,904,905,906,909],{},"xlsxwriter knows both rules. ",[14,907,908],{},"write_dynamic_array_formula"," adds the prefixes and the spill metadata for you:",[285,911,913],{"className":317,"code":912,"language":319,"meta":290,"style":290},"import xlsxwriter\n\nbook = xlsxwriter.Workbook(\"dynamic.xlsx\")\nws = book.add_worksheet(\"Sales\")\nws.write_row(0, 0, [\"Region\", \"Sales\"])\ndata = [(\"North\", 10), (\"South\", 20), (\"North\", 30), (\"East\", 40), (\"South\", 15)]\nfor i, (region, sales) in enumerate(data, start=1):\n    ws.write_row(i, 0, [region, sales])\n\nws.write(\"D1\", \"Regions\")\nws.write_dynamic_array_formula(\"D2\", \"=SORT(UNIQUE(A2:A6))\")\nws.write(\"F1\", \"Big orders\")\nws.write_dynamic_array_formula(\"F2\", \"=FILTER(A2:B6, B2:B6>15)\")\nbook.close()\n\nfor cell in formula_xml(\"dynamic.xlsx\"):\n    print(cell)\nprint(\"xl\u002Fmetadata.xml\" in zipfile.ZipFile(\"dynamic.xlsx\").namelist())   # True\n",[14,914,915,921,925,940,953,976,1028,1054,1064,1068,1083,1097,1112,1127,1133,1138,1155,1164],{"__ignoreMap":290},[294,916,917,919],{"class":296,"line":297},[294,918,327],{"class":326},[294,920,311],{"class":330},[294,922,923],{"class":296,"line":334},[294,924,346],{"emptyLinePlaceholder":345},[294,926,927,930,932,935,938],{"class":296,"line":342},[294,928,929],{"class":330},"book ",[294,931,391],{"class":326},[294,933,934],{"class":330}," xlsxwriter.Workbook(",[294,936,937],{"class":304},"\"dynamic.xlsx\"",[294,939,648],{"class":330},[294,941,942,944,946,949,951],{"class":296,"line":349},[294,943,539],{"class":330},[294,945,391],{"class":326},[294,947,948],{"class":330}," book.add_worksheet(",[294,950,567],{"class":304},[294,952,648],{"class":330},[294,954,955,958,960,962,964,967,969,971,973],{"class":296,"line":385},[294,956,957],{"class":330},"ws.write_row(",[294,959,64],{"class":362},[294,961,564],{"class":330},[294,963,64],{"class":362},[294,965,966],{"class":330},", [",[294,968,561],{"class":304},[294,970,564],{"class":330},[294,972,567],{"class":304},[294,974,975],{"class":330},"])\n",[294,977,978,981,983,986,988,990,992,995,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025],{"class":296,"line":397},[294,979,980],{"class":330},"data ",[294,982,391],{"class":326},[294,984,985],{"class":330}," [(",[294,987,573],{"class":304},[294,989,564],{"class":330},[294,991,174],{"class":362},[294,993,994],{"class":330},"), (",[294,996,582],{"class":304},[294,998,564],{"class":330},[294,1000,201],{"class":362},[294,1002,994],{"class":330},[294,1004,573],{"class":304},[294,1006,564],{"class":330},[294,1008,226],{"class":362},[294,1010,994],{"class":330},[294,1012,599],{"class":304},[294,1014,564],{"class":330},[294,1016,248],{"class":362},[294,1018,994],{"class":330},[294,1020,582],{"class":304},[294,1022,564],{"class":330},[294,1024,268],{"class":362},[294,1026,1027],{"class":330},")]\n",[294,1029,1030,1032,1035,1037,1040,1043,1047,1049,1051],{"class":296,"line":622},[294,1031,549],{"class":326},[294,1033,1034],{"class":330}," i, (region, sales) ",[294,1036,555],{"class":326},[294,1038,1039],{"class":362}," enumerate",[294,1041,1042],{"class":330},"(data, ",[294,1044,1046],{"class":1045},"sa561","start",[294,1048,391],{"class":326},[294,1050,137],{"class":362},[294,1052,1053],{"class":330},"):\n",[294,1055,1056,1059,1061],{"class":296,"line":639},[294,1057,1058],{"class":330},"    ws.write_row(i, ",[294,1060,64],{"class":362},[294,1062,1063],{"class":330},", [region, sales])\n",[294,1065,1066],{"class":296,"line":651},[294,1067,346],{"emptyLinePlaceholder":345},[294,1069,1070,1073,1076,1078,1081],{"class":296,"line":656},[294,1071,1072],{"class":330},"ws.write(",[294,1074,1075],{"class":304},"\"D1\"",[294,1077,564],{"class":330},[294,1079,1080],{"class":304},"\"Regions\"",[294,1082,648],{"class":330},[294,1084,1085,1088,1090,1092,1095],{"class":296,"line":670},[294,1086,1087],{"class":330},"ws.write_dynamic_array_formula(",[294,1089,628],{"class":304},[294,1091,564],{"class":330},[294,1093,1094],{"class":304},"\"=SORT(UNIQUE(A2:A6))\"",[294,1096,648],{"class":330},[294,1098,1100,1102,1105,1107,1110],{"class":296,"line":1099},12,[294,1101,1072],{"class":330},[294,1103,1104],{"class":304},"\"F1\"",[294,1106,564],{"class":330},[294,1108,1109],{"class":304},"\"Big orders\"",[294,1111,648],{"class":330},[294,1113,1115,1117,1120,1122,1125],{"class":296,"line":1114},13,[294,1116,1087],{"class":330},[294,1118,1119],{"class":304},"\"F2\"",[294,1121,564],{"class":330},[294,1123,1124],{"class":304},"\"=FILTER(A2:B6, B2:B6>15)\"",[294,1126,648],{"class":330},[294,1128,1130],{"class":296,"line":1129},14,[294,1131,1132],{"class":330},"book.close()\n",[294,1134,1136],{"class":296,"line":1135},15,[294,1137,346],{"emptyLinePlaceholder":345},[294,1139,1141,1143,1146,1148,1151,1153],{"class":296,"line":1140},16,[294,1142,549],{"class":326},[294,1144,1145],{"class":330}," cell ",[294,1147,555],{"class":326},[294,1149,1150],{"class":330}," formula_xml(",[294,1152,937],{"class":304},[294,1154,1053],{"class":330},[294,1156,1158,1161],{"class":296,"line":1157},17,[294,1159,1160],{"class":362},"    print",[294,1162,1163],{"class":330},"(cell)\n",[294,1165,1167,1169,1172,1175,1178,1181,1183,1186],{"class":296,"line":1166},18,[294,1168,659],{"class":362},[294,1170,1171],{"class":330},"(",[294,1173,1174],{"class":304},"\"xl\u002Fmetadata.xml\"",[294,1176,1177],{"class":326}," in",[294,1179,1180],{"class":330}," zipfile.ZipFile(",[294,1182,937],{"class":304},[294,1184,1185],{"class":330},").namelist())   ",[294,1187,1188],{"class":673},"# True\n",[10,1190,1191,1192,1194,1195,706,1198,1201,1202,1205,1206,564,1208,706,1210,1213],{},"The output shows ",[14,1193,722],{}," on each cell and the formulas stored as ",[14,1196,1197],{},"_xlfn._xlws.SORT(_xlfn.UNIQUE(A2:A6))",[14,1199,1200],{},"_xlfn._xlws.FILTER(A2:B6,B2:B6>15)",". You write the formula exactly as you would type it; xlsxwriter translates. Even plain ",[14,1203,1204],{},"write_formula"," recognises dynamic functions such as ",[14,1207,709],{},[14,1209,705],{},[14,1211,1212],{},"FILTER"," and applies the same treatment, which makes it hard to get wrong.",[280,1215,1217],{"id":1216},"step-3-point-dynamic-formulas-at-a-table","Step 3 — Point dynamic formulas at a table",[10,1219,1220],{},"Dynamic arrays pair naturally with Excel tables: a table grows when rows are added, and a formula over its columns keeps up. Combine the two so the report stays correct after someone pastes new data:",[285,1222,1224],{"className":317,"code":1223,"language":319,"meta":290,"style":290},"book = xlsxwriter.Workbook(\"dynamic_table.xlsx\")\nws = book.add_worksheet(\"Sales\")\nws.add_table(\"A1:C6\", {\n    \"name\": \"Orders\",\n    \"data\": [[\"North\", \"Ana\", 10], [\"South\", \"Ben\", 20], [\"North\", \"Cy\", 30],\n             [\"East\", \"Dee\", 40], [\"South\", \"Eve\", 15]],\n    \"columns\": [{\"header\": \"Region\"}, {\"header\": \"Rep\"}, {\"header\": \"Sales\"}],\n})\nws.write(\"E1\", \"Top reps (sales > 15)\")\nws.write_dynamic_array_formula(\"E2\", \"=SORT(FILTER(Orders[Rep], Orders[Sales]>15))\")\nws.write(\"G1\", \"Total by region\")\nws.write_dynamic_array_formula(\"G2\", \"=UNIQUE(Orders[Region])\")\nws.write_dynamic_array_formula(\"H2\", \"=SUMIFS(Orders[Sales], Orders[Region], ANCHORARRAY(G2))\")\nbook.close()\nprint(formula_xml(\"dynamic_table.xlsx\")[-1])\n",[14,1225,1226,1239,1251,1262,1276,1324,1356,1392,1397,1411,1425,1439,1453,1467,1471],{"__ignoreMap":290},[294,1227,1228,1230,1232,1234,1237],{"class":296,"line":297},[294,1229,929],{"class":330},[294,1231,391],{"class":326},[294,1233,934],{"class":330},[294,1235,1236],{"class":304},"\"dynamic_table.xlsx\"",[294,1238,648],{"class":330},[294,1240,1241,1243,1245,1247,1249],{"class":296,"line":334},[294,1242,539],{"class":330},[294,1244,391],{"class":326},[294,1246,948],{"class":330},[294,1248,567],{"class":304},[294,1250,648],{"class":330},[294,1252,1253,1256,1259],{"class":296,"line":342},[294,1254,1255],{"class":330},"ws.add_table(",[294,1257,1258],{"class":304},"\"A1:C6\"",[294,1260,1261],{"class":330},", {\n",[294,1263,1264,1267,1270,1273],{"class":296,"line":349},[294,1265,1266],{"class":304},"    \"name\"",[294,1268,1269],{"class":330},": ",[294,1271,1272],{"class":304},"\"Orders\"",[294,1274,1275],{"class":330},",\n",[294,1277,1278,1281,1284,1286,1288,1291,1293,1295,1297,1299,1301,1304,1306,1308,1310,1312,1314,1317,1319,1321],{"class":296,"line":385},[294,1279,1280],{"class":304},"    \"data\"",[294,1282,1283],{"class":330},": [[",[294,1285,573],{"class":304},[294,1287,564],{"class":330},[294,1289,1290],{"class":304},"\"Ana\"",[294,1292,564],{"class":330},[294,1294,174],{"class":362},[294,1296,570],{"class":330},[294,1298,582],{"class":304},[294,1300,564],{"class":330},[294,1302,1303],{"class":304},"\"Ben\"",[294,1305,564],{"class":330},[294,1307,201],{"class":362},[294,1309,570],{"class":330},[294,1311,573],{"class":304},[294,1313,564],{"class":330},[294,1315,1316],{"class":304},"\"Cy\"",[294,1318,564],{"class":330},[294,1320,226],{"class":362},[294,1322,1323],{"class":330},"],\n",[294,1325,1326,1329,1331,1333,1336,1338,1340,1342,1344,1346,1349,1351,1353],{"class":296,"line":397},[294,1327,1328],{"class":330},"             [",[294,1330,599],{"class":304},[294,1332,564],{"class":330},[294,1334,1335],{"class":304},"\"Dee\"",[294,1337,564],{"class":330},[294,1339,248],{"class":362},[294,1341,570],{"class":330},[294,1343,582],{"class":304},[294,1345,564],{"class":330},[294,1347,1348],{"class":304},"\"Eve\"",[294,1350,564],{"class":330},[294,1352,268],{"class":362},[294,1354,1355],{"class":330},"]],\n",[294,1357,1358,1361,1364,1367,1369,1371,1374,1376,1378,1381,1383,1385,1387,1389],{"class":296,"line":622},[294,1359,1360],{"class":304},"    \"columns\"",[294,1362,1363],{"class":330},": [{",[294,1365,1366],{"class":304},"\"header\"",[294,1368,1269],{"class":330},[294,1370,561],{"class":304},[294,1372,1373],{"class":330},"}, {",[294,1375,1366],{"class":304},[294,1377,1269],{"class":330},[294,1379,1380],{"class":304},"\"Rep\"",[294,1382,1373],{"class":330},[294,1384,1366],{"class":304},[294,1386,1269],{"class":330},[294,1388,567],{"class":304},[294,1390,1391],{"class":330},"}],\n",[294,1393,1394],{"class":296,"line":639},[294,1395,1396],{"class":330},"})\n",[294,1398,1399,1401,1404,1406,1409],{"class":296,"line":651},[294,1400,1072],{"class":330},[294,1402,1403],{"class":304},"\"E1\"",[294,1405,564],{"class":330},[294,1407,1408],{"class":304},"\"Top reps (sales > 15)\"",[294,1410,648],{"class":330},[294,1412,1413,1415,1418,1420,1423],{"class":296,"line":656},[294,1414,1087],{"class":330},[294,1416,1417],{"class":304},"\"E2\"",[294,1419,564],{"class":330},[294,1421,1422],{"class":304},"\"=SORT(FILTER(Orders[Rep], Orders[Sales]>15))\"",[294,1424,648],{"class":330},[294,1426,1427,1429,1432,1434,1437],{"class":296,"line":670},[294,1428,1072],{"class":330},[294,1430,1431],{"class":304},"\"G1\"",[294,1433,564],{"class":330},[294,1435,1436],{"class":304},"\"Total by region\"",[294,1438,648],{"class":330},[294,1440,1441,1443,1446,1448,1451],{"class":296,"line":1099},[294,1442,1087],{"class":330},[294,1444,1445],{"class":304},"\"G2\"",[294,1447,564],{"class":330},[294,1449,1450],{"class":304},"\"=UNIQUE(Orders[Region])\"",[294,1452,648],{"class":330},[294,1454,1455,1457,1460,1462,1465],{"class":296,"line":1114},[294,1456,1087],{"class":330},[294,1458,1459],{"class":304},"\"H2\"",[294,1461,564],{"class":330},[294,1463,1464],{"class":304},"\"=SUMIFS(Orders[Sales], Orders[Region], ANCHORARRAY(G2))\"",[294,1466,648],{"class":330},[294,1468,1469],{"class":296,"line":1129},[294,1470,1132],{"class":330},[294,1472,1473,1475,1477,1479,1482,1485,1487],{"class":296,"line":1135},[294,1474,659],{"class":362},[294,1476,662],{"class":330},[294,1478,1236],{"class":304},[294,1480,1481],{"class":330},")[",[294,1483,1484],{"class":326},"-",[294,1486,137],{"class":362},[294,1488,975],{"class":330},[10,1490,1491,1494,1495,1498,1499,1502,1503,1506,1507,1510,1511,1513,1514,1517,1518,1521,1522,713],{},[14,1492,1493],{},"ANCHORARRAY(G2)"," is how the file format stores the spill-range operator that Excel displays as ",[14,1496,1497],{},"G2#",": it refers to whatever ",[14,1500,1501],{},"G2"," spills, however many rows that is, so the SUMIFS beside it produces one total per region and resizes with the list. Write the function form from Python — xlsxwriter adds the ",[14,1504,1505],{},"_xlfn."," prefix to ",[14,1508,1509],{},"ANCHORARRAY"," but passes a literal ",[14,1512,1497],{}," through untouched, and a ",[14,1515,1516],{},"#"," in the stored formula is not valid file content. Structured references such as ",[14,1519,1520],{},"Orders[Sales]"," are covered in ",[36,1523,1525],{"href":1524},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fuse-structured-table-references-in-formulas-from-python\u002F","Use Structured Table References in Excel Formulas from Python",[42,1527,51,1532,51,1535,51,1538,51,1541,51,1546,51,1554,51,1560,51,1565,51,1569,51,1574,51,1577,51,1584,51,1588,51,1591,51,1595,51,1598,51,1601,51,1607,51,1613,51,1617,51,1621],{"viewBox":1528,"role":45,"ariaLabelledBy":1529,"xmlns":49,"style":737},"4 0 752 168",[1530,1531],"dyn-chain-t","dyn-chain-d",[53,1533,1534],{"id":1530},"A dynamic array report built on a table",[57,1536,1537],{"id":1531},"The Orders table holds the data. UNIQUE of the region column spills the list of regions from G2. SUMIFS referencing G2# computes one total per spilled region. When rows are added to the table, the region list and the totals resize automatically.",[61,1539],{"x":217,"y":64,"width":746,"height":1540,"fill":67},"168",[61,1542],{"x":750,"y":71,"width":1543,"height":1544,"rx":1545,"fill":79,"stroke":74,"style":756},"216.0","110.5","12",[758,1547,1549],{"transform":1548},"translate(116.0,37.8) scale(1.000)",[762,1550],{"d":1551,"fill":765,"stroke":1552,"style":1553},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[81,1555,1559],{"x":1556,"y":1557,"style":1558},"128.0","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Orders table",[81,1561,1564],{"x":1556,"y":1562,"style":1563},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","grows with new rows",[296,1566],{"x1":1567,"y1":1568,"x2":83,"y2":1568,"stroke":180,"style":756},"241.0","71.2",[1570,1571],"polygon",{"points":1572,"fill":1573},"267.0,71.2 258.0,76.2 258.0,66.2","#5b5cf0",[61,1575],{"x":1576,"y":71,"width":1543,"height":1544,"rx":1545,"fill":179,"stroke":180,"style":756},"272.0",[758,1578,1580],{"transform":1579},"translate(368.0,37.8) scale(1.000)",[762,1581],{"d":1582,"fill":765,"stroke":1583,"style":1553},"M3 5h18l-7 8v6l-4 2v-8z","var(--brand-strong,#4338ca)",[81,1585,1587],{"x":275,"y":1557,"style":1586},"font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","=UNIQUE(Orders[Region])",[81,1589,1590],{"x":275,"y":1562,"style":1563},"spills from G2",[296,1592],{"x1":1593,"y1":1568,"x2":1594,"y2":1568,"stroke":180,"style":756},"493.0","515.0",[1570,1596],{"points":1597,"fill":1573},"519.0,71.2 510.0,76.2 510.0,66.2",[61,1599],{"x":1600,"y":71,"width":1543,"height":1544,"rx":1545,"fill":206,"stroke":207,"style":756},"524.0",[758,1602,1604],{"transform":1603},"translate(620.0,29.0) scale(1.000)",[762,1605],{"d":1606,"fill":765,"stroke":864,"style":1553},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[81,1608,1612],{"x":1609,"y":1610,"style":1611},"632.0","75.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","=SUMIFS(…,",[81,1614,1616],{"x":1609,"y":1615,"style":1611},"93.0","ANCHORARRAY(G2))",[81,1618,1620],{"x":1609,"y":1619,"style":1563},"112.5","shown in Excel as G2#",[81,1622,1624],{"x":275,"y":1623,"style":277},"148.5","ANCHORARRAY(G2) — displayed as G2# — means everything G2 spills, so the totals resize with the list",[280,1626,1628],{"id":1627},"step-4-if-you-must-use-openpyxl","Step 4 — If you must use openpyxl",[10,1630,1631,1632,1634,1635,1638],{},"Sometimes the workbook is being edited with openpyxl for other reasons and you only need to add one dynamic formula. openpyxl cannot write the spill metadata, so the best it can do is the prefixed text, which Excel will open as a single-value formula with ",[14,1633,33],{},". You can still get a ",[23,1636,1637],{},"correct"," result in that case by writing a legacy array formula over a fixed range, where the size is known in advance:",[285,1640,1642],{"className":317,"code":1641,"language":319,"meta":290,"style":290},"from openpyxl.worksheet.formula import ArrayFormula\n\nwb = Workbook()\nws = wb.active\nfor row in [[\"Region\", \"Sales\"], [\"North\", 10], [\"South\", 20], [\"North\", 30], [\"East\", 40], [\"South\", 15]]:\n    ws.append(row)\nws[\"D2\"] = ArrayFormula(\"D2:D4\", \"=_xlfn._xlws.SORT(_xlfn.UNIQUE(A2:A6))\")\nwb.save(\"openpyxl_array.xlsx\")\nprint(formula_xml(\"openpyxl_array.xlsx\"))\n",[14,1643,1644,1656,1660,1668,1676,1734,1738,1761,1770],{"__ignoreMap":290},[294,1645,1646,1648,1651,1653],{"class":296,"line":297},[294,1647,512],{"class":326},[294,1649,1650],{"class":330}," openpyxl.worksheet.formula ",[294,1652,327],{"class":326},[294,1654,1655],{"class":330}," ArrayFormula\n",[294,1657,1658],{"class":296,"line":334},[294,1659,346],{"emptyLinePlaceholder":345},[294,1661,1662,1664,1666],{"class":296,"line":342},[294,1663,529],{"class":330},[294,1665,391],{"class":326},[294,1667,534],{"class":330},[294,1669,1670,1672,1674],{"class":296,"line":349},[294,1671,539],{"class":330},[294,1673,391],{"class":326},[294,1675,544],{"class":330},[294,1677,1678,1680,1682,1684,1686,1688,1690,1692,1694,1696,1698,1700,1702,1704,1706,1708,1710,1712,1714,1716,1718,1720,1722,1724,1726,1728,1730,1732],{"class":296,"line":385},[294,1679,549],{"class":326},[294,1681,552],{"class":330},[294,1683,555],{"class":326},[294,1685,558],{"class":330},[294,1687,561],{"class":304},[294,1689,564],{"class":330},[294,1691,567],{"class":304},[294,1693,570],{"class":330},[294,1695,573],{"class":304},[294,1697,564],{"class":330},[294,1699,174],{"class":362},[294,1701,570],{"class":330},[294,1703,582],{"class":304},[294,1705,564],{"class":330},[294,1707,201],{"class":362},[294,1709,570],{"class":330},[294,1711,573],{"class":304},[294,1713,564],{"class":330},[294,1715,226],{"class":362},[294,1717,570],{"class":330},[294,1719,599],{"class":304},[294,1721,564],{"class":330},[294,1723,248],{"class":362},[294,1725,570],{"class":330},[294,1727,582],{"class":304},[294,1729,564],{"class":330},[294,1731,268],{"class":362},[294,1733,614],{"class":330},[294,1735,1736],{"class":296,"line":397},[294,1737,619],{"class":330},[294,1739,1740,1742,1744,1746,1748,1751,1754,1756,1759],{"class":296,"line":622},[294,1741,625],{"class":330},[294,1743,628],{"class":304},[294,1745,631],{"class":330},[294,1747,391],{"class":326},[294,1749,1750],{"class":330}," ArrayFormula(",[294,1752,1753],{"class":304},"\"D2:D4\"",[294,1755,564],{"class":330},[294,1757,1758],{"class":304},"\"=_xlfn._xlws.SORT(_xlfn.UNIQUE(A2:A6))\"",[294,1760,648],{"class":330},[294,1762,1763,1765,1768],{"class":296,"line":639},[294,1764,642],{"class":330},[294,1766,1767],{"class":304},"\"openpyxl_array.xlsx\"",[294,1769,648],{"class":330},[294,1771,1772,1774,1776,1778],{"class":296,"line":651},[294,1773,659],{"class":362},[294,1775,662],{"class":330},[294,1777,1767],{"class":304},[294,1779,667],{"class":330},[10,1781,1782,1783,1786,1787,1790],{},"This writes a classic array formula across D2:D4 — the braces ",[14,1784,1785],{},"{=…}"," style. It does not resize, so if a fourth region appears it will not be shown, and if one disappears the last cell shows ",[14,1788,1789],{},"#N\u002FA",". It is a workaround for known-size outputs, not a replacement. When the output size can change, write the workbook, or at least that sheet, with xlsxwriter.",[280,1792,1794],{"id":1793},"step-5-check-generated-workbooks-before-shipping","Step 5 — Check generated workbooks before shipping",[10,1796,1797],{},"Whichever library produced a workbook, a short scan catches the two faults before a reader does: dynamic functions stored without their prefix, and dynamic-array cells without the spill metadata. The scan reads the sheet XML directly, so it runs in milliseconds and needs no Excel:",[285,1799,1801],{"className":317,"code":1800,"language":319,"meta":290,"style":290},"DYNAMIC = (\"FILTER\", \"UNIQUE\", \"SORT\", \"SORTBY\", \"SEQUENCE\", \"XLOOKUP\", \"XMATCH\", \"LET\", \"RANDARRAY\")\n\ndef dynamic_formula_problems(path: str) -> list[str]:\n    problems = []\n    with zipfile.ZipFile(path) as z:\n        for name in z.namelist():\n            if not (name.startswith(\"xl\u002Fworksheets\u002Fsheet\") and name.endswith(\".xml\")):\n                continue\n            for cell, attrs, text in re.findall(r'\u003Cc r=\"([A-Z]+\\d+)\"([^>]*)>\\s*\u003Cf[^>]*>([^\u003C]*)\u003C\u002Ff>', z.read(name).decode()):\n                used = [fn for fn in DYNAMIC if re.search(rf\"(?\u003C![._\\w]){fn}\\(\", text)]\n                if used:\n                    problems.append(f\"{name}:{cell} unprefixed {used}\")\n                elif \"_xlfn.\" in text and any(fn in text for fn in DYNAMIC) and 'cm=\"1\"' not in attrs:\n                    problems.append(f\"{name}:{cell} no spill metadata\")\n    return problems\n\nprint(dynamic_formula_problems(\"openpyxl_dynamic.xlsx\"))   # [\"xl\u002Fworksheets\u002Fsheet1.xml:D2 unprefixed ['UNIQUE', 'SORT']\"]\nprint(dynamic_formula_problems(\"dynamic.xlsx\"))            # []\n",[14,1802,1803,1858,1862,1879,1889,1903,1916,1945,1950,2035,2082,2090,2131,2178,2205,2212,2216,2231],{"__ignoreMap":290},[294,1804,1805,1808,1810,1813,1816,1818,1821,1823,1826,1828,1831,1833,1836,1838,1841,1843,1846,1848,1851,1853,1856],{"class":296,"line":297},[294,1806,1807],{"class":362},"DYNAMIC",[294,1809,371],{"class":326},[294,1811,1812],{"class":330}," (",[294,1814,1815],{"class":304},"\"FILTER\"",[294,1817,564],{"class":330},[294,1819,1820],{"class":304},"\"UNIQUE\"",[294,1822,564],{"class":330},[294,1824,1825],{"class":304},"\"SORT\"",[294,1827,564],{"class":330},[294,1829,1830],{"class":304},"\"SORTBY\"",[294,1832,564],{"class":330},[294,1834,1835],{"class":304},"\"SEQUENCE\"",[294,1837,564],{"class":330},[294,1839,1840],{"class":304},"\"XLOOKUP\"",[294,1842,564],{"class":330},[294,1844,1845],{"class":304},"\"XMATCH\"",[294,1847,564],{"class":330},[294,1849,1850],{"class":304},"\"LET\"",[294,1852,564],{"class":330},[294,1854,1855],{"class":304},"\"RANDARRAY\"",[294,1857,648],{"class":330},[294,1859,1860],{"class":296,"line":334},[294,1861,346],{"emptyLinePlaceholder":345},[294,1863,1864,1866,1869,1871,1873,1875,1877],{"class":296,"line":342},[294,1865,352],{"class":326},[294,1867,1868],{"class":355}," dynamic_formula_problems",[294,1870,359],{"class":330},[294,1872,363],{"class":362},[294,1874,377],{"class":330},[294,1876,363],{"class":362},[294,1878,382],{"class":330},[294,1880,1881,1884,1886],{"class":296,"line":349},[294,1882,1883],{"class":330},"    problems ",[294,1885,391],{"class":326},[294,1887,1888],{"class":330}," []\n",[294,1890,1891,1894,1897,1900],{"class":296,"line":385},[294,1892,1893],{"class":326},"    with",[294,1895,1896],{"class":330}," zipfile.ZipFile(path) ",[294,1898,1899],{"class":326},"as",[294,1901,1902],{"class":330}," z:\n",[294,1904,1905,1908,1911,1913],{"class":296,"line":397},[294,1906,1907],{"class":326},"        for",[294,1909,1910],{"class":330}," name ",[294,1912,555],{"class":326},[294,1914,1915],{"class":330}," z.namelist():\n",[294,1917,1918,1921,1924,1927,1930,1933,1936,1939,1942],{"class":296,"line":622},[294,1919,1920],{"class":326},"            if",[294,1922,1923],{"class":326}," not",[294,1925,1926],{"class":330}," (name.startswith(",[294,1928,1929],{"class":304},"\"xl\u002Fworksheets\u002Fsheet\"",[294,1931,1932],{"class":330},") ",[294,1934,1935],{"class":326},"and",[294,1937,1938],{"class":330}," name.endswith(",[294,1940,1941],{"class":304},"\".xml\"",[294,1943,1944],{"class":330},")):\n",[294,1946,1947],{"class":296,"line":639},[294,1948,1949],{"class":326},"                continue\n",[294,1951,1952,1955,1958,1960,1962,1964,1966,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989,1991,1993,1995,1997,2000,2002,2004,2006,2008,2010,2012,2014,2016,2018,2020,2023,2025,2027,2029,2032],{"class":296,"line":651},[294,1953,1954],{"class":326},"            for",[294,1956,1957],{"class":330}," cell, attrs, text ",[294,1959,555],{"class":326},[294,1961,403],{"class":330},[294,1963,406],{"class":326},[294,1965,409],{"class":304},[294,1967,1968],{"class":362},"([",[294,1970,416],{"class":415},[294,1972,419],{"class":362},[294,1974,422],{"class":326},[294,1976,425],{"class":362},[294,1978,422],{"class":326},[294,1980,457],{"class":362},[294,1982,430],{"class":304},[294,1984,1968],{"class":362},[294,1986,435],{"class":326},[294,1988,438],{"class":415},[294,1990,419],{"class":362},[294,1992,443],{"class":326},[294,1994,457],{"class":362},[294,1996,438],{"class":304},[294,1998,1999],{"class":362},"\\s",[294,2001,443],{"class":326},[294,2003,465],{"class":304},[294,2005,412],{"class":362},[294,2007,435],{"class":326},[294,2009,438],{"class":415},[294,2011,419],{"class":362},[294,2013,443],{"class":326},[294,2015,438],{"class":304},[294,2017,1968],{"class":362},[294,2019,435],{"class":326},[294,2021,2022],{"class":415},"\u003C",[294,2024,419],{"class":362},[294,2026,443],{"class":326},[294,2028,457],{"class":362},[294,2030,2031],{"class":304},"\u003C\u002Ff>'",[294,2033,2034],{"class":330},", z.read(name).decode()):\n",[294,2036,2037,2040,2042,2045,2047,2050,2052,2055,2058,2061,2064,2067,2070,2073,2076,2079],{"class":296,"line":656},[294,2038,2039],{"class":330},"                used ",[294,2041,391],{"class":326},[294,2043,2044],{"class":330}," [fn ",[294,2046,549],{"class":326},[294,2048,2049],{"class":330}," fn ",[294,2051,555],{"class":326},[294,2053,2054],{"class":362}," DYNAMIC",[294,2056,2057],{"class":326}," if",[294,2059,2060],{"class":330}," re.search(",[294,2062,2063],{"class":326},"rf",[294,2065,2066],{"class":304},"\"(?\u003C![._\\w])",[294,2068,2069],{"class":415},"{",[294,2071,2072],{"class":330},"fn",[294,2074,2075],{"class":415},"}",[294,2077,2078],{"class":304},"\\(\"",[294,2080,2081],{"class":330},", text)]\n",[294,2083,2084,2087],{"class":296,"line":670},[294,2085,2086],{"class":326},"                if",[294,2088,2089],{"class":330}," used:\n",[294,2091,2092,2095,2098,2100,2102,2105,2107,2110,2112,2115,2117,2120,2122,2125,2127,2129],{"class":296,"line":1099},[294,2093,2094],{"class":330},"                    problems.append(",[294,2096,2097],{"class":326},"f",[294,2099,430],{"class":304},[294,2101,2069],{"class":415},[294,2103,2104],{"class":330},"name",[294,2106,2075],{"class":415},[294,2108,2109],{"class":304},":",[294,2111,2069],{"class":415},[294,2113,2114],{"class":330},"cell",[294,2116,2075],{"class":415},[294,2118,2119],{"class":304}," unprefixed ",[294,2121,2069],{"class":415},[294,2123,2124],{"class":330},"used",[294,2126,2075],{"class":415},[294,2128,430],{"class":304},[294,2130,648],{"class":330},[294,2132,2133,2136,2139,2141,2144,2146,2149,2152,2154,2156,2158,2160,2162,2164,2166,2168,2171,2173,2175],{"class":296,"line":1114},[294,2134,2135],{"class":326},"                elif",[294,2137,2138],{"class":304}," \"_xlfn.\"",[294,2140,1177],{"class":326},[294,2142,2143],{"class":330}," text ",[294,2145,1935],{"class":326},[294,2147,2148],{"class":362}," any",[294,2150,2151],{"class":330},"(fn ",[294,2153,555],{"class":326},[294,2155,2143],{"class":330},[294,2157,549],{"class":326},[294,2159,2049],{"class":330},[294,2161,555],{"class":326},[294,2163,2054],{"class":362},[294,2165,1932],{"class":330},[294,2167,1935],{"class":326},[294,2169,2170],{"class":304}," 'cm=\"1\"'",[294,2172,1923],{"class":326},[294,2174,1177],{"class":326},[294,2176,2177],{"class":330}," attrs:\n",[294,2179,2180,2182,2184,2186,2188,2190,2192,2194,2196,2198,2200,2203],{"class":296,"line":1129},[294,2181,2094],{"class":330},[294,2183,2097],{"class":326},[294,2185,430],{"class":304},[294,2187,2069],{"class":415},[294,2189,2104],{"class":330},[294,2191,2075],{"class":415},[294,2193,2109],{"class":304},[294,2195,2069],{"class":415},[294,2197,2114],{"class":330},[294,2199,2075],{"class":415},[294,2201,2202],{"class":304}," no spill metadata\"",[294,2204,648],{"class":330},[294,2206,2207,2209],{"class":296,"line":1135},[294,2208,400],{"class":326},[294,2210,2211],{"class":330}," problems\n",[294,2213,2214],{"class":296,"line":1140},[294,2215,346],{"emptyLinePlaceholder":345},[294,2217,2218,2220,2223,2225,2228],{"class":296,"line":1157},[294,2219,659],{"class":362},[294,2221,2222],{"class":330},"(dynamic_formula_problems(",[294,2224,645],{"class":304},[294,2226,2227],{"class":330},"))   ",[294,2229,2230],{"class":673},"# [\"xl\u002Fworksheets\u002Fsheet1.xml:D2 unprefixed ['UNIQUE', 'SORT']\"]\n",[294,2232,2233,2235,2237,2239,2242],{"class":296,"line":1166},[294,2234,659],{"class":362},[294,2236,2222],{"class":330},[294,2238,937],{"class":304},[294,2240,2241],{"class":330},"))            ",[294,2243,2244],{"class":673},"# []\n",[10,2246,2247,2248,2250],{},"Run it as a final step in the job that produces the workbook, and fail the run if it returns anything. It is also a quick way to audit workbooks produced by older scripts: every entry is a cell that shows ",[14,2249,29],{}," or a single value where a list was intended.",[280,2252,2254],{"id":2253},"common-pitfalls","Common pitfalls",[679,2256,2257,2274,2284,2290],{},[682,2258,2259,2262,2263,564,2265,706,2267,2269,2270,2273],{},[685,2260,2261],{},"Testing with LibreOffice."," LibreOffice calculates ",[14,2264,705],{},[14,2266,709],{},[14,2268,1212],{},", but it does not spill dynamic arrays loaded from an .xlsx file; a recalculation returns only the first value. Check the formula text and ",[14,2271,2272],{},"cm"," attribute as shown here, or open the file in Excel.",[682,2275,2276,2279,2280,2283],{},[685,2277,2278],{},"Spill collisions."," A spilling formula needs empty cells below or beside it. If Python also writes values into that area, Excel shows ",[14,2281,2282],{},"#SPILL!",". Leave the spill range empty.",[682,2285,2286,2289],{},[685,2287,2288],{},"Older Excel versions."," Excel 2016 and earlier open dynamic formulas as legacy arrays. If recipients may use old versions, compute the list in pandas and write values instead.",[682,2291,2292,2295,2296,2299,2300,2303,2304,713],{},[685,2293,2294],{},"Reading results back."," A file written by Python has no cached results, so ",[14,2297,2298],{},"load_workbook(..., data_only=True)"," returns ",[14,2301,2302],{},"None"," for these cells until Excel has opened and saved the file. See ",[36,2305,2307],{"href":2306},"\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",[280,2309,2311],{"id":2310},"performance-and-scale-notes","Performance and scale notes",[10,2313,2314],{},"A dynamic array formula costs nothing to write — it is one cell — and Excel evaluates it quickly even over tens of thousands of rows. The practical limit is the spill size: a FILTER that returns 100,000 rows is better computed in pandas and written as data, because a huge spill range slows recalculation every time anything changes. Use formulas for the derived views a reader interacts with, and values for bulk output.",[280,2316,2318],{"id":2317},"conclusion","Conclusion",[10,2320,2321,2322,2324,2325,17,2327,2329,2330,2332,2333,2335,2336,2338],{},"Excel needs two things in the file for a spilling formula: ",[14,2323,690],{}," prefixes on post-2010 functions and the dynamic array metadata. openpyxl writes neither, so its formulas open as ",[14,2326,29],{},[14,2328,33],{},". xlsxwriter's ",[14,2331,908],{}," — and ",[14,2334,1204],{}," for recognised functions — writes both, and pairs well with tables and the ",[14,2337,1516],{}," spill operator. Verify by inspecting the saved XML rather than by recalculating in LibreOffice.",[280,2340,2342],{"id":2341},"frequently-asked-questions","Frequently asked questions",[10,2344,2345,2348],{},[685,2346,2347],{},"Why does my UNIQUE formula show #NAME? when written with openpyxl?","\nFunctions added to Excel after 2010 must be stored in the file with a prefix such as _xlfn. openpyxl writes the formula text exactly as you give it, so UNIQUE without the prefix is an unknown function when the file opens.",[10,2350,2351,2354],{},[685,2352,2353],{},"Why does Excel put an @ in front of my formula?","\nThe formula was saved without the metadata that marks it as a dynamic array, so Excel opens it as a legacy formula and adds the implicit intersection operator. It then returns a single value instead of spilling.",[10,2356,2357,2360],{},[685,2358,2359],{},"Does xlsxwriter handle the prefixes automatically?","\nYes. write_dynamic_array_formula adds the prefixes and the spill metadata, and write_formula detects dynamic functions such as FILTER, SORT and UNIQUE and does the same.",[10,2362,2363,2366],{},[685,2364,2365],{},"Can LibreOffice calculate these formulas for testing?","\nNot reliably. Recent LibreOffice versions support the functions but do not spill dynamic arrays loaded from xlsx files, so recalculating with LibreOffice returns only the first value. Test the formula text and metadata instead, or open the file in Excel.",[280,2368,2370],{"id":2369},"related","Related",[679,2372,2373,2379,2386,2393],{},[682,2374,2375,2376,2378],{},"Up one level: ",[36,2377,39],{"href":38}," — writing and reading formulas.",[682,2380,2381,2385],{},[36,2382,2384],{"href":2383},"\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 basics of formula text.",[682,2387,2388,2392],{},[36,2389,2391],{"href":2390},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002F","Building Excel Reports with xlsxwriter"," — the library that handles dynamic arrays.",[682,2394,2395,2399],{},[36,2396,2398],{"href":2397},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fxlookup-equivalent-in-pandas\u002F","XLOOKUP Equivalent in pandas"," — when to compute in Python instead.",[2401,2402,2403],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":290,"searchDepth":334,"depth":334,"links":2405},[2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416],{"id":282,"depth":334,"text":283},{"id":498,"depth":334,"text":499},{"id":901,"depth":334,"text":902},{"id":1216,"depth":334,"text":1217},{"id":1627,"depth":334,"text":1628},{"id":1793,"depth":334,"text":1794},{"id":2253,"depth":334,"text":2254},{"id":2310,"depth":334,"text":2311},{"id":2317,"depth":334,"text":2318},{"id":2341,"depth":334,"text":2342},{"id":2369,"depth":334,"text":2370},"2026-09-23","Write spilling Excel formulas — FILTER, UNIQUE, SORT, SEQUENCE, XLOOKUP — from Python so they work when the file opens. Why openpyxl output shows #NAME? or an @, what the _xlfn prefix and array metadata are, and how xlsxwriter handles both.","md",[2421,2423,2425,2427],{"q":2347,"a":2422},"Functions added to Excel after 2010 must be stored in the file with a prefix such as _xlfn. openpyxl writes the formula text exactly as you give it, so UNIQUE without the prefix is an unknown function when the file opens.",{"q":2353,"a":2424},"The formula was saved without the metadata that marks it as a dynamic array, so Excel opens it as a legacy formula and adds the implicit intersection operator. It then returns a single value instead of spilling.",{"q":2359,"a":2426},"Yes. write_dynamic_array_formula adds the prefixes and the spill metadata, and write_formula detects dynamic functions such as FILTER, SORT and UNIQUE and does the same.",{"q":2365,"a":2428},"Not reliably. Recent LibreOffice versions support the functions but do not spill dynamic arrays loaded from xlsx files, so recalculating with LibreOffice returns only the first value. Test the formula text and metadata instead, or open the file in Excel.","write dynamic array formulas like filter and unique from python",null,{"breadcrumb":2432},[2433,2436,2439],{"name":2434,"item":2435},"Home","\u002F",{"name":2437,"item":2438},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":39,"item":38},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python",{"title":2442,"description":2443},"Write FILTER, UNIQUE and SORT Dynamic Array Formulas from Python","Dynamic array formulas written with openpyxl show #NAME? or @. Learn the _xlfn prefixes and spill metadata Excel needs, write them correctly with xlsxwriter, and check the saved XML yourself.","write-dynamic-array-formulas-with-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python\u002Findex",[2447,2448,2449],"excel-formulas","openpyxl","xlsxwriter","how-to","m7sDe3vFFef9g2842nTgERYWyC3epdQbCs_WtIxrOV8",[2453,2456],{"title":1525,"path":2454,"stem":2455,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fuse-structured-table-references-in-formulas-from-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fuse-structured-table-references-in-formulas-from-python\u002Findex",{"title":2384,"path":2457,"stem":2458,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002Findex",[2460,2462],{"path":2461,"title":2437},"\u002Fgetting-started-with-python-excel-automation",{"path":2463,"title":39},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python",[],[2466,2469,2473,2476,2479,2482],{"path":2467,"title":2468,"kind":2450},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fstore-formula-results-with-xlsxwriter","Store Formula Results with xlsxwriter So Every Viewer Shows Values",{"path":2470,"title":2471,"kind":2472},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-with-formula-errors-using-conditional-formatting","Highlight Cells with Formula Errors Using Conditional Formatting","fix",{"path":2474,"title":2475,"kind":2450},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgoal-seek-in-python-instead-of-excel","Goal Seek in Python Instead of Excel",{"path":2477,"title":2478,"kind":2450},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-that-totals-in-a-report-add-up","Check That Totals in a Report Add Up",{"path":2480,"title":2481,"kind":2450},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","Call Excel Worksheet Functions Through Microsoft Graph",{"path":2483,"title":2484,"kind":2450},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions","Recalculate Excel Online Workbooks with Graph Sessions",1790972075879]