[{"data":1,"prerenderedAt":2182},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas":2147,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas":2156,"children:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas":2161,"related:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas":2162},{"id":4,"title":5,"body":6,"dateModified":2111,"datePublished":2111,"description":2112,"extension":2113,"faq":2114,"keyword":2123,"kind":2124,"meta":2125,"navigation":300,"path":2135,"seo":2136,"slug":2139,"stem":2140,"topics":2141,"type":2145,"__hash__":2146},"docs\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas\u002Findex.md","Read a Named Range into pandas",{"type":7,"value":8,"toc":2098},"minimark",[9,47,52,80,110,253,257,272,454,481,485,499,676,694,971,977,1015,1019,1025,1191,1198,1202,1205,1432,1490,1493,1550,1717,1721,1746,1878,1900,1904,1991,1995,2010,2014,2035,2039,2045,2051,2057,2063,2067,2094],[10,11,12,13,17,18,17,21,24,25,28,29,32,33,36,37,40,41,46],"p",{},"Spreadsheet builders often name the block of cells that matters — ",[14,15,16],"code",{},"SalesData",", ",[14,19,20],{},"RateTable",[14,22,23],{},"Assumptions"," — so formulas can say ",[14,26,27],{},"=VLOOKUP(A2, RateTable, 2, FALSE)"," instead of ",[14,30,31],{},"=VLOOKUP(A2, Rates!$B$4:$C$19, 2, FALSE)",". The name is also the most stable way for a script to find that block: rows get inserted above it, the sheet gets renamed, the table moves two columns right, and the name follows. ",[14,34,35],{},"pandas.read_excel"," has no ",[14,38,39],{},"range_name="," argument, though, so reading a named range takes one step with openpyxl to resolve the name to a sheet and a cell reference, then a few lines to turn those cells into a DataFrame. This page is part of ",[42,43,45],"a",{"href":44},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002F","Reading Excel Files with pandas",".",[48,49,51],"h2",{"id":50},"prerequisites","Prerequisites",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,60,61],{"__ignoreMap":58},[62,63,66,70,74,77],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"sMTad","pip",[62,71,73],{"class":72},"srMev"," install",[62,75,76],{"class":72}," pandas",[62,78,79],{"class":72}," openpyxl\n",[10,81,82,83,86,87,90,91,94,95,97,98,101,102,105,106,109],{},"The examples use ",[14,84,85],{},"pricing.xlsx",". Its ",[14,88,89],{},"Inputs"," sheet has a rate table in ",[14,92,93],{},"A4:C8"," named ",[14,96,20],{}," and a single-cell name ",[14,99,100],{},"TaxRate","; two quarterly sheets each carry a sheet-scoped name ",[14,103,104],{},"Totals",". A few awkward names are included on purpose: one covering two separate areas, one whose target was deleted, and one defined by an ",[14,107,108],{},"OFFSET"," formula.",[111,112,120,121,120,125,120,129,120,137,120,147,120,158,120,165,120,169,120,175,120,180,120,184,120,191,120,196,120,199,120,203,120,206,120,211,120,218,120,223,120,226,120,230,120,233,120,238,120,245,120,250],"svg",{"viewBox":113,"role":114,"ariaLabelledBy":115,"xmlns":118,"style":119},"4 0 752 125","img",[116,117],"nr-flow-t","nr-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[122,123,124],"title",{"id":116},"From a range name to a DataFrame",[126,127,128],"desc",{"id":117},"The name is looked up in the workbook's defined names, resolved to a sheet and cell reference, the cells are read, and the first row becomes the header of a DataFrame.",[130,131],"rect",{"x":132,"y":133,"width":134,"height":135,"fill":136},"4","0","752","125","#ffffff",[130,138],{"x":139,"y":140,"width":141,"height":142,"rx":143,"fill":144,"stroke":145,"style":146},"20.0","16.0","153.0","93.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[148,149,151],"g",{"transform":150},"translate(84.5,29.0) scale(1.000)",[152,153],"path",{"d":154,"fill":155,"stroke":156,"style":157},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[159,160,164],"text",{"x":161,"y":162,"style":163},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Name",[159,166,20],{"x":161,"y":167,"style":168},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[64,170],{"x1":171,"y1":172,"x2":173,"y2":172,"stroke":174,"style":146},"178.0","62.5","200.0","var(--brand,#5b5cf0)",[176,177],"polygon",{"points":178,"fill":179},"204.0,62.5 195.0,67.5 195.0,57.5","#5b5cf0",[130,181],{"x":182,"y":140,"width":141,"height":142,"rx":143,"fill":183,"stroke":174,"style":146},"209.0","#f0f4ff",[148,185,187],{"transform":186},"translate(273.5,29.0) scale(1.000)",[152,188],{"d":189,"fill":155,"stroke":190,"style":157},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--brand-strong,#4338ca)",[159,192,195],{"x":193,"y":162,"style":194},"285.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Defined name",[159,197,198],{"x":193,"y":167,"style":168},"Inputs!$A$4:$C$8",[64,200],{"x1":201,"y1":172,"x2":202,"y2":172,"stroke":174,"style":146},"367.0","389.0",[176,204],{"points":205,"fill":179},"393.0,62.5 384.0,67.5 384.0,57.5",[130,207],{"x":208,"y":140,"width":141,"height":142,"rx":143,"fill":209,"stroke":210,"style":146},"398.0","#d9f4f1","var(--teal,#0f9488)",[148,212,214],{"transform":213},"translate(462.5,29.0) scale(1.000)",[152,215],{"d":216,"fill":155,"stroke":217,"style":157},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--teal-ink,#0b6157)",[159,219,222],{"x":220,"y":162,"style":221},"474.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Cells",[159,224,225],{"x":220,"y":167,"style":168},"ws['A4:C8'] values",[64,227],{"x1":228,"y1":172,"x2":229,"y2":172,"stroke":174,"style":146},"556.0","578.0",[176,231],{"points":232,"fill":179},"582.0,62.5 573.0,67.5 573.0,57.5",[130,234],{"x":235,"y":140,"width":141,"height":142,"rx":143,"fill":236,"stroke":237,"style":146},"587.0","#fdefd8","var(--gold,#b4740a)",[148,239,241],{"transform":240},"translate(651.5,29.0) scale(1.000)",[152,242],{"d":243,"fill":155,"stroke":244,"style":157},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--gold-ink,#7a4e06)",[159,246,249],{"x":247,"y":162,"style":248},"663.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","DataFrame",[159,251,252],{"x":247,"y":167,"style":168},"first row as header",[48,254,256],{"id":255},"step-1-list-the-names-in-the-workbook","Step 1 — List the names in the workbook",[10,258,259,260,263,264,267,268,271],{},"Open the file with openpyxl and look at ",[14,261,262],{},"wb.defined_names",", a dictionary of workbook-scoped names. Each value is a ",[14,265,266],{},"DefinedName"," whose ",[14,269,270],{},"attr_text"," is the formula the name stands for:",[53,273,277],{"className":274,"code":275,"language":276,"meta":58,"style":58},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nwb = load_workbook(\"pricing.xlsx\", data_only=True)\nfor name, dn in wb.defined_names.items():\n    print(f\"{name:10} {dn.attr_text}\")\nfor ws in wb.worksheets:\n    for name, dn in ws.defined_names.items():\n        print(f\"{ws.title}: {name} -> {dn.attr_text}\")\n","python",[14,278,279,295,302,332,347,387,400,413],{"__ignoreMap":58},[62,280,281,285,289,292],{"class":64,"line":65},[62,282,284],{"class":283},"s-kum","from",[62,286,288],{"class":287},"skGVy"," openpyxl ",[62,290,291],{"class":283},"import",[62,293,294],{"class":287}," load_workbook\n",[62,296,298],{"class":64,"line":297},2,[62,299,301],{"emptyLinePlaceholder":300},true,"\n",[62,303,305,308,311,314,317,319,323,325,329],{"class":64,"line":304},3,[62,306,307],{"class":287},"wb ",[62,309,310],{"class":283},"=",[62,312,313],{"class":287}," load_workbook(",[62,315,316],{"class":72},"\"pricing.xlsx\"",[62,318,17],{"class":287},[62,320,322],{"class":321},"sa561","data_only",[62,324,310],{"class":283},[62,326,328],{"class":327},"sP0c6","True",[62,330,331],{"class":287},")\n",[62,333,335,338,341,344],{"class":64,"line":334},4,[62,336,337],{"class":283},"for",[62,339,340],{"class":287}," name, dn ",[62,342,343],{"class":283},"in",[62,345,346],{"class":287}," wb.defined_names.items():\n",[62,348,350,353,356,359,362,366,369,372,375,378,381,383,385],{"class":64,"line":349},5,[62,351,352],{"class":327},"    print",[62,354,355],{"class":287},"(",[62,357,358],{"class":283},"f",[62,360,361],{"class":72},"\"",[62,363,365],{"class":364},"sSjpA","{",[62,367,368],{"class":287},"name",[62,370,371],{"class":283},":10",[62,373,374],{"class":364},"}",[62,376,377],{"class":364}," {",[62,379,380],{"class":287},"dn.attr_text",[62,382,374],{"class":364},[62,384,361],{"class":72},[62,386,331],{"class":287},[62,388,390,392,395,397],{"class":64,"line":389},6,[62,391,337],{"class":283},[62,393,394],{"class":287}," ws ",[62,396,343],{"class":283},[62,398,399],{"class":287}," wb.worksheets:\n",[62,401,403,406,408,410],{"class":64,"line":402},7,[62,404,405],{"class":283},"    for",[62,407,340],{"class":287},[62,409,343],{"class":283},[62,411,412],{"class":287}," ws.defined_names.items():\n",[62,414,416,419,421,423,425,427,430,432,435,437,439,441,444,446,448,450,452],{"class":64,"line":415},8,[62,417,418],{"class":327},"        print",[62,420,355],{"class":287},[62,422,358],{"class":283},[62,424,361],{"class":72},[62,426,365],{"class":364},[62,428,429],{"class":287},"ws.title",[62,431,374],{"class":364},[62,433,434],{"class":72},": ",[62,436,365],{"class":364},[62,438,368],{"class":287},[62,440,374],{"class":364},[62,442,443],{"class":72}," -> ",[62,445,365],{"class":364},[62,447,380],{"class":287},[62,449,374],{"class":364},[62,451,361],{"class":72},[62,453,331],{"class":287},[10,455,456,457,460,461,463,464,467,468,471,472,475,476,480],{},"Workbook-scoped names live on the workbook; names scoped to a single sheet live on that worksheet's own ",[14,458,459],{},"defined_names",". That is how two sheets can both define ",[14,462,104],{}," without clashing — the Q3 and Q4 sheets each have their own. openpyxl 3.1 changed this API: older answers online use ",[14,465,466],{},"wb.defined_names.definedName"," or ",[14,469,470],{},"wb.get_named_range()",", which no longer exist. ",[14,473,474],{},"data_only=True"," makes cells return the values Excel last calculated rather than formula strings, which is what you want when reading data (see ",[42,477,479],{"href":478},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Read Formula Results with openpyxl data_only",").",[48,482,484],{"id":483},"step-2-resolve-the-name-and-build-the-dataframe","Step 2 — Resolve the name and build the DataFrame",[10,486,487,490,491,494,495,498],{},[14,488,489],{},"destinations"," turns the reference into ",[14,492,493],{},"(sheet title, cell range)"," pairs — the quotes around the sheet name are removed, the ",[14,496,497],{},"$"," signs are kept, and a worksheet accepts the reference either way. Index the worksheet with the range to get the cells, pull out their values, and hand the rows to pandas:",[53,500,502],{"className":274,"code":501,"language":276,"meta":58,"style":58},"import pandas as pd\n\ndef read_named_range(wb, name, header=True):\n    dn = wb.defined_names[name]\n    (sheet, ref), = dn.destinations          # exactly one area expected\n    rows = [[c.value for c in row] for row in wb[sheet][ref]]\n    if header:\n        return pd.DataFrame(rows[1:], columns=rows[0])\n    return pd.DataFrame(rows)\n\nrates = read_named_range(wb, \"RateTable\")\nprint(rates)\nprint(rates.dtypes.to_dict())\n",[14,503,504,517,521,540,550,564,594,602,629,638,643,659,668],{"__ignoreMap":58},[62,505,506,508,511,514],{"class":64,"line":65},[62,507,291],{"class":283},[62,509,510],{"class":287}," pandas ",[62,512,513],{"class":283},"as",[62,515,516],{"class":287}," pd\n",[62,518,519],{"class":64,"line":297},[62,520,301],{"emptyLinePlaceholder":300},[62,522,523,526,530,533,535,537],{"class":64,"line":304},[62,524,525],{"class":283},"def",[62,527,529],{"class":528},"s_Opv"," read_named_range",[62,531,532],{"class":287},"(wb, name, header",[62,534,310],{"class":283},[62,536,328],{"class":327},[62,538,539],{"class":287},"):\n",[62,541,542,545,547],{"class":64,"line":334},[62,543,544],{"class":287},"    dn ",[62,546,310],{"class":283},[62,548,549],{"class":287}," wb.defined_names[name]\n",[62,551,552,555,557,560],{"class":64,"line":349},[62,553,554],{"class":287},"    (sheet, ref), ",[62,556,310],{"class":283},[62,558,559],{"class":287}," dn.destinations          ",[62,561,563],{"class":562},"s-wDw","# exactly one area expected\n",[62,565,566,569,571,574,576,579,581,584,586,589,591],{"class":64,"line":389},[62,567,568],{"class":287},"    rows ",[62,570,310],{"class":283},[62,572,573],{"class":287}," [[c.value ",[62,575,337],{"class":283},[62,577,578],{"class":287}," c ",[62,580,343],{"class":283},[62,582,583],{"class":287}," row] ",[62,585,337],{"class":283},[62,587,588],{"class":287}," row ",[62,590,343],{"class":283},[62,592,593],{"class":287}," wb[sheet][ref]]\n",[62,595,596,599],{"class":64,"line":402},[62,597,598],{"class":283},"    if",[62,600,601],{"class":287}," header:\n",[62,603,604,607,610,613,616,619,621,624,626],{"class":64,"line":415},[62,605,606],{"class":283},"        return",[62,608,609],{"class":287}," pd.DataFrame(rows[",[62,611,612],{"class":327},"1",[62,614,615],{"class":287},":], ",[62,617,618],{"class":321},"columns",[62,620,310],{"class":283},[62,622,623],{"class":287},"rows[",[62,625,133],{"class":327},[62,627,628],{"class":287},"])\n",[62,630,632,635],{"class":64,"line":631},9,[62,633,634],{"class":283},"    return",[62,636,637],{"class":287}," pd.DataFrame(rows)\n",[62,639,641],{"class":64,"line":640},10,[62,642,301],{"emptyLinePlaceholder":300},[62,644,646,649,651,654,657],{"class":64,"line":645},11,[62,647,648],{"class":287},"rates ",[62,650,310],{"class":283},[62,652,653],{"class":287}," read_named_range(wb, ",[62,655,656],{"class":72},"\"RateTable\"",[62,658,331],{"class":287},[62,660,662,665],{"class":64,"line":661},12,[62,663,664],{"class":327},"print",[62,666,667],{"class":287},"(rates)\n",[62,669,671,673],{"class":64,"line":670},13,[62,672,664],{"class":327},[62,674,675],{"class":287},"(rates.dtypes.to_dict())\n",[10,677,678,679,682,683,685,686,689,690,693],{},"The rate table comes back with its header from the first row of the range and proper numeric types. The title in ",[14,680,681],{},"A1"," and the blank rows above the table are ignored, because the name covers only ",[14,684,93],{}," — which is the advantage over ",[14,687,688],{},"skiprows"," and ",[14,691,692],{},"nrows",", where the offsets break as soon as someone adds a line above the table.",[111,695,120,701,120,704,120,707,120,712,120,719,120,724,120,731,120,736,120,741,120,744,120,750,120,755,120,760,120,763,120,769,120,773,120,777,120,781,120,785,120,788,120,793,120,795,120,797,120,799,120,802,120,805,120,807,120,811,120,813,120,816,120,818,120,821,120,824,120,828,120,830,120,834,120,836,120,839,120,841,120,844,120,847,120,851,120,853,120,856,120,858,120,861,120,863,120,865,120,868,120,872,120,874,120,877,120,879,120,882,120,884,120,887,120,890,120,893,120,895,120,898,120,900,120,903,120,905,120,908,120,913,120,919,120,924,120,928,120,933,120,937,120,941,120,945,120,948,120,954,120,959,120,963,120,967],{"viewBox":696,"role":114,"ariaLabelledBy":697,"xmlns":118,"style":700},"132 0 495 306",[698,699],"nr-sheet-t","nr-sheet-d","width:100%;max-width:495px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[122,702,703],{"id":698},"The named range covers only the table",[126,705,706],{"id":699},"A worksheet with a title in row 1 and blank rows, and a highlighted rate table in A4 to C8 that the name RateTable refers to.",[130,708],{"x":709,"y":133,"width":710,"height":711,"fill":136},"132","495","306",[130,713],{"x":714,"y":140,"width":715,"height":716,"rx":717,"fill":144,"stroke":145,"style":718},"148.9","242.2","30.0","8","stroke-width:1.5px",[130,720],{"x":714,"y":721,"width":715,"height":722,"rx":133,"fill":144,"stroke":145,"style":723},"38.0","8.0","stroke-width:0px",[148,725,727],{"transform":726},"translate(158.9,23.0) scale(0.667)",[152,728],{"d":729,"fill":155,"stroke":217,"style":730},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[159,732,85],{"x":733,"y":734,"style":735},"180.9","36.0","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[130,737],{"x":714,"y":738,"width":715,"height":739,"rx":133,"fill":136,"stroke":145,"style":740},"46.0","28.0","stroke-width:1px",[130,742],{"x":714,"y":738,"width":743,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"40.0",[159,745,749],{"x":746,"y":747,"style":748},"168.9","65.0","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","fx",[159,751,754],{"x":752,"y":747,"style":753},"198.9","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=Inputs!$A$4:$C$8",[130,756],{"x":714,"y":757,"width":758,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"74.0","32.0","24.0",[130,761],{"x":733,"y":757,"width":762,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"66.7",[159,764,768],{"x":765,"y":766,"style":767},"214.3","91.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[130,770],{"x":771,"y":757,"width":772,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"247.6","62.2",[159,774,776],{"x":775,"y":766,"style":767},"278.7","B",[130,778],{"x":779,"y":757,"width":780,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"309.8","81.3",[159,782,784],{"x":783,"y":766,"style":767},"350.5","C",[130,786],{"x":714,"y":787,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"98.0",[159,789,792],{"x":790,"y":791,"style":767},"164.9","117.0","3",[130,794],{"x":733,"y":787,"width":762,"height":739,"rx":133,"fill":136,"stroke":145,"style":740},[130,796],{"x":771,"y":787,"width":772,"height":739,"rx":133,"fill":136,"stroke":145,"style":740},[130,798],{"x":779,"y":787,"width":780,"height":739,"rx":133,"fill":136,"stroke":145,"style":740},[130,800],{"x":714,"y":801,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"126.0",[159,803,132],{"x":790,"y":804,"style":767},"145.0",[130,806],{"x":733,"y":801,"width":762,"height":739,"rx":133,"fill":183,"stroke":174,"style":718},[159,808,810],{"x":765,"y":804,"style":809},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Region",[130,812],{"x":771,"y":801,"width":772,"height":739,"rx":133,"fill":183,"stroke":174,"style":718},[159,814,815],{"x":775,"y":804,"style":809},"Rate",[130,817],{"x":779,"y":801,"width":780,"height":739,"rx":133,"fill":183,"stroke":174,"style":718},[159,819,820],{"x":783,"y":804,"style":809},"Minimum",[130,822],{"x":714,"y":823,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"154.0",[159,825,827],{"x":790,"y":826,"style":767},"173.0","5",[130,829],{"x":733,"y":823,"width":762,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,831,833],{"x":765,"y":826,"style":832},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","North",[130,835],{"x":771,"y":823,"width":772,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,837,838],{"x":775,"y":826,"style":832},"0.042",[130,840],{"x":779,"y":823,"width":780,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,842,843],{"x":783,"y":826,"style":832},"250",[130,845],{"x":714,"y":846,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"182.0",[159,848,850],{"x":790,"y":849,"style":767},"201.0","6",[130,852],{"x":733,"y":846,"width":762,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,854,855],{"x":765,"y":849,"style":832},"South",[130,857],{"x":771,"y":846,"width":772,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,859,860],{"x":775,"y":849,"style":832},"0.038",[130,862],{"x":779,"y":846,"width":780,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,864,843],{"x":783,"y":849,"style":832},[130,866],{"x":714,"y":867,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"210.0",[159,869,871],{"x":790,"y":870,"style":767},"229.0","7",[130,873],{"x":733,"y":867,"width":762,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,875,876],{"x":765,"y":870,"style":832},"East",[130,878],{"x":771,"y":867,"width":772,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,880,881],{"x":775,"y":870,"style":832},"0.045",[130,883],{"x":779,"y":867,"width":780,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,885,886],{"x":783,"y":870,"style":832},"300",[130,888],{"x":714,"y":889,"width":758,"height":739,"rx":133,"fill":144,"stroke":145,"style":740},"238.0",[159,891,717],{"x":790,"y":892,"style":767},"257.0",[130,894],{"x":733,"y":889,"width":762,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,896,897],{"x":765,"y":892,"style":832},"West",[130,899],{"x":771,"y":889,"width":772,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,901,902],{"x":775,"y":892,"style":832},"0.040",[130,904],{"x":779,"y":889,"width":780,"height":739,"rx":133,"fill":209,"stroke":210,"style":718},[159,906,907],{"x":783,"y":892,"style":832},"200",[130,909],{"x":910,"y":911,"width":912,"height":759,"rx":133,"fill":136,"stroke":145,"style":740},"156.9","266.0","61.2",[64,914],{"x1":915,"y1":916,"x2":917,"y2":916,"stroke":210,"style":918},"158.9","289.0","216.1","stroke-width:3px",[159,920,89],{"x":921,"y":922,"style":923},"187.5","282.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[130,925],{"x":926,"y":911,"width":927,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"220.1","75.0",[159,929,932],{"x":930,"y":922,"style":931},"257.6","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","Q3 Sales",[130,934],{"x":935,"y":911,"width":936,"height":759,"rx":133,"fill":144,"stroke":145,"style":740},"297.1","75.3",[159,938,940],{"x":939,"y":922,"style":931},"334.7","Q4 Sales",[64,942],{"x1":943,"y1":141,"x2":944,"y2":141,"stroke":174,"style":146},"399.1","431.1",[176,946],{"points":947,"fill":179},"435.1,153.0 426.1,158.0 426.1,148.0",[130,949],{"x":950,"y":951,"width":952,"height":953,"rx":143,"fill":183,"stroke":174,"style":146},"441.1","97.8","170.0","110.5",[148,955,957],{"transform":956},"translate(514.1,110.8) scale(1.000)",[152,958],{"d":243,"fill":155,"stroke":190,"style":157},[159,960,20],{"x":961,"y":962,"style":194},"526.1","157.2",[159,964,966],{"x":961,"y":965,"style":168},"176.8","header row + 4 records",[159,968,970],{"x":961,"y":969,"style":168},"194.2","rows above are ignored",[10,972,973,974,976],{},"A single-cell name such as ",[14,975,100],{}," is usually a parameter rather than a table. Read its one value directly:",[53,978,980],{"className":274,"code":979,"language":276,"meta":58,"style":58},"(sheet, ref), = wb.defined_names[\"TaxRate\"].destinations\ntax_rate = wb[sheet][ref].value\nprint(sheet, ref, tax_rate)\n",[14,981,982,998,1008],{"__ignoreMap":58},[62,983,984,987,989,992,995],{"class":64,"line":65},[62,985,986],{"class":287},"(sheet, ref), ",[62,988,310],{"class":283},[62,990,991],{"class":287}," wb.defined_names[",[62,993,994],{"class":72},"\"TaxRate\"",[62,996,997],{"class":287},"].destinations\n",[62,999,1000,1003,1005],{"class":64,"line":297},[62,1001,1002],{"class":287},"tax_rate ",[62,1004,310],{"class":283},[62,1006,1007],{"class":287}," wb[sheet][ref].value\n",[62,1009,1010,1012],{"class":64,"line":304},[62,1011,664],{"class":327},[62,1013,1014],{"class":287},"(sheet, ref, tax_rate)\n",[48,1016,1018],{"id":1017},"step-3-read-sheet-scoped-names","Step 3 — Read sheet-scoped names",[10,1020,1021,1022,1024],{},"A name scoped to a sheet is not in ",[14,1023,262],{},"; look on the worksheet instead. Resolve it the same way:",[53,1026,1028],{"className":274,"code":1027,"language":276,"meta":58,"style":58},"def read_sheet_name(wb, sheet, name):\n    dn = wb[sheet].defined_names[name]\n    (target, ref), = dn.destinations\n    rows = [[c.value for c in row] for row in wb[target][ref]]\n    return pd.DataFrame(rows[1:], columns=rows[0])\n\nboth = pd.concat({s: read_sheet_name(wb, s, \"Totals\") for s in [\"Q3 Sales\", \"Q4 Sales\"]},\n                 names=[\"Sheet\", None])\nprint(both)\nprint(\"in workbook scope:\", \"Totals\" in wb.defined_names)\n",[14,1029,1030,1040,1049,1059,1084,1104,1108,1145,1165,1172],{"__ignoreMap":58},[62,1031,1032,1034,1037],{"class":64,"line":65},[62,1033,525],{"class":283},[62,1035,1036],{"class":528}," read_sheet_name",[62,1038,1039],{"class":287},"(wb, sheet, name):\n",[62,1041,1042,1044,1046],{"class":64,"line":297},[62,1043,544],{"class":287},[62,1045,310],{"class":283},[62,1047,1048],{"class":287}," wb[sheet].defined_names[name]\n",[62,1050,1051,1054,1056],{"class":64,"line":304},[62,1052,1053],{"class":287},"    (target, ref), ",[62,1055,310],{"class":283},[62,1057,1058],{"class":287}," dn.destinations\n",[62,1060,1061,1063,1065,1067,1069,1071,1073,1075,1077,1079,1081],{"class":64,"line":334},[62,1062,568],{"class":287},[62,1064,310],{"class":283},[62,1066,573],{"class":287},[62,1068,337],{"class":283},[62,1070,578],{"class":287},[62,1072,343],{"class":283},[62,1074,583],{"class":287},[62,1076,337],{"class":283},[62,1078,588],{"class":287},[62,1080,343],{"class":283},[62,1082,1083],{"class":287}," wb[target][ref]]\n",[62,1085,1086,1088,1090,1092,1094,1096,1098,1100,1102],{"class":64,"line":349},[62,1087,634],{"class":283},[62,1089,609],{"class":287},[62,1091,612],{"class":327},[62,1093,615],{"class":287},[62,1095,618],{"class":321},[62,1097,310],{"class":283},[62,1099,623],{"class":287},[62,1101,133],{"class":327},[62,1103,628],{"class":287},[62,1105,1106],{"class":64,"line":389},[62,1107,301],{"emptyLinePlaceholder":300},[62,1109,1110,1113,1115,1118,1121,1124,1126,1129,1131,1134,1137,1139,1142],{"class":64,"line":402},[62,1111,1112],{"class":287},"both ",[62,1114,310],{"class":283},[62,1116,1117],{"class":287}," pd.concat({s: read_sheet_name(wb, s, ",[62,1119,1120],{"class":72},"\"Totals\"",[62,1122,1123],{"class":287},") ",[62,1125,337],{"class":283},[62,1127,1128],{"class":287}," s ",[62,1130,343],{"class":283},[62,1132,1133],{"class":287}," [",[62,1135,1136],{"class":72},"\"Q3 Sales\"",[62,1138,17],{"class":287},[62,1140,1141],{"class":72},"\"Q4 Sales\"",[62,1143,1144],{"class":287},"]},\n",[62,1146,1147,1150,1152,1155,1158,1160,1163],{"class":64,"line":415},[62,1148,1149],{"class":321},"                 names",[62,1151,310],{"class":283},[62,1153,1154],{"class":287},"[",[62,1156,1157],{"class":72},"\"Sheet\"",[62,1159,17],{"class":287},[62,1161,1162],{"class":327},"None",[62,1164,628],{"class":287},[62,1166,1167,1169],{"class":64,"line":631},[62,1168,664],{"class":327},[62,1170,1171],{"class":287},"(both)\n",[62,1173,1174,1176,1178,1181,1183,1185,1188],{"class":64,"line":640},[62,1175,664],{"class":327},[62,1177,355],{"class":287},[62,1179,1180],{"class":72},"\"in workbook scope:\"",[62,1182,17],{"class":287},[62,1184,1120],{"class":72},[62,1186,1187],{"class":283}," in",[62,1189,1190],{"class":287}," wb.defined_names)\n",[10,1192,1193,1194,1197],{},"Reading the same local name from several sheets and stacking the results with ",[14,1195,1196],{},"pd.concat"," is a neat way to combine per-period sheets that were all built from one template. The keys of the dictionary become an outer index level, so each row still says which sheet it came from.",[48,1199,1201],{"id":1200},"step-4-handle-names-that-do-not-point-at-one-block","Step 4 — Handle names that do not point at one block",[10,1203,1204],{},"Not every name is a clean rectangle. Check the awkward cases before trusting the result:",[53,1206,1208],{"className":274,"code":1207,"language":276,"meta":58,"style":58},"def describe(dn):\n    if dn.attr_text.startswith(\"#REF!\") or \"#REF!\" in dn.attr_text:\n        return \"broken: target was deleted\"\n    if dn.is_reserved:\n        return \"reserved (print area, filter database)\"\n    try:\n        dests = list(dn.destinations)\n    except Exception as exc:\n        return f\"not a plain reference: {type(exc).__name__}\"\n    if not dests:\n        return \"formula, not a cell reference\"\n    return f\"{len(dests)} area(s): {dests}\"\n\nfor name in [\"RateTable\", \"Regions\", \"OldLookup\", \"SalesDyn\"]:\n    print(f\"{name:10}\", describe(wb.defined_names[name]))\n",[14,1209,1210,1220,1243,1250,1257,1264,1272,1285,1299,1325,1335,1342,1372,1376,1408],{"__ignoreMap":58},[62,1211,1212,1214,1217],{"class":64,"line":65},[62,1213,525],{"class":283},[62,1215,1216],{"class":528}," describe",[62,1218,1219],{"class":287},"(dn):\n",[62,1221,1222,1224,1227,1230,1232,1235,1238,1240],{"class":64,"line":297},[62,1223,598],{"class":283},[62,1225,1226],{"class":287}," dn.attr_text.startswith(",[62,1228,1229],{"class":72},"\"#REF!\"",[62,1231,1123],{"class":287},[62,1233,1234],{"class":283},"or",[62,1236,1237],{"class":72}," \"#REF!\"",[62,1239,1187],{"class":283},[62,1241,1242],{"class":287}," dn.attr_text:\n",[62,1244,1245,1247],{"class":64,"line":304},[62,1246,606],{"class":283},[62,1248,1249],{"class":72}," \"broken: target was deleted\"\n",[62,1251,1252,1254],{"class":64,"line":334},[62,1253,598],{"class":283},[62,1255,1256],{"class":287}," dn.is_reserved:\n",[62,1258,1259,1261],{"class":64,"line":349},[62,1260,606],{"class":283},[62,1262,1263],{"class":72}," \"reserved (print area, filter database)\"\n",[62,1265,1266,1269],{"class":64,"line":389},[62,1267,1268],{"class":283},"    try",[62,1270,1271],{"class":287},":\n",[62,1273,1274,1277,1279,1282],{"class":64,"line":402},[62,1275,1276],{"class":287},"        dests ",[62,1278,310],{"class":283},[62,1280,1281],{"class":327}," list",[62,1283,1284],{"class":287},"(dn.destinations)\n",[62,1286,1287,1290,1293,1296],{"class":64,"line":415},[62,1288,1289],{"class":283},"    except",[62,1291,1292],{"class":327}," Exception",[62,1294,1295],{"class":283}," as",[62,1297,1298],{"class":287}," exc:\n",[62,1300,1301,1303,1306,1309,1311,1314,1317,1320,1322],{"class":64,"line":631},[62,1302,606],{"class":283},[62,1304,1305],{"class":283}," f",[62,1307,1308],{"class":72},"\"not a plain reference: ",[62,1310,365],{"class":364},[62,1312,1313],{"class":327},"type",[62,1315,1316],{"class":287},"(exc).",[62,1318,1319],{"class":327},"__name__",[62,1321,374],{"class":364},[62,1323,1324],{"class":72},"\"\n",[62,1326,1327,1329,1332],{"class":64,"line":640},[62,1328,598],{"class":283},[62,1330,1331],{"class":283}," not",[62,1333,1334],{"class":287}," dests:\n",[62,1336,1337,1339],{"class":64,"line":645},[62,1338,606],{"class":283},[62,1340,1341],{"class":72}," \"formula, not a cell reference\"\n",[62,1343,1344,1346,1348,1350,1352,1355,1358,1360,1363,1365,1368,1370],{"class":64,"line":661},[62,1345,634],{"class":283},[62,1347,1305],{"class":283},[62,1349,361],{"class":72},[62,1351,365],{"class":364},[62,1353,1354],{"class":327},"len",[62,1356,1357],{"class":287},"(dests)",[62,1359,374],{"class":364},[62,1361,1362],{"class":72}," area(s): ",[62,1364,365],{"class":364},[62,1366,1367],{"class":287},"dests",[62,1369,374],{"class":364},[62,1371,1324],{"class":72},[62,1373,1374],{"class":64,"line":670},[62,1375,301],{"emptyLinePlaceholder":300},[62,1377,1379,1381,1384,1386,1388,1390,1392,1395,1397,1400,1402,1405],{"class":64,"line":1378},14,[62,1380,337],{"class":283},[62,1382,1383],{"class":287}," name ",[62,1385,343],{"class":283},[62,1387,1133],{"class":287},[62,1389,656],{"class":72},[62,1391,17],{"class":287},[62,1393,1394],{"class":72},"\"Regions\"",[62,1396,17],{"class":287},[62,1398,1399],{"class":72},"\"OldLookup\"",[62,1401,17],{"class":287},[62,1403,1404],{"class":72},"\"SalesDyn\"",[62,1406,1407],{"class":287},"]:\n",[62,1409,1411,1413,1415,1417,1419,1421,1423,1425,1427,1429],{"class":64,"line":1410},15,[62,1412,352],{"class":327},[62,1414,355],{"class":287},[62,1416,358],{"class":283},[62,1418,361],{"class":72},[62,1420,365],{"class":364},[62,1422,368],{"class":287},[62,1424,371],{"class":283},[62,1426,374],{"class":364},[62,1428,361],{"class":72},[62,1430,1431],{"class":287},", describe(wb.defined_names[name]))\n",[1433,1434,1435,1457,1470],"ul",{},[1436,1437,1438,1442,1443,1446,1447,689,1450,1453,1454,1456],"li",{},[1439,1440,1441],"strong",{},"Several areas."," ",[14,1444,1445],{},"Regions"," refers to ",[14,1448,1449],{},"A5:A6",[14,1451,1452],{},"A7:A8",", two areas joined by a comma. ",[14,1455,489],{}," yields both; read each and concatenate them rather than unpacking a single pair.",[1436,1458,1459,1462,1463,1466,1467,46],{},[1439,1460,1461],{},"Broken names."," A name whose rows or sheet were deleted refers to ",[14,1464,1465],{},"#REF!",". Excel keeps it; your script should report it rather than fail with a confusing ",[14,1468,1469],{},"KeyError",[1436,1471,1472,1475,1476,1479,1480,689,1482,1485,1486,480],{},[1439,1473,1474],{},"Formula names."," A dynamic name such as ",[14,1477,1478],{},"SalesDyn",", defined with ",[14,1481,108],{},[14,1483,1484],{},"COUNTA"," to grow with the data, is a formula, not a reference. openpyxl cannot evaluate it, so it has no destinations. Read the anchor cell and expand it yourself, or — better — convert the block to an Excel table and read the table by name (see ",[42,1487,1489],{"href":1488},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fread-an-excel-table-by-name-into-pandas\u002F","Read an Excel Table by Name into pandas",[10,1491,1492],{},"Multiple areas are read and joined like this:",[53,1494,1496],{"className":274,"code":1495,"language":276,"meta":58,"style":58},"parts = [[c.value for row in wb[s][ref] for c in row] for s, ref in wb.defined_names[\"Regions\"].destinations]\nprint(sum(parts, []))\n",[14,1497,1498,1538],{"__ignoreMap":58},[62,1499,1500,1503,1505,1507,1509,1511,1513,1516,1518,1520,1522,1524,1526,1529,1531,1533,1535],{"class":64,"line":65},[62,1501,1502],{"class":287},"parts ",[62,1504,310],{"class":283},[62,1506,573],{"class":287},[62,1508,337],{"class":283},[62,1510,588],{"class":287},[62,1512,343],{"class":283},[62,1514,1515],{"class":287}," wb[s][ref] ",[62,1517,337],{"class":283},[62,1519,578],{"class":287},[62,1521,343],{"class":283},[62,1523,583],{"class":287},[62,1525,337],{"class":283},[62,1527,1528],{"class":287}," s, ref ",[62,1530,343],{"class":283},[62,1532,991],{"class":287},[62,1534,1394],{"class":72},[62,1536,1537],{"class":287},"].destinations]\n",[62,1539,1540,1542,1544,1547],{"class":64,"line":297},[62,1541,664],{"class":327},[62,1543,355],{"class":287},[62,1545,1546],{"class":327},"sum",[62,1548,1549],{"class":287},"(parts, []))\n",[111,1551,120,1556,120,1559,120,1562,120,1565,120,1569,120,1574,120,1578,120,1582,120,1586,120,1589,120,1592,120,1600,120,1605,120,1609,120,1615,120,1619,120,1623,120,1626,120,1629,120,1632,120,1636,120,1639,120,1642,120,1648,120,1652,120,1656,120,1659,120,1662,120,1666,120,1668,120,1673,120,1680,120,1684,120,1687,120,1689,120,1692,120,1696,120,1699,120,1702,120,1708,120,1711,120,1714],{"viewBox":1552,"role":114,"ariaLabelledBy":1553,"xmlns":118,"style":119},"4 0 752 290",[1554,1555],"nr-kinds-t","nr-kinds-d",[122,1557,1558],{"id":1554},"What kind of name is it?",[126,1560,1561],{"id":1555},"A decision diagram. A name that refers to one area is read directly; several areas are read and concatenated; a",[130,1563],{"x":132,"y":133,"width":134,"height":1564,"fill":136},"290",[130,1566],{"x":1567,"y":140,"width":1568,"height":162,"rx":143,"fill":183,"stroke":174,"style":146},"240.0","280.0",[148,1570,1572],{"transform":1571},"translate(368.0,29.0) scale(1.000)",[152,1573],{"d":189,"fill":155,"stroke":190,"style":157},[159,1575,1577],{"x":1576,"y":162,"style":194},"380.0","What does the name refer to?",[64,1579],{"x1":1576,"y1":1580,"x2":1576,"y2":1581,"stroke":174,"style":146},"91.5","111.5",[64,1583],{"x1":1584,"y1":1581,"x2":1585,"y2":1581,"stroke":174,"style":146},"104.0","656.0",[64,1587],{"x1":1584,"y1":1581,"x2":1584,"y2":1588,"stroke":174,"style":146},"157.5",[176,1590],{"points":1591,"fill":179},"104.0,161.5 99.0,152.5 109.0,152.5",[130,1593],{"x":1594,"y":1595,"width":1596,"height":1597,"rx":1598,"fill":136,"stroke":145,"style":1599},"69.4","121.5","69.1","21.0","10","stroke-width:1.2px",[159,1601,1604],{"x":1584,"y":1602,"style":1603},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","one area",[130,1606],{"x":139,"y":1607,"width":1608,"height":953,"rx":143,"fill":209,"stroke":210,"style":146},"163.5","168.0",[148,1610,1612],{"transform":1611},"translate(92.0,176.5) scale(1.000)",[152,1613],{"d":1614,"fill":155,"stroke":217,"style":157},"M5 12.5l4.5 4.5L19 7",[159,1616,1618],{"x":1584,"y":1617,"style":221},"223.0","Read directly",[159,1620,1622],{"x":1584,"y":1621,"style":168},"242.5","(sheet, ref), =",[159,1624,489],{"x":1584,"y":1625,"style":168},"260.0",[64,1627],{"x1":1628,"y1":1581,"x2":1628,"y2":1588,"stroke":174,"style":146},"288.0",[176,1630],{"points":1631,"fill":179},"288.0,161.5 283.0,152.5 293.0,152.5",[130,1633],{"x":1634,"y":1595,"width":1635,"height":1597,"rx":1598,"fill":136,"stroke":145,"style":1599},"239.3","97.4",[159,1637,1638],{"x":1628,"y":1602,"style":1603},"several areas",[130,1640],{"x":1641,"y":1607,"width":1608,"height":953,"rx":143,"fill":183,"stroke":174,"style":146},"204.0",[148,1643,1645],{"transform":1644},"translate(276.0,185.2) scale(1.000)",[152,1646],{"d":1647,"fill":155,"stroke":190,"style":157},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5",[159,1649,1651],{"x":1628,"y":1650,"style":194},"231.8","Read each and join",[159,1653,1655],{"x":1628,"y":1654,"style":168},"251.2","loop over destinations",[64,1657],{"x1":1658,"y1":1581,"x2":1658,"y2":1588,"stroke":174,"style":146},"472.0",[176,1660],{"points":1661,"fill":179},"472.0,161.5 467.0,152.5 477.0,152.5",[130,1663],{"x":1664,"y":1595,"width":1665,"height":1597,"rx":1598,"fill":136,"stroke":145,"style":1599},"445.9","52.3",[159,1667,1465],{"x":1658,"y":1602,"style":1603},[130,1669],{"x":1670,"y":1607,"width":1608,"height":953,"rx":143,"fill":1671,"stroke":1672,"style":146},"388.0","#fce9e9","var(--danger,#dc2626)",[148,1674,1676],{"transform":1675},"translate(460.0,185.2) scale(1.000)",[152,1677],{"d":1678,"fill":155,"stroke":1679,"style":157},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)",[159,1681,1683],{"x":1658,"y":1650,"style":1682},"font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Report as broken",[159,1685,1686],{"x":1658,"y":1654,"style":168},"target was deleted",[64,1688],{"x1":1585,"y1":1581,"x2":1585,"y2":1588,"stroke":174,"style":146},[176,1690],{"points":1691,"fill":179},"656.0,161.5 651.0,152.5 661.0,152.5",[130,1693],{"x":1694,"y":1595,"width":1695,"height":1597,"rx":1598,"fill":136,"stroke":145,"style":1599},"613.0","86.0",[159,1697,1698],{"x":1585,"y":1602,"style":1603},"OFFSET(...)",[130,1700],{"x":1701,"y":1607,"width":1608,"height":953,"rx":143,"fill":236,"stroke":237,"style":146},"572.0",[148,1703,1705],{"transform":1704},"translate(644.0,176.5) scale(1.000)",[152,1706],{"d":1707,"fill":155,"stroke":244,"style":157},"M9 8l-4 4 4 4 M15 8l4 4-4 4",[159,1709,1710],{"x":1585,"y":1617,"style":248},"Formula name",[159,1712,1713],{"x":1585,"y":1621,"style":168},"no destinations — use a",[159,1715,1716],{"x":1585,"y":1625,"style":168},"table",[48,1718,1720],{"id":1719},"step-5-use-the-name-to-drive-read_excel","Step 5 — Use the name to drive read_excel",[10,1722,1723,1724,1727,1728,17,1731,17,1734,1737,1738,17,1740,689,1742,1745],{},"If you prefer ",[14,1725,1726],{},"read_excel","'s type handling and options (",[14,1729,1730],{},"dtype",[14,1732,1733],{},"converters",[14,1735,1736],{},"parse_dates","), use openpyxl only to find where the range is, then translate the reference into ",[14,1739,688],{},[14,1741,692],{},[14,1743,1744],{},"usecols",":",[53,1747,1749],{"className":274,"code":1748,"language":276,"meta":58,"style":58},"from openpyxl.utils import range_boundaries\n\n(sheet, ref), = wb.defined_names[\"RateTable\"].destinations\nmin_col, min_row, max_col, max_row = range_boundaries(ref)\ndf = pd.read_excel(\"pricing.xlsx\", sheet_name=sheet, header=0,\n                   skiprows=min_row - 1, nrows=max_row - min_row,\n                   usecols=range(min_col - 1, max_col))\nprint(df)\n",[14,1750,1751,1763,1767,1779,1789,1821,1851,1871],{"__ignoreMap":58},[62,1752,1753,1755,1758,1760],{"class":64,"line":65},[62,1754,284],{"class":283},[62,1756,1757],{"class":287}," openpyxl.utils ",[62,1759,291],{"class":283},[62,1761,1762],{"class":287}," range_boundaries\n",[62,1764,1765],{"class":64,"line":297},[62,1766,301],{"emptyLinePlaceholder":300},[62,1768,1769,1771,1773,1775,1777],{"class":64,"line":304},[62,1770,986],{"class":287},[62,1772,310],{"class":283},[62,1774,991],{"class":287},[62,1776,656],{"class":72},[62,1778,997],{"class":287},[62,1780,1781,1784,1786],{"class":64,"line":334},[62,1782,1783],{"class":287},"min_col, min_row, max_col, max_row ",[62,1785,310],{"class":283},[62,1787,1788],{"class":287}," range_boundaries(ref)\n",[62,1790,1791,1794,1796,1799,1801,1803,1806,1808,1811,1814,1816,1818],{"class":64,"line":349},[62,1792,1793],{"class":287},"df ",[62,1795,310],{"class":283},[62,1797,1798],{"class":287}," pd.read_excel(",[62,1800,316],{"class":72},[62,1802,17],{"class":287},[62,1804,1805],{"class":321},"sheet_name",[62,1807,310],{"class":283},[62,1809,1810],{"class":287},"sheet, ",[62,1812,1813],{"class":321},"header",[62,1815,310],{"class":283},[62,1817,133],{"class":327},[62,1819,1820],{"class":287},",\n",[62,1822,1823,1826,1828,1831,1834,1837,1839,1841,1843,1846,1848],{"class":64,"line":389},[62,1824,1825],{"class":321},"                   skiprows",[62,1827,310],{"class":283},[62,1829,1830],{"class":287},"min_row ",[62,1832,1833],{"class":283},"-",[62,1835,1836],{"class":327}," 1",[62,1838,17],{"class":287},[62,1840,692],{"class":321},[62,1842,310],{"class":283},[62,1844,1845],{"class":287},"max_row ",[62,1847,1833],{"class":283},[62,1849,1850],{"class":287}," min_row,\n",[62,1852,1853,1856,1858,1861,1864,1866,1868],{"class":64,"line":402},[62,1854,1855],{"class":321},"                   usecols",[62,1857,310],{"class":283},[62,1859,1860],{"class":327},"range",[62,1862,1863],{"class":287},"(min_col ",[62,1865,1833],{"class":283},[62,1867,1836],{"class":327},[62,1869,1870],{"class":287},", max_col))\n",[62,1872,1873,1875],{"class":64,"line":415},[62,1874,664],{"class":327},[62,1876,1877],{"class":287},"(df)\n",[10,1879,1880,1883,1884,1886,1887,1889,1890,1893,1894,1896,1897,1899],{},[14,1881,1882],{},"range_boundaries"," returns 1-based column and row numbers; ",[14,1885,688],{}," counts the rows to drop before the header, and ",[14,1888,692],{}," counts data rows after it, which is why it is ",[14,1891,1892],{},"max_row - min_row"," and not one more. ",[14,1895,1744],{}," accepts zero-based column positions. The file is opened twice — once by openpyxl for the name and once by ",[14,1898,1726],{}," — which is fine for small workbooks. For large ones, read the cells from the workbook you already have open instead.",[48,1901,1903],{"id":1902},"common-pitfalls","Common pitfalls",[1433,1905,1906,1918,1935,1955,1969,1979],{},[1436,1907,1908,1913,1914,1917],{},[1439,1909,1910,1911,46],{},"Looking only at ",[14,1912,262],{}," Sheet-scoped names are on ",[14,1915,1916],{},"ws.defined_names",". A missing name may simply be local to a sheet.",[1436,1919,1920,1442,1923,689,1925,1927,1928,1931,1932,1934],{},[1439,1921,1922],{},"Code written for openpyxl before 3.1.",[14,1924,466],{},[14,1926,470],{}," from older answers raise ",[14,1929,1930],{},"AttributeError"," on current versions; ",[14,1933,262],{}," is now a plain dictionary keyed by name.",[1436,1936,1937,1940,1941,1943,1944,1947,1948,1950,1951,480],{},[1439,1938,1939],{},"Reading formulas instead of values."," Without ",[14,1942,474],{},", cells with formulas return strings like ",[14,1945,1946],{},"=B5*1.2",". With it, they return the last value Excel saved — which is ",[14,1949,1162],{}," for files written by Python and never opened in Excel (see ",[42,1952,1954],{"href":1953},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel",[1436,1956,1957,1960,1961,1964,1965,1968],{},[1439,1958,1959],{},"Assuming the first row is a header."," A name like ",[14,1962,1963],{},"Rates"," might cover only the numbers. Pass ",[14,1966,1967],{},"header=False"," and set column names yourself when the range has no header row.",[1436,1970,1971,1974,1975,1978],{},[1439,1972,1973],{},"Hard-coding the header row."," The point of the name is that the block can move. If the script also passes ",[14,1976,1977],{},"skiprows=3",", it breaks the first time someone inserts a row — derive every offset from the name, as in Step 5.",[1436,1980,1981,1984,1985,1987,1988,1990],{},[1439,1982,1983],{},"Names with the same spelling in two scopes."," A workbook-level ",[14,1986,104],{}," and a sheet-level ",[14,1989,104],{}," can coexist. Excel formulas on that sheet use the local one; decide explicitly which one your script means rather than taking whichever lookup succeeds first.",[48,1992,1994],{"id":1993},"performance-notes","Performance notes",[10,1996,1997,1998,2000,2001,2004,2005,2009],{},"Read-only mode supports everything on this page: ",[14,1999,262],{},", the sheet-scoped names on each worksheet, and indexing a worksheet with a range. Resolving a name costs nothing; the work is loading the workbook. For a large file where only one named block matters, ",[14,2002,2003],{},"load_workbook(..., read_only=True, data_only=True)"," streams the sheet and indexing a range reads only the rows it covers. When you need the same range from hundreds of files, ",[42,2006,2008],{"href":2007},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fprocess-multiple-excel-files-in-parallel-with-python\u002F","process them in parallel"," and return small DataFrames from each worker.",[48,2011,2013],{"id":2012},"conclusion","Conclusion",[10,2015,2016,2017,2019,2020,2022,2023,2025,2026,2028,2029,2031,2032,2034],{},"Named ranges are the most robust handle a script can have on a block of cells. Resolve the name with openpyxl — ",[14,2018,262],{}," for workbook scope, ",[14,2021,1916],{}," for sheet scope — take ",[14,2024,489],{},", index the worksheet with the reference, and build the DataFrame from the first row and the rest. Check for multiple areas, ",[14,2027,1465],{}," targets and formula-based names before trusting the result, and fall back to ",[14,2030,1882],{}," plus ",[14,2033,1726],{}," when you need its parsing options.",[48,2036,2038],{"id":2037},"frequently-asked-questions","Frequently asked questions",[10,2040,2041,2044],{},[1439,2042,2043],{},"Can pandas read_excel read a named range directly?","\nNo. read_excel takes a sheet name plus skiprows, nrows and usecols, but no range name. Resolve the name with openpyxl first, then either build the DataFrame from the cells or translate the reference into read_excel arguments with range_boundaries.",[10,2046,2047,2050],{},[1439,2048,2049],{},"Why does wb.defined_names not contain a name I can see in Excel?","\nIt is probably scoped to one sheet. In Excel's Name Manager the Scope column shows it; in openpyxl it is in that worksheet's defined_names, not the workbook's. Hidden names and reserved names such as _xlnm.Print_Area are also stored differently.",[10,2052,2053,2056],{},[1439,2054,2055],{},"How do I read a dynamic named range defined with OFFSET?","\nopenpyxl does not evaluate formulas, so a name defined with OFFSET or INDEX has no fixed destination. Read from the anchor cell until the first empty row, or convert the block to an Excel table, which records its current size in the file and can be read by name.",[10,2058,2059,2062],{},[1439,2060,2061],{},"How do I read a named range that covers several areas?","\nLoop over dn.destinations, which yields one (sheet, reference) pair per area, read each area's cells, and concatenate the results. Unpacking a single pair raises a ValueError when there is more than one area.",[48,2064,2066],{"id":2065},"related","Related",[1433,2068,2069,2075,2082,2089],{},[1436,2070,2071,2072,2074],{},"Up one level: ",[42,2073,45],{"href":44}," — every option that decides what lands in the DataFrame.",[1436,2076,2077,2081],{},[42,2078,2080],{"href":2079},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002F","Create a Named Range in Excel with openpyxl"," — the writing side of the same API.",[1436,2083,2084,2088],{},[42,2085,2087],{"href":2086},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-cell-range-into-a-pandas-dataframe\u002F","Read a Cell Range into a pandas DataFrame"," — when the block has an address but no name.",[1436,2090,2091,2093],{},[42,2092,1489],{"href":1488}," — tables resize themselves, names do not.",[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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}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_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":58,"searchDepth":297,"depth":297,"links":2099},[2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110],{"id":50,"depth":297,"text":51},{"id":255,"depth":297,"text":256},{"id":483,"depth":297,"text":484},{"id":1017,"depth":297,"text":1018},{"id":1200,"depth":297,"text":1201},{"id":1719,"depth":297,"text":1720},{"id":1902,"depth":297,"text":1903},{"id":1993,"depth":297,"text":1994},{"id":2012,"depth":297,"text":2013},{"id":2037,"depth":297,"text":2038},{"id":2065,"depth":297,"text":2066},"2026-09-29","Load the cells behind an Excel named range into a DataFrame: look the name up with openpyxl, resolve workbook- and sheet-scoped names, handle ranges that span several areas or point at #REF!, and read named ranges from files you cannot edit.","md",[2115,2117,2119,2121],{"q":2043,"a":2116},"No. read_excel takes a sheet name plus skiprows, nrows and usecols, but no range name. Resolve the name with openpyxl first, then either build the DataFrame from the cells or translate the reference into read_excel arguments with range_boundaries.",{"q":2049,"a":2118},"It is probably scoped to one sheet. In Excel's Name Manager the Scope column shows it; in openpyxl it is in that worksheet's defined_names, not the workbook's. Hidden names and reserved names such as _xlnm.Print_Area are also stored differently.",{"q":2055,"a":2120},"openpyxl does not evaluate formulas, so a name defined with OFFSET or INDEX has no fixed destination. Read from the anchor cell until the first empty row, or convert the block to an Excel table, which records its current size in the file and can be read by name.",{"q":2061,"a":2122},"Loop over dn.destinations, which yields one (sheet, reference) pair per area, read each area's cells, and concatenate the results. Unpacking a single pair raises a ValueError when there is more than one area.","read a named range into pandas",null,{"breadcrumb":2126},[2127,2130,2133],{"name":2128,"item":2129},"Home","\u002F",{"name":2131,"item":2132},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":2134,"item":44},"Reading Excel Files with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas",{"title":2137,"description":2138},"Read an Excel Named Range into a pandas DataFrame","pandas has no named-range argument, so resolve the name with openpyxl: wb.defined_names, sheet-scoped names, destinations, multi-area ranges, dynamic OFFSET names and #REF! errors, then build the DataFrame.","read-a-named-range-into-pandas","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas\u002Findex",[2142,2143,2144],"reading-excel","excel-formulas","openpyxl","how-to","n0Pm0CXAMpr3M7YpT57zIZn6FFHO9PfKG-EjBuWddSg",[2148,2152],{"title":2149,"path":2150,"stem":2151,"children":-1},"Read a Cell Range Like B3:F20 into a pandas DataFrame","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-cell-range-into-a-pandas-dataframe","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-cell-range-into-a-pandas-dataframe\u002Findex",{"title":2153,"path":2154,"stem":2155,"children":-1},"Read an Excel File That Is Open in Excel","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel\u002Findex",[2157,2159],{"path":2158,"title":2131},"\u002Fgetting-started-with-python-excel-automation",{"path":2160,"title":2134},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas",[],[2163,2166,2169,2172,2175,2178],{"path":2164,"title":2165,"kind":2145},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-an-excel-file-from-a-url-or-bytes-in-python","Read an Excel File from a URL or Bytes in Python",{"path":2167,"title":2168,"kind":2145},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl","Find the Last Row with Data in openpyxl",{"path":2170,"title":2171,"kind":2145},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl","Iterate over Rows and Columns with openpyxl",{"path":2173,"title":2174,"kind":2145},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl","Read a Cell Value From Excel With openpyxl",{"path":2176,"title":2177,"kind":2145},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas","Standardise Excel Column Names with pandas",{"path":2179,"title":2180,"kind":2181},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets","Extracting Data from Messy Excel Sheets","guide",1790972065125]