[{"data":1,"prerenderedAt":2283},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter":2248,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter":2257,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter":2262,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter":2263},{"id":4,"title":5,"body":6,"dateModified":2215,"datePublished":2215,"description":2216,"extension":2217,"faq":2218,"keyword":2227,"kind":2228,"meta":2229,"navigation":122,"path":2238,"seo":2239,"slug":2241,"stem":2242,"topics":2243,"type":2246,"__hash__":2247},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter\u002Findex.md","Write an Excel Table with Formula Columns Using XlsxWriter",{"type":7,"value":8,"toc":2204},"minimark",[9,29,34,65,72,76,86,657,683,944,948,1120,1155,1159,1162,1353,1376,1763,1777,1866,1870,1883,2053,2057,2103,2107,2122,2126,2132,2151,2157,2167,2171,2200],[10,11,12,13,17,18,22,23,28],"p",{},"An Excel ",[14,15,16],"em",{},"table"," — the banded, filterable range created with Insert › Table — is more than formatting. Its columns have names that formulas can use (",[19,20,21],"code",{},"Orders[Net]","), a calculated column fills its formula into every row and into rows added later, and a totals row can sum, count or average whatever is currently filtered. XlsxWriter writes tables with all of that from Python, which makes generated reports easier for people to extend: they type a new order under the table and the net amount calculates itself. The details that trip people up are the table's range, which must include the header and totals rows, and the form of the structured references, because one form works in Excel only while another works in every spreadsheet application. This page writes a DataFrame as a table with formula columns and totals, compares the two reference styles, and checks the results by recalculating the file. It belongs to ",[24,25,27],"a",{"href":26},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002F","Building Excel Reports with xlsxwriter",".",[30,31,33],"h2",{"id":32},"prerequisites","Prerequisites",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlsxwriter pandas openpyxl\n","bash","",[19,42,43],{"__ignoreMap":40},[44,45,48,52,56,59,62],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"sMTad","pip",[44,53,55],{"class":54},"srMev"," install",[44,57,58],{"class":54}," xlsxwriter",[44,60,61],{"class":54}," pandas",[44,63,64],{"class":54}," openpyxl\n",[10,66,67,68,71],{},"LibreOffice (",[19,69,70],{},"soffice",") is used in the last step to recalculate the workbook, and openpyxl to read the results.",[30,73,75],{"id":74},"step-1-a-dataframe-as-a-table-with-a-formula-column","Step 1 — A DataFrame as a table, with a formula column",[10,77,78,81,82,85],{},[19,79,80],{},"add_table()"," takes the range, the data as a list of rows, and a list of column definitions. A column with a ",[19,83,84],{},"formula"," becomes a calculated column:",[35,87,91],{"className":88,"code":89,"language":90,"meta":40,"style":40},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\nimport xlsxwriter\n\norders = pd.DataFrame({\"Product\": [\"Bolts\", \"Nuts\", \"Washers\", \"Rivets\"], \"Qty\": [120, 80, 300, 45],\n                       \"Price\": [0.45, 0.12, 0.05, 1.80], \"Discount\": [0.10, 0.0, 0.05, 0.15]})\n\nwb = xlsxwriter.Workbook(\"orders_table.xlsx\")\nws = wb.add_worksheet(\"Orders\")\nmoney = wb.add_format({\"num_format\": \"#,##0.00\"})\npct = wb.add_format({\"num_format\": \"0%\"})\ncolumns = [\n    {\"header\": \"Product\", \"total_string\": \"Total\"},\n    {\"header\": \"Qty\", \"total_function\": \"sum\"},\n    {\"header\": \"Price\", \"format\": money},\n    {\"header\": \"Discount\", \"format\": pct},\n    {\"header\": \"Net\", \"format\": money, \"total_function\": \"sum\",\n     \"formula\": \"=[@Qty]*[@Price]*(1-[@Discount])\"},\n]\nlast_row = len(orders) + 1                       # header + data rows + totals row, zero-based\nws.add_table(0, 0, last_row, len(columns) - 1, {\"name\": \"Orders\", \"data\": orders.values.tolist(),\n             \"columns\": columns, \"total_row\": True, \"style\": \"Table Style Medium 2\"})\nws.set_column(0, 0, 12)\nws.set_column(1, 4, 11)\nwb.close()\nprint(f\"table Orders: A1:E{last_row + 1} ({len(orders)} data rows, header and totals)\")\n","python",[19,92,93,109,117,124,191,244,249,266,282,305,324,335,361,384,404,422,450,463,469,493,538,567,586,606,612],{"__ignoreMap":40},[44,94,95,99,103,106],{"class":46,"line":47},[44,96,98],{"class":97},"s-kum","import",[44,100,102],{"class":101},"skGVy"," pandas ",[44,104,105],{"class":97},"as",[44,107,108],{"class":101}," pd\n",[44,110,112,114],{"class":46,"line":111},2,[44,113,98],{"class":97},[44,115,116],{"class":101}," xlsxwriter\n",[44,118,120],{"class":46,"line":119},3,[44,121,123],{"emptyLinePlaceholder":122},true,"\n",[44,125,127,130,133,136,139,142,145,148,151,153,156,158,161,164,167,169,173,175,178,180,183,185,188],{"class":46,"line":126},4,[44,128,129],{"class":101},"orders ",[44,131,132],{"class":97},"=",[44,134,135],{"class":101}," pd.DataFrame({",[44,137,138],{"class":54},"\"Product\"",[44,140,141],{"class":101},": [",[44,143,144],{"class":54},"\"Bolts\"",[44,146,147],{"class":101},", ",[44,149,150],{"class":54},"\"Nuts\"",[44,152,147],{"class":101},[44,154,155],{"class":54},"\"Washers\"",[44,157,147],{"class":101},[44,159,160],{"class":54},"\"Rivets\"",[44,162,163],{"class":101},"], ",[44,165,166],{"class":54},"\"Qty\"",[44,168,141],{"class":101},[44,170,172],{"class":171},"sP0c6","120",[44,174,147],{"class":101},[44,176,177],{"class":171},"80",[44,179,147],{"class":101},[44,181,182],{"class":171},"300",[44,184,147],{"class":101},[44,186,187],{"class":171},"45",[44,189,190],{"class":101},"],\n",[44,192,194,197,199,202,204,207,209,212,214,217,219,222,224,227,229,232,234,236,238,241],{"class":46,"line":193},5,[44,195,196],{"class":54},"                       \"Price\"",[44,198,141],{"class":101},[44,200,201],{"class":171},"0.45",[44,203,147],{"class":101},[44,205,206],{"class":171},"0.12",[44,208,147],{"class":101},[44,210,211],{"class":171},"0.05",[44,213,147],{"class":101},[44,215,216],{"class":171},"1.80",[44,218,163],{"class":101},[44,220,221],{"class":54},"\"Discount\"",[44,223,141],{"class":101},[44,225,226],{"class":171},"0.10",[44,228,147],{"class":101},[44,230,231],{"class":171},"0.0",[44,233,147],{"class":101},[44,235,211],{"class":171},[44,237,147],{"class":101},[44,239,240],{"class":171},"0.15",[44,242,243],{"class":101},"]})\n",[44,245,247],{"class":46,"line":246},6,[44,248,123],{"emptyLinePlaceholder":122},[44,250,252,255,257,260,263],{"class":46,"line":251},7,[44,253,254],{"class":101},"wb ",[44,256,132],{"class":97},[44,258,259],{"class":101}," xlsxwriter.Workbook(",[44,261,262],{"class":54},"\"orders_table.xlsx\"",[44,264,265],{"class":101},")\n",[44,267,269,272,274,277,280],{"class":46,"line":268},8,[44,270,271],{"class":101},"ws ",[44,273,132],{"class":97},[44,275,276],{"class":101}," wb.add_worksheet(",[44,278,279],{"class":54},"\"Orders\"",[44,281,265],{"class":101},[44,283,285,288,290,293,296,299,302],{"class":46,"line":284},9,[44,286,287],{"class":101},"money ",[44,289,132],{"class":97},[44,291,292],{"class":101}," wb.add_format({",[44,294,295],{"class":54},"\"num_format\"",[44,297,298],{"class":101},": ",[44,300,301],{"class":54},"\"#,##0.00\"",[44,303,304],{"class":101},"})\n",[44,306,308,311,313,315,317,319,322],{"class":46,"line":307},10,[44,309,310],{"class":101},"pct ",[44,312,132],{"class":97},[44,314,292],{"class":101},[44,316,295],{"class":54},[44,318,298],{"class":101},[44,320,321],{"class":54},"\"0%\"",[44,323,304],{"class":101},[44,325,327,330,332],{"class":46,"line":326},11,[44,328,329],{"class":101},"columns ",[44,331,132],{"class":97},[44,333,334],{"class":101}," [\n",[44,336,338,341,344,346,348,350,353,355,358],{"class":46,"line":337},12,[44,339,340],{"class":101},"    {",[44,342,343],{"class":54},"\"header\"",[44,345,298],{"class":101},[44,347,138],{"class":54},[44,349,147],{"class":101},[44,351,352],{"class":54},"\"total_string\"",[44,354,298],{"class":101},[44,356,357],{"class":54},"\"Total\"",[44,359,360],{"class":101},"},\n",[44,362,364,366,368,370,372,374,377,379,382],{"class":46,"line":363},13,[44,365,340],{"class":101},[44,367,343],{"class":54},[44,369,298],{"class":101},[44,371,166],{"class":54},[44,373,147],{"class":101},[44,375,376],{"class":54},"\"total_function\"",[44,378,298],{"class":101},[44,380,381],{"class":54},"\"sum\"",[44,383,360],{"class":101},[44,385,387,389,391,393,396,398,401],{"class":46,"line":386},14,[44,388,340],{"class":101},[44,390,343],{"class":54},[44,392,298],{"class":101},[44,394,395],{"class":54},"\"Price\"",[44,397,147],{"class":101},[44,399,400],{"class":54},"\"format\"",[44,402,403],{"class":101},": money},\n",[44,405,407,409,411,413,415,417,419],{"class":46,"line":406},15,[44,408,340],{"class":101},[44,410,343],{"class":54},[44,412,298],{"class":101},[44,414,221],{"class":54},[44,416,147],{"class":101},[44,418,400],{"class":54},[44,420,421],{"class":101},": pct},\n",[44,423,425,427,429,431,434,436,438,441,443,445,447],{"class":46,"line":424},16,[44,426,340],{"class":101},[44,428,343],{"class":54},[44,430,298],{"class":101},[44,432,433],{"class":54},"\"Net\"",[44,435,147],{"class":101},[44,437,400],{"class":54},[44,439,440],{"class":101},": money, ",[44,442,376],{"class":54},[44,444,298],{"class":101},[44,446,381],{"class":54},[44,448,449],{"class":101},",\n",[44,451,453,456,458,461],{"class":46,"line":452},17,[44,454,455],{"class":54},"     \"formula\"",[44,457,298],{"class":101},[44,459,460],{"class":54},"\"=[@Qty]*[@Price]*(1-[@Discount])\"",[44,462,360],{"class":101},[44,464,466],{"class":46,"line":465},18,[44,467,468],{"class":101},"]\n",[44,470,472,475,477,480,483,486,489],{"class":46,"line":471},19,[44,473,474],{"class":101},"last_row ",[44,476,132],{"class":97},[44,478,479],{"class":171}," len",[44,481,482],{"class":101},"(orders) ",[44,484,485],{"class":97},"+",[44,487,488],{"class":171}," 1",[44,490,492],{"class":491},"s-wDw","                       # header + data rows + totals row, zero-based\n",[44,494,496,499,502,504,506,509,512,515,518,520,523,526,528,530,532,535],{"class":46,"line":495},20,[44,497,498],{"class":101},"ws.add_table(",[44,500,501],{"class":171},"0",[44,503,147],{"class":101},[44,505,501],{"class":171},[44,507,508],{"class":101},", last_row, ",[44,510,511],{"class":171},"len",[44,513,514],{"class":101},"(columns) ",[44,516,517],{"class":97},"-",[44,519,488],{"class":171},[44,521,522],{"class":101},", {",[44,524,525],{"class":54},"\"name\"",[44,527,298],{"class":101},[44,529,279],{"class":54},[44,531,147],{"class":101},[44,533,534],{"class":54},"\"data\"",[44,536,537],{"class":101},": orders.values.tolist(),\n",[44,539,541,544,547,550,552,555,557,560,562,565],{"class":46,"line":540},21,[44,542,543],{"class":54},"             \"columns\"",[44,545,546],{"class":101},": columns, ",[44,548,549],{"class":54},"\"total_row\"",[44,551,298],{"class":101},[44,553,554],{"class":171},"True",[44,556,147],{"class":101},[44,558,559],{"class":54},"\"style\"",[44,561,298],{"class":101},[44,563,564],{"class":54},"\"Table Style Medium 2\"",[44,566,304],{"class":101},[44,568,570,573,575,577,579,581,584],{"class":46,"line":569},22,[44,571,572],{"class":101},"ws.set_column(",[44,574,501],{"class":171},[44,576,147],{"class":101},[44,578,501],{"class":171},[44,580,147],{"class":101},[44,582,583],{"class":171},"12",[44,585,265],{"class":101},[44,587,589,591,594,596,599,601,604],{"class":46,"line":588},23,[44,590,572],{"class":101},[44,592,593],{"class":171},"1",[44,595,147],{"class":101},[44,597,598],{"class":171},"4",[44,600,147],{"class":101},[44,602,603],{"class":171},"11",[44,605,265],{"class":101},[44,607,609],{"class":46,"line":608},24,[44,610,611],{"class":101},"wb.close()\n",[44,613,615,618,621,624,627,631,633,635,637,640,643,645,647,650,652,655],{"class":46,"line":614},25,[44,616,617],{"class":171},"print",[44,619,620],{"class":101},"(",[44,622,623],{"class":97},"f",[44,625,626],{"class":54},"\"table Orders: A1:E",[44,628,630],{"class":629},"sSjpA","{",[44,632,474],{"class":101},[44,634,485],{"class":97},[44,636,488],{"class":171},[44,638,639],{"class":629},"}",[44,641,642],{"class":54}," (",[44,644,630],{"class":629},[44,646,511],{"class":171},[44,648,649],{"class":101},"(orders)",[44,651,639],{"class":629},[44,653,654],{"class":54}," data rows, header and totals)\"",[44,656,265],{"class":101},[10,658,659,660,662,663,666,667,670,671,674,675,678,679,682],{},"The range passed to ",[19,661,80],{}," must cover the header row, every data row and the totals row: with four orders that is rows 0 to 5, zero-based, or A1:E6. Getting it one row short is the common mistake — the last data row silently falls outside the table, or worse, ends up as the totals row. The DataFrame contributes only its values (",[19,664,665],{},"orders.values.tolist()","); the headers come from the column definitions, which is also where per-column formats live. The ",[14,668,669],{},"Net"," column has no data, only a formula, written with Excel's ",[19,672,673],{},"[@Column]"," \"this row\" shorthand. Table names follow the rules for defined names — letters, digits and underscores, starting with a letter, unique in the workbook — so ",[19,676,677],{},"Orders"," works but ",[19,680,681],{},"Q3 orders"," does not; choosing a meaningful name pays off every time someone writes a formula against the table.",[684,685,693,694,693,698,693,702,693,709,693,718,693,722,693,729,693,733,693,737,693,741,693,745,693,748,693,752,693,756,693,760,693,764,693,768,693,773,693,777,693,779,693,782,693,784,693,787,693,789,693,792,693,794,693,796,693,799,693,803,693,805,693,809,693,811,693,813,693,815,693,817,693,819,693,822,693,826,693,830,693,833,693,837,693,839,693,842,693,844,693,846,693,848,693,850,693,852,693,855,693,857,693,859,693,862,693,865,693,867,693,870,693,872,693,874,693,876,693,878,693,880,693,883,693,885,693,887,693,890,693,894,693,896,693,899,693,901,693,903,693,905,693,907,693,909,693,912,693,914,693,916,693,919,693,923,693,927,693,931,693,933,693,936,693,938,693,940,693,942],"svg",{"viewBox":686,"role":687,"ariaLabelledBy":688,"xmlns":691,"style":692},"146 0 468 224","img",[689,690],"xt-table-t","xt-table-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:468px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[695,696,697],"title",{"id":689},"The table as written",[699,700,701],"desc",{"id":690},"A header row, four orders with product, quantity, price and discount, a calculated Net column filled with the same structured-reference formula in every row, and a totals row with the label Total and sums under Qty and Net.",[703,704],"rect",{"x":705,"y":501,"width":706,"height":707,"fill":708},"146","468","224","#ffffff",[703,710],{"x":711,"y":712,"width":713,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"162.5","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[703,719],{"x":720,"y":712,"width":721,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"194.5","79.0",[723,724,728],"text",{"x":725,"y":726,"style":727},"234.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[703,730],{"x":731,"y":712,"width":732,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"273.5","92.9",[723,734,736],{"x":735,"y":726,"style":727},"319.9","B",[703,738],{"x":739,"y":712,"width":740,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"366.4","55.6",[723,742,744],{"x":743,"y":726,"style":727},"394.2","C",[703,746],{"x":747,"y":712,"width":721,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"422.0",[723,749,751],{"x":750,"y":726,"style":727},"461.5","D",[703,753],{"x":754,"y":712,"width":755,"height":714,"rx":501,"fill":715,"stroke":716,"style":717},"501.0","96.5",[723,757,759],{"x":758,"y":726,"style":727},"549.3","E",[703,761],{"x":711,"y":762,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"40.0","28.0",[723,765,593],{"x":766,"y":767,"style":727},"178.5","59.0",[703,769],{"x":720,"y":762,"width":721,"height":763,"rx":501,"fill":770,"stroke":771,"style":772},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[723,774,776],{"x":725,"y":767,"style":775},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Product",[703,778],{"x":731,"y":762,"width":732,"height":763,"rx":501,"fill":770,"stroke":771,"style":772},[723,780,781],{"x":735,"y":767,"style":775},"Qty",[703,783],{"x":739,"y":762,"width":740,"height":763,"rx":501,"fill":770,"stroke":771,"style":772},[723,785,786],{"x":743,"y":767,"style":775},"Price",[703,788],{"x":747,"y":762,"width":721,"height":763,"rx":501,"fill":770,"stroke":771,"style":772},[723,790,791],{"x":750,"y":767,"style":775},"Discount",[703,793],{"x":754,"y":762,"width":755,"height":763,"rx":501,"fill":770,"stroke":771,"style":772},[723,795,669],{"x":758,"y":767,"style":775},[703,797],{"x":711,"y":798,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"68.0",[723,800,802],{"x":766,"y":801,"style":727},"87.0","2",[703,804],{"x":720,"y":798,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,806,808],{"x":725,"y":801,"style":807},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Bolts",[703,810],{"x":731,"y":798,"width":732,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,812,172],{"x":735,"y":801,"style":807},[703,814],{"x":739,"y":798,"width":740,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,816,201],{"x":743,"y":801,"style":807},[703,818],{"x":747,"y":798,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,820,821],{"x":750,"y":801,"style":807},"10%",[703,823],{"x":754,"y":798,"width":755,"height":763,"rx":501,"fill":824,"stroke":825,"style":772},"#d9f4f1","var(--teal,#0f9488)",[723,827,829],{"x":758,"y":801,"style":828},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","=[@Qty]*…",[703,831],{"x":711,"y":832,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"96.0",[723,834,836],{"x":766,"y":835,"style":727},"115.0","3",[703,838],{"x":720,"y":832,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,840,841],{"x":725,"y":835,"style":807},"Nuts",[703,843],{"x":731,"y":832,"width":732,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,845,177],{"x":735,"y":835,"style":807},[703,847],{"x":739,"y":832,"width":740,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,849,206],{"x":743,"y":835,"style":807},[703,851],{"x":747,"y":832,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,853,854],{"x":750,"y":835,"style":807},"0%",[703,856],{"x":754,"y":832,"width":755,"height":763,"rx":501,"fill":824,"stroke":825,"style":772},[723,858,829],{"x":758,"y":835,"style":828},[703,860],{"x":711,"y":861,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"124.0",[723,863,598],{"x":766,"y":864,"style":727},"143.0",[703,866],{"x":720,"y":861,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,868,869],{"x":725,"y":864,"style":807},"Washers",[703,871],{"x":731,"y":861,"width":732,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,873,182],{"x":735,"y":864,"style":807},[703,875],{"x":739,"y":861,"width":740,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,877,211],{"x":743,"y":864,"style":807},[703,879],{"x":747,"y":861,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,881,882],{"x":750,"y":864,"style":807},"5%",[703,884],{"x":754,"y":861,"width":755,"height":763,"rx":501,"fill":824,"stroke":825,"style":772},[723,886,829],{"x":758,"y":864,"style":828},[703,888],{"x":711,"y":889,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"152.0",[723,891,893],{"x":766,"y":892,"style":727},"171.0","5",[703,895],{"x":720,"y":889,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,897,898],{"x":725,"y":892,"style":807},"Rivets",[703,900],{"x":731,"y":889,"width":732,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,902,187],{"x":735,"y":892,"style":807},[703,904],{"x":739,"y":889,"width":740,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,906,216],{"x":743,"y":892,"style":807},[703,908],{"x":747,"y":889,"width":721,"height":763,"rx":501,"fill":708,"stroke":716,"style":717},[723,910,911],{"x":750,"y":892,"style":807},"15%",[703,913],{"x":754,"y":889,"width":755,"height":763,"rx":501,"fill":824,"stroke":825,"style":772},[723,915,829],{"x":758,"y":892,"style":828},[703,917],{"x":711,"y":918,"width":713,"height":763,"rx":501,"fill":715,"stroke":716,"style":717},"180.0",[723,920,922],{"x":766,"y":921,"style":727},"199.0","6",[703,924],{"x":720,"y":918,"width":721,"height":763,"rx":501,"fill":925,"stroke":926,"style":772},"#fdefd8","var(--gold,#b4740a)",[723,928,930],{"x":725,"y":921,"style":929},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Total",[703,932],{"x":731,"y":918,"width":732,"height":763,"rx":501,"fill":925,"stroke":926,"style":772},[723,934,935],{"x":735,"y":921,"style":929},"SUBTOTAL",[703,937],{"x":739,"y":918,"width":740,"height":763,"rx":501,"fill":925,"stroke":926,"style":772},[703,939],{"x":747,"y":918,"width":721,"height":763,"rx":501,"fill":925,"stroke":926,"style":772},[703,941],{"x":754,"y":918,"width":755,"height":763,"rx":501,"fill":925,"stroke":926,"style":772},[723,943,935],{"x":758,"y":921,"style":929},[30,945,947],{"id":946},"step-2-what-actually-ends-up-in-the-file","Step 2 — What actually ends up in the file",[35,949,951],{"className":88,"code":950,"language":90,"meta":40,"style":40},"import zipfile\n\nwith zipfile.ZipFile(\"orders_table.xlsx\") as z:\n    sheet = z.read(\"xl\u002Fworksheets\u002Fsheet1.xml\").decode()\n    table = z.read(\"xl\u002Ftables\u002Ftable1.xml\").decode()\ncell = sheet[sheet.find('\u003Cc r=\"E2\"'):]\nprint(\"E2 formula:\", cell[cell.find(\"\u003Cf>\") + 3:cell.find(\"\u003C\u002Ff>\")])\ntotal = sheet[sheet.find('\u003Cc r=\"E6\"'):]\nprint(\"E6 formula:\", total[total.find(\"\u003Cf>\") + 3:total.find(\"\u003C\u002Ff>\")])\nprint(\"calculated column stored:\", \"\u003CcalculatedColumnFormula>\" in table)\n",[19,952,953,960,964,982,998,1012,1028,1059,1073,1100],{"__ignoreMap":40},[44,954,955,957],{"class":46,"line":47},[44,956,98],{"class":97},[44,958,959],{"class":101}," zipfile\n",[44,961,962],{"class":46,"line":111},[44,963,123],{"emptyLinePlaceholder":122},[44,965,966,969,972,974,977,979],{"class":46,"line":119},[44,967,968],{"class":97},"with",[44,970,971],{"class":101}," zipfile.ZipFile(",[44,973,262],{"class":54},[44,975,976],{"class":101},") ",[44,978,105],{"class":97},[44,980,981],{"class":101}," z:\n",[44,983,984,987,989,992,995],{"class":46,"line":126},[44,985,986],{"class":101},"    sheet ",[44,988,132],{"class":97},[44,990,991],{"class":101}," z.read(",[44,993,994],{"class":54},"\"xl\u002Fworksheets\u002Fsheet1.xml\"",[44,996,997],{"class":101},").decode()\n",[44,999,1000,1003,1005,1007,1010],{"class":46,"line":193},[44,1001,1002],{"class":101},"    table ",[44,1004,132],{"class":97},[44,1006,991],{"class":101},[44,1008,1009],{"class":54},"\"xl\u002Ftables\u002Ftable1.xml\"",[44,1011,997],{"class":101},[44,1013,1014,1017,1019,1022,1025],{"class":46,"line":246},[44,1015,1016],{"class":101},"cell ",[44,1018,132],{"class":97},[44,1020,1021],{"class":101}," sheet[sheet.find(",[44,1023,1024],{"class":54},"'\u003Cc r=\"E2\"'",[44,1026,1027],{"class":101},"):]\n",[44,1029,1030,1032,1034,1037,1040,1043,1045,1047,1050,1053,1056],{"class":46,"line":251},[44,1031,617],{"class":171},[44,1033,620],{"class":101},[44,1035,1036],{"class":54},"\"E2 formula:\"",[44,1038,1039],{"class":101},", cell[cell.find(",[44,1041,1042],{"class":54},"\"\u003Cf>\"",[44,1044,976],{"class":101},[44,1046,485],{"class":97},[44,1048,1049],{"class":171}," 3",[44,1051,1052],{"class":101},":cell.find(",[44,1054,1055],{"class":54},"\"\u003C\u002Ff>\"",[44,1057,1058],{"class":101},")])\n",[44,1060,1061,1064,1066,1068,1071],{"class":46,"line":268},[44,1062,1063],{"class":101},"total ",[44,1065,132],{"class":97},[44,1067,1021],{"class":101},[44,1069,1070],{"class":54},"'\u003Cc r=\"E6\"'",[44,1072,1027],{"class":101},[44,1074,1075,1077,1079,1082,1085,1087,1089,1091,1093,1096,1098],{"class":46,"line":284},[44,1076,617],{"class":171},[44,1078,620],{"class":101},[44,1080,1081],{"class":54},"\"E6 formula:\"",[44,1083,1084],{"class":101},", total[total.find(",[44,1086,1042],{"class":54},[44,1088,976],{"class":101},[44,1090,485],{"class":97},[44,1092,1049],{"class":171},[44,1094,1095],{"class":101},":total.find(",[44,1097,1055],{"class":54},[44,1099,1058],{"class":101},[44,1101,1102,1104,1106,1109,1111,1114,1117],{"class":46,"line":307},[44,1103,617],{"class":171},[44,1105,620],{"class":101},[44,1107,1108],{"class":54},"\"calculated column stored:\"",[44,1110,147],{"class":101},[44,1112,1113],{"class":54},"\"\u003CcalculatedColumnFormula>\"",[44,1115,1116],{"class":97}," in",[44,1118,1119],{"class":101}," table)\n",[10,1121,1122,1123,1126,1127,1130,1131,1134,1135,1138,1139,1142,1143,1146,1147,1150,1151,28],{},"XlsxWriter translates ",[19,1124,1125],{},"[@Qty]"," into the form Excel 2010 and later save, ",[19,1128,1129],{},"[[#This Row],Qty]"," — a reference to the current row ",[14,1132,1133],{},"without"," the table name, which Excel resolves from the table the cell is in. The built-in ",[19,1136,1137],{},"sum"," total becomes ",[19,1140,1141],{},"SUBTOTAL(109,[Net])","; function 109 sums only visible rows, so the total follows the table's filters. The formula is also recorded as the column's ",[19,1144,1145],{},"calculatedColumnFormula",", which is what makes Excel fill it into new rows typed under the table. XlsxWriter cannot calculate formulas, so it stores 0 as each formula's last result; Excel recalculates on opening, but anything that reads cached values — pandas, openpyxl with ",[19,1148,1149],{},"data_only=True"," — sees zeros until the file has been opened and saved in a spreadsheet application. The options for that are covered in ",[24,1152,1154],{"href":1153},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fstore-formula-results-with-xlsxwriter\u002F","Store Formula Results with xlsxwriter",[30,1156,1158],{"id":1157},"step-3-references-that-work-everywhere","Step 3 — References that work everywhere",[10,1160,1161],{},"The shortened references are valid Excel, but not every application resolves a reference without its table name. Recalculate the file in LibreOffice and look:",[35,1163,1165],{"className":88,"code":1164,"language":90,"meta":40,"style":40},"import subprocess\nfrom openpyxl import load_workbook\n\ndef recalculated(path):\n    load_workbook(path).save(\"roundtrip.xlsx\")            # drop XlsxWriter's cached zeros so LibreOffice recalculates\n    subprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"calc\", \"roundtrip.xlsx\"], check=True, capture_output=True)\n    return [row for row in load_workbook(\"calc\u002Froundtrip.xlsx\", data_only=True)[\"Orders\"].iter_rows(values_only=True)]\n\nfor row in recalculated(\"orders_table.xlsx\"):\n    print(row)\n",[19,1166,1167,1174,1187,1191,1203,1217,1275,1325,1329,1345],{"__ignoreMap":40},[44,1168,1169,1171],{"class":46,"line":47},[44,1170,98],{"class":97},[44,1172,1173],{"class":101}," subprocess\n",[44,1175,1176,1179,1182,1184],{"class":46,"line":111},[44,1177,1178],{"class":97},"from",[44,1180,1181],{"class":101}," openpyxl ",[44,1183,98],{"class":97},[44,1185,1186],{"class":101}," load_workbook\n",[44,1188,1189],{"class":46,"line":119},[44,1190,123],{"emptyLinePlaceholder":122},[44,1192,1193,1196,1200],{"class":46,"line":126},[44,1194,1195],{"class":97},"def",[44,1197,1199],{"class":1198},"s_Opv"," recalculated",[44,1201,1202],{"class":101},"(path):\n",[44,1204,1205,1208,1211,1214],{"class":46,"line":193},[44,1206,1207],{"class":101},"    load_workbook(path).save(",[44,1209,1210],{"class":54},"\"roundtrip.xlsx\"",[44,1212,1213],{"class":101},")            ",[44,1215,1216],{"class":491},"# drop XlsxWriter's cached zeros so LibreOffice recalculates\n",[44,1218,1219,1222,1225,1227,1230,1232,1235,1237,1240,1242,1245,1247,1250,1252,1254,1256,1260,1262,1264,1266,1269,1271,1273],{"class":46,"line":246},[44,1220,1221],{"class":101},"    subprocess.run([",[44,1223,1224],{"class":54},"\"soffice\"",[44,1226,147],{"class":101},[44,1228,1229],{"class":54},"\"--headless\"",[44,1231,147],{"class":101},[44,1233,1234],{"class":54},"\"--convert-to\"",[44,1236,147],{"class":101},[44,1238,1239],{"class":54},"\"xlsx\"",[44,1241,147],{"class":101},[44,1243,1244],{"class":54},"\"--outdir\"",[44,1246,147],{"class":101},[44,1248,1249],{"class":54},"\"calc\"",[44,1251,147],{"class":101},[44,1253,1210],{"class":54},[44,1255,163],{"class":101},[44,1257,1259],{"class":1258},"sa561","check",[44,1261,132],{"class":97},[44,1263,554],{"class":171},[44,1265,147],{"class":101},[44,1267,1268],{"class":1258},"capture_output",[44,1270,132],{"class":97},[44,1272,554],{"class":171},[44,1274,265],{"class":101},[44,1276,1277,1280,1283,1286,1289,1292,1295,1298,1300,1303,1305,1307,1310,1312,1315,1318,1320,1322],{"class":46,"line":251},[44,1278,1279],{"class":97},"    return",[44,1281,1282],{"class":101}," [row ",[44,1284,1285],{"class":97},"for",[44,1287,1288],{"class":101}," row ",[44,1290,1291],{"class":97},"in",[44,1293,1294],{"class":101}," load_workbook(",[44,1296,1297],{"class":54},"\"calc\u002Froundtrip.xlsx\"",[44,1299,147],{"class":101},[44,1301,1302],{"class":1258},"data_only",[44,1304,132],{"class":97},[44,1306,554],{"class":171},[44,1308,1309],{"class":101},")[",[44,1311,279],{"class":54},[44,1313,1314],{"class":101},"].iter_rows(",[44,1316,1317],{"class":1258},"values_only",[44,1319,132],{"class":97},[44,1321,554],{"class":171},[44,1323,1324],{"class":101},")]\n",[44,1326,1327],{"class":46,"line":268},[44,1328,123],{"emptyLinePlaceholder":122},[44,1330,1331,1333,1335,1337,1340,1342],{"class":46,"line":284},[44,1332,1285],{"class":97},[44,1334,1288],{"class":101},[44,1336,1291],{"class":97},[44,1338,1339],{"class":101}," recalculated(",[44,1341,262],{"class":54},[44,1343,1344],{"class":101},"):\n",[44,1346,1347,1350],{"class":46,"line":307},[44,1348,1349],{"class":171},"    print",[44,1351,1352],{"class":101},"(row)\n",[10,1354,1355,1356,1359,1360,1363,1364,1367,1368,1371,1372,1375],{},"The ",[19,1357,1358],{},"recalculated()"," helper is a testing trick, not something to ship: re-saving with openpyxl removes the cached zeros, which makes LibreOffice compute every formula on load, and the converted copy is then read back with its calculated values. The Net column shows ",[19,1361,1362],{},"#N\u002FA"," and the totals ",[19,1365,1366],{},"#NAME?",": LibreOffice does not resolve the shortened, table-less references. Writing the references in full — ",[19,1369,1370],{},"Orders[[#This Row],[Qty]]"," for the row formula and an explicit ",[19,1373,1374],{},"=SUBTOTAL(109,Orders[Net])"," as a custom total — gives formulas that Excel and LibreOffice both calculate:",[35,1377,1379],{"className":88,"code":1378,"language":90,"meta":40,"style":40},"columns[1][\"total_function\"] = \"=SUBTOTAL(109,Orders[Qty])\"\ncolumns[4][\"total_function\"] = \"=SUBTOTAL(109,Orders[Net])\"\ncolumns[4][\"formula\"] = \"=Orders[[#This Row],[Qty]]*Orders[[#This Row],[Price]]*(1-Orders[[#This Row],[Discount]])\"\nwb = xlsxwriter.Workbook(\"orders_table.xlsx\")\nws = wb.add_worksheet(\"Orders\")\nfor c in columns:                                        # formats belong to this workbook\n    if \"format\" in c:\n        c[\"format\"] = wb.add_format({\"num_format\": \"#,##0.00\" if c[\"header\"] != \"Discount\" else \"0%\"})\nws.add_table(0, 0, last_row, len(columns) - 1, {\"name\": \"Orders\", \"data\": orders.values.tolist(),\n             \"columns\": columns, \"total_row\": True, \"style\": \"Table Style Medium 2\"})\nwb.close()\n\nrows = recalculated(\"orders_table.xlsx\")\nfor row in rows:\n    print(row)\nexpected = (orders[\"Qty\"] * orders[\"Price\"] * (1 - orders[\"Discount\"])).round(2)\nprint(\"Net matches pandas:\", [round(r[4], 2) for r in rows[1:-1]] == expected.tolist(), \"| total\", round(rows[-1][4], 2), \"vs\", round(expected.sum(), 2))\n",[19,1380,1381,1401,1418,1436,1448,1460,1475,1488,1531,1565,1587,1591,1595,1608,1619,1625,1669],{"__ignoreMap":40},[44,1382,1383,1386,1388,1391,1393,1396,1398],{"class":46,"line":47},[44,1384,1385],{"class":101},"columns[",[44,1387,593],{"class":171},[44,1389,1390],{"class":101},"][",[44,1392,376],{"class":54},[44,1394,1395],{"class":101},"] ",[44,1397,132],{"class":97},[44,1399,1400],{"class":54}," \"=SUBTOTAL(109,Orders[Qty])\"\n",[44,1402,1403,1405,1407,1409,1411,1413,1415],{"class":46,"line":111},[44,1404,1385],{"class":101},[44,1406,598],{"class":171},[44,1408,1390],{"class":101},[44,1410,376],{"class":54},[44,1412,1395],{"class":101},[44,1414,132],{"class":97},[44,1416,1417],{"class":54}," \"=SUBTOTAL(109,Orders[Net])\"\n",[44,1419,1420,1422,1424,1426,1429,1431,1433],{"class":46,"line":119},[44,1421,1385],{"class":101},[44,1423,598],{"class":171},[44,1425,1390],{"class":101},[44,1427,1428],{"class":54},"\"formula\"",[44,1430,1395],{"class":101},[44,1432,132],{"class":97},[44,1434,1435],{"class":54}," \"=Orders[[#This Row],[Qty]]*Orders[[#This Row],[Price]]*(1-Orders[[#This Row],[Discount]])\"\n",[44,1437,1438,1440,1442,1444,1446],{"class":46,"line":126},[44,1439,254],{"class":101},[44,1441,132],{"class":97},[44,1443,259],{"class":101},[44,1445,262],{"class":54},[44,1447,265],{"class":101},[44,1449,1450,1452,1454,1456,1458],{"class":46,"line":193},[44,1451,271],{"class":101},[44,1453,132],{"class":97},[44,1455,276],{"class":101},[44,1457,279],{"class":54},[44,1459,265],{"class":101},[44,1461,1462,1464,1467,1469,1472],{"class":46,"line":246},[44,1463,1285],{"class":97},[44,1465,1466],{"class":101}," c ",[44,1468,1291],{"class":97},[44,1470,1471],{"class":101}," columns:                                        ",[44,1473,1474],{"class":491},"# formats belong to this workbook\n",[44,1476,1477,1480,1483,1485],{"class":46,"line":251},[44,1478,1479],{"class":97},"    if",[44,1481,1482],{"class":54}," \"format\"",[44,1484,1116],{"class":97},[44,1486,1487],{"class":101}," c:\n",[44,1489,1490,1493,1495,1497,1499,1501,1503,1505,1507,1510,1513,1515,1517,1520,1523,1526,1529],{"class":46,"line":268},[44,1491,1492],{"class":101},"        c[",[44,1494,400],{"class":54},[44,1496,1395],{"class":101},[44,1498,132],{"class":97},[44,1500,292],{"class":101},[44,1502,295],{"class":54},[44,1504,298],{"class":101},[44,1506,301],{"class":54},[44,1508,1509],{"class":97}," if",[44,1511,1512],{"class":101}," c[",[44,1514,343],{"class":54},[44,1516,1395],{"class":101},[44,1518,1519],{"class":97},"!=",[44,1521,1522],{"class":54}," \"Discount\"",[44,1524,1525],{"class":97}," else",[44,1527,1528],{"class":54}," \"0%\"",[44,1530,304],{"class":101},[44,1532,1533,1535,1537,1539,1541,1543,1545,1547,1549,1551,1553,1555,1557,1559,1561,1563],{"class":46,"line":284},[44,1534,498],{"class":101},[44,1536,501],{"class":171},[44,1538,147],{"class":101},[44,1540,501],{"class":171},[44,1542,508],{"class":101},[44,1544,511],{"class":171},[44,1546,514],{"class":101},[44,1548,517],{"class":97},[44,1550,488],{"class":171},[44,1552,522],{"class":101},[44,1554,525],{"class":54},[44,1556,298],{"class":101},[44,1558,279],{"class":54},[44,1560,147],{"class":101},[44,1562,534],{"class":54},[44,1564,537],{"class":101},[44,1566,1567,1569,1571,1573,1575,1577,1579,1581,1583,1585],{"class":46,"line":307},[44,1568,543],{"class":54},[44,1570,546],{"class":101},[44,1572,549],{"class":54},[44,1574,298],{"class":101},[44,1576,554],{"class":171},[44,1578,147],{"class":101},[44,1580,559],{"class":54},[44,1582,298],{"class":101},[44,1584,564],{"class":54},[44,1586,304],{"class":101},[44,1588,1589],{"class":46,"line":326},[44,1590,611],{"class":101},[44,1592,1593],{"class":46,"line":337},[44,1594,123],{"emptyLinePlaceholder":122},[44,1596,1597,1600,1602,1604,1606],{"class":46,"line":363},[44,1598,1599],{"class":101},"rows ",[44,1601,132],{"class":97},[44,1603,1339],{"class":101},[44,1605,262],{"class":54},[44,1607,265],{"class":101},[44,1609,1610,1612,1614,1616],{"class":46,"line":386},[44,1611,1285],{"class":97},[44,1613,1288],{"class":101},[44,1615,1291],{"class":97},[44,1617,1618],{"class":101}," rows:\n",[44,1620,1621,1623],{"class":46,"line":406},[44,1622,1349],{"class":171},[44,1624,1352],{"class":101},[44,1626,1627,1630,1632,1635,1637,1639,1642,1645,1647,1649,1651,1653,1655,1658,1660,1662,1665,1667],{"class":46,"line":424},[44,1628,1629],{"class":101},"expected ",[44,1631,132],{"class":97},[44,1633,1634],{"class":101}," (orders[",[44,1636,166],{"class":54},[44,1638,1395],{"class":101},[44,1640,1641],{"class":97},"*",[44,1643,1644],{"class":101}," orders[",[44,1646,395],{"class":54},[44,1648,1395],{"class":101},[44,1650,1641],{"class":97},[44,1652,642],{"class":101},[44,1654,593],{"class":171},[44,1656,1657],{"class":97}," -",[44,1659,1644],{"class":101},[44,1661,221],{"class":54},[44,1663,1664],{"class":101},"])).round(",[44,1666,802],{"class":171},[44,1668,265],{"class":101},[44,1670,1671,1673,1675,1678,1681,1684,1687,1689,1691,1693,1695,1697,1700,1702,1705,1707,1710,1712,1714,1717,1720,1723,1726,1728,1730,1733,1735,1737,1739,1741,1743,1745,1748,1751,1753,1755,1758,1760],{"class":46,"line":452},[44,1672,617],{"class":171},[44,1674,620],{"class":101},[44,1676,1677],{"class":54},"\"Net matches pandas:\"",[44,1679,1680],{"class":101},", [",[44,1682,1683],{"class":171},"round",[44,1685,1686],{"class":101},"(r[",[44,1688,598],{"class":171},[44,1690,163],{"class":101},[44,1692,802],{"class":171},[44,1694,976],{"class":101},[44,1696,1285],{"class":97},[44,1698,1699],{"class":101}," r ",[44,1701,1291],{"class":97},[44,1703,1704],{"class":101}," rows[",[44,1706,593],{"class":171},[44,1708,1709],{"class":101},":",[44,1711,517],{"class":97},[44,1713,593],{"class":171},[44,1715,1716],{"class":101},"]] ",[44,1718,1719],{"class":97},"==",[44,1721,1722],{"class":101}," expected.tolist(), ",[44,1724,1725],{"class":54},"\"| total\"",[44,1727,147],{"class":101},[44,1729,1683],{"class":171},[44,1731,1732],{"class":101},"(rows[",[44,1734,517],{"class":97},[44,1736,593],{"class":171},[44,1738,1390],{"class":101},[44,1740,598],{"class":171},[44,1742,163],{"class":101},[44,1744,802],{"class":171},[44,1746,1747],{"class":101},"), ",[44,1749,1750],{"class":54},"\"vs\"",[44,1752,147],{"class":101},[44,1754,1683],{"class":171},[44,1756,1757],{"class":101},"(expected.sum(), ",[44,1759,802],{"class":171},[44,1761,1762],{"class":101},"))\n",[10,1764,1765,1766,1768,1769,1772,1773,28],{},"Every Net value now calculates and matches the same calculation in pandas, and the totals row sums both columns. A string starting with ",[19,1767,132],{}," as ",[19,1770,1771],{},"total_function"," is written as a custom total formula. Excel treats the full references exactly like the short ones — it may even display them in the short form — so nothing is lost for Excel users, and the workbook also works for anyone opening it in LibreOffice or reading it with tools that evaluate formulas. Table references in openpyxl-built workbooks follow the same rules, as covered in ",[24,1774,1776],{"href":1775},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fuse-structured-table-references-in-formulas-from-python\u002F","Use Structured Table References in Formulas from Python",[684,1778,693,1784,693,1787,693,1790,693,1794,693,1801,693,1812,693,1818,693,1823,693,1827,693,1830,693,1834,693,1838,693,1841,693,1848,693,1852,693,1856,693,1859,693,1861,693,1863],{"viewBox":1779,"role":687,"ariaLabelledBy":1780,"xmlns":691,"style":1783},"4 0 752 233",[1781,1782],"xt-refs-t","xt-refs-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[695,1785,1786],{"id":1781},"Short or fully qualified structured references",[699,1788,1789],{"id":1782},"The shorthand [@Qty] is saved as a reference without the table name and the built-in sum total as SUBTOTAL of a table-less column; Excel resolves both but LibreOffice shows errors. Writing Orders[[#This Row],[Qty]] and SUBTOTAL(109,Orders[Net]) calculates in both.",[703,1791],{"x":598,"y":501,"width":1792,"height":1793,"fill":708},"752","233",[703,1795],{"x":1796,"y":712,"width":1797,"height":1798,"rx":1799,"fill":925,"stroke":926,"style":1800},"20.0","351.0","201.0","14","stroke-width:2px",[1802,1803,1805],"g",{"transform":1804},"translate(182.5,29.0) scale(1.083)",[1806,1807],"path",{"d":1808,"fill":1809,"stroke":1810,"style":1811},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","none","var(--gold-ink,#7a4e06)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[723,1813,1817],{"x":1814,"y":1815,"style":1816},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Shorthand [@Qty], total 'sum'",[46,1819],{"x1":1820,"y1":1821,"x2":1822,"y2":1821,"stroke":926,"style":717},"36.0","91.0","355.0",[723,1824,1826],{"x":1814,"y":1825,"style":807},"111.0","saved as [[#This Row],Qty]",[723,1828,1141],{"x":1814,"y":1829,"style":807},"137.0",[723,1831,1833],{"x":1814,"y":1832,"style":807},"163.0","Excel calculates",[723,1835,1837],{"x":1814,"y":1836,"style":807},"189.0","LibreOffice: #N\u002FA, #NAME?",[703,1839],{"x":1840,"y":712,"width":1797,"height":1798,"rx":1799,"fill":824,"stroke":825,"style":1800},"389.0",[1802,1842,1844],{"transform":1843},"translate(551.5,29.0) scale(1.083)",[1806,1845],{"d":1846,"fill":1809,"stroke":1847,"style":1811},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[723,1849,1370],{"x":1850,"y":1815,"style":1851},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[46,1853],{"x1":1854,"y1":1821,"x2":1855,"y2":1821,"stroke":825,"style":717},"405.0","724.0",[723,1857,1858],{"x":1850,"y":1825,"style":807},"table name included",[723,1860,1374],{"x":1850,"y":1829,"style":807},[723,1862,1833],{"x":1850,"y":1832,"style":807},[723,1864,1865],{"x":1850,"y":1836,"style":807},"LibreOffice calculates",[30,1867,1869],{"id":1868},"letting-people-extend-the-table","Letting people extend the table",[10,1871,1872,1873,1875,1876,1878,1879,1882],{},"The point of writing a table rather than a plain range is what happens after the file leaves Python. In Excel, typing a new product in the row directly below the last data row — above the totals row, which moves down — extends the table automatically: the new row takes the table's banding and column formats, the ",[14,1874,669],{}," column fills in from the stored calculated-column formula, and the totals include the new row. Formulas elsewhere that refer to ",[19,1877,21],{}," include it too, with no ranges to edit. That makes tables the right structure for any generated workbook people are expected to add to — order logs, budget lines, issue trackers. Data validation on input columns, added with ",[19,1880,1881],{},"ws.data_validation()"," over the table's data rows, keeps those additions clean; new rows in a table inherit validation from the rows above.",[684,1884,693,1889,693,1892,693,1895,693,1898,693,1902,693,1910,693,1916,693,1920,693,1925,693,1929,693,1934,693,1939,693,1942,693,1949,693,1955,693,1959,693,1963,693,1967,693,1971,693,1975,693,1978,693,1981,693,1987,693,1991,693,1994,693,1997,693,2000,693,2004,693,2007,693,2010,693,2016,693,2021,693,2024,693,2028,693,2031,693,2034,693,2039,693,2044,693,2047,693,2050],{"viewBox":1885,"role":687,"ariaLabelledBy":1886,"xmlns":691,"style":1783},"4 0 752 178",[1887,1888],"xt-check-t","xt-check-d",[695,1890,1891],{"id":1887},"Checking the table's formulas from Python",[699,1893,1894],{"id":1888},"Write the table with XlsxWriter, inspect the sheet and table XML for the formulas and the calculated-column definition, re-save with openpyxl to drop cached zeros, recalculate with LibreOffice, and compare every calculated value with pandas.",[703,1896],{"x":598,"y":501,"width":1792,"height":1897,"fill":708},"178",[703,1899],{"x":1796,"y":712,"width":1900,"height":1901,"rx":583,"fill":770,"stroke":771,"style":1800},"115.2","145.5",[1802,1903,1905],{"transform":1904},"translate(65.6,37.8) scale(1.000)",[1806,1906],{"d":1907,"fill":1809,"stroke":1908,"style":1909},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[723,1911,1915],{"x":1912,"y":1913,"style":1914},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Write with",[723,1917,1919],{"x":1912,"y":1918,"style":1914},"101.8","XlsxWriter",[723,1921,1924],{"x":1912,"y":1922,"style":1923},"121.2","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","add_table,",[723,1926,1928],{"x":1912,"y":1927,"style":1923},"138.8","formulas",[46,1930],{"x1":1931,"y1":1932,"x2":1933,"y2":1932,"stroke":771,"style":1800},"140.2","88.8","162.2",[1935,1936],"polygon",{"points":1937,"fill":1938},"166.2,88.8 157.2,93.8 157.2,83.8","#5b5cf0",[703,1940],{"x":1941,"y":712,"width":1900,"height":1901,"rx":583,"fill":715,"stroke":716,"style":1800},"171.2",[1802,1943,1945],{"transform":1944},"translate(216.8,29.0) scale(1.000)",[1806,1946],{"d":1947,"fill":1809,"stroke":1948,"style":1909},"M9 8l-4 4 4 4 M15 8l4 4-4 4","var(--text,#172033)",[723,1950,1954],{"x":1951,"y":1952,"style":1953},"228.8","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Inspect the",[723,1956,1958],{"x":1951,"y":1957,"style":1953},"93.0","XML",[723,1960,1962],{"x":1951,"y":1961,"style":1923},"112.5","formulas,",[723,1964,1966],{"x":1951,"y":1965,"style":1923},"130.0","calculated",[723,1968,1970],{"x":1951,"y":1969,"style":1923},"147.5","column",[46,1972],{"x1":1973,"y1":1932,"x2":1974,"y2":1932,"stroke":771,"style":1800},"291.4","313.4",[1935,1976],{"points":1977,"fill":1938},"317.4,88.8 308.4,93.8 308.4,83.8",[703,1979],{"x":1980,"y":712,"width":1900,"height":1901,"rx":583,"fill":715,"stroke":716,"style":1800},"322.4",[1802,1982,1984],{"transform":1983},"translate(368.0,37.8) scale(1.000)",[1806,1985],{"d":1986,"fill":1809,"stroke":1948,"style":1909},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[723,1988,1990],{"x":1989,"y":1913,"style":1953},"380.0","Drop cached",[723,1992,1993],{"x":1989,"y":1918,"style":1953},"zeros",[723,1995,1996],{"x":1989,"y":1922,"style":1923},"re-save with",[723,1998,1999],{"x":1989,"y":1927,"style":1923},"openpyxl",[46,2001],{"x1":2002,"y1":1932,"x2":2003,"y2":1932,"stroke":771,"style":1800},"442.6","464.6",[1935,2005],{"points":2006,"fill":1938},"468.6,88.8 459.6,93.8 459.6,83.8",[703,2008],{"x":2009,"y":712,"width":1900,"height":1901,"rx":583,"fill":925,"stroke":926,"style":1800},"473.6",[1802,2011,2013],{"transform":2012},"translate(519.2,55.2) scale(1.000)",[1806,2014],{"d":2015,"fill":1809,"stroke":1810,"style":1909},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1",[723,2017,2020],{"x":2018,"y":1918,"style":2019},"531.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Recalculate",[723,2022,2023],{"x":2018,"y":1922,"style":1923},"LibreOffice",[46,2025],{"x1":2026,"y1":1932,"x2":2027,"y2":1932,"stroke":771,"style":1800},"593.8","615.8",[1935,2029],{"points":2030,"fill":1938},"619.8,88.8 610.8,93.8 610.8,83.8",[703,2032],{"x":2033,"y":712,"width":1900,"height":1901,"rx":583,"fill":824,"stroke":825,"style":1800},"624.8",[1802,2035,2037],{"transform":2036},"translate(670.4,37.8) scale(1.000)",[1806,2038],{"d":1846,"fill":1809,"stroke":1847,"style":1909},[723,2040,2043],{"x":2041,"y":1913,"style":2042},"682.4","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Compare",[723,2045,2046],{"x":2041,"y":1918,"style":2042},"with pandas",[723,2048,2049],{"x":2041,"y":1922,"style":1923},"Net and totals",[723,2051,2052],{"x":2041,"y":1927,"style":1923},"match",[30,2054,2056],{"id":2055},"common-pitfalls","Common pitfalls",[2058,2059,2060,2071,2081,2087,2097],"ul",{},[2061,2062,2063,2067,2068,2070],"li",{},[2064,2065,2066],"strong",{},"A range one row short."," Include the header and totals rows in the ",[19,2069,80],{}," range.",[2061,2072,2073,2076,2077,2080],{},[2064,2074,2075],{},"Headers from the DataFrame."," Table headers come from the ",[19,2078,2079],{},"columns"," list, not the data.",[2061,2082,2083,2086],{},[2064,2084,2085],{},"Expecting cached results."," XlsxWriter stores 0; Excel recalculates, other readers do not.",[2061,2088,2089,2092,2093,2096],{},[2064,2090,2091],{},"Shorthand references outside Excel."," Use ",[19,2094,2095],{},"Table[[#This Row],[Column]]"," for portability.",[2061,2098,2099,2102],{},[2064,2100,2101],{},"Reusing formats across workbooks."," Formats belong to the workbook that created them.",[30,2104,2106],{"id":2105},"conclusion","Conclusion",[10,2108,2109,2110,2112,2113,2115,2116,2118,2119,2121],{},"To write a DataFrame as an Excel table with XlsxWriter, pass its values to ",[19,2111,80],{}," with a range covering header, data and totals rows, and describe each column — header, format, total function, and a ",[19,2114,84],{}," for calculated columns. Write structured references with the table name, ",[19,2117,1370],{},", and custom totals such as ",[19,2120,1374],{},", so the formulas calculate in every spreadsheet application, and check them by recalculating the file and comparing with pandas.",[30,2123,2125],{"id":2124},"frequently-asked-questions","Frequently asked questions",[10,2127,2128,2131],{},[2064,2129,2130],{},"How do I write a pandas DataFrame as an Excel table with XlsxWriter?","\nCall worksheet.add_table with a range that covers the header and data rows, pass df.values.tolist() as data and a columns list with a header for each column.",[10,2133,2134,2137,2138,1641,2141,2144,2145,2147,2148,2150],{},[2064,2135,2136],{},"How do I add a calculated column to an XlsxWriter table?","\nGive the column definition a formula using structured references, for example =",[44,2139,2140],{},"@Qty",[44,2142,2143],{},"@Price"," or the fully qualified Orders[[#This Row],",[44,2146,781],{},"]*Orders[[#This Row],",[44,2149,786],{},"].",[10,2152,2153,2156],{},[2064,2154,2155],{},"How do I add a totals row to an XlsxWriter table?","\nSet total_row to True, extend the range by one row, and give columns a total_function such as sum or a custom formula starting with =.",[10,2158,2159,2162,2163,2166],{},[2064,2160,2161],{},"Why do my XlsxWriter table formulas show errors in LibreOffice?","\nXlsxWriter saves ",[44,2164,2165],{},"@Column"," as a reference without the table name, which LibreOffice does not resolve; write the reference with the table name instead.",[30,2168,2170],{"id":2169},"related","Related",[2058,2172,2173,2179,2186,2193],{},[2061,2174,2175,2176,2178],{},"Up one level: ",[24,2177,27],{"href":26}," — reports with XlsxWriter.",[2061,2180,2181,2185],{},[24,2182,2184],{"href":2183},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fcreate-an-excel-table-with-python\u002F","Create an Excel Table with Python"," — tables with openpyxl.",[2061,2187,2188,2192],{},[24,2189,2191],{"href":2190},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-a-total-row-to-an-excel-table\u002F","Add a Total Row to an Excel Table"," — totals rows in detail.",[2061,2194,2195,2199],{},[24,2196,2198],{"href":2197},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-calculated-columns-to-an-excel-table-with-python\u002F","Add Calculated Columns to an Excel Table with Python"," — adding them to existing tables.",[2201,2202,2203],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .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}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":40,"searchDepth":111,"depth":111,"links":2205},[2206,2207,2208,2209,2210,2211,2212,2213,2214],{"id":32,"depth":111,"text":33},{"id":74,"depth":111,"text":75},{"id":946,"depth":111,"text":947},{"id":1157,"depth":111,"text":1158},{"id":1868,"depth":111,"text":1869},{"id":2055,"depth":111,"text":2056},{"id":2105,"depth":111,"text":2106},{"id":2124,"depth":111,"text":2125},{"id":2169,"depth":111,"text":2170},"2026-10-02","Write a pandas DataFrame as a real Excel table with XlsxWriter, including calculated columns that use structured references, a totals row, column formats and a table style: get the range right, choose between the [@Column] shorthand and fully qualified references that also work outside Excel, add custom totals, and verify the formulas by recalculating.","md",[2219,2221,2223,2225],{"q":2130,"a":2220},"Call worksheet.add_table with a range that covers the header and data rows, pass df.values.tolist() as data and a columns list with a header for each column.",{"q":2136,"a":2222},"Give the column definition a formula using structured references, for example =[@Qty]*[@Price] or the fully qualified Orders[[#This Row],[Qty]]*Orders[[#This Row],[Price]].",{"q":2155,"a":2224},"Set total_row to True, extend the range by one row, and give columns a total_function such as sum or a custom formula starting with =.",{"q":2161,"a":2226},"XlsxWriter saves [@Column] as a reference without the table name, which LibreOffice does not resolve; write the reference with the table name instead.","excel table with formula columns xlsxwriter",null,{"breadcrumb":2230},[2231,2234,2237],{"name":2232,"item":2233},"Home","\u002F",{"name":2235,"item":2236},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":27,"item":26},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter",{"title":5,"description":2240},"XlsxWriter add_table from a DataFrame with calculated formula columns, structured references, total_function and custom totals, formats and styles, plus compatibility of [@Col] and Table[[#This Row],[Col]] and a recalculation check.","write-an-excel-table-with-formula-columns-using-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter\u002Findex",[2244,2245],"xlsxwriter","tables-and-filters","how-to","0vZnHe1ro9CHfQT8nF6-d0xwtEEdiy3CY_Pe5IAmstM",[2249,2253],{"title":2250,"path":2251,"stem":2252,"children":-1},"Write a Million Rows to Excel with xlsxwriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory\u002Findex",{"title":2254,"path":2255,"stem":2256,"children":-1},"Write Dates, Times and Durations with XlsxWriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-dates-times-and-durations-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-dates-times-and-durations-with-xlsxwriter\u002Findex",[2258,2260],{"path":2259,"title":27},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter",{"path":2261,"title":2235},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2264,2268,2271,2274,2277,2280],{"path":2265,"title":2266,"kind":2267},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-budget-vs-actual-variance-report","Build a Budget vs Actual Variance Report in Excel with Python","compare",{"path":2269,"title":2270,"kind":2246},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fcombine-chart-types-and-chartsheets-with-xlsxwriter","Combine Chart Types and Chartsheets with XlsxWriter",{"path":2272,"title":2273,"kind":2246},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter","Define Names and Protect Sheets with XlsxWriter",{"path":2275,"title":2276,"kind":2246},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Ffreeze-panes-and-set-zoom-with-xlsxwriter","Freeze Panes and Set Zoom with XlsxWriter",{"path":2278,"title":2279,"kind":2246},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Finsert-checkboxes-with-xlsxwriter","Insert Checkboxes in Excel with xlsxwriter",{"path":2281,"title":2282,"kind":2246},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fset-print-options-headers-and-footers-with-xlsxwriter","Set Print Options, Headers and Footers with XlsxWriter",1790972108743]