[{"data":1,"prerenderedAt":2941},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python":2933},{"id":4,"title":5,"body":6,"dateModified":2901,"datePublished":2901,"description":2902,"extension":2903,"faq":2904,"meta":2915,"navigation":229,"path":2926,"seo":2927,"slug":2929,"stem":2930,"type":2931,"__hash__":2932},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002Findex.md","Add SUM and AVERAGE Formulas with Python",{"type":7,"value":8,"toc":2887},"minimark",[9,19,160,165,194,198,201,828,836,848,852,855,1382,1385,1389,1403,1548,1642,1666,1670,1673,2163,2186,2290,2294,2297,2373,2376,2380,2383,2621,2629,2633,2755,2759,2771,2775,2781,2785,2795,2814,2825,2835,2839,2842,2851,2854,2883],[10,11,12,13,18],"p",{},"A total row is the first thing a reader looks at and the first thing that breaks when next month's file has forty rows instead of thirty. The fix is to generate the range from the data you just wrote rather than typing it once and hoping. This guide, part of ",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", builds total rows that stay correct, then covers averages, filtered subtotals and conditional aggregates.",[20,21,29,30,29,34,29,38,29,45,29,52,29,59,29,63,29,67,29,71,29,76,29,79,29,82,29,86,29,90,29,93,29,96,29,99,29,103,29,106,29,109,29,114,29,120,29,127,29,132,29,136,29,141,29,145,29,149,29,153,29,156],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 720 268","img",[25,26],"sum-anat-t","sum-anat-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"A total row generated from the last data row",[35,36,37],"desc",{"id":26},"A sheet with a header row, three data rows and a blank spacer, then a total row whose formula reads SUM of D2 through D4. The range end is derived from the last appended row, so adding a fourth data row moves both the total row and the range end.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","720","268","#ffffff",[39,46],{"x":47,"y":48,"width":49,"height":50,"fill":51},"40","24","380","32","#5b5cf0",[53,54,58],"text",{"x":55,"y":56,"style":57},"60","45","font-size:12px;font-weight:700;fill:#ffffff","SKU",[53,60,62],{"x":61,"y":56,"style":57},"200","Qty",[53,64,66],{"x":65,"y":56,"style":57},"300","Line_Total (D)",[39,68],{"x":47,"y":69,"width":49,"height":50,"fill":44,"stroke":70},"56","var(--line,#cdd5e6)",[53,72,75],{"x":55,"y":73,"style":74},"77","font-size:12px;fill:var(--text,#172033)","A-100",[53,77,78],{"x":61,"y":73,"style":74},"4",[53,80,81],{"x":65,"y":73,"style":74},"79.96",[39,83],{"x":47,"y":84,"width":49,"height":50,"fill":85,"stroke":70},"88","#f0f4ff",[53,87,89],{"x":55,"y":88,"style":74},"109","B-200",[53,91,92],{"x":61,"y":88,"style":74},"2",[53,94,95],{"x":65,"y":88,"style":74},"99.00",[39,97],{"x":47,"y":98,"width":49,"height":50,"fill":44,"stroke":70},"120",[53,100,102],{"x":55,"y":101,"style":74},"141","C-300",[53,104,105],{"x":61,"y":101,"style":74},"7",[53,107,108],{"x":65,"y":101,"style":74},"85.75",[39,110],{"x":47,"y":111,"width":49,"height":112,"fill":113},"152","22","#ebebfd",[53,115,119],{"x":116,"y":117,"style":118},"230","167","font-size:11px;font-style:italic;fill:var(--muted,#5b6780);text-anchor:middle","spacer row",[39,121],{"x":47,"y":122,"width":49,"height":123,"fill":124,"stroke":125,"style":126},"174","34","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[53,128,131],{"x":55,"y":129,"style":130},"196","font-size:12px;font-weight:700;fill:var(--teal,#0f9488)","Total",[53,133,135],{"x":65,"y":129,"style":134},"font-size:12px;font-weight:700;fill:var(--teal,#0f9488);font-family:ui-monospace,Menlo,monospace","=SUM(D2:D4)",[53,137,140],{"x":138,"y":56,"style":139},"440","font-size:11.5px;fill:var(--muted,#5b6780)","row 1 — header",[53,142,144],{"x":138,"y":101,"style":143},"font-size:11.5px;font-weight:700;fill:var(--text,#172033)","last = ws.max_row",[53,146,148],{"x":138,"y":147,"style":139},"159","(row 4 here)",[53,150,152],{"x":138,"y":129,"style":151},"font-size:11.5px;font-weight:700;fill:var(--teal,#0f9488)","total_row = last + 2",[53,154,155],{"x":138,"y":116,"style":139},"both move together when",[53,157,159],{"x":138,"y":158,"style":139},"248","the data grows",[161,162,164],"h2",{"id":163},"prerequisites","Prerequisites",[166,167,172],"pre",{"className":168,"code":169,"language":170,"meta":171,"style":171},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[173,174,175],"code",{"__ignoreMap":171},[176,177,180,184,188,191],"span",{"class":178,"line":179},"line",1,[176,181,183],{"class":182},"sMTad","pip",[176,185,187],{"class":186},"srMev"," install",[176,189,190],{"class":186}," openpyxl",[176,192,193],{"class":186}," pandas\n",[161,195,197],{"id":196},"a-total-row-that-tracks-the-data","A total row that tracks the data",[10,199,200],{},"Write the rows, capture the last row number, then place the total two rows below with a range that ends exactly where the data does:",[166,202,206],{"className":203,"code":204,"language":205,"meta":171,"style":171},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nrows = [\n    (\"A-100\", 4, 19.99),\n    (\"B-200\", 2, 49.50),\n    (\"C-300\", 7, 12.25),\n]\n\nwb = Workbook()\nws = wb.active\nws.title = \"Orders\"\nws.append([\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\"])\n\nfor sku, qty, price in rows:\n    ws.append([sku, qty, price])\n    r = ws.max_row\n    ws[f\"D{r}\"] = f\"=B{r}*C{r}\"\n\nfirst_data, last_data = 2, ws.max_row\ntotal_row = last_data + 2\n\nws[f\"C{total_row}\"] = \"Total\"\nws[f\"D{total_row}\"] = f\"=SUM(D{first_data}:D{last_data})\"\nws[f\"C{total_row + 1}\"] = \"Average line\"\nws[f\"D{total_row + 1}\"] = f\"=AVERAGE(D{first_data}:D{last_data})\"\nws[f\"C{total_row + 2}\"] = \"Lines\"\nws[f\"D{total_row + 2}\"] = f\"=COUNT(D{first_data}:D{last_data})\"\n\nfor r in range(total_row, total_row + 3):\n    ws[f\"D{r}\"].number_format = \"#,##0.00\"\n\nwb.save(\"orders_totals.xlsx\")\nprint(ws[f\"D{total_row}\"].value)     # '=SUM(D2:D4)'\n","python",[173,207,208,224,231,243,266,285,304,310,315,326,337,348,375,380,395,401,412,466,471,485,502,507,534,580,609,655,683,729,734,758,783,788,800],{"__ignoreMap":171},[176,209,210,214,218,221],{"class":178,"line":179},[176,211,213],{"class":212},"s-kum","from",[176,215,217],{"class":216},"skGVy"," openpyxl ",[176,219,220],{"class":212},"import",[176,222,223],{"class":216}," Workbook\n",[176,225,227],{"class":178,"line":226},2,[176,228,230],{"emptyLinePlaceholder":229},true,"\n",[176,232,234,237,240],{"class":178,"line":233},3,[176,235,236],{"class":216},"rows ",[176,238,239],{"class":212},"=",[176,241,242],{"class":216}," [\n",[176,244,246,249,252,255,258,260,263],{"class":178,"line":245},4,[176,247,248],{"class":216},"    (",[176,250,251],{"class":186},"\"A-100\"",[176,253,254],{"class":216},", ",[176,256,78],{"class":257},"sP0c6",[176,259,254],{"class":216},[176,261,262],{"class":257},"19.99",[176,264,265],{"class":216},"),\n",[176,267,269,271,274,276,278,280,283],{"class":178,"line":268},5,[176,270,248],{"class":216},[176,272,273],{"class":186},"\"B-200\"",[176,275,254],{"class":216},[176,277,92],{"class":257},[176,279,254],{"class":216},[176,281,282],{"class":257},"49.50",[176,284,265],{"class":216},[176,286,288,290,293,295,297,299,302],{"class":178,"line":287},6,[176,289,248],{"class":216},[176,291,292],{"class":186},"\"C-300\"",[176,294,254],{"class":216},[176,296,105],{"class":257},[176,298,254],{"class":216},[176,300,301],{"class":257},"12.25",[176,303,265],{"class":216},[176,305,307],{"class":178,"line":306},7,[176,308,309],{"class":216},"]\n",[176,311,313],{"class":178,"line":312},8,[176,314,230],{"emptyLinePlaceholder":229},[176,316,318,321,323],{"class":178,"line":317},9,[176,319,320],{"class":216},"wb ",[176,322,239],{"class":212},[176,324,325],{"class":216}," Workbook()\n",[176,327,329,332,334],{"class":178,"line":328},10,[176,330,331],{"class":216},"ws ",[176,333,239],{"class":212},[176,335,336],{"class":216}," wb.active\n",[176,338,340,343,345],{"class":178,"line":339},11,[176,341,342],{"class":216},"ws.title ",[176,344,239],{"class":212},[176,346,347],{"class":186}," \"Orders\"\n",[176,349,351,354,357,359,362,364,367,369,372],{"class":178,"line":350},12,[176,352,353],{"class":216},"ws.append([",[176,355,356],{"class":186},"\"SKU\"",[176,358,254],{"class":216},[176,360,361],{"class":186},"\"Quantity\"",[176,363,254],{"class":216},[176,365,366],{"class":186},"\"Unit_Price\"",[176,368,254],{"class":216},[176,370,371],{"class":186},"\"Line_Total\"",[176,373,374],{"class":216},"])\n",[176,376,378],{"class":178,"line":377},13,[176,379,230],{"emptyLinePlaceholder":229},[176,381,383,386,389,392],{"class":178,"line":382},14,[176,384,385],{"class":212},"for",[176,387,388],{"class":216}," sku, qty, price ",[176,390,391],{"class":212},"in",[176,393,394],{"class":216}," rows:\n",[176,396,398],{"class":178,"line":397},15,[176,399,400],{"class":216},"    ws.append([sku, qty, price])\n",[176,402,404,407,409],{"class":178,"line":403},16,[176,405,406],{"class":216},"    r ",[176,408,239],{"class":212},[176,410,411],{"class":216}," ws.max_row\n",[176,413,415,418,421,424,428,431,434,437,440,442,445,448,450,452,454,457,459,461,463],{"class":178,"line":414},17,[176,416,417],{"class":216},"    ws[",[176,419,420],{"class":212},"f",[176,422,423],{"class":186},"\"D",[176,425,427],{"class":426},"sSjpA","{",[176,429,430],{"class":216},"r",[176,432,433],{"class":426},"}",[176,435,436],{"class":186},"\"",[176,438,439],{"class":216},"] ",[176,441,239],{"class":212},[176,443,444],{"class":212}," f",[176,446,447],{"class":186},"\"=B",[176,449,427],{"class":426},[176,451,430],{"class":216},[176,453,433],{"class":426},[176,455,456],{"class":186},"*C",[176,458,427],{"class":426},[176,460,430],{"class":216},[176,462,433],{"class":426},[176,464,465],{"class":186},"\"\n",[176,467,469],{"class":178,"line":468},18,[176,470,230],{"emptyLinePlaceholder":229},[176,472,474,477,479,482],{"class":178,"line":473},19,[176,475,476],{"class":216},"first_data, last_data ",[176,478,239],{"class":212},[176,480,481],{"class":257}," 2",[176,483,484],{"class":216},", ws.max_row\n",[176,486,488,491,493,496,499],{"class":178,"line":487},20,[176,489,490],{"class":216},"total_row ",[176,492,239],{"class":212},[176,494,495],{"class":216}," last_data ",[176,497,498],{"class":212},"+",[176,500,501],{"class":257}," 2\n",[176,503,505],{"class":178,"line":504},21,[176,506,230],{"emptyLinePlaceholder":229},[176,508,510,513,515,518,520,523,525,527,529,531],{"class":178,"line":509},22,[176,511,512],{"class":216},"ws[",[176,514,420],{"class":212},[176,516,517],{"class":186},"\"C",[176,519,427],{"class":426},[176,521,522],{"class":216},"total_row",[176,524,433],{"class":426},[176,526,436],{"class":186},[176,528,439],{"class":216},[176,530,239],{"class":212},[176,532,533],{"class":186}," \"Total\"\n",[176,535,537,539,541,543,545,547,549,551,553,555,557,560,562,565,567,570,572,575,577],{"class":178,"line":536},23,[176,538,512],{"class":216},[176,540,420],{"class":212},[176,542,423],{"class":186},[176,544,427],{"class":426},[176,546,522],{"class":216},[176,548,433],{"class":426},[176,550,436],{"class":186},[176,552,439],{"class":216},[176,554,239],{"class":212},[176,556,444],{"class":212},[176,558,559],{"class":186},"\"=SUM(D",[176,561,427],{"class":426},[176,563,564],{"class":216},"first_data",[176,566,433],{"class":426},[176,568,569],{"class":186},":D",[176,571,427],{"class":426},[176,573,574],{"class":216},"last_data",[176,576,433],{"class":426},[176,578,579],{"class":186},")\"\n",[176,581,583,585,587,589,591,593,595,598,600,602,604,606],{"class":178,"line":582},24,[176,584,512],{"class":216},[176,586,420],{"class":212},[176,588,517],{"class":186},[176,590,427],{"class":426},[176,592,490],{"class":216},[176,594,498],{"class":212},[176,596,597],{"class":257}," 1",[176,599,433],{"class":426},[176,601,436],{"class":186},[176,603,439],{"class":216},[176,605,239],{"class":212},[176,607,608],{"class":186}," \"Average line\"\n",[176,610,612,614,616,618,620,622,624,626,628,630,632,634,636,639,641,643,645,647,649,651,653],{"class":178,"line":611},25,[176,613,512],{"class":216},[176,615,420],{"class":212},[176,617,423],{"class":186},[176,619,427],{"class":426},[176,621,490],{"class":216},[176,623,498],{"class":212},[176,625,597],{"class":257},[176,627,433],{"class":426},[176,629,436],{"class":186},[176,631,439],{"class":216},[176,633,239],{"class":212},[176,635,444],{"class":212},[176,637,638],{"class":186},"\"=AVERAGE(D",[176,640,427],{"class":426},[176,642,564],{"class":216},[176,644,433],{"class":426},[176,646,569],{"class":186},[176,648,427],{"class":426},[176,650,574],{"class":216},[176,652,433],{"class":426},[176,654,579],{"class":186},[176,656,658,660,662,664,666,668,670,672,674,676,678,680],{"class":178,"line":657},26,[176,659,512],{"class":216},[176,661,420],{"class":212},[176,663,517],{"class":186},[176,665,427],{"class":426},[176,667,490],{"class":216},[176,669,498],{"class":212},[176,671,481],{"class":257},[176,673,433],{"class":426},[176,675,436],{"class":186},[176,677,439],{"class":216},[176,679,239],{"class":212},[176,681,682],{"class":186}," \"Lines\"\n",[176,684,686,688,690,692,694,696,698,700,702,704,706,708,710,713,715,717,719,721,723,725,727],{"class":178,"line":685},27,[176,687,512],{"class":216},[176,689,420],{"class":212},[176,691,423],{"class":186},[176,693,427],{"class":426},[176,695,490],{"class":216},[176,697,498],{"class":212},[176,699,481],{"class":257},[176,701,433],{"class":426},[176,703,436],{"class":186},[176,705,439],{"class":216},[176,707,239],{"class":212},[176,709,444],{"class":212},[176,711,712],{"class":186},"\"=COUNT(D",[176,714,427],{"class":426},[176,716,564],{"class":216},[176,718,433],{"class":426},[176,720,569],{"class":186},[176,722,427],{"class":426},[176,724,574],{"class":216},[176,726,433],{"class":426},[176,728,579],{"class":186},[176,730,732],{"class":178,"line":731},28,[176,733,230],{"emptyLinePlaceholder":229},[176,735,737,739,742,744,747,750,752,755],{"class":178,"line":736},29,[176,738,385],{"class":212},[176,740,741],{"class":216}," r ",[176,743,391],{"class":212},[176,745,746],{"class":257}," range",[176,748,749],{"class":216},"(total_row, total_row ",[176,751,498],{"class":212},[176,753,754],{"class":257}," 3",[176,756,757],{"class":216},"):\n",[176,759,761,763,765,767,769,771,773,775,778,780],{"class":178,"line":760},30,[176,762,417],{"class":216},[176,764,420],{"class":212},[176,766,423],{"class":186},[176,768,427],{"class":426},[176,770,430],{"class":216},[176,772,433],{"class":426},[176,774,436],{"class":186},[176,776,777],{"class":216},"].number_format ",[176,779,239],{"class":212},[176,781,782],{"class":186}," \"#,##0.00\"\n",[176,784,786],{"class":178,"line":785},31,[176,787,230],{"emptyLinePlaceholder":229},[176,789,791,794,797],{"class":178,"line":790},32,[176,792,793],{"class":216},"wb.save(",[176,795,796],{"class":186},"\"orders_totals.xlsx\"",[176,798,799],{"class":216},")\n",[176,801,803,806,809,811,813,815,817,819,821,824],{"class":178,"line":802},33,[176,804,805],{"class":257},"print",[176,807,808],{"class":216},"(ws[",[176,810,420],{"class":212},[176,812,423],{"class":186},[176,814,427],{"class":426},[176,816,522],{"class":216},[176,818,433],{"class":426},[176,820,436],{"class":186},[176,822,823],{"class":216},"].value)     ",[176,825,827],{"class":826},"s-wDw","# '=SUM(D2:D4)'\n",[10,829,830,832,833,835],{},[173,831,564],{}," and ",[173,834,574],{}," are the only two numbers the formulas depend on, and both come from the data rather than from a constant. Add a fourth order and every formula moves with it — no edit required.",[10,837,838,839,842,843,847],{},"The spacer row is not decoration. A total immediately under the last data row gets swallowed when a reader turns the range into an Excel table or applies a filter, and it makes ",[173,840,841],{},"ws.max_row","-based logic in the ",[844,845,846],"em",{},"next"," run treat the total as data.",[161,849,851],{"id":850},"totalling-several-columns-at-once","Totalling several columns at once",[10,853,854],{},"Most reports need the same aggregate across a block of columns. Generate them in a loop so a new column cannot be forgotten:",[166,856,858],{"className":203,"code":857,"language":205,"meta":171,"style":171},"from openpyxl import Workbook\nfrom openpyxl.utils import get_column_letter\n\nheaders = [\"Region\", \"Q1\", \"Q2\", \"Q3\", \"Q4\"]\ndata = [\n    (\"North\", 26700, 24100, 29800, 31200),\n    (\"South\", 20900, 22400, 19850, 23100),\n    (\"West\", 7600, 8100, 9400, 10250),\n]\n\nwb = Workbook()\nws = wb.active\nws.append(headers)\nfor row in data:\n    ws.append(row)\n\nlast = ws.max_row\ntotal_row = last + 2\nws[f\"A{total_row}\"] = \"Total\"\n\nfor col in range(2, len(headers) + 1):\n    letter = get_column_letter(col)\n    ws[f\"{letter}{total_row}\"] = f\"=SUM({letter}2:{letter}{last})\"\n    ws[f\"{letter}{total_row}\"].number_format = \"#,##0\"\n\n# A row total per region, and a grand total in the corner\ngrand_col = get_column_letter(len(headers) + 1)\nws[f\"{grand_col}1\"] = \"Year\"\nfor r in range(2, total_row + 1):\n    ws[f\"{grand_col}{r}\"] = f\"=SUM(B{r}:{get_column_letter(len(headers))}{r})\"\n\nwb.save(\"quarterly_totals.xlsx\")\n",[173,859,860,870,882,886,921,930,959,988,1017,1021,1025,1033,1041,1046,1058,1063,1067,1076,1089,1112,1116,1146,1156,1209,1236,1240,1245,1265,1290,1313,1369,1373],{"__ignoreMap":171},[176,861,862,864,866,868],{"class":178,"line":179},[176,863,213],{"class":212},[176,865,217],{"class":216},[176,867,220],{"class":212},[176,869,223],{"class":216},[176,871,872,874,877,879],{"class":178,"line":226},[176,873,213],{"class":212},[176,875,876],{"class":216}," openpyxl.utils ",[176,878,220],{"class":212},[176,880,881],{"class":216}," get_column_letter\n",[176,883,884],{"class":178,"line":233},[176,885,230],{"emptyLinePlaceholder":229},[176,887,888,891,893,896,899,901,904,906,909,911,914,916,919],{"class":178,"line":245},[176,889,890],{"class":216},"headers ",[176,892,239],{"class":212},[176,894,895],{"class":216}," [",[176,897,898],{"class":186},"\"Region\"",[176,900,254],{"class":216},[176,902,903],{"class":186},"\"Q1\"",[176,905,254],{"class":216},[176,907,908],{"class":186},"\"Q2\"",[176,910,254],{"class":216},[176,912,913],{"class":186},"\"Q3\"",[176,915,254],{"class":216},[176,917,918],{"class":186},"\"Q4\"",[176,920,309],{"class":216},[176,922,923,926,928],{"class":178,"line":268},[176,924,925],{"class":216},"data ",[176,927,239],{"class":212},[176,929,242],{"class":216},[176,931,932,934,937,939,942,944,947,949,952,954,957],{"class":178,"line":287},[176,933,248],{"class":216},[176,935,936],{"class":186},"\"North\"",[176,938,254],{"class":216},[176,940,941],{"class":257},"26700",[176,943,254],{"class":216},[176,945,946],{"class":257},"24100",[176,948,254],{"class":216},[176,950,951],{"class":257},"29800",[176,953,254],{"class":216},[176,955,956],{"class":257},"31200",[176,958,265],{"class":216},[176,960,961,963,966,968,971,973,976,978,981,983,986],{"class":178,"line":306},[176,962,248],{"class":216},[176,964,965],{"class":186},"\"South\"",[176,967,254],{"class":216},[176,969,970],{"class":257},"20900",[176,972,254],{"class":216},[176,974,975],{"class":257},"22400",[176,977,254],{"class":216},[176,979,980],{"class":257},"19850",[176,982,254],{"class":216},[176,984,985],{"class":257},"23100",[176,987,265],{"class":216},[176,989,990,992,995,997,1000,1002,1005,1007,1010,1012,1015],{"class":178,"line":312},[176,991,248],{"class":216},[176,993,994],{"class":186},"\"West\"",[176,996,254],{"class":216},[176,998,999],{"class":257},"7600",[176,1001,254],{"class":216},[176,1003,1004],{"class":257},"8100",[176,1006,254],{"class":216},[176,1008,1009],{"class":257},"9400",[176,1011,254],{"class":216},[176,1013,1014],{"class":257},"10250",[176,1016,265],{"class":216},[176,1018,1019],{"class":178,"line":317},[176,1020,309],{"class":216},[176,1022,1023],{"class":178,"line":328},[176,1024,230],{"emptyLinePlaceholder":229},[176,1026,1027,1029,1031],{"class":178,"line":339},[176,1028,320],{"class":216},[176,1030,239],{"class":212},[176,1032,325],{"class":216},[176,1034,1035,1037,1039],{"class":178,"line":350},[176,1036,331],{"class":216},[176,1038,239],{"class":212},[176,1040,336],{"class":216},[176,1042,1043],{"class":178,"line":377},[176,1044,1045],{"class":216},"ws.append(headers)\n",[176,1047,1048,1050,1053,1055],{"class":178,"line":382},[176,1049,385],{"class":212},[176,1051,1052],{"class":216}," row ",[176,1054,391],{"class":212},[176,1056,1057],{"class":216}," data:\n",[176,1059,1060],{"class":178,"line":397},[176,1061,1062],{"class":216},"    ws.append(row)\n",[176,1064,1065],{"class":178,"line":403},[176,1066,230],{"emptyLinePlaceholder":229},[176,1068,1069,1072,1074],{"class":178,"line":414},[176,1070,1071],{"class":216},"last ",[176,1073,239],{"class":212},[176,1075,411],{"class":216},[176,1077,1078,1080,1082,1085,1087],{"class":178,"line":468},[176,1079,490],{"class":216},[176,1081,239],{"class":212},[176,1083,1084],{"class":216}," last ",[176,1086,498],{"class":212},[176,1088,501],{"class":257},[176,1090,1091,1093,1095,1098,1100,1102,1104,1106,1108,1110],{"class":178,"line":473},[176,1092,512],{"class":216},[176,1094,420],{"class":212},[176,1096,1097],{"class":186},"\"A",[176,1099,427],{"class":426},[176,1101,522],{"class":216},[176,1103,433],{"class":426},[176,1105,436],{"class":186},[176,1107,439],{"class":216},[176,1109,239],{"class":212},[176,1111,533],{"class":186},[176,1113,1114],{"class":178,"line":487},[176,1115,230],{"emptyLinePlaceholder":229},[176,1117,1118,1120,1123,1125,1127,1130,1132,1134,1137,1140,1142,1144],{"class":178,"line":504},[176,1119,385],{"class":212},[176,1121,1122],{"class":216}," col ",[176,1124,391],{"class":212},[176,1126,746],{"class":257},[176,1128,1129],{"class":216},"(",[176,1131,92],{"class":257},[176,1133,254],{"class":216},[176,1135,1136],{"class":257},"len",[176,1138,1139],{"class":216},"(headers) ",[176,1141,498],{"class":212},[176,1143,597],{"class":257},[176,1145,757],{"class":216},[176,1147,1148,1151,1153],{"class":178,"line":509},[176,1149,1150],{"class":216},"    letter ",[176,1152,239],{"class":212},[176,1154,1155],{"class":216}," get_column_letter(col)\n",[176,1157,1158,1160,1162,1164,1166,1169,1172,1174,1176,1178,1180,1182,1184,1187,1189,1191,1193,1196,1198,1200,1202,1205,1207],{"class":178,"line":536},[176,1159,417],{"class":216},[176,1161,420],{"class":212},[176,1163,436],{"class":186},[176,1165,427],{"class":426},[176,1167,1168],{"class":216},"letter",[176,1170,1171],{"class":426},"}{",[176,1173,522],{"class":216},[176,1175,433],{"class":426},[176,1177,436],{"class":186},[176,1179,439],{"class":216},[176,1181,239],{"class":212},[176,1183,444],{"class":212},[176,1185,1186],{"class":186},"\"=SUM(",[176,1188,427],{"class":426},[176,1190,1168],{"class":216},[176,1192,433],{"class":426},[176,1194,1195],{"class":186},"2:",[176,1197,427],{"class":426},[176,1199,1168],{"class":216},[176,1201,1171],{"class":426},[176,1203,1204],{"class":216},"last",[176,1206,433],{"class":426},[176,1208,579],{"class":186},[176,1210,1211,1213,1215,1217,1219,1221,1223,1225,1227,1229,1231,1233],{"class":178,"line":582},[176,1212,417],{"class":216},[176,1214,420],{"class":212},[176,1216,436],{"class":186},[176,1218,427],{"class":426},[176,1220,1168],{"class":216},[176,1222,1171],{"class":426},[176,1224,522],{"class":216},[176,1226,433],{"class":426},[176,1228,436],{"class":186},[176,1230,777],{"class":216},[176,1232,239],{"class":212},[176,1234,1235],{"class":186}," \"#,##0\"\n",[176,1237,1238],{"class":178,"line":611},[176,1239,230],{"emptyLinePlaceholder":229},[176,1241,1242],{"class":178,"line":657},[176,1243,1244],{"class":826},"# A row total per region, and a grand total in the corner\n",[176,1246,1247,1250,1252,1255,1257,1259,1261,1263],{"class":178,"line":685},[176,1248,1249],{"class":216},"grand_col ",[176,1251,239],{"class":212},[176,1253,1254],{"class":216}," get_column_letter(",[176,1256,1136],{"class":257},[176,1258,1139],{"class":216},[176,1260,498],{"class":212},[176,1262,597],{"class":257},[176,1264,799],{"class":216},[176,1266,1267,1269,1271,1273,1275,1278,1280,1283,1285,1287],{"class":178,"line":731},[176,1268,512],{"class":216},[176,1270,420],{"class":212},[176,1272,436],{"class":186},[176,1274,427],{"class":426},[176,1276,1277],{"class":216},"grand_col",[176,1279,433],{"class":426},[176,1281,1282],{"class":186},"1\"",[176,1284,439],{"class":216},[176,1286,239],{"class":212},[176,1288,1289],{"class":186}," \"Year\"\n",[176,1291,1292,1294,1296,1298,1300,1302,1304,1307,1309,1311],{"class":178,"line":736},[176,1293,385],{"class":212},[176,1295,741],{"class":216},[176,1297,391],{"class":212},[176,1299,746],{"class":257},[176,1301,1129],{"class":216},[176,1303,92],{"class":257},[176,1305,1306],{"class":216},", total_row ",[176,1308,498],{"class":212},[176,1310,597],{"class":257},[176,1312,757],{"class":216},[176,1314,1315,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335,1337,1339,1342,1344,1346,1348,1351,1353,1356,1358,1361,1363,1365,1367],{"class":178,"line":760},[176,1316,417],{"class":216},[176,1318,420],{"class":212},[176,1320,436],{"class":186},[176,1322,427],{"class":426},[176,1324,1277],{"class":216},[176,1326,1171],{"class":426},[176,1328,430],{"class":216},[176,1330,433],{"class":426},[176,1332,436],{"class":186},[176,1334,439],{"class":216},[176,1336,239],{"class":212},[176,1338,444],{"class":212},[176,1340,1341],{"class":186},"\"=SUM(B",[176,1343,427],{"class":426},[176,1345,430],{"class":216},[176,1347,433],{"class":426},[176,1349,1350],{"class":186},":",[176,1352,427],{"class":426},[176,1354,1355],{"class":216},"get_column_letter(",[176,1357,1136],{"class":257},[176,1359,1360],{"class":216},"(headers))",[176,1362,1171],{"class":426},[176,1364,430],{"class":216},[176,1366,433],{"class":426},[176,1368,579],{"class":186},[176,1370,1371],{"class":178,"line":785},[176,1372,230],{"emptyLinePlaceholder":229},[176,1374,1375,1377,1380],{"class":178,"line":790},[176,1376,793],{"class":216},[176,1378,1379],{"class":186},"\"quarterly_totals.xlsx\"",[176,1381,799],{"class":216},[10,1383,1384],{},"The corner cell now sums the row totals, which will agree with the sum of the column totals — a small built-in cross-check that catches a mis-generated range instantly when a reader opens the file.",[161,1386,1388],{"id":1387},"subtotal-when-the-sheet-has-a-filter","SUBTOTAL when the sheet has a filter",[10,1390,1391,1394,1395,1399,1400,1350],{},[173,1392,1393],{},"SUM"," always covers every row in its range, including rows a filter has hidden. If you have added an ",[14,1396,1398],{"href":1397},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fadd-an-autofilter-to-an-excel-sheet-with-openpyxl\u002F","autofilter"," so readers can slice the data, they will expect the total to follow their filter — that is ",[173,1401,1402],{},"SUBTOTAL",[166,1404,1406],{"className":203,"code":1405,"language":205,"meta":171,"style":171},"from openpyxl import load_workbook\n\nwb = load_workbook(\"orders_totals.xlsx\")\nws = wb[\"Orders\"]\n\nlast_data = 4\ntotal_row = 6\n\n# 109 = SUM, ignoring rows hidden by a filter (9 would include manually hidden rows)\nws[f\"D{total_row}\"] = f\"=SUBTOTAL(109,D2:D{last_data})\"\nws.auto_filter.ref = f\"A1:D{last_data}\"\n\nwb.save(\"orders_filtered_total.xlsx\")\n",[173,1407,1408,1419,1423,1436,1450,1454,1464,1473,1477,1482,1515,1535,1539],{"__ignoreMap":171},[176,1409,1410,1412,1414,1416],{"class":178,"line":179},[176,1411,213],{"class":212},[176,1413,217],{"class":216},[176,1415,220],{"class":212},[176,1417,1418],{"class":216}," load_workbook\n",[176,1420,1421],{"class":178,"line":226},[176,1422,230],{"emptyLinePlaceholder":229},[176,1424,1425,1427,1429,1432,1434],{"class":178,"line":233},[176,1426,320],{"class":216},[176,1428,239],{"class":212},[176,1430,1431],{"class":216}," load_workbook(",[176,1433,796],{"class":186},[176,1435,799],{"class":216},[176,1437,1438,1440,1442,1445,1448],{"class":178,"line":245},[176,1439,331],{"class":216},[176,1441,239],{"class":212},[176,1443,1444],{"class":216}," wb[",[176,1446,1447],{"class":186},"\"Orders\"",[176,1449,309],{"class":216},[176,1451,1452],{"class":178,"line":268},[176,1453,230],{"emptyLinePlaceholder":229},[176,1455,1456,1459,1461],{"class":178,"line":287},[176,1457,1458],{"class":216},"last_data ",[176,1460,239],{"class":212},[176,1462,1463],{"class":257}," 4\n",[176,1465,1466,1468,1470],{"class":178,"line":306},[176,1467,490],{"class":216},[176,1469,239],{"class":212},[176,1471,1472],{"class":257}," 6\n",[176,1474,1475],{"class":178,"line":312},[176,1476,230],{"emptyLinePlaceholder":229},[176,1478,1479],{"class":178,"line":317},[176,1480,1481],{"class":826},"# 109 = SUM, ignoring rows hidden by a filter (9 would include manually hidden rows)\n",[176,1483,1484,1486,1488,1490,1492,1494,1496,1498,1500,1502,1504,1507,1509,1511,1513],{"class":178,"line":328},[176,1485,512],{"class":216},[176,1487,420],{"class":212},[176,1489,423],{"class":186},[176,1491,427],{"class":426},[176,1493,522],{"class":216},[176,1495,433],{"class":426},[176,1497,436],{"class":186},[176,1499,439],{"class":216},[176,1501,239],{"class":212},[176,1503,444],{"class":212},[176,1505,1506],{"class":186},"\"=SUBTOTAL(109,D2:D",[176,1508,427],{"class":426},[176,1510,574],{"class":216},[176,1512,433],{"class":426},[176,1514,579],{"class":186},[176,1516,1517,1520,1522,1524,1527,1529,1531,1533],{"class":178,"line":339},[176,1518,1519],{"class":216},"ws.auto_filter.ref ",[176,1521,239],{"class":212},[176,1523,444],{"class":212},[176,1525,1526],{"class":186},"\"A1:D",[176,1528,427],{"class":426},[176,1530,574],{"class":216},[176,1532,433],{"class":426},[176,1534,465],{"class":186},[176,1536,1537],{"class":178,"line":350},[176,1538,230],{"emptyLinePlaceholder":229},[176,1540,1541,1543,1546],{"class":178,"line":377},[176,1542,793],{"class":216},[176,1544,1545],{"class":186},"\"orders_filtered_total.xlsx\"",[176,1547,799],{"class":216},[20,1549,29,1554,29,1557,29,1560,29,1563,29,1569,29,1573,29,1576,29,1580,29,1584,29,1589,29,1593,29,1596,29,1599,29,1602,29,1609,29,1615,29,1620,29,1623,29,1628,29,1632,29,1638],{"viewBox":1550,"role":23,"ariaLabelledBy":1551,"xmlns":27,"style":28},"0 0 720 236",[1552,1553],"sum-sub-t","sum-sub-d",[31,1555,1556],{"id":1552},"SUM and SUBTOTAL on a filtered sheet",[35,1558,1559],{"id":1553},"With a filter hiding the South row, SUM still returns the total of all three regions while SUBTOTAL 109 returns the total of only the two visible rows.",[39,1561],{"x":41,"y":41,"width":42,"height":1562,"fill":44},"236",[53,1564,1568],{"x":1565,"y":1566,"style":1567},"360","26","font-size:12.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","filter active: region ≠ South",[39,1570],{"x":47,"y":47,"width":1571,"height":1572,"fill":44,"stroke":70},"290","30",[53,1574,1575],{"x":69,"y":55,"style":74},"North",[53,1577,1579],{"x":1571,"y":55,"style":1578},"font-size:12px;fill:var(--text,#172033);text-anchor:end","26,700",[39,1581],{"x":47,"y":1582,"width":1571,"height":1572,"fill":1583,"stroke":70},"70","#f0f2f5",[53,1585,1588],{"x":69,"y":1586,"style":1587},"90","font-size:12px;fill:var(--muted,#5b6780)","South (hidden)",[53,1590,1592],{"x":1571,"y":1586,"style":1591},"font-size:12px;fill:var(--muted,#5b6780);text-anchor:end","20,900",[39,1594],{"x":47,"y":1595,"width":1571,"height":1572,"fill":44,"stroke":70},"100",[53,1597,1598],{"x":69,"y":98,"style":74},"West",[53,1600,1601],{"x":1571,"y":98,"style":1578},"7,600",[39,1603],{"x":49,"y":1604,"width":65,"height":1605,"rx":1606,"fill":1607,"stroke":1608},"48","72","12","#fdefd8","var(--gold,#b4740a)",[53,1610,1614],{"x":1611,"y":1612,"style":1613},"530","74","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","=SUM(B2:B4)",[53,1616,1619],{"x":1611,"y":1617,"style":1618},"98","font-size:12.5px;fill:var(--text,#172033);text-anchor:middle","55,200 — includes hidden rows",[39,1621],{"x":49,"y":1622,"width":65,"height":1605,"rx":1606,"fill":124,"stroke":125,"style":126},"136",[53,1624,1627],{"x":1611,"y":1625,"style":1626},"162","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","=SUBTOTAL(109,B2:B4)",[53,1629,1631],{"x":1611,"y":1630,"style":1618},"186","34,300 — visible rows only",[53,1633,1637],{"x":1634,"y":1635,"style":1636},"185","170","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","The reader sees two rows",[53,1639,1641],{"x":1634,"y":1640,"style":1636},"192","and expects the total to match.",[10,1643,1644,1645,1647,1648,1651,1652,1655,1656,832,1659,1662,1663,1665],{},"The function number matters: ",[173,1646,88],{}," ignores filtered ",[844,1649,1650],{},"and"," manually hidden rows, while ",[173,1653,1654],{},"9"," ignores only filtered ones. For averages the pair is ",[173,1657,1658],{},"101",[173,1660,1661],{},"1",". A nested ",[173,1664,1402],{}," inside a range is skipped automatically, which is what stops group subtotals being double-counted in a grand total.",[161,1667,1669],{"id":1668},"conditional-aggregates-sumif-and-averageif","Conditional aggregates: SUMIF and AVERAGEIF",[10,1671,1672],{},"When the total needs a condition, generate the criteria range and the criteria cell rather than embedding a literal:",[166,1674,1676],{"className":203,"code":1675,"language":205,"meta":171,"style":171},"from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.append([\"Region\", \"Rep\", \"Revenue\"])\ndata = [\n    (\"North\", \"Ada\", 12000), (\"North\", \"Ben\", 14700),\n    (\"South\", \"Cal\", 9800), (\"South\", \"Dee\", 11100),\n    (\"West\", \"Eli\", 7600),\n]\nfor row in data:\n    ws.append(row)\nlast = ws.max_row\n\nsummary_start = last + 2\nws[f\"A{summary_start}\"] = \"Region\"\nws[f\"B{summary_start}\"] = \"Revenue\"\nws[f\"C{summary_start}\"] = \"Average deal\"\n\nfor i, region in enumerate(sorted({r[0] for r in data}), start=summary_start + 1):\n    ws[f\"A{i}\"] = region\n    ws[f\"B{i}\"] = f\"=SUMIF($A$2:$A${last},$A{i},$C$2:$C${last})\"\n    ws[f\"C{i}\"] = f\"=AVERAGEIF($A$2:$A${last},$A{i},$C$2:$C${last})\"\n    ws[f\"B{i}\"].number_format = \"#,##0\"\n    ws[f\"C{i}\"].number_format = \"#,##0.0\"\n\nwb.save(\"orders_by_region.xlsx\")\n",[173,1677,1678,1688,1692,1700,1708,1726,1734,1767,1799,1816,1820,1830,1834,1842,1846,1859,1883,1907,1930,1934,1981,2005,2056,2105,2127,2150,2154],{"__ignoreMap":171},[176,1679,1680,1682,1684,1686],{"class":178,"line":179},[176,1681,213],{"class":212},[176,1683,217],{"class":216},[176,1685,220],{"class":212},[176,1687,223],{"class":216},[176,1689,1690],{"class":178,"line":226},[176,1691,230],{"emptyLinePlaceholder":229},[176,1693,1694,1696,1698],{"class":178,"line":233},[176,1695,320],{"class":216},[176,1697,239],{"class":212},[176,1699,325],{"class":216},[176,1701,1702,1704,1706],{"class":178,"line":245},[176,1703,331],{"class":216},[176,1705,239],{"class":212},[176,1707,336],{"class":216},[176,1709,1710,1712,1714,1716,1719,1721,1724],{"class":178,"line":268},[176,1711,353],{"class":216},[176,1713,898],{"class":186},[176,1715,254],{"class":216},[176,1717,1718],{"class":186},"\"Rep\"",[176,1720,254],{"class":216},[176,1722,1723],{"class":186},"\"Revenue\"",[176,1725,374],{"class":216},[176,1727,1728,1730,1732],{"class":178,"line":287},[176,1729,925],{"class":216},[176,1731,239],{"class":212},[176,1733,242],{"class":216},[176,1735,1736,1738,1740,1742,1745,1747,1750,1753,1755,1757,1760,1762,1765],{"class":178,"line":306},[176,1737,248],{"class":216},[176,1739,936],{"class":186},[176,1741,254],{"class":216},[176,1743,1744],{"class":186},"\"Ada\"",[176,1746,254],{"class":216},[176,1748,1749],{"class":257},"12000",[176,1751,1752],{"class":216},"), (",[176,1754,936],{"class":186},[176,1756,254],{"class":216},[176,1758,1759],{"class":186},"\"Ben\"",[176,1761,254],{"class":216},[176,1763,1764],{"class":257},"14700",[176,1766,265],{"class":216},[176,1768,1769,1771,1773,1775,1778,1780,1783,1785,1787,1789,1792,1794,1797],{"class":178,"line":312},[176,1770,248],{"class":216},[176,1772,965],{"class":186},[176,1774,254],{"class":216},[176,1776,1777],{"class":186},"\"Cal\"",[176,1779,254],{"class":216},[176,1781,1782],{"class":257},"9800",[176,1784,1752],{"class":216},[176,1786,965],{"class":186},[176,1788,254],{"class":216},[176,1790,1791],{"class":186},"\"Dee\"",[176,1793,254],{"class":216},[176,1795,1796],{"class":257},"11100",[176,1798,265],{"class":216},[176,1800,1801,1803,1805,1807,1810,1812,1814],{"class":178,"line":317},[176,1802,248],{"class":216},[176,1804,994],{"class":186},[176,1806,254],{"class":216},[176,1808,1809],{"class":186},"\"Eli\"",[176,1811,254],{"class":216},[176,1813,999],{"class":257},[176,1815,265],{"class":216},[176,1817,1818],{"class":178,"line":328},[176,1819,309],{"class":216},[176,1821,1822,1824,1826,1828],{"class":178,"line":339},[176,1823,385],{"class":212},[176,1825,1052],{"class":216},[176,1827,391],{"class":212},[176,1829,1057],{"class":216},[176,1831,1832],{"class":178,"line":350},[176,1833,1062],{"class":216},[176,1835,1836,1838,1840],{"class":178,"line":377},[176,1837,1071],{"class":216},[176,1839,239],{"class":212},[176,1841,411],{"class":216},[176,1843,1844],{"class":178,"line":382},[176,1845,230],{"emptyLinePlaceholder":229},[176,1847,1848,1851,1853,1855,1857],{"class":178,"line":397},[176,1849,1850],{"class":216},"summary_start ",[176,1852,239],{"class":212},[176,1854,1084],{"class":216},[176,1856,498],{"class":212},[176,1858,501],{"class":257},[176,1860,1861,1863,1865,1867,1869,1872,1874,1876,1878,1880],{"class":178,"line":403},[176,1862,512],{"class":216},[176,1864,420],{"class":212},[176,1866,1097],{"class":186},[176,1868,427],{"class":426},[176,1870,1871],{"class":216},"summary_start",[176,1873,433],{"class":426},[176,1875,436],{"class":186},[176,1877,439],{"class":216},[176,1879,239],{"class":212},[176,1881,1882],{"class":186}," \"Region\"\n",[176,1884,1885,1887,1889,1892,1894,1896,1898,1900,1902,1904],{"class":178,"line":414},[176,1886,512],{"class":216},[176,1888,420],{"class":212},[176,1890,1891],{"class":186},"\"B",[176,1893,427],{"class":426},[176,1895,1871],{"class":216},[176,1897,433],{"class":426},[176,1899,436],{"class":186},[176,1901,439],{"class":216},[176,1903,239],{"class":212},[176,1905,1906],{"class":186}," \"Revenue\"\n",[176,1908,1909,1911,1913,1915,1917,1919,1921,1923,1925,1927],{"class":178,"line":468},[176,1910,512],{"class":216},[176,1912,420],{"class":212},[176,1914,517],{"class":186},[176,1916,427],{"class":426},[176,1918,1871],{"class":216},[176,1920,433],{"class":426},[176,1922,436],{"class":186},[176,1924,439],{"class":216},[176,1926,239],{"class":212},[176,1928,1929],{"class":186}," \"Average deal\"\n",[176,1931,1932],{"class":178,"line":473},[176,1933,230],{"emptyLinePlaceholder":229},[176,1935,1936,1938,1941,1943,1946,1948,1951,1954,1956,1958,1960,1962,1964,1967,1971,1973,1975,1977,1979],{"class":178,"line":487},[176,1937,385],{"class":212},[176,1939,1940],{"class":216}," i, region ",[176,1942,391],{"class":212},[176,1944,1945],{"class":257}," enumerate",[176,1947,1129],{"class":216},[176,1949,1950],{"class":257},"sorted",[176,1952,1953],{"class":216},"({r[",[176,1955,41],{"class":257},[176,1957,439],{"class":216},[176,1959,385],{"class":212},[176,1961,741],{"class":216},[176,1963,391],{"class":212},[176,1965,1966],{"class":216}," data}), ",[176,1968,1970],{"class":1969},"sa561","start",[176,1972,239],{"class":212},[176,1974,1850],{"class":216},[176,1976,498],{"class":212},[176,1978,597],{"class":257},[176,1980,757],{"class":216},[176,1982,1983,1985,1987,1989,1991,1994,1996,1998,2000,2002],{"class":178,"line":504},[176,1984,417],{"class":216},[176,1986,420],{"class":212},[176,1988,1097],{"class":186},[176,1990,427],{"class":426},[176,1992,1993],{"class":216},"i",[176,1995,433],{"class":426},[176,1997,436],{"class":186},[176,1999,439],{"class":216},[176,2001,239],{"class":212},[176,2003,2004],{"class":216}," region\n",[176,2006,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2030,2032,2034,2036,2039,2041,2043,2045,2048,2050,2052,2054],{"class":178,"line":509},[176,2008,417],{"class":216},[176,2010,420],{"class":212},[176,2012,1891],{"class":186},[176,2014,427],{"class":426},[176,2016,1993],{"class":216},[176,2018,433],{"class":426},[176,2020,436],{"class":186},[176,2022,439],{"class":216},[176,2024,239],{"class":212},[176,2026,444],{"class":212},[176,2028,2029],{"class":186},"\"=SUMIF($A$2:$A$",[176,2031,427],{"class":426},[176,2033,1204],{"class":216},[176,2035,433],{"class":426},[176,2037,2038],{"class":186},",$A",[176,2040,427],{"class":426},[176,2042,1993],{"class":216},[176,2044,433],{"class":426},[176,2046,2047],{"class":186},",$C$2:$C$",[176,2049,427],{"class":426},[176,2051,1204],{"class":216},[176,2053,433],{"class":426},[176,2055,579],{"class":186},[176,2057,2058,2060,2062,2064,2066,2068,2070,2072,2074,2076,2078,2081,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103],{"class":178,"line":536},[176,2059,417],{"class":216},[176,2061,420],{"class":212},[176,2063,517],{"class":186},[176,2065,427],{"class":426},[176,2067,1993],{"class":216},[176,2069,433],{"class":426},[176,2071,436],{"class":186},[176,2073,439],{"class":216},[176,2075,239],{"class":212},[176,2077,444],{"class":212},[176,2079,2080],{"class":186},"\"=AVERAGEIF($A$2:$A$",[176,2082,427],{"class":426},[176,2084,1204],{"class":216},[176,2086,433],{"class":426},[176,2088,2038],{"class":186},[176,2090,427],{"class":426},[176,2092,1993],{"class":216},[176,2094,433],{"class":426},[176,2096,2047],{"class":186},[176,2098,427],{"class":426},[176,2100,1204],{"class":216},[176,2102,433],{"class":426},[176,2104,579],{"class":186},[176,2106,2107,2109,2111,2113,2115,2117,2119,2121,2123,2125],{"class":178,"line":582},[176,2108,417],{"class":216},[176,2110,420],{"class":212},[176,2112,1891],{"class":186},[176,2114,427],{"class":426},[176,2116,1993],{"class":216},[176,2118,433],{"class":426},[176,2120,436],{"class":186},[176,2122,777],{"class":216},[176,2124,239],{"class":212},[176,2126,1235],{"class":186},[176,2128,2129,2131,2133,2135,2137,2139,2141,2143,2145,2147],{"class":178,"line":611},[176,2130,417],{"class":216},[176,2132,420],{"class":212},[176,2134,517],{"class":186},[176,2136,427],{"class":426},[176,2138,1993],{"class":216},[176,2140,433],{"class":426},[176,2142,436],{"class":186},[176,2144,777],{"class":216},[176,2146,239],{"class":212},[176,2148,2149],{"class":186}," \"#,##0.0\"\n",[176,2151,2152],{"class":178,"line":657},[176,2153,230],{"emptyLinePlaceholder":229},[176,2155,2156,2158,2161],{"class":178,"line":685},[176,2157,793],{"class":216},[176,2159,2160],{"class":186},"\"orders_by_region.xlsx\"",[176,2162,799],{"class":216},[10,2164,2165,2166,2169,2170,2173,2174,2177,2178,2181,2182,2185],{},"Every range is anchored with ",[173,2167,2168],{},"$"," and only the criteria cell (",[173,2171,2172],{},"$A{i}",") moves, so the block behaves exactly like a formula the reader dragged down. If the conditions come from more than one column, ",[173,2175,2176],{},"SUMIFS"," takes the value range first and then criteria pairs — the argument order is reversed compared with ",[173,2179,2180],{},"SUMIF",", which is a frequent source of ",[173,2183,2184],{},"#VALUE!",".",[20,2187,29,2192,29,2195,29,2198,29,2201,29,2208,29,2211,29,2215,29,2219,29,2224,29,2228,29,2232,29,2234,29,2239,29,2242,29,2245,29,2248,29,2251,29,2254,29,2258,29,2261,29,2264,29,2267,29,2270,29,2274,29,2277,29,2280,29,2283,29,2287],{"viewBox":2188,"role":23,"ariaLabelledBy":2189,"xmlns":27,"style":28},"0 0 720 208",[2190,2191],"sum-pick-t","sum-pick-d",[31,2193,2194],{"id":2190},"Picking the right aggregate function for a generated total",[35,2196,2197],{"id":2191},"Four cards: SUM for every row regardless of filters, SUBTOTAL 109 for the visible rows under a filter, SUMIF for one condition, and SUMIFS for several conditions with the value range first.",[39,2199],{"x":41,"y":41,"width":42,"height":2200,"fill":44},"208",[39,2202],{"x":2203,"y":2204,"width":2205,"height":2206,"rx":1606,"fill":85,"stroke":2207,"style":126},"16","28","164","160","var(--brand,#5b5cf0)",[53,2209,1393],{"x":1617,"y":69,"style":2210},"font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[53,2212,2214],{"x":1617,"y":2213,"style":1636},"84","every row in",[53,2216,2218],{"x":1617,"y":2217,"style":1636},"102","the range",[53,2220,2223],{"x":1617,"y":2221,"style":2222},"132","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","use when the total",[53,2225,2227],{"x":1617,"y":2226,"style":2222},"150","must always be",[53,2229,2231],{"x":1617,"y":2230,"style":2222},"168","the full picture",[39,2233],{"x":129,"y":2204,"width":2205,"height":2206,"rx":1606,"fill":124,"stroke":125,"style":126},[53,2235,2238],{"x":2236,"y":69,"style":2237},"278","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","SUBTOTAL(109…)",[53,2240,2241],{"x":2236,"y":2213,"style":1636},"visible rows only",[53,2243,2244],{"x":2236,"y":2217,"style":1636},"nested ones skipped",[53,2246,2247],{"x":2236,"y":2221,"style":2222},"use when a filter",[53,2249,2250],{"x":2236,"y":2226,"style":2222},"is on the sheet",[39,2252],{"x":2253,"y":2204,"width":2205,"height":2206,"rx":1606,"fill":1607,"stroke":1608,"style":126},"376",[53,2255,2180],{"x":2256,"y":69,"style":2257},"458","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[53,2259,2260],{"x":2256,"y":2213,"style":1636},"criteria range first",[53,2262,2263],{"x":2256,"y":2217,"style":1636},"values last",[53,2265,2266],{"x":2256,"y":2221,"style":2222},"use for one",[53,2268,2269],{"x":2256,"y":2226,"style":2222},"condition per cell",[39,2271],{"x":2272,"y":2204,"width":2273,"height":2206,"rx":1606,"fill":113,"stroke":70},"556","148",[53,2275,2176],{"x":2276,"y":69,"style":2210},"630",[53,2278,2279],{"x":2276,"y":2213,"style":1636},"values FIRST",[53,2281,2282],{"x":2276,"y":2217,"style":1636},"then pairs",[53,2284,2286],{"x":2276,"y":2221,"style":2285},"font-size:11.5px;fill:var(--danger,#dc2626);text-anchor:middle","argument order is",[53,2288,2289],{"x":2276,"y":2226,"style":2285},"reversed vs SUMIF",[161,2291,2293],{"id":2292},"cross-checking-the-totals-in-python","Cross-checking the totals in Python",[10,2295,2296],{},"A generated formula that points at the wrong range still looks plausible. Compute the same aggregate in pandas and compare before you ship the file:",[166,2298,2300],{"className":203,"code":2299,"language":205,"meta":171,"style":171},"import pandas as pd\n\ndf = pd.read_excel(\"orders_by_region.xlsx\", nrows=5)\nexpected = df.groupby(\"Region\")[\"Revenue\"].sum().to_dict()\nprint(expected)     # {'North': 26700, 'South': 20900, 'West': 7600}\n",[173,2301,2302,2315,2319,2343,2363],{"__ignoreMap":171},[176,2303,2304,2306,2309,2312],{"class":178,"line":179},[176,2305,220],{"class":212},[176,2307,2308],{"class":216}," pandas ",[176,2310,2311],{"class":212},"as",[176,2313,2314],{"class":216}," pd\n",[176,2316,2317],{"class":178,"line":226},[176,2318,230],{"emptyLinePlaceholder":229},[176,2320,2321,2324,2326,2329,2331,2333,2336,2338,2341],{"class":178,"line":233},[176,2322,2323],{"class":216},"df ",[176,2325,239],{"class":212},[176,2327,2328],{"class":216}," pd.read_excel(",[176,2330,2160],{"class":186},[176,2332,254],{"class":216},[176,2334,2335],{"class":1969},"nrows",[176,2337,239],{"class":212},[176,2339,2340],{"class":257},"5",[176,2342,799],{"class":216},[176,2344,2345,2348,2350,2353,2355,2358,2360],{"class":178,"line":245},[176,2346,2347],{"class":216},"expected ",[176,2349,239],{"class":212},[176,2351,2352],{"class":216}," df.groupby(",[176,2354,898],{"class":186},[176,2356,2357],{"class":216},")[",[176,2359,1723],{"class":186},[176,2361,2362],{"class":216},"].sum().to_dict()\n",[176,2364,2365,2367,2370],{"class":178,"line":268},[176,2366,805],{"class":257},[176,2368,2369],{"class":216},"(expected)     ",[176,2371,2372],{"class":826},"# {'North': 26700, 'South': 20900, 'West': 7600}\n",[10,2374,2375],{},"Log those figures next to the file you produce. When a reader queries a number three weeks later, the log tells you what the data said at the time, independently of what Excel is showing.",[161,2377,2379],{"id":2378},"totals-in-a-workbook-you-did-not-write","Totals in a workbook you did not write",[10,2381,2382],{},"When the file arrives from someone else, the total row is wherever they put it. Locate it by content rather than by position, so a script that refreshes the numbers keeps working when a row is inserted above:",[166,2384,2386],{"className":203,"code":2385,"language":205,"meta":171,"style":171},"from openpyxl import load_workbook\n\nwb = load_workbook(\"supplier_report.xlsx\")\nws = wb.active\n\nlabel_col = \"C\"\ntotal_cells = [\n    cell for cell in ws[label_col]\n    if isinstance(cell.value, str) and cell.value.strip().lower() in {\"total\", \"grand total\"}\n]\nif not total_cells:\n    raise SystemExit(\"No total row found — check the label column\")\n\ntotal_row = total_cells[-1].row\nlast_data = total_row - 2\nws[f\"D{total_row}\"] = f\"=SUM(D2:D{last_data})\"\nprint(\"rewrote the total at row\", total_row)\n\nwb.save(\"supplier_report_refreshed.xlsx\")\n",[173,2387,2388,2398,2402,2415,2423,2427,2437,2446,2461,2499,2503,2514,2529,2533,2550,2563,2596,2608,2612],{"__ignoreMap":171},[176,2389,2390,2392,2394,2396],{"class":178,"line":179},[176,2391,213],{"class":212},[176,2393,217],{"class":216},[176,2395,220],{"class":212},[176,2397,1418],{"class":216},[176,2399,2400],{"class":178,"line":226},[176,2401,230],{"emptyLinePlaceholder":229},[176,2403,2404,2406,2408,2410,2413],{"class":178,"line":233},[176,2405,320],{"class":216},[176,2407,239],{"class":212},[176,2409,1431],{"class":216},[176,2411,2412],{"class":186},"\"supplier_report.xlsx\"",[176,2414,799],{"class":216},[176,2416,2417,2419,2421],{"class":178,"line":245},[176,2418,331],{"class":216},[176,2420,239],{"class":212},[176,2422,336],{"class":216},[176,2424,2425],{"class":178,"line":268},[176,2426,230],{"emptyLinePlaceholder":229},[176,2428,2429,2432,2434],{"class":178,"line":287},[176,2430,2431],{"class":216},"label_col ",[176,2433,239],{"class":212},[176,2435,2436],{"class":186}," \"C\"\n",[176,2438,2439,2442,2444],{"class":178,"line":306},[176,2440,2441],{"class":216},"total_cells ",[176,2443,239],{"class":212},[176,2445,242],{"class":216},[176,2447,2448,2451,2453,2456,2458],{"class":178,"line":312},[176,2449,2450],{"class":216},"    cell ",[176,2452,385],{"class":212},[176,2454,2455],{"class":216}," cell ",[176,2457,391],{"class":212},[176,2459,2460],{"class":216}," ws[label_col]\n",[176,2462,2463,2466,2469,2472,2475,2478,2480,2483,2485,2488,2491,2493,2496],{"class":178,"line":317},[176,2464,2465],{"class":212},"    if",[176,2467,2468],{"class":257}," isinstance",[176,2470,2471],{"class":216},"(cell.value, ",[176,2473,2474],{"class":257},"str",[176,2476,2477],{"class":216},") ",[176,2479,1650],{"class":212},[176,2481,2482],{"class":216}," cell.value.strip().lower() ",[176,2484,391],{"class":212},[176,2486,2487],{"class":216}," {",[176,2489,2490],{"class":186},"\"total\"",[176,2492,254],{"class":216},[176,2494,2495],{"class":186},"\"grand total\"",[176,2497,2498],{"class":216},"}\n",[176,2500,2501],{"class":178,"line":328},[176,2502,309],{"class":216},[176,2504,2505,2508,2511],{"class":178,"line":339},[176,2506,2507],{"class":212},"if",[176,2509,2510],{"class":212}," not",[176,2512,2513],{"class":216}," total_cells:\n",[176,2515,2516,2519,2522,2524,2527],{"class":178,"line":350},[176,2517,2518],{"class":212},"    raise",[176,2520,2521],{"class":257}," SystemExit",[176,2523,1129],{"class":216},[176,2525,2526],{"class":186},"\"No total row found — check the label column\"",[176,2528,799],{"class":216},[176,2530,2531],{"class":178,"line":377},[176,2532,230],{"emptyLinePlaceholder":229},[176,2534,2535,2537,2539,2542,2545,2547],{"class":178,"line":382},[176,2536,490],{"class":216},[176,2538,239],{"class":212},[176,2540,2541],{"class":216}," total_cells[",[176,2543,2544],{"class":212},"-",[176,2546,1661],{"class":257},[176,2548,2549],{"class":216},"].row\n",[176,2551,2552,2554,2556,2559,2561],{"class":178,"line":397},[176,2553,1458],{"class":216},[176,2555,239],{"class":212},[176,2557,2558],{"class":216}," total_row ",[176,2560,2544],{"class":212},[176,2562,501],{"class":257},[176,2564,2565,2567,2569,2571,2573,2575,2577,2579,2581,2583,2585,2588,2590,2592,2594],{"class":178,"line":403},[176,2566,512],{"class":216},[176,2568,420],{"class":212},[176,2570,423],{"class":186},[176,2572,427],{"class":426},[176,2574,522],{"class":216},[176,2576,433],{"class":426},[176,2578,436],{"class":186},[176,2580,439],{"class":216},[176,2582,239],{"class":212},[176,2584,444],{"class":212},[176,2586,2587],{"class":186},"\"=SUM(D2:D",[176,2589,427],{"class":426},[176,2591,574],{"class":216},[176,2593,433],{"class":426},[176,2595,579],{"class":186},[176,2597,2598,2600,2602,2605],{"class":178,"line":414},[176,2599,805],{"class":257},[176,2601,1129],{"class":216},[176,2603,2604],{"class":186},"\"rewrote the total at row\"",[176,2606,2607],{"class":216},", total_row)\n",[176,2609,2610],{"class":178,"line":468},[176,2611,230],{"emptyLinePlaceholder":229},[176,2613,2614,2616,2619],{"class":178,"line":473},[176,2615,793],{"class":216},[176,2617,2618],{"class":186},"\"supplier_report_refreshed.xlsx\"",[176,2620,799],{"class":216},[10,2622,2623,2624,2628],{},"Searching for the label makes the script self-correcting: if the supplier adds a row of notes at the top, the total moves and so does the formula. Combine it with the ",[14,2625,2627],{"href":2626},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002F","formula inventory"," trick and you can log exactly which aggregates a third-party file contains before your job depends on them.",[161,2630,2632],{"id":2631},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[2634,2635,2636,2652],"table",{},[2637,2638,2639],"thead",{},[2640,2641,2642,2646,2649],"tr",{},[2643,2644,2645],"th",{},"Symptom",[2643,2647,2648],{},"Cause",[2643,2650,2651],{},"Fix",[2653,2654,2655,2670,2681,2699,2716,2736],"tbody",{},[2640,2656,2657,2661,2664],{},[2658,2659,2660],"td",{},"Total misses the last rows",[2658,2662,2663],{},"Range end was a constant from an earlier run",[2658,2665,2666,2667,2669],{},"Build it from ",[173,2668,841],{}," after appending",[2640,2671,2672,2675,2678],{},[2658,2673,2674],{},"Total counts itself, growing each run",[2658,2676,2677],{},"Total row sits inside the summed range",[2658,2679,2680],{},"Leave a spacer row and end the range at the last data row",[2640,2682,2683,2689,2692],{},[2658,2684,2685,2688],{},[173,2686,2687],{},"AVERAGE"," ignores rows",[2658,2690,2691],{},"Numbers stored as text, or blanks",[2658,2693,2694,2695,2698],{},"Coerce with ",[173,2696,2697],{},"pd.to_numeric"," before writing",[2640,2700,2701,2704,2710],{},[2658,2702,2703],{},"Filtered total does not move",[2658,2705,2706,2707,2709],{},"Used ",[173,2708,1393],{}," on a filtered sheet",[2658,2711,2712,2713],{},"Use ",[173,2714,2715],{},"SUBTOTAL(109, …)",[2640,2717,2718,2726,2729],{},[2658,2719,2720,2723,2724],{},[173,2721,2722],{},"#DIV\u002F0!"," from ",[173,2725,2687],{},[2658,2727,2728],{},"No numeric cells in the range",[2658,2730,2731,2732,2735],{},"Wrap in ",[173,2733,2734],{},"IFERROR(…, \"\")"," or check the count first",[2640,2737,2738,2745,2750],{},[2658,2739,2740,2742,2743],{},[173,2741,2176],{}," returns ",[173,2744,2184],{},[2658,2746,2747,2748],{},"Argument order confused with ",[173,2749,2180],{},[2658,2751,2752],{},[173,2753,2754],{},"SUMIFS(sum_range, criteria_range, criteria, …)",[161,2756,2758],{"id":2757},"performance-and-scale-notes","Performance and scale notes",[10,2760,2761,2763,2764,2766,2767,2770],{},[173,2762,1393],{}," over a contiguous range is close to free even at hundreds of thousands of rows. The expensive patterns are per-row conditional aggregates: a ",[173,2765,2180],{}," on each of 50,000 rows scans the whole criteria range 50,000 times, which is why files like that take a minute to open. When the summary is per-category rather than per-row, generate one formula per category — five formulas instead of fifty thousand — or aggregate in pandas with ",[173,2768,2769],{},"groupby"," and write literal values, keeping formulas only for the handful of cells the reader may want to inspect.",[161,2772,2774],{"id":2773},"conclusion","Conclusion",[10,2776,2777,2778,2780],{},"Total rows break for one reason: someone wrote the range once. Capture the first and last data rows as variables, generate every aggregate from them, leave a spacer so the total never joins the data, and pick ",[173,2779,1402],{}," whenever a filter is in play. Cross-check the result in pandas before the file leaves your machine, and the number at the bottom of the sheet will still be right next quarter.",[161,2782,2784],{"id":2783},"frequently-asked-questions","Frequently asked questions",[10,2786,2787,2791,2792,2794],{},[2788,2789,2790],"strong",{},"How do I make the total row grow with my data?","\nBuild the range from the last data row rather than a constant — track it as you append, or read ",[173,2793,841],{}," immediately after writing the final data row.",[10,2796,2797,2806,2807,2810,2811,2813],{},[2788,2798,2799,2800,2802,2803,2805],{},"Should I use ",[173,2801,1393],{}," or ",[173,2804,1402],{},"?","\nUse ",[173,2808,2809],{},"SUBTOTAL(109, range)"," when the sheet has a filter and the reader expects the total to reflect the visible rows. Use ",[173,2812,1393],{}," when the total must always cover every row.",[10,2815,2816,2822,2824],{},[2788,2817,2818,2819,2821],{},"Why does ",[173,2820,2687],{}," ignore some of my rows?",[173,2823,2687],{}," skips blanks and text. Numbers stored as text look like data to a reader but are invisible to the function, so coerce the column to numeric before writing it.",[10,2826,2827,2830,2831,2834],{},[2788,2828,2829],{},"Can I total a whole column without knowing its length?","\nYes — ",[173,2832,2833],{},"=SUM(D:D)"," works, but it also picks up anything you later add to the column. Prefer an explicit range ending at the last data row.",[161,2836,2838],{"id":2837},"related","Related",[10,2840,2841],{},"Up to the parent guide:",[2843,2844,2845],"ul",{},[2846,2847,2848,2850],"li",{},[14,2849,17],{"href":16}," — the whole formula workflow.",[10,2852,2853],{},"Related guides:",[2843,2855,2856,2862,2869,2876],{},[2846,2857,2858,2861],{},[14,2859,2860],{"href":2626},"Write a Formula to an Excel Cell with openpyxl"," — assignment, formats and verification.",[2846,2863,2864,2868],{},[14,2865,2867],{"href":2866},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl\u002F","Copy a Formula Down a Column in openpyxl"," — per-row formulas across a table.",[2846,2870,2871,2875],{},[14,2872,2874],{"href":2873},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002F","Add a Summary Sheet to an Excel Report with Python"," — where those totals belong in a multi-sheet report.",[2846,2877,2878,2882],{},[14,2879,2881],{"href":2880},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data"," — when the aggregation is bigger than a total row.",[2884,2885,2886],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .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":171,"searchDepth":226,"depth":226,"links":2888},[2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899,2900],{"id":163,"depth":226,"text":164},{"id":196,"depth":226,"text":197},{"id":850,"depth":226,"text":851},{"id":1387,"depth":226,"text":1388},{"id":1668,"depth":226,"text":1669},{"id":2292,"depth":226,"text":2293},{"id":2378,"depth":226,"text":2379},{"id":2631,"depth":226,"text":2632},{"id":2757,"depth":226,"text":2758},{"id":2773,"depth":226,"text":2774},{"id":2783,"depth":226,"text":2784},{"id":2837,"depth":226,"text":2838},"2026-08-01","Generate SUM, AVERAGE, SUBTOTAL and conditional aggregate formulas from Python so a total row always covers the data you just wrote, however many rows there are.","md",[2905,2907,2910,2913],{"q":2790,"a":2906},"Build the range from the last data row rather than a constant — track it as you append, or read ws.max_row immediately after writing the final data row.",{"q":2908,"a":2909},"Should I use SUM or SUBTOTAL?","Use SUBTOTAL(109, range) when the sheet has a filter and the reader expects the total to reflect the visible rows. Use SUM when the total must always cover every row.",{"q":2911,"a":2912},"Why does AVERAGE ignore some of my rows?","AVERAGE skips blanks and text. Numbers stored as text look like data to a reader but are invisible to the function, so coerce the column to numeric before writing it.",{"q":2829,"a":2914},"Yes — =SUM(D:D) works, but it also picks up anything you later add to the column. Prefer an explicit range ending at the last data row.",{"breadcrumb":2916},[2917,2920,2923,2924],{"name":2918,"item":2919},"Home","\u002F",{"name":2921,"item":2922},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},{"name":5,"item":2925},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002F","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python",{"title":5,"description":2928},"Write total rows with openpyxl that track the data: SUM and AVERAGE over generated ranges, SUBTOTAL with filters, SUMIF\u002FAVERAGEIF, and column-wide totals.","add-sum-and-average-formulas-with-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002Findex","how-to","1BCR8IdwWL3rhDV4DAGUGp9ph6QmDU90lt1yxsAdjNQ",[2934,2937],{"title":17,"path":2935,"stem":2936,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Findex",{"title":2938,"path":2939,"stem":2940,"children":-1},"Convert Excel Formulas to Values with Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fconvert-excel-formulas-to-values-with-python\u002Findex",1785584462714]