[{"data":1,"prerenderedAt":2176},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas":2142,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas":2151,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas":2156,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas":2157},{"id":4,"title":5,"body":6,"dateModified":2110,"datePublished":2110,"description":2111,"extension":2112,"faq":2113,"keyword":2122,"kind":2123,"meta":2124,"navigation":144,"path":2133,"seo":2134,"slug":2136,"stem":2137,"topics":2138,"type":2140,"__hash__":2141},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas\u002Findex.md","INDIRECT and OFFSET Equivalents in pandas",{"type":7,"value":8,"toc":2098},"minimark",[9,53,58,86,96,100,108,701,715,719,722,969,999,1159,1163,1176,1349,1380,1384,1390,1497,1520,1666,1749,1753,1759,1941,1951,1955,1994,1998,2016,2020,2033,2046,2055,2061,2065,2094],[10,11,12,16,17,20,21,24,25,28,29,32,33,37,38,41,42,16,44,46,47,52],"p",{},[13,14,15],"code",{},"INDIRECT"," and ",[13,18,19],{},"OFFSET"," are the two Excel functions that build references while the workbook calculates. ",[13,22,23],{},"INDIRECT(\"'\"&A1&\"'!B5\")"," reads cell B5 from whichever sheet is named in A1; ",[13,26,27],{},"SUM(OFFSET(B2,0,0,COUNT(B:B),1))"," sums a range that grows as rows are added; ",[13,30,31],{},"AVERAGE(OFFSET(B2,COUNT(B:B)-3,0,3,1))"," averages the last three values. They are clever, volatile — Excel recalculates them on every change — and hard to read. When such a workbook moves to Python there is no need for an equivalent of the ",[34,35,36],"em",{},"function","; what is needed is an equivalent of each ",[34,39,40],{},"job"," it does, and pandas has a direct, readable tool for every one. This page takes the common ",[13,43,15],{},[13,45,19],{}," patterns, writes them as formulas in a workbook, recalculates them to get Excel-style answers, and reproduces each in pandas. It belongs to ",[48,49,51],"a",{"href":50},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",".",[54,55,57],"h2",{"id":56},"prerequisites","Prerequisites",[59,60,65],"pre",{"className":61,"code":62,"language":63,"meta":64,"style":64},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[13,66,67],{"__ignoreMap":64},[68,69,72,76,80,83],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"sMTad","pip",[68,77,79],{"class":78},"srMev"," install",[68,81,82],{"class":78}," pandas",[68,84,85],{"class":78}," openpyxl\n",[10,87,88,91,92,95],{},[13,89,90],{},"indirect_offset.xlsx"," has a Summary sheet with two settings — a month name and a measure name — three monthly sheets (Jun, Jul, Aug) with sales and returns by region, and a Daily sheet of ten days of units. LibreOffice (",[13,93,94],{},"soffice",") recalculates the Excel formulas in Step 1 so the pandas versions can be checked against them.",[54,97,99],{"id":98},"step-1-the-formulas-being-replaced","Step 1 — The formulas being replaced",[10,101,102,103,16,105,107],{},"Write the typical ",[13,104,15],{},[13,106,19],{}," formulas into the Summary sheet and recalculate, to have the spreadsheet's answers to compare against:",[59,109,113],{"className":110,"code":111,"language":112,"meta":64,"style":64},"language-python shiki shiki-themes github-light github-dark-high-contrast","import subprocess\nfrom openpyxl import load_workbook\n\nFORMULAS = {\n    \"sheet named in B1, cell C3\": \"=INDIRECT(\\\"'\\\"&B1&\\\"'!C3\\\")\",\n    \"sum of column named in B2\": \"=SUM(INDIRECT(\\\"'\\\"&B1&\\\"'!\\\"&IF(B2=\\\"Sales\\\",\\\"B\\\",\\\"C\\\")&\\\"2:\\\"&IF(B2=\\\"Sales\\\",\\\"B\\\",\\\"C\\\")&\\\"5\\\"))\",\n    \"sum of growing range\": \"=SUM(OFFSET(Daily!B2,0,0,COUNT(Daily!B:B),1))\",\n    \"average of last 3\": \"=AVERAGE(OFFSET(Daily!B2,COUNT(Daily!B:B)-3,0,3,1))\",\n    \"value 2 rows below B4\": \"=OFFSET(Daily!B4,2,0)\",\n    \"sales across sheets\": \"=SUMPRODUCT(SUM(INDIRECT(\\\"'\\\"&{\\\"Jun\\\",\\\"Jul\\\",\\\"Aug\\\"}&\\\"'!B2:B5\\\")))\",\n}\nwb = load_workbook(\"indirect_offset.xlsx\")\nws = wb[\"Summary\"]\nfor i, (label, f) in enumerate(FORMULAS.items(), start=4):\n    ws.cell(i, 1, label)\n    ws.cell(i, 2, f)\nwb.save(\"with_formulas.xlsx\")\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"calc\", \"with_formulas.xlsx\"], check=True, capture_output=True)\ncalc = load_workbook(\"calc\u002Fwith_formulas.xlsx\", data_only=True)[\"Summary\"]\nexcel = {calc.cell(i, 1).value: calc.cell(i, 2).value for i in range(4, 4 + len(FORMULAS))}\nfor k, v in excel.items():\n    print(f\"{k:28} {v}\")\n","python",[13,114,115,125,139,146,159,196,299,312,325,338,398,404,422,439,474,486,497,508,569,598,650,663],{"__ignoreMap":64},[68,116,117,121],{"class":70,"line":71},[68,118,120],{"class":119},"s-kum","import",[68,122,124],{"class":123},"skGVy"," subprocess\n",[68,126,128,131,134,136],{"class":70,"line":127},2,[68,129,130],{"class":119},"from",[68,132,133],{"class":123}," openpyxl ",[68,135,120],{"class":119},[68,137,138],{"class":123}," load_workbook\n",[68,140,142],{"class":70,"line":141},3,[68,143,145],{"emptyLinePlaceholder":144},true,"\n",[68,147,149,153,156],{"class":70,"line":148},4,[68,150,152],{"class":151},"sP0c6","FORMULAS",[68,154,155],{"class":119}," =",[68,157,158],{"class":123}," {\n",[68,160,162,165,168,171,175,178,180,183,185,188,190,193],{"class":70,"line":161},5,[68,163,164],{"class":78},"    \"sheet named in B1, cell C3\"",[68,166,167],{"class":123},": ",[68,169,170],{"class":78},"\"=INDIRECT(",[68,172,174],{"class":173},"sSjpA","\\\"",[68,176,177],{"class":78},"'",[68,179,174],{"class":173},[68,181,182],{"class":78},"&B1&",[68,184,174],{"class":173},[68,186,187],{"class":78},"'!C3",[68,189,174],{"class":173},[68,191,192],{"class":78},")\"",[68,194,195],{"class":123},",\n",[68,197,199,202,204,207,209,211,213,215,217,220,222,225,227,230,232,235,237,240,242,244,246,249,251,254,256,259,261,263,265,267,269,271,273,275,277,279,281,283,285,287,289,292,294,297],{"class":70,"line":198},6,[68,200,201],{"class":78},"    \"sum of column named in B2\"",[68,203,167],{"class":123},[68,205,206],{"class":78},"\"=SUM(INDIRECT(",[68,208,174],{"class":173},[68,210,177],{"class":78},[68,212,174],{"class":173},[68,214,182],{"class":78},[68,216,174],{"class":173},[68,218,219],{"class":78},"'!",[68,221,174],{"class":173},[68,223,224],{"class":78},"&IF(B2=",[68,226,174],{"class":173},[68,228,229],{"class":78},"Sales",[68,231,174],{"class":173},[68,233,234],{"class":78},",",[68,236,174],{"class":173},[68,238,239],{"class":78},"B",[68,241,174],{"class":173},[68,243,234],{"class":78},[68,245,174],{"class":173},[68,247,248],{"class":78},"C",[68,250,174],{"class":173},[68,252,253],{"class":78},")&",[68,255,174],{"class":173},[68,257,258],{"class":78},"2:",[68,260,174],{"class":173},[68,262,224],{"class":78},[68,264,174],{"class":173},[68,266,229],{"class":78},[68,268,174],{"class":173},[68,270,234],{"class":78},[68,272,174],{"class":173},[68,274,239],{"class":78},[68,276,174],{"class":173},[68,278,234],{"class":78},[68,280,174],{"class":173},[68,282,248],{"class":78},[68,284,174],{"class":173},[68,286,253],{"class":78},[68,288,174],{"class":173},[68,290,291],{"class":78},"5",[68,293,174],{"class":173},[68,295,296],{"class":78},"))\"",[68,298,195],{"class":123},[68,300,302,305,307,310],{"class":70,"line":301},7,[68,303,304],{"class":78},"    \"sum of growing range\"",[68,306,167],{"class":123},[68,308,309],{"class":78},"\"=SUM(OFFSET(Daily!B2,0,0,COUNT(Daily!B:B),1))\"",[68,311,195],{"class":123},[68,313,315,318,320,323],{"class":70,"line":314},8,[68,316,317],{"class":78},"    \"average of last 3\"",[68,319,167],{"class":123},[68,321,322],{"class":78},"\"=AVERAGE(OFFSET(Daily!B2,COUNT(Daily!B:B)-3,0,3,1))\"",[68,324,195],{"class":123},[68,326,328,331,333,336],{"class":70,"line":327},9,[68,329,330],{"class":78},"    \"value 2 rows below B4\"",[68,332,167],{"class":123},[68,334,335],{"class":78},"\"=OFFSET(Daily!B4,2,0)\"",[68,337,195],{"class":123},[68,339,341,344,346,349,351,353,355,358,360,363,365,367,369,372,374,376,378,381,383,386,388,391,393,396],{"class":70,"line":340},10,[68,342,343],{"class":78},"    \"sales across sheets\"",[68,345,167],{"class":123},[68,347,348],{"class":78},"\"=SUMPRODUCT(SUM(INDIRECT(",[68,350,174],{"class":173},[68,352,177],{"class":78},[68,354,174],{"class":173},[68,356,357],{"class":78},"&{",[68,359,174],{"class":173},[68,361,362],{"class":78},"Jun",[68,364,174],{"class":173},[68,366,234],{"class":78},[68,368,174],{"class":173},[68,370,371],{"class":78},"Jul",[68,373,174],{"class":173},[68,375,234],{"class":78},[68,377,174],{"class":173},[68,379,380],{"class":78},"Aug",[68,382,174],{"class":173},[68,384,385],{"class":78},"}&",[68,387,174],{"class":173},[68,389,390],{"class":78},"'!B2:B5",[68,392,174],{"class":173},[68,394,395],{"class":78},")))\"",[68,397,195],{"class":123},[68,399,401],{"class":70,"line":400},11,[68,402,403],{"class":123},"}\n",[68,405,407,410,413,416,419],{"class":70,"line":406},12,[68,408,409],{"class":123},"wb ",[68,411,412],{"class":119},"=",[68,414,415],{"class":123}," load_workbook(",[68,417,418],{"class":78},"\"indirect_offset.xlsx\"",[68,420,421],{"class":123},")\n",[68,423,425,428,430,433,436],{"class":70,"line":424},13,[68,426,427],{"class":123},"ws ",[68,429,412],{"class":119},[68,431,432],{"class":123}," wb[",[68,434,435],{"class":78},"\"Summary\"",[68,437,438],{"class":123},"]\n",[68,440,442,445,448,451,454,457,459,462,466,468,471],{"class":70,"line":441},14,[68,443,444],{"class":119},"for",[68,446,447],{"class":123}," i, (label, f) ",[68,449,450],{"class":119},"in",[68,452,453],{"class":151}," enumerate",[68,455,456],{"class":123},"(",[68,458,152],{"class":151},[68,460,461],{"class":123},".items(), ",[68,463,465],{"class":464},"sa561","start",[68,467,412],{"class":119},[68,469,470],{"class":151},"4",[68,472,473],{"class":123},"):\n",[68,475,477,480,483],{"class":70,"line":476},15,[68,478,479],{"class":123},"    ws.cell(i, ",[68,481,482],{"class":151},"1",[68,484,485],{"class":123},", label)\n",[68,487,489,491,494],{"class":70,"line":488},16,[68,490,479],{"class":123},[68,492,493],{"class":151},"2",[68,495,496],{"class":123},", f)\n",[68,498,500,503,506],{"class":70,"line":499},17,[68,501,502],{"class":123},"wb.save(",[68,504,505],{"class":78},"\"with_formulas.xlsx\"",[68,507,421],{"class":123},[68,509,511,514,517,520,523,525,528,530,533,535,538,540,543,545,547,550,553,555,558,560,563,565,567],{"class":70,"line":510},18,[68,512,513],{"class":123},"subprocess.run([",[68,515,516],{"class":78},"\"soffice\"",[68,518,519],{"class":123},", ",[68,521,522],{"class":78},"\"--headless\"",[68,524,519],{"class":123},[68,526,527],{"class":78},"\"--convert-to\"",[68,529,519],{"class":123},[68,531,532],{"class":78},"\"xlsx\"",[68,534,519],{"class":123},[68,536,537],{"class":78},"\"--outdir\"",[68,539,519],{"class":123},[68,541,542],{"class":78},"\"calc\"",[68,544,519],{"class":123},[68,546,505],{"class":78},[68,548,549],{"class":123},"], ",[68,551,552],{"class":464},"check",[68,554,412],{"class":119},[68,556,557],{"class":151},"True",[68,559,519],{"class":123},[68,561,562],{"class":464},"capture_output",[68,564,412],{"class":119},[68,566,557],{"class":151},[68,568,421],{"class":123},[68,570,572,575,577,579,582,584,587,589,591,594,596],{"class":70,"line":571},19,[68,573,574],{"class":123},"calc ",[68,576,412],{"class":119},[68,578,415],{"class":123},[68,580,581],{"class":78},"\"calc\u002Fwith_formulas.xlsx\"",[68,583,519],{"class":123},[68,585,586],{"class":464},"data_only",[68,588,412],{"class":119},[68,590,557],{"class":151},[68,592,593],{"class":123},")[",[68,595,435],{"class":78},[68,597,438],{"class":123},[68,599,601,604,606,609,611,614,616,619,621,624,626,629,631,633,635,637,640,643,645,647],{"class":70,"line":600},20,[68,602,603],{"class":123},"excel ",[68,605,412],{"class":119},[68,607,608],{"class":123}," {calc.cell(i, ",[68,610,482],{"class":151},[68,612,613],{"class":123},").value: calc.cell(i, ",[68,615,493],{"class":151},[68,617,618],{"class":123},").value ",[68,620,444],{"class":119},[68,622,623],{"class":123}," i ",[68,625,450],{"class":119},[68,627,628],{"class":151}," range",[68,630,456],{"class":123},[68,632,470],{"class":151},[68,634,519],{"class":123},[68,636,470],{"class":151},[68,638,639],{"class":119}," +",[68,641,642],{"class":151}," len",[68,644,456],{"class":123},[68,646,152],{"class":151},[68,648,649],{"class":123},"))}\n",[68,651,653,655,658,660],{"class":70,"line":652},21,[68,654,444],{"class":119},[68,656,657],{"class":123}," k, v ",[68,659,450],{"class":119},[68,661,662],{"class":123}," excel.items():\n",[68,664,666,669,671,674,677,680,683,686,689,692,695,697,699],{"class":70,"line":665},22,[68,667,668],{"class":151},"    print",[68,670,456],{"class":123},[68,672,673],{"class":119},"f",[68,675,676],{"class":78},"\"",[68,678,679],{"class":173},"{",[68,681,682],{"class":123},"k",[68,684,685],{"class":119},":28",[68,687,688],{"class":173},"}",[68,690,691],{"class":173}," {",[68,693,694],{"class":123},"v",[68,696,688],{"class":173},[68,698,676],{"class":78},[68,700,421],{"class":123},[10,702,703,704,706,707,710,711,714],{},"Each formula is a typical use: reading a cell from a sheet whose name is in a settings cell, summing a column chosen by name, summing a range whose length follows the data, averaging the last three values, reading a value a fixed number of rows away, and summing the same range across several sheets. All of these are volatile in Excel. Look at the last result: the three month sheets hold 1,482 in sales between them, but LibreOffice returned 5,928 — four times as much — because ",[13,705,15],{}," with an array of sheet names inside ",[13,708,709],{},"SUMPRODUCT(SUM(...))"," is evaluated differently from one spreadsheet program to another (the usual Excel idiom wraps a ",[13,712,713],{},"SUMIF"," instead). A formula assembled from text can be wrong in ways that are very hard to see, which is one more reason to replace it.",[54,716,718],{"id":717},"step-2-indirect-for-sheets-and-columns-just-index-by-name","Step 2 — INDIRECT for sheets and columns: just index by name",[10,720,721],{},"In pandas, a sheet chosen by name is a dictionary lookup, and a column chosen by name is ordinary column selection:",[59,723,725],{"className":110,"code":724,"language":112,"meta":64,"style":64},"import pandas as pd\n\nsheets = pd.read_excel(\"indirect_offset.xlsx\", sheet_name=[\"Jun\", \"Jul\", \"Aug\"])\nsettings = pd.read_excel(\"indirect_offset.xlsx\", sheet_name=\"Summary\", header=None, index_col=0, nrows=2)[1]\nmonth, measure = settings[\"Show month\"], settings[\"Show measure\"]\n\ncell_c3 = sheets[month].iloc[1][\"Returns\"]                    # row 3 of the sheet = second data row\ncolumn_sum = sheets[month][measure].sum()\nprint(\"C3 of\", month, \"→\", cell_c3, \"| Excel:\", excel[\"sheet named in B1, cell C3\"])\nprint(f\"sum of {measure} in {month} →\", column_sum, \"| Excel:\", excel[\"sum of column named in B2\"])\n",[13,726,727,740,744,782,836,857,861,886,896,926],{"__ignoreMap":64},[68,728,729,731,734,737],{"class":70,"line":71},[68,730,120],{"class":119},[68,732,733],{"class":123}," pandas ",[68,735,736],{"class":119},"as",[68,738,739],{"class":123}," pd\n",[68,741,742],{"class":70,"line":127},[68,743,145],{"emptyLinePlaceholder":144},[68,745,746,749,751,754,756,758,761,763,766,769,771,774,776,779],{"class":70,"line":141},[68,747,748],{"class":123},"sheets ",[68,750,412],{"class":119},[68,752,753],{"class":123}," pd.read_excel(",[68,755,418],{"class":78},[68,757,519],{"class":123},[68,759,760],{"class":464},"sheet_name",[68,762,412],{"class":119},[68,764,765],{"class":123},"[",[68,767,768],{"class":78},"\"Jun\"",[68,770,519],{"class":123},[68,772,773],{"class":78},"\"Jul\"",[68,775,519],{"class":123},[68,777,778],{"class":78},"\"Aug\"",[68,780,781],{"class":123},"])\n",[68,783,784,787,789,791,793,795,797,799,801,803,806,808,811,813,816,818,821,823,826,828,830,832,834],{"class":70,"line":148},[68,785,786],{"class":123},"settings ",[68,788,412],{"class":119},[68,790,753],{"class":123},[68,792,418],{"class":78},[68,794,519],{"class":123},[68,796,760],{"class":464},[68,798,412],{"class":119},[68,800,435],{"class":78},[68,802,519],{"class":123},[68,804,805],{"class":464},"header",[68,807,412],{"class":119},[68,809,810],{"class":151},"None",[68,812,519],{"class":123},[68,814,815],{"class":464},"index_col",[68,817,412],{"class":119},[68,819,820],{"class":151},"0",[68,822,519],{"class":123},[68,824,825],{"class":464},"nrows",[68,827,412],{"class":119},[68,829,493],{"class":151},[68,831,593],{"class":123},[68,833,482],{"class":151},[68,835,438],{"class":123},[68,837,838,841,843,846,849,852,855],{"class":70,"line":161},[68,839,840],{"class":123},"month, measure ",[68,842,412],{"class":119},[68,844,845],{"class":123}," settings[",[68,847,848],{"class":78},"\"Show month\"",[68,850,851],{"class":123},"], settings[",[68,853,854],{"class":78},"\"Show measure\"",[68,856,438],{"class":123},[68,858,859],{"class":70,"line":198},[68,860,145],{"emptyLinePlaceholder":144},[68,862,863,866,868,871,873,876,879,882],{"class":70,"line":301},[68,864,865],{"class":123},"cell_c3 ",[68,867,412],{"class":119},[68,869,870],{"class":123}," sheets[month].iloc[",[68,872,482],{"class":151},[68,874,875],{"class":123},"][",[68,877,878],{"class":78},"\"Returns\"",[68,880,881],{"class":123},"]                    ",[68,883,885],{"class":884},"s-wDw","# row 3 of the sheet = second data row\n",[68,887,888,891,893],{"class":70,"line":314},[68,889,890],{"class":123},"column_sum ",[68,892,412],{"class":119},[68,894,895],{"class":123}," sheets[month][measure].sum()\n",[68,897,898,901,903,906,909,912,915,918,921,924],{"class":70,"line":327},[68,899,900],{"class":151},"print",[68,902,456],{"class":123},[68,904,905],{"class":78},"\"C3 of\"",[68,907,908],{"class":123},", month, ",[68,910,911],{"class":78},"\"→\"",[68,913,914],{"class":123},", cell_c3, ",[68,916,917],{"class":78},"\"| Excel:\"",[68,919,920],{"class":123},", excel[",[68,922,923],{"class":78},"\"sheet named in B1, cell C3\"",[68,925,781],{"class":123},[68,927,928,930,932,934,937,939,942,944,947,949,952,954,957,960,962,964,967],{"class":70,"line":340},[68,929,900],{"class":151},[68,931,456],{"class":123},[68,933,673],{"class":119},[68,935,936],{"class":78},"\"sum of ",[68,938,679],{"class":173},[68,940,941],{"class":123},"measure",[68,943,688],{"class":173},[68,945,946],{"class":78}," in ",[68,948,679],{"class":173},[68,950,951],{"class":123},"month",[68,953,688],{"class":173},[68,955,956],{"class":78}," →\"",[68,958,959],{"class":123},", column_sum, ",[68,961,917],{"class":78},[68,963,920],{"class":123},[68,965,966],{"class":78},"\"sum of column named in B2\"",[68,968,781],{"class":123},[10,970,971,974,975,978,979,982,983,986,987,990,991,994,995,52],{},[13,972,973],{},"pd.read_excel(..., sheet_name=[...])"," returns a dictionary of DataFrames keyed by sheet name, so \"the sheet named in B1\" is ",[13,976,977],{},"sheets[month]",". \"The column named in B2\" is ",[13,980,981],{},"df[measure]"," — no need to translate a header into a column letter, which is what the ",[13,984,985],{},"IF(B2=\"Sales\",\"B\",\"C\")"," part of the formula was doing by hand. Cell C3 becomes \"the Returns value of the second data row\", and here it is better to stop thinking in cell addresses altogether: ",[13,988,989],{},"sheets[month].set_index(\"Region\").loc[\"South\", \"Returns\"]"," says what the cell ",[34,992,993],{},"means",". Reading every sheet at once is covered in ",[48,996,998],{"href":997},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fread-all-sheets-from-an-excel-file-into-dataframes\u002F","Read All Sheets from an Excel File into DataFrames",[1000,1001,1009,1010,1009,1014,1009,1018,1009,1025,1009,1034,1009,1038,1009,1045,1009,1049,1009,1053,1009,1055,1009,1059,1009,1062,1009,1065,1009,1067,1009,1071,1009,1074,1009,1078,1009,1080,1009,1082,1009,1085,1009,1088,1009,1090,1009,1093,1009,1098,1009,1103,1009,1108,1009,1112,1009,1115,1009,1119,1009,1122,1009,1126,1009,1132,1009,1134,1009,1140,1009,1145,1009,1153],"svg",{"viewBox":1002,"role":1003,"ariaLabelledBy":1004,"xmlns":1007,"style":1008},"105 0 549 192","img",[1005,1006],"io-indirect-t","io-indirect-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:549px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[1011,1012,1013],"title",{"id":1005},"INDIRECT built from text becomes a lookup by name",[1015,1016,1017],"desc",{"id":1006},"The formula INDIRECT with a sheet name from B1 and a column letter chosen with IF from the measure in B2 becomes a dictionary lookup of the month sheet followed by selection of the measure column by its header.",[1019,1020],"rect",{"x":1021,"y":820,"width":1022,"height":1023,"fill":1024},"105","549","192","#ffffff",[1019,1026],{"x":1027,"y":1028,"width":1029,"height":1030,"rx":820,"fill":1031,"stroke":1032,"style":1033},"121.9","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[1019,1035],{"x":1036,"y":1028,"width":1037,"height":1030,"rx":820,"fill":1031,"stroke":1032,"style":1033},"153.9","152.3",[1039,1040,1044],"text",{"x":1041,"y":1042,"style":1043},"230.1","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[1019,1046],{"x":1027,"y":1047,"width":1029,"height":1048,"rx":820,"fill":1031,"stroke":1032,"style":1033},"64.0","28.0",[1039,1050,482],{"x":1051,"y":1052,"style":1043},"137.9","83.0",[1019,1054],{"x":1036,"y":1047,"width":1037,"height":1048,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1056,1058],{"x":1041,"y":1052,"style":1057},"font-size:10.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","formula part",[1019,1060],{"x":1027,"y":1061,"width":1029,"height":1048,"rx":820,"fill":1031,"stroke":1032,"style":1033},"92.0",[1039,1063,493],{"x":1051,"y":1064,"style":1043},"111.0",[1019,1066],{"x":1036,"y":1061,"width":1037,"height":1048,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1068,1070],{"x":1041,"y":1064,"style":1069},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","\"'\"&B1&\"'!\"",[1019,1072],{"x":1027,"y":1073,"width":1029,"height":1048,"rx":820,"fill":1031,"stroke":1032,"style":1033},"120.0",[1039,1075,1077],{"x":1051,"y":1076,"style":1043},"139.0","3",[1019,1079],{"x":1036,"y":1073,"width":1037,"height":1048,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1081,985],{"x":1041,"y":1076,"style":1069},[1019,1083],{"x":1027,"y":1084,"width":1029,"height":1048,"rx":820,"fill":1031,"stroke":1032,"style":1033},"148.0",[1039,1086,470],{"x":1051,"y":1087,"style":1043},"167.0",[1019,1089],{"x":1036,"y":1084,"width":1037,"height":1048,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1091,1092],{"x":1041,"y":1087,"style":1069},"&\"2:\"&…&\"5\"",[1019,1094],{"x":1095,"y":1028,"width":1096,"height":1048,"rx":820,"fill":1031,"stroke":1032,"style":1097},"534.8","103.3","stroke-width:1.5px",[1039,1099,1102],{"x":1100,"y":1101,"style":1057},"586.4","59.0","pandas",[1019,1104],{"x":1095,"y":1105,"width":1096,"height":1048,"rx":820,"fill":1106,"stroke":1107,"style":1097},"68.0","#d9f4f1","var(--teal,#0f9488)",[1039,1109,977],{"x":1100,"y":1110,"style":1111},"87.0","font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[1019,1113],{"x":1095,"y":1114,"width":1096,"height":1048,"rx":820,"fill":1106,"stroke":1107,"style":1097},"96.0",[1039,1116,1118],{"x":1100,"y":1117,"style":1111},"115.0","[measure]",[1019,1120],{"x":1095,"y":1121,"width":1096,"height":1048,"rx":820,"fill":1106,"stroke":1107,"style":1097},"124.0",[1039,1123,1125],{"x":1100,"y":1124,"style":1111},"143.0",".sum()",[1039,1127,1131],{"x":1128,"y":1129,"style":1130},"214.1","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Excel",[1039,1133,1102],{"x":1100,"y":1129,"style":1130},[70,1135],{"x1":1136,"y1":1114,"x2":1137,"y2":1114,"stroke":1138,"style":1139},"316.2","520.8","var(--brand,#5b5cf0)","stroke-width:2px",[1141,1142],"polygon",{"points":1143,"fill":1144},"524.8,96.0 515.8,101.0 515.8,91.0","#5b5cf0",[1019,1146],{"x":1147,"y":1148,"width":1149,"height":1150,"rx":1151,"fill":1024,"stroke":1032,"style":1152},"345.1","63.0","150.8","22.0","11","stroke-width:1.2px",[1039,1154,1158],{"x":1155,"y":1156,"style":1157},"420.5","78.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","dict + column name",[54,1160,1162],{"id":1161},"step-3-offset-for-growing-ranges-and-the-last-n-values","Step 3 — OFFSET for growing ranges and the last N values",[10,1164,1165,1167,1168,1171,1172,1175],{},[13,1166,19],{}," with ",[13,1169,1170],{},"COUNT"," exists to make a range follow the data. A DataFrame already ",[34,1173,1174],{},"is"," the data, however many rows it has, so most of these collapse to one method call:",[59,1177,1179],{"className":110,"code":1178,"language":112,"meta":64,"style":64},"daily = pd.read_excel(\"indirect_offset.xlsx\", sheet_name=\"Daily\")\ntotal = daily[\"Units\"].sum()\nlast3 = daily[\"Units\"].tail(3).mean()\ntwo_below_b4 = daily[\"Units\"].iloc[2 + 2]                      # B4 is the third data row; two rows further down\nprint(\"sum of growing range →\", total, \"| Excel:\", excel[\"sum of growing range\"])\nprint(\"average of last 3 →\", round(last3, 6), \"| Excel:\", round(excel[\"average of last 3\"], 6))\nprint(\"value 2 rows below B4 →\", two_below_b4, \"| Excel:\", excel[\"value 2 rows below B4\"])\n",[13,1180,1181,1203,1219,1238,1265,1286,1328],{"__ignoreMap":64},[68,1182,1183,1186,1188,1190,1192,1194,1196,1198,1201],{"class":70,"line":71},[68,1184,1185],{"class":123},"daily ",[68,1187,412],{"class":119},[68,1189,753],{"class":123},[68,1191,418],{"class":78},[68,1193,519],{"class":123},[68,1195,760],{"class":464},[68,1197,412],{"class":119},[68,1199,1200],{"class":78},"\"Daily\"",[68,1202,421],{"class":123},[68,1204,1205,1208,1210,1213,1216],{"class":70,"line":127},[68,1206,1207],{"class":123},"total ",[68,1209,412],{"class":119},[68,1211,1212],{"class":123}," daily[",[68,1214,1215],{"class":78},"\"Units\"",[68,1217,1218],{"class":123},"].sum()\n",[68,1220,1221,1224,1226,1228,1230,1233,1235],{"class":70,"line":141},[68,1222,1223],{"class":123},"last3 ",[68,1225,412],{"class":119},[68,1227,1212],{"class":123},[68,1229,1215],{"class":78},[68,1231,1232],{"class":123},"].tail(",[68,1234,1077],{"class":151},[68,1236,1237],{"class":123},").mean()\n",[68,1239,1240,1243,1245,1247,1249,1252,1254,1256,1259,1262],{"class":70,"line":148},[68,1241,1242],{"class":123},"two_below_b4 ",[68,1244,412],{"class":119},[68,1246,1212],{"class":123},[68,1248,1215],{"class":78},[68,1250,1251],{"class":123},"].iloc[",[68,1253,493],{"class":151},[68,1255,639],{"class":119},[68,1257,1258],{"class":151}," 2",[68,1260,1261],{"class":123},"]                      ",[68,1263,1264],{"class":884},"# B4 is the third data row; two rows further down\n",[68,1266,1267,1269,1271,1274,1277,1279,1281,1284],{"class":70,"line":161},[68,1268,900],{"class":151},[68,1270,456],{"class":123},[68,1272,1273],{"class":78},"\"sum of growing range →\"",[68,1275,1276],{"class":123},", total, ",[68,1278,917],{"class":78},[68,1280,920],{"class":123},[68,1282,1283],{"class":78},"\"sum of growing range\"",[68,1285,781],{"class":123},[68,1287,1288,1290,1292,1295,1297,1300,1303,1306,1309,1311,1313,1315,1318,1321,1323,1325],{"class":70,"line":198},[68,1289,900],{"class":151},[68,1291,456],{"class":123},[68,1293,1294],{"class":78},"\"average of last 3 →\"",[68,1296,519],{"class":123},[68,1298,1299],{"class":151},"round",[68,1301,1302],{"class":123},"(last3, ",[68,1304,1305],{"class":151},"6",[68,1307,1308],{"class":123},"), ",[68,1310,917],{"class":78},[68,1312,519],{"class":123},[68,1314,1299],{"class":151},[68,1316,1317],{"class":123},"(excel[",[68,1319,1320],{"class":78},"\"average of last 3\"",[68,1322,549],{"class":123},[68,1324,1305],{"class":151},[68,1326,1327],{"class":123},"))\n",[68,1329,1330,1332,1334,1337,1340,1342,1344,1347],{"class":70,"line":301},[68,1331,900],{"class":151},[68,1333,456],{"class":123},[68,1335,1336],{"class":78},"\"value 2 rows below B4 →\"",[68,1338,1339],{"class":123},", two_below_b4, ",[68,1341,917],{"class":78},[68,1343,920],{"class":123},[68,1345,1346],{"class":78},"\"value 2 rows below B4\"",[68,1348,781],{"class":123},[10,1350,1351,1353,1354,1357,1358,1360,1361,1364,1365,1368,1369,1371,1372,1375,1376,1379],{},[13,1352,27],{}," is ",[13,1355,1356],{},"sum()","; the range grows with the data automatically. ",[13,1359,31],{}," — \"the last three values\" — is ",[13,1362,1363],{},"tail(3).mean()",". A single offset from a fixed cell is positional indexing with ",[13,1366,1367],{},"iloc",", where B4 is the third data row (index 2) and two rows further down is index 4. One subtlety: ",[13,1370,1170],{}," counts ",[34,1373,1374],{},"numeric"," cells, so in Excel a stray text value in the column silently shortens the range; in pandas, ",[13,1377,1378],{},"tail(3)"," counts rows, and a text value would show up as a non-numeric column that refuses to average — a louder and better failure.",[54,1381,1383],{"id":1382},"step-4-offset-inside-a-calculation-shift-and-rolling","Step 4 — OFFSET inside a calculation: shift and rolling",[10,1385,1386,1387,1389],{},"The other common use of ",[13,1388,19],{}," is relative: the previous row, the next row, or a moving window, copied down a whole column. pandas does these for every row at once:",[59,1391,1393],{"className":110,"code":1392,"language":112,"meta":64,"style":64},"daily[\"Prev day\"] = daily[\"Units\"].shift(1)                    # =OFFSET(B3,-1,0) copied down\ndaily[\"Change\"] = daily[\"Units\"] - daily[\"Prev day\"]\ndaily[\"3-day avg\"] = daily[\"Units\"].rolling(3).mean().round(2) # =AVERAGE(OFFSET(B4,-2,0,3,1)) copied down\nprint(daily.to_string(index=False))\n",[13,1394,1395,1423,1449,1480],{"__ignoreMap":64},[68,1396,1397,1400,1403,1406,1408,1410,1412,1415,1417,1420],{"class":70,"line":71},[68,1398,1399],{"class":123},"daily[",[68,1401,1402],{"class":78},"\"Prev day\"",[68,1404,1405],{"class":123},"] ",[68,1407,412],{"class":119},[68,1409,1212],{"class":123},[68,1411,1215],{"class":78},[68,1413,1414],{"class":123},"].shift(",[68,1416,482],{"class":151},[68,1418,1419],{"class":123},")                    ",[68,1421,1422],{"class":884},"# =OFFSET(B3,-1,0) copied down\n",[68,1424,1425,1427,1430,1432,1434,1436,1438,1440,1443,1445,1447],{"class":70,"line":127},[68,1426,1399],{"class":123},[68,1428,1429],{"class":78},"\"Change\"",[68,1431,1405],{"class":123},[68,1433,412],{"class":119},[68,1435,1212],{"class":123},[68,1437,1215],{"class":78},[68,1439,1405],{"class":123},[68,1441,1442],{"class":119},"-",[68,1444,1212],{"class":123},[68,1446,1402],{"class":78},[68,1448,438],{"class":123},[68,1450,1451,1453,1456,1458,1460,1462,1464,1467,1469,1472,1474,1477],{"class":70,"line":141},[68,1452,1399],{"class":123},[68,1454,1455],{"class":78},"\"3-day avg\"",[68,1457,1405],{"class":123},[68,1459,412],{"class":119},[68,1461,1212],{"class":123},[68,1463,1215],{"class":78},[68,1465,1466],{"class":123},"].rolling(",[68,1468,1077],{"class":151},[68,1470,1471],{"class":123},").mean().round(",[68,1473,493],{"class":151},[68,1475,1476],{"class":123},") ",[68,1478,1479],{"class":884},"# =AVERAGE(OFFSET(B4,-2,0,3,1)) copied down\n",[68,1481,1482,1484,1487,1490,1492,1495],{"class":70,"line":148},[68,1483,900],{"class":151},[68,1485,1486],{"class":123},"(daily.to_string(",[68,1488,1489],{"class":464},"index",[68,1491,412],{"class":119},[68,1493,1494],{"class":151},"False",[68,1496,1327],{"class":123},[10,1498,1499,1502,1503,1506,1507,1510,1511,1514,1515,1519],{},[13,1500,1501],{},"shift(1)"," is \"the cell one row up\" for every row, with an empty value on the first row instead of a ",[13,1504,1505],{},"#REF!"," error. ",[13,1508,1509],{},"rolling(3).mean()"," is a three-row moving window ending at each row — the job of an ",[13,1512,1513],{},"AVERAGE(OFFSET(...,-2,0,3,1))"," copied down the column — and its first two rows are empty because a full window is not yet available, which is honest rather than an average of fewer values. These are the same tools as ",[48,1516,1518],{"href":1517},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling\u002F","Calculate Moving Averages with pandas rolling",", seen from the Excel side.",[1000,1521,1009,1527,1009,1530,1009,1533,1009,1537,1009,1542,1009,1547,1009,1553,1009,1556,1009,1559,1009,1563,1009,1567,1009,1569,1009,1574,1009,1576,1009,1579,1009,1582,1009,1586,1009,1588,1009,1591,1009,1593,1009,1595,1009,1597,1009,1601,1009,1603,1009,1607,1009,1609,1009,1612,1009,1615,1009,1619,1009,1621,1009,1625,1009,1627,1009,1630,1009,1633,1009,1637,1009,1639,1009,1643,1009,1645,1009,1648,1009,1651,1009,1655,1009,1657,1009,1661,1009,1663],{"viewBox":1522,"role":1003,"ariaLabelledBy":1523,"xmlns":1007,"style":1526},"4 0 752 276",[1524,1525],"io-map-t","io-map-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[1011,1528,1529],{"id":1524},"INDIRECT and OFFSET jobs and their pandas tools",[1015,1531,1532],{"id":1525},"A sheet named in a cell becomes a dictionary lookup of sheets. A column named in a cell becomes column selection. A range that grows with the data becomes a plain sum. The last N values become tail. One row up becomes shift. A moving window becomes rolling. The same range across sheets becomes concat and groupby.",[1019,1534],{"x":470,"y":820,"width":1535,"height":1536,"fill":1024},"752","276",[1019,1538],{"x":1539,"y":1540,"width":1541,"height":1028,"rx":820,"fill":1024,"stroke":1032,"style":1033},"20.0","16.0","134.2",[1019,1543],{"x":1544,"y":1540,"width":1545,"height":1028,"rx":820,"fill":1546,"stroke":1138,"style":1152},"154.2","292.9","#f0f4ff",[1039,1548,1552],{"x":1549,"y":1550,"style":1551},"300.6","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Excel pattern",[1019,1554],{"x":1555,"y":1540,"width":1545,"height":1028,"rx":820,"fill":1546,"stroke":1138,"style":1152},"447.1",[1039,1557,1102],{"x":1558,"y":1550,"style":1551},"593.5",[1019,1560],{"x":1539,"y":1561,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},"56.0","34.0",[1039,1564,1566],{"x":1029,"y":1156,"style":1565},"font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","sheet from a cell",[1019,1568],{"x":1544,"y":1561,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1570,1573],{"x":1549,"y":1571,"style":1572},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","INDIRECT(B1&\"!C3\")",[1019,1575],{"x":1555,"y":1561,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1577,1578],{"x":1558,"y":1571,"style":1572},"sheets[name]",[1019,1580],{"x":1539,"y":1581,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},"90.0",[1039,1583,1585],{"x":1029,"y":1584,"style":1565},"112.0","growing range",[1019,1587],{"x":1544,"y":1581,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1589,1590],{"x":1549,"y":1064,"style":1572},"SUM(OFFSET(B2,0,0,COUNT(B:B)))",[1019,1592],{"x":1555,"y":1581,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1594,1125],{"x":1558,"y":1064,"style":1572},[1019,1596],{"x":1539,"y":1121,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},[1039,1598,1600],{"x":1029,"y":1599,"style":1565},"146.0","last N values",[1019,1602],{"x":1544,"y":1121,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1604,1606],{"x":1549,"y":1605,"style":1572},"145.0","OFFSET(…,COUNT-3,0,3)",[1019,1608],{"x":1555,"y":1121,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1610,1611],{"x":1558,"y":1605,"style":1572},".tail(3)",[1019,1613],{"x":1539,"y":1614,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},"158.0",[1039,1616,1618],{"x":1029,"y":1617,"style":1565},"180.0","previous row",[1019,1620],{"x":1544,"y":1614,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1622,1624],{"x":1549,"y":1623,"style":1572},"179.0","OFFSET(B3,-1,0)",[1019,1626],{"x":1555,"y":1614,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1628,1629],{"x":1558,"y":1623,"style":1572},".shift(1)",[1019,1631],{"x":1539,"y":1632,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},"192.0",[1039,1634,1636],{"x":1029,"y":1635,"style":1565},"214.0","moving window",[1019,1638],{"x":1544,"y":1632,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1640,1642],{"x":1549,"y":1641,"style":1572},"213.0","AVERAGE(OFFSET(B4,-2,0,3))",[1019,1644],{"x":1555,"y":1632,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1646,1647],{"x":1558,"y":1641,"style":1572},".rolling(3)",[1019,1649],{"x":1539,"y":1650,"width":1541,"height":1562,"rx":820,"fill":1031,"stroke":1032,"style":1033},"226.0",[1039,1652,1654],{"x":1029,"y":1653,"style":1565},"248.0","across sheets",[1019,1656],{"x":1544,"y":1650,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1658,1660],{"x":1549,"y":1659,"style":1572},"247.0","SUM(INDIRECT({…}&\"!B2:B5\"))",[1019,1662],{"x":1555,"y":1650,"width":1545,"height":1562,"rx":820,"fill":1024,"stroke":1032,"style":1033},[1039,1664,1665],{"x":1558,"y":1659,"style":1572},"concat + groupby",[1000,1667,1009,1672,1009,1675,1009,1678,1009,1681,1009,1688,1009,1699,1009,1704,1009,1709,1009,1713,1009,1717,1009,1721,1009,1724,1009,1731,1009,1736,1009,1740,1009,1743,1009,1746],{"viewBox":1668,"role":1003,"ariaLabelledBy":1669,"xmlns":1007,"style":1526},"4 0 752 207",[1670,1671],"io-why-t","io-why-d",[1011,1673,1674],{"id":1670},"Why replace rather than imitate",[1015,1676,1677],{"id":1671},"INDIRECT and OFFSET are volatile and recalculate on every change, hide their intent inside text, and can evaluate differently between spreadsheet programs, as the cross-sheet sum did. The pandas versions select by name, run once, and say what they mean.",[1019,1679],{"x":470,"y":820,"width":1535,"height":1680,"fill":1024},"207",[1019,1682],{"x":1539,"y":1540,"width":1683,"height":1684,"rx":1685,"fill":1686,"stroke":1687,"style":1139},"351.0","175.0","14","#fce9e9","var(--danger,#dc2626)",[1689,1690,1692],"g",{"transform":1691},"translate(182.5,29.0) scale(1.083)",[1693,1694],"path",{"d":1695,"fill":1696,"stroke":1697,"style":1698},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","none","var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[1039,1700,1703],{"x":1701,"y":1156,"style":1702},"195.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","INDIRECT \u002F OFFSET in Excel",[70,1705],{"x1":1706,"y1":1707,"x2":1708,"y2":1707,"stroke":1687,"style":1033},"36.0","91.0","355.0",[1039,1710,1712],{"x":1701,"y":1064,"style":1711},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","volatile: recalculates on every edit",[1039,1714,1716],{"x":1701,"y":1715,"style":1711},"137.0","intent hidden in text references",[1039,1718,1720],{"x":1701,"y":1719,"style":1711},"163.0","array cases differ between programs",[1019,1722],{"x":1723,"y":1540,"width":1683,"height":1684,"rx":1685,"fill":1106,"stroke":1107,"style":1139},"389.0",[1689,1725,1727],{"transform":1726},"translate(551.5,29.0) scale(1.083)",[1693,1728],{"d":1729,"fill":1696,"stroke":1730,"style":1698},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[1039,1732,1735],{"x":1733,"y":1156,"style":1734},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","pandas equivalents",[70,1737],{"x1":1738,"y1":1707,"x2":1739,"y2":1707,"stroke":1107,"style":1033},"405.0","724.0",[1039,1741,1742],{"x":1733,"y":1064,"style":1711},"sheets[name], df[column]",[1039,1744,1745],{"x":1733,"y":1715,"style":1711},"sum, tail, shift, rolling",[1039,1747,1748],{"x":1733,"y":1719,"style":1711},"one clear expression per job",[54,1750,1752],{"id":1751},"step-5-the-same-range-across-many-sheets","Step 5 — The same range across many sheets",[10,1754,1755,1756,1758],{},"Workbooks with a sheet per month often use ",[13,1757,15],{}," with a list of sheet names to sum or look up across them. In pandas, stack the sheets into one table with a column saying which sheet each row came from, and every cross-sheet question becomes a group-by:",[59,1760,1762],{"className":110,"code":1761,"language":112,"meta":64,"style":64},"long = pd.concat(sheets, names=[\"Month\", \"row\"]).reset_index(level=\"Month\").reset_index(drop=True)\nprint(\"all sales across sheets →\", long[\"Sales\"].sum(), \"| spreadsheet formula:\", excel[\"sales across sheets\"])\nprint(long.pivot_table(index=\"Region\", columns=\"Month\", values=\"Sales\", aggfunc=\"sum\")[[\"Jun\", \"Jul\", \"Aug\"]])\nprint(\"July sales only →\", long.loc[long[\"Month\"] == \"Jul\", \"Sales\"].sum())\n",[13,1763,1764,1811,1842,1904],{"__ignoreMap":64},[68,1765,1766,1769,1771,1774,1777,1779,1781,1784,1786,1789,1792,1795,1797,1799,1802,1805,1807,1809],{"class":70,"line":71},[68,1767,1768],{"class":464},"long",[68,1770,155],{"class":119},[68,1772,1773],{"class":123}," pd.concat(sheets, ",[68,1775,1776],{"class":464},"names",[68,1778,412],{"class":119},[68,1780,765],{"class":123},[68,1782,1783],{"class":78},"\"Month\"",[68,1785,519],{"class":123},[68,1787,1788],{"class":78},"\"row\"",[68,1790,1791],{"class":123},"]).reset_index(",[68,1793,1794],{"class":464},"level",[68,1796,412],{"class":119},[68,1798,1783],{"class":78},[68,1800,1801],{"class":123},").reset_index(",[68,1803,1804],{"class":464},"drop",[68,1806,412],{"class":119},[68,1808,557],{"class":151},[68,1810,421],{"class":123},[68,1812,1813,1815,1817,1820,1822,1824,1826,1829,1832,1835,1837,1840],{"class":70,"line":127},[68,1814,900],{"class":151},[68,1816,456],{"class":123},[68,1818,1819],{"class":78},"\"all sales across sheets →\"",[68,1821,519],{"class":123},[68,1823,1768],{"class":464},[68,1825,765],{"class":123},[68,1827,1828],{"class":78},"\"Sales\"",[68,1830,1831],{"class":123},"].sum(), ",[68,1833,1834],{"class":78},"\"| spreadsheet formula:\"",[68,1836,920],{"class":123},[68,1838,1839],{"class":78},"\"sales across sheets\"",[68,1841,781],{"class":123},[68,1843,1844,1846,1848,1850,1853,1855,1857,1860,1862,1865,1867,1869,1871,1874,1876,1878,1880,1883,1885,1888,1891,1893,1895,1897,1899,1901],{"class":70,"line":141},[68,1845,900],{"class":151},[68,1847,456],{"class":123},[68,1849,1768],{"class":464},[68,1851,1852],{"class":123},".pivot_table(",[68,1854,1489],{"class":464},[68,1856,412],{"class":119},[68,1858,1859],{"class":78},"\"Region\"",[68,1861,519],{"class":123},[68,1863,1864],{"class":464},"columns",[68,1866,412],{"class":119},[68,1868,1783],{"class":78},[68,1870,519],{"class":123},[68,1872,1873],{"class":464},"values",[68,1875,412],{"class":119},[68,1877,1828],{"class":78},[68,1879,519],{"class":123},[68,1881,1882],{"class":464},"aggfunc",[68,1884,412],{"class":119},[68,1886,1887],{"class":78},"\"sum\"",[68,1889,1890],{"class":123},")[[",[68,1892,768],{"class":78},[68,1894,519],{"class":123},[68,1896,773],{"class":78},[68,1898,519],{"class":123},[68,1900,778],{"class":78},[68,1902,1903],{"class":123},"]])\n",[68,1905,1906,1908,1910,1913,1915,1917,1920,1922,1924,1926,1928,1931,1934,1936,1938],{"class":70,"line":148},[68,1907,900],{"class":151},[68,1909,456],{"class":123},[68,1911,1912],{"class":78},"\"July sales only →\"",[68,1914,519],{"class":123},[68,1916,1768],{"class":464},[68,1918,1919],{"class":123},".loc[",[68,1921,1768],{"class":464},[68,1923,765],{"class":123},[68,1925,1783],{"class":78},[68,1927,1405],{"class":123},[68,1929,1930],{"class":119},"==",[68,1932,1933],{"class":78}," \"Jul\"",[68,1935,519],{"class":123},[68,1937,1828],{"class":78},[68,1939,1940],{"class":123},"].sum())\n",[10,1942,1943,1946,1947,52],{},[13,1944,1945],{},"pd.concat"," on the dictionary of sheets uses the sheet names as an extra index level, which becomes a Month column. The total, 1,482, is the correct sum of all three months — not the 5,928 the array formula produced — and the pivot shows each month and region separately, so the figure can be checked by eye. Asking for July alone is a filter rather than a carefully rebuilt text reference. Summing the same cell across sheets has its own page: ",[48,1948,1950],{"href":1949},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references\u002F","Sum the Same Cell Across Sheets with 3D References",[54,1952,1954],{"id":1953},"common-pitfalls","Common pitfalls",[1956,1957,1958,1966,1972,1978,1988],"ul",{},[1959,1960,1961,1965],"li",{},[1962,1963,1964],"strong",{},"Translating INDIRECT literally."," Building references from text is unnecessary; index by sheet and column name.",[1959,1967,1968,1971],{},[1962,1969,1970],{},"Recreating cell addresses."," Use labels — region, month, measure — instead of C3 and B4.",[1959,1973,1974,1977],{},[1962,1975,1976],{},"COUNT-based ranges with stray text."," Excel silently shortens them; check types in pandas.",[1959,1979,1980,1987],{},[1962,1981,1982,1983,1986],{},"Plain ",[13,1984,1985],{},"shift"," across groups."," Group first when rows belong to different series.",[1959,1989,1990,1993],{},[1962,1991,1992],{},"Keeping a sheet per month."," Stack sheets into one long table; cross-sheet formulas become group-bys.",[54,1995,1997],{"id":1996},"conclusion","Conclusion",[10,1999,2000,16,2002,2004,2005,2008,2009,2011,2012,2015],{},[13,2001,15],{},[13,2003,19],{}," let Excel build references at calculation time, but each of their jobs has a direct pandas tool: a sheet or column chosen by name is a dictionary or column lookup, a range that grows with the data is simply the column, the last N values are ",[13,2006,2007],{},"tail",", a relative row is ",[13,2010,1985],{},", a moving window is ",[13,2013,2014],{},"rolling",", and a range repeated across sheets becomes one long table and a group-by. Check each translation against the workbook's recalculated formulas once, and the Python version is both easier to read and free of volatile recalculation.",[54,2017,2019],{"id":2018},"frequently-asked-questions","Frequently asked questions",[10,2021,2022,2025,2026,2029,2030,52],{},[1962,2023,2024],{},"What is the pandas equivalent of Excel's INDIRECT?","\nThere is no need for one. Read the sheets into a dictionary with read_excel(sheet_name=None) or a list of names, then select the sheet with sheets",[68,2027,2028],{},"name"," and the column with df",[68,2031,2032],{},"column_name",[10,2034,2035,2038,2039,2042,2043,2045],{},[1962,2036,2037],{},"How do I replace OFFSET with a dynamic range in pandas?","\nA DataFrame column already contains all rows, so SUM(OFFSET(B2,0,0,COUNT(B:B),1)) is just df",[68,2040,2041],{},"\"col\"",".sum(). For the last N values use df",[68,2044,2041],{},".tail(N).",[10,2047,2048,2051,2052,2054],{},[1962,2049,2050],{},"What replaces OFFSET for the previous row?","\ndf",[68,2053,2041],{},".shift(1) gives the value from the row above for every row, with a missing value on the first row.",[10,2056,2057,2060],{},[1962,2058,2059],{},"How do I sum the same range across several sheets in pandas?","\nConcatenate the sheets with pd.concat, keeping the sheet name as a column, then group by or filter on that column.",[54,2062,2064],{"id":2063},"related","Related",[1956,2066,2067,2073,2080,2087],{},[1959,2068,2069,2070,2072],{},"Up one level: ",[48,2071,51],{"href":50}," — spreadsheet functions in Python.",[1959,2074,2075,2079],{},[48,2076,2078],{"href":2077},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findex-match-equivalent-in-pandas\u002F","INDEX and MATCH Equivalent in pandas"," — the non-volatile lookup.",[1959,2081,2082,2086],{},[48,2083,2085],{"href":2084},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Ffind-out-why-an-excel-file-is-slow-to-open\u002F","Find Out Why an Excel File Is Slow to Open"," — the cost of volatile formulas.",[1959,2088,2089,2093],{},[48,2090,2092],{"href":2091},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Frunning-totals-and-year-over-year-growth-in-pandas\u002F","Running Totals and Year-over-Year Growth in pandas"," — more relative calculations.",[2095,2096,2097],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":64,"searchDepth":127,"depth":127,"links":2099},[2100,2101,2102,2103,2104,2105,2106,2107,2108,2109],{"id":56,"depth":127,"text":57},{"id":98,"depth":127,"text":99},{"id":717,"depth":127,"text":718},{"id":1161,"depth":127,"text":1162},{"id":1382,"depth":127,"text":1383},{"id":1751,"depth":127,"text":1752},{"id":1953,"depth":127,"text":1954},{"id":1996,"depth":127,"text":1997},{"id":2018,"depth":127,"text":2019},{"id":2063,"depth":127,"text":2064},"2026-10-02","Translate the jobs Excel workbooks do with INDIRECT and OFFSET into plain pandas: pick a sheet or column whose name is stored in a cell, sum a range that grows with the data, take the last N periods, look a fixed number of rows up or across, and build a summary across monthly sheets — with each formula checked against a spreadsheet's own result.","md",[2114,2116,2118,2120],{"q":2024,"a":2115},"There is no need for one. Read the sheets into a dictionary with read_excel(sheet_name=None) or a list of names, then select the sheet with sheets[name] and the column with df[column_name].",{"q":2037,"a":2117},"A DataFrame column already contains all rows, so SUM(OFFSET(B2,0,0,COUNT(B:B),1)) is just df[\"col\"].sum(). For the last N values use df[\"col\"].tail(N).",{"q":2050,"a":2119},"df[\"col\"].shift(1) gives the value from the row above for every row, with a missing value on the first row.",{"q":2059,"a":2121},"Concatenate the sheets with pd.concat, keeping the sheet name as a column, then group by or filter on that column.","indirect and offset equivalents in pandas",null,{"breadcrumb":2125},[2126,2129,2132],{"name":2127,"item":2128},"Home","\u002F",{"name":2130,"item":2131},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":51,"item":50},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas",{"title":5,"description":2135},"Replace Excel INDIRECT and OFFSET with pandas: sheets and columns chosen by name, dynamic ranges, last-N rolling sums, shift for offsets, and cross-sheet summaries, verified against recalculated formulas.","indirect-and-offset-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findirect-and-offset-equivalents-in-pandas\u002Findex",[2139],"formula-equivalents","how-to","z1RpenEpwjUBMw-y0pqpYvMQIaKD_Fb8ggG1Tl4XJ7Y",[2143,2147],{"title":2144,"path":2145,"stem":2146,"children":-1},"INDEX MATCH Equivalent in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findex-match-equivalent-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Findex-match-equivalent-in-pandas\u002Findex",{"title":2148,"path":2149,"stem":2150,"children":-1},"IPMT, PPMT, EFFECT and NOMINAL Equivalents in Python","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fipmt-ppmt-effect-and-nominal-equivalents-in-python","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fipmt-ppmt-effect-and-nominal-equivalents-in-python\u002Findex",[2152,2154],{"path":2153,"title":51},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":2155,"title":2130},"\u002Fadvanced-data-transformation-and-cleaning",[],[2158,2161,2164,2167,2170,2173],{"path":2159,"title":2160,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files","The VLOOKUP Equivalent in pandas for Excel Files",{"path":2162,"title":2163,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas","Extract Numbers and Codes from Text with pandas",{"path":2165,"title":2166,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fsplit-full-names-into-first-and-last-names-with-pandas","Split Full Names into First and Last Names with pandas",{"path":2168,"title":2169,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":2171,"title":2172,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas","Calculate Percent of Total per Group in pandas",{"path":2174,"title":2175,"kind":2140},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcount-distinct-values-per-group-in-pandas","Count Distinct Values per Group in pandas",1790972088322]