[{"data":1,"prerenderedAt":1970},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes":1937,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes":1945,"children:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes":1950,"related:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes":1951},{"id":4,"title":5,"body":6,"dateModified":1903,"datePublished":1903,"description":1904,"extension":1905,"faq":1906,"keyword":1915,"kind":1916,"meta":1917,"navigation":263,"path":1926,"seo":1927,"slug":1930,"stem":1931,"topics":1932,"type":1935,"__hash__":1936},"docs\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes\u002Findex.md","Speed Up xlwings with Bulk Reads and Writes",{"type":7,"value":8,"toc":1888},"minimark",[9,28,186,191,194,221,228,232,235,429,432,436,446,552,562,677,681,688,856,871,875,878,1096,1099,1242,1246,1253,1337,1340,1344,1347,1465,1468,1472,1475,1734,1744,1748,1760,1764,1810,1814,1823,1827,1833,1839,1845,1851,1855,1884],[10,11,12,13,17,18,21,22,27],"p",{},"xlwings makes a running Excel workbook feel like a Python object: ",[14,15,16],"code",{},"sheet[\"B2\"].value = 42"," and the cell changes on screen. That convenience hides the cost that makes most slow xlwings scripts slow. Every ",[14,19,20],{},".value"," read or write is a call from your Python process into the Excel process — across a process boundary, through COM on Windows or AppleScript on macOS. One call is quick. Ten thousand calls in a loop are not. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002F","Automating Excel With xlwings: The Basics",", replaces cell loops with bulk transfers and turns off the Excel features that slow down large updates.",[29,30,38,39,38,43,38,47,38,55,38,65,38,76,38,83,38,90,38,96,38,103,38,107,38,112,38,116,38,121,38,125,38,131,38,136,38,143,38,148,38,152,38,156,38,162,38,165,38,170,38,173,38,178,38,181],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"4 0 752 233","img",[34,35],"xw-calls-t","xw-calls-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  ",[40,41,42],"title",{"id":34},"Cell-by-cell versus bulk transfers between Python and Excel",[44,45,46],"desc",{"id":35},"Looping over 10,000 cells makes 10,000 separate calls into the Excel process, each paying the cross-process overhead. Reading or writing the same 10,000 cells as one range makes a single call that moves all the values at once.",[48,49],"rect",{"x":50,"y":51,"width":52,"height":53,"fill":54},"4","0","752","233","#ffffff",[48,56],{"x":57,"y":58,"width":59,"height":60,"rx":61,"fill":62,"stroke":63,"style":64},"20.0","16.0","351.0","201.0","14","#fee8f2","var(--accent,#f43f8f)","stroke-width:2px",[66,67,69],"g",{"transform":68},"translate(182.5,29.0) scale(1.083)",[70,71],"path",{"d":72,"fill":73,"stroke":74,"style":75},"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",[77,78,82],"text",{"x":79,"y":80,"style":81},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Cell by cell",[84,85],"line",{"x1":86,"y1":87,"x2":88,"y2":87,"stroke":63,"style":89},"36.0","91.0","355.0","stroke-width:1px",[77,91,95],{"x":92,"y":93,"style":94},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","one call per cell",[66,97,99],{"transform":98},"translate(38.5,99.5) scale(0.625)",[70,100],{"d":101,"fill":73,"stroke":63,"style":102},"M6 6l12 12 M18 6L6 18","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[77,104,106],{"x":92,"y":105,"style":94},"137.0","10,000 cells = 10,000 round trips",[66,108,110],{"transform":109},"translate(38.5,125.5) scale(0.625)",[70,111],{"d":101,"fill":73,"stroke":63,"style":102},[77,113,115],{"x":92,"y":114,"style":94},"163.0","Excel redraws after each change",[66,117,119],{"transform":118},"translate(38.5,151.5) scale(0.625)",[70,120],{"d":101,"fill":73,"stroke":63,"style":102},[77,122,124],{"x":92,"y":123,"style":94},"189.0","fine for a handful of cells",[66,126,128],{"transform":127},"translate(38.5,177.5) scale(0.625)",[70,129],{"d":72,"fill":73,"stroke":130,"style":102},"var(--gold,#b4740a)",[48,132],{"x":133,"y":58,"width":59,"height":60,"rx":61,"fill":134,"stroke":135,"style":64},"389.0","#d9f4f1","var(--teal,#0f9488)",[66,137,139],{"transform":138},"translate(551.5,29.0) scale(1.083)",[70,140],{"d":141,"fill":73,"stroke":142,"style":75},"M13 2L4 14h7l-1 8 9-12h-7z","var(--teal-ink,#0b6157)",[77,144,147],{"x":145,"y":80,"style":146},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Whole range",[84,149],{"x1":150,"y1":87,"x2":151,"y2":87,"stroke":135,"style":89},"405.0","724.0",[77,153,155],{"x":154,"y":93,"style":94},"429.0","one call per range",[66,157,159],{"transform":158},"translate(407.5,99.5) scale(0.625)",[70,160],{"d":161,"fill":73,"stroke":135,"style":102},"M5 12.5l4.5 4.5L19 7",[77,163,164],{"x":154,"y":105,"style":94},"10,000 cells = 1 round trip",[66,166,168],{"transform":167},"translate(407.5,125.5) scale(0.625)",[70,169],{"d":161,"fill":73,"stroke":135,"style":102},[77,171,172],{"x":154,"y":114,"style":94},"values arrive as a 2D list",[66,174,176],{"transform":175},"translate(407.5,151.5) scale(0.625)",[70,177],{"d":161,"fill":73,"stroke":135,"style":102},[77,179,180],{"x":154,"y":123,"style":94},"or straight into a DataFrame",[66,182,184],{"transform":183},"translate(407.5,177.5) scale(0.625)",[70,185],{"d":161,"fill":73,"stroke":135,"style":102},[187,188,190],"h2",{"id":189},"prerequisites","Prerequisites",[10,192,193],{},"xlwings and a desktop Excel on Windows or macOS:",[195,196,201],"pre",{"className":197,"code":198,"language":199,"meta":200,"style":200},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlwings pandas\n","bash","",[14,202,203],{"__ignoreMap":200},[204,205,207,211,215,218],"span",{"class":84,"line":206},1,[204,208,210],{"class":209},"sMTad","pip",[204,212,214],{"class":213},"srMev"," install",[204,216,217],{"class":213}," xlwings",[204,219,220],{"class":213}," pandas\n",[10,222,223,224,227],{},"The examples assume a workbook with a sales table starting in cell A1 of a sheet named ",[14,225,226],{},"Data",".",[187,229,231],{"id":230},"step-1-recognise-the-slow-pattern","Step 1 — Recognise the slow pattern",[10,233,234],{},"This is the pattern to look for. It reads a column one cell at a time and writes a result back one cell at a time — two calls into Excel per row:",[195,236,240],{"className":237,"code":238,"language":239,"meta":200,"style":200},"language-python shiki shiki-themes github-light github-dark-high-contrast","import xlwings as xw\n\nwb = xw.Book(\"sales.xlsx\")\nsheet = wb.sheets[\"Data\"]\nlast = sheet.range(\"A1\").end(\"down\").row\nfor row in range(2, last + 1):\n    amount = sheet.range(f\"C{row}\").value          # one call\n    sheet.range(f\"D{row}\").value = amount * 1.2    # another call\n","python",[14,241,242,258,265,283,300,323,357,393],{"__ignoreMap":200},[204,243,244,248,252,255],{"class":84,"line":206},[204,245,247],{"class":246},"s-kum","import",[204,249,251],{"class":250},"skGVy"," xlwings ",[204,253,254],{"class":246},"as",[204,256,257],{"class":250}," xw\n",[204,259,261],{"class":84,"line":260},2,[204,262,264],{"emptyLinePlaceholder":263},true,"\n",[204,266,268,271,274,277,280],{"class":84,"line":267},3,[204,269,270],{"class":250},"wb ",[204,272,273],{"class":246},"=",[204,275,276],{"class":250}," xw.Book(",[204,278,279],{"class":213},"\"sales.xlsx\"",[204,281,282],{"class":250},")\n",[204,284,286,289,291,294,297],{"class":84,"line":285},4,[204,287,288],{"class":250},"sheet ",[204,290,273],{"class":246},[204,292,293],{"class":250}," wb.sheets[",[204,295,296],{"class":213},"\"Data\"",[204,298,299],{"class":250},"]\n",[204,301,303,306,308,311,314,317,320],{"class":84,"line":302},5,[204,304,305],{"class":250},"last ",[204,307,273],{"class":246},[204,309,310],{"class":250}," sheet.range(",[204,312,313],{"class":213},"\"A1\"",[204,315,316],{"class":250},").end(",[204,318,319],{"class":213},"\"down\"",[204,321,322],{"class":250},").row\n",[204,324,326,329,332,335,339,342,345,348,351,354],{"class":84,"line":325},6,[204,327,328],{"class":246},"for",[204,330,331],{"class":250}," row ",[204,333,334],{"class":246},"in",[204,336,338],{"class":337},"sP0c6"," range",[204,340,341],{"class":250},"(",[204,343,344],{"class":337},"2",[204,346,347],{"class":250},", last ",[204,349,350],{"class":246},"+",[204,352,353],{"class":337}," 1",[204,355,356],{"class":250},"):\n",[204,358,360,363,365,367,370,373,377,380,383,386,389],{"class":84,"line":359},7,[204,361,362],{"class":250},"    amount ",[204,364,273],{"class":246},[204,366,310],{"class":250},[204,368,369],{"class":246},"f",[204,371,372],{"class":213},"\"C",[204,374,376],{"class":375},"sSjpA","{",[204,378,379],{"class":250},"row",[204,381,382],{"class":375},"}",[204,384,385],{"class":213},"\"",[204,387,388],{"class":250},").value          ",[204,390,392],{"class":391},"s-wDw","# one call\n",[204,394,396,399,401,404,406,408,410,412,415,417,420,423,426],{"class":84,"line":395},8,[204,397,398],{"class":250},"    sheet.range(",[204,400,369],{"class":246},[204,402,403],{"class":213},"\"D",[204,405,376],{"class":375},[204,407,379],{"class":250},[204,409,382],{"class":375},[204,411,385],{"class":213},[204,413,414],{"class":250},").value ",[204,416,273],{"class":246},[204,418,419],{"class":250}," amount ",[204,421,422],{"class":246},"*",[204,424,425],{"class":337}," 1.2",[204,427,428],{"class":391},"    # another call\n",[10,430,431],{},"On a few dozen rows nobody notices. On ten thousand rows it takes long enough that people assume Excel has hung. The logic is trivial; the time is all in the round trips.",[187,433,435],{"id":434},"step-2-read-and-write-whole-ranges","Step 2 — Read and write whole ranges",[10,437,438,439,442,443,445],{},"The fix is to move the data once in each direction and do the work in Python. ",[14,440,441],{},"expand()"," finds the contiguous block from a starting cell, and ",[14,444,20],{}," on a multi-cell range returns a list of rows:",[195,447,449],{"className":237,"code":448,"language":239,"meta":200,"style":200},"data = sheet.range(\"A1\").expand().value          # one call: list of rows, header first\nheader, rows = data[0], data[1:]\ncol = header.index(\"amount\")\nresults = [[r[col] * 1.2] for r in rows]          # plain Python, no Excel calls\nsheet.range(\"D2\").value = results                 # one call writes the whole column\n",[14,450,451,468,489,504,534],{"__ignoreMap":200},[204,452,453,456,458,460,462,465],{"class":84,"line":206},[204,454,455],{"class":250},"data ",[204,457,273],{"class":246},[204,459,310],{"class":250},[204,461,313],{"class":213},[204,463,464],{"class":250},").expand().value          ",[204,466,467],{"class":391},"# one call: list of rows, header first\n",[204,469,470,473,475,478,480,483,486],{"class":84,"line":260},[204,471,472],{"class":250},"header, rows ",[204,474,273],{"class":246},[204,476,477],{"class":250}," data[",[204,479,51],{"class":337},[204,481,482],{"class":250},"], data[",[204,484,485],{"class":337},"1",[204,487,488],{"class":250},":]\n",[204,490,491,494,496,499,502],{"class":84,"line":267},[204,492,493],{"class":250},"col ",[204,495,273],{"class":246},[204,497,498],{"class":250}," header.index(",[204,500,501],{"class":213},"\"amount\"",[204,503,282],{"class":250},[204,505,506,509,511,514,516,518,521,523,526,528,531],{"class":84,"line":285},[204,507,508],{"class":250},"results ",[204,510,273],{"class":246},[204,512,513],{"class":250}," [[r[col] ",[204,515,422],{"class":246},[204,517,425],{"class":337},[204,519,520],{"class":250},"] ",[204,522,328],{"class":246},[204,524,525],{"class":250}," r ",[204,527,334],{"class":246},[204,529,530],{"class":250}," rows]          ",[204,532,533],{"class":391},"# plain Python, no Excel calls\n",[204,535,536,539,542,544,546,549],{"class":84,"line":302},[204,537,538],{"class":250},"sheet.range(",[204,540,541],{"class":213},"\"D2\"",[204,543,414],{"class":250},[204,545,273],{"class":246},[204,547,548],{"class":250}," results                 ",[204,550,551],{"class":391},"# one call writes the whole column\n",[10,553,554,555,558,559,227],{},"Writing a nested list fills a block starting at the given cell: a list of one-element lists fills a column, a list of lists fills a rectangle. Writing a flat list fills a ",[556,557,379],"em",{},"; to write a flat list down a column instead, use ",[14,560,561],{},".options(transpose=True)",[29,563,38,568,38,571,38,574,38,577,38,583,38,591,38,597,38,602,38,609,38,614,38,619,38,621,38,626,38,631,38,639,38,645,38,650,38,653,38,658,38,663,38,666,38,669,38,673],{"viewBox":564,"role":32,"ariaLabelledBy":565,"xmlns":36,"style":37},"4 0 752 274",[566,567],"xw-seq-t","xw-seq-d",[40,569,570],{"id":566},"Round trips for a bulk update",[44,572,573],{"id":567},"Python sends one request to read the whole range, Excel returns all the values at once, Python computes the results locally, and a second request writes the whole result column back. Two round trips instead of two per row.",[48,575],{"x":50,"y":51,"width":52,"height":576,"fill":54},"274",[48,578],{"x":57,"y":58,"width":579,"height":80,"rx":580,"fill":581,"stroke":582,"style":64},"180.0","12","#f0f4ff","var(--brand,#5b5cf0)",[66,584,586],{"transform":585},"translate(98.0,30.5) scale(1.000)",[70,587],{"d":588,"fill":73,"stroke":589,"style":590},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[77,592,596],{"x":593,"y":594,"style":595},"110.0","76.5","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Python script",[48,598],{"x":599,"y":58,"width":579,"height":80,"rx":580,"fill":600,"stroke":601,"style":64},"560.0","#e7ebef","var(--line,#cdd5e6)",[66,603,605],{"transform":604},"translate(638.0,30.5) scale(1.000)",[70,606],{"d":607,"fill":73,"stroke":608,"style":590},"M3 5.5l8-1.2v7.2H3z M13 4l8-1.2v8.7h-8z M3 13.5h8v7.2l-8-1.2z M13 13.5h8v8.7l-8-1.2z","var(--text,#172033)",[77,610,613],{"x":611,"y":594,"style":612},"650.0","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Excel",[84,615],{"x1":593,"y1":616,"x2":593,"y2":617,"stroke":601,"style":618},"94.0","258.0","stroke-width:1.5px;stroke-dasharray:5 4",[84,620],{"x1":611,"y1":616,"x2":611,"y2":617,"stroke":601,"style":618},[84,622],{"x1":623,"y1":624,"x2":625,"y2":624,"stroke":582,"style":64},"115.0","134.0","641.0",[627,628],"polygon",{"points":629,"fill":630},"645.0,134.0 636.0,139.0 636.0,129.0","#5b5cf0",[48,632],{"x":633,"y":634,"width":635,"height":636,"rx":637,"fill":54,"stroke":601,"style":638},"315.1","105.0","129.8","21.0","10","stroke-width:1.2px",[77,640,644],{"x":641,"y":642,"style":643},"380.0","119.5","font-size:11.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","1. read A1 expand()",[84,646],{"x1":647,"y1":648,"x2":649,"y2":648,"stroke":582,"style":64},"645.0","182.0","119.0",[627,651],{"points":652,"fill":630},"115.0,182.0 124.0,177.0 124.0,187.0",[48,654],{"x":655,"y":656,"width":657,"height":636,"rx":637,"fill":134,"stroke":135,"style":638},"307.7","153.0","144.6",[77,659,662],{"x":641,"y":660,"style":661},"167.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","2. all rows as a 2D list",[84,664],{"x1":623,"y1":665,"x2":625,"y2":665,"stroke":582,"style":64},"230.0",[627,667],{"points":668,"fill":630},"645.0,230.0 636.0,235.0 636.0,225.0",[48,670],{"x":671,"y":60,"width":672,"height":636,"rx":637,"fill":54,"stroke":601,"style":638},"284.2","191.7",[77,674,676],{"x":641,"y":675,"style":643},"215.5","3. write D2:D10001 in one call",[187,678,680],{"id":679},"step-3-go-straight-to-pandas","Step 3 — Go straight to pandas",[10,682,683,684,687],{},"For table-shaped data, the ",[14,685,686],{},"options"," converter reads a block directly into a DataFrame and writes one back, still in a single call each way:",[195,689,691],{"className":237,"code":690,"language":239,"meta":200,"style":200},"import pandas as pd\n\ndf = sheet.range(\"A1\").options(pd.DataFrame, header=1, index=False, expand=\"table\").value\ndf[\"amount_incl_tax\"] = df[\"amount\"] * 1.2\nsummary = df.groupby(\"region\", as_index=False)[\"amount_incl_tax\"].sum()\n\nout = wb.sheets[\"Summary\"]\nout.clear_contents()\nout.range(\"A1\").options(index=False).value = summary\n",[14,692,693,705,709,755,779,809,813,827,832],{"__ignoreMap":200},[204,694,695,697,700,702],{"class":84,"line":206},[204,696,247],{"class":246},[204,698,699],{"class":250}," pandas ",[204,701,254],{"class":246},[204,703,704],{"class":250}," pd\n",[204,706,707],{"class":84,"line":260},[204,708,264],{"emptyLinePlaceholder":263},[204,710,711,714,716,718,720,723,727,729,731,734,737,739,742,744,747,749,752],{"class":84,"line":267},[204,712,713],{"class":250},"df ",[204,715,273],{"class":246},[204,717,310],{"class":250},[204,719,313],{"class":213},[204,721,722],{"class":250},").options(pd.DataFrame, ",[204,724,726],{"class":725},"sa561","header",[204,728,273],{"class":246},[204,730,485],{"class":337},[204,732,733],{"class":250},", ",[204,735,736],{"class":725},"index",[204,738,273],{"class":246},[204,740,741],{"class":337},"False",[204,743,733],{"class":250},[204,745,746],{"class":725},"expand",[204,748,273],{"class":246},[204,750,751],{"class":213},"\"table\"",[204,753,754],{"class":250},").value\n",[204,756,757,760,763,765,767,770,772,774,776],{"class":84,"line":285},[204,758,759],{"class":250},"df[",[204,761,762],{"class":213},"\"amount_incl_tax\"",[204,764,520],{"class":250},[204,766,273],{"class":246},[204,768,769],{"class":250}," df[",[204,771,501],{"class":213},[204,773,520],{"class":250},[204,775,422],{"class":246},[204,777,778],{"class":337}," 1.2\n",[204,780,781,784,786,789,792,794,797,799,801,804,806],{"class":84,"line":302},[204,782,783],{"class":250},"summary ",[204,785,273],{"class":246},[204,787,788],{"class":250}," df.groupby(",[204,790,791],{"class":213},"\"region\"",[204,793,733],{"class":250},[204,795,796],{"class":725},"as_index",[204,798,273],{"class":246},[204,800,741],{"class":337},[204,802,803],{"class":250},")[",[204,805,762],{"class":213},[204,807,808],{"class":250},"].sum()\n",[204,810,811],{"class":84,"line":325},[204,812,264],{"emptyLinePlaceholder":263},[204,814,815,818,820,822,825],{"class":84,"line":359},[204,816,817],{"class":250},"out ",[204,819,273],{"class":246},[204,821,293],{"class":250},[204,823,824],{"class":213},"\"Summary\"",[204,826,299],{"class":250},[204,828,829],{"class":84,"line":395},[204,830,831],{"class":250},"out.clear_contents()\n",[204,833,835,838,840,843,845,847,849,851,853],{"class":84,"line":834},9,[204,836,837],{"class":250},"out.range(",[204,839,313],{"class":213},[204,841,842],{"class":250},").options(",[204,844,736],{"class":725},[204,846,273],{"class":246},[204,848,741],{"class":337},[204,850,414],{"class":250},[204,852,273],{"class":246},[204,854,855],{"class":250}," summary\n",[10,857,858,861,862,865,866,870],{},[14,859,860],{},"header=1"," treats the first row as column names; ",[14,863,864],{},"index=False"," stops xlwings from using the first column as the index on read and from writing the index on write. Doing the transformation in pandas rather than cell by cell is the same shift described for openpyxl in ",[23,867,869],{"href":868},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002F","Speed Up openpyxl with read_only Mode",": move data out, compute, move it back.",[187,872,874],{"id":873},"step-4-stop-excel-from-redrawing-and-recalculating","Step 4 — Stop Excel from redrawing and recalculating",[10,876,877],{},"When a script makes many changes — writing several ranges, formatting, inserting rows — Excel by default repaints the window, fires workbook events and recalculates dependent formulas after each one. Turn those off for the duration of the update and restore them afterwards, even if the script fails. A context manager makes that automatic:",[195,879,881],{"className":237,"code":880,"language":239,"meta":200,"style":200},"from contextlib import contextmanager\n\n@contextmanager\ndef fast_mode(app):\n    previous = (app.screen_updating, app.enable_events, app.calculation)\n    app.screen_updating = False\n    app.enable_events = False\n    app.calculation = \"manual\"\n    try:\n        yield app\n    finally:\n        app.calculation = previous[2]\n        app.enable_events = previous[1]\n        app.screen_updating = previous[0]\n\nwith fast_mode(wb.app):\n    out.range(\"A1\").options(index=False).value = summary\n    out.range(\"A1\").expand(\"right\").font.bold = True\n    out.autofit()\nwb.app.calculate()          # one recalculation at the end\n",[14,882,883,896,900,906,917,927,937,946,956,964,973,981,996,1010,1024,1029,1038,1060,1081,1087],{"__ignoreMap":200},[204,884,885,888,891,893],{"class":84,"line":206},[204,886,887],{"class":246},"from",[204,889,890],{"class":250}," contextlib ",[204,892,247],{"class":246},[204,894,895],{"class":250}," contextmanager\n",[204,897,898],{"class":84,"line":260},[204,899,264],{"emptyLinePlaceholder":263},[204,901,902],{"class":84,"line":267},[204,903,905],{"class":904},"s_Opv","@contextmanager\n",[204,907,908,911,914],{"class":84,"line":285},[204,909,910],{"class":246},"def",[204,912,913],{"class":904}," fast_mode",[204,915,916],{"class":250},"(app):\n",[204,918,919,922,924],{"class":84,"line":302},[204,920,921],{"class":250},"    previous ",[204,923,273],{"class":246},[204,925,926],{"class":250}," (app.screen_updating, app.enable_events, app.calculation)\n",[204,928,929,932,934],{"class":84,"line":325},[204,930,931],{"class":250},"    app.screen_updating ",[204,933,273],{"class":246},[204,935,936],{"class":337}," False\n",[204,938,939,942,944],{"class":84,"line":359},[204,940,941],{"class":250},"    app.enable_events ",[204,943,273],{"class":246},[204,945,936],{"class":337},[204,947,948,951,953],{"class":84,"line":395},[204,949,950],{"class":250},"    app.calculation ",[204,952,273],{"class":246},[204,954,955],{"class":213}," \"manual\"\n",[204,957,958,961],{"class":84,"line":834},[204,959,960],{"class":246},"    try",[204,962,963],{"class":250},":\n",[204,965,967,970],{"class":84,"line":966},10,[204,968,969],{"class":246},"        yield",[204,971,972],{"class":250}," app\n",[204,974,976,979],{"class":84,"line":975},11,[204,977,978],{"class":246},"    finally",[204,980,963],{"class":250},[204,982,984,987,989,992,994],{"class":84,"line":983},12,[204,985,986],{"class":250},"        app.calculation ",[204,988,273],{"class":246},[204,990,991],{"class":250}," previous[",[204,993,344],{"class":337},[204,995,299],{"class":250},[204,997,999,1002,1004,1006,1008],{"class":84,"line":998},13,[204,1000,1001],{"class":250},"        app.enable_events ",[204,1003,273],{"class":246},[204,1005,991],{"class":250},[204,1007,485],{"class":337},[204,1009,299],{"class":250},[204,1011,1013,1016,1018,1020,1022],{"class":84,"line":1012},14,[204,1014,1015],{"class":250},"        app.screen_updating ",[204,1017,273],{"class":246},[204,1019,991],{"class":250},[204,1021,51],{"class":337},[204,1023,299],{"class":250},[204,1025,1027],{"class":84,"line":1026},15,[204,1028,264],{"emptyLinePlaceholder":263},[204,1030,1032,1035],{"class":84,"line":1031},16,[204,1033,1034],{"class":246},"with",[204,1036,1037],{"class":250}," fast_mode(wb.app):\n",[204,1039,1041,1044,1046,1048,1050,1052,1054,1056,1058],{"class":84,"line":1040},17,[204,1042,1043],{"class":250},"    out.range(",[204,1045,313],{"class":213},[204,1047,842],{"class":250},[204,1049,736],{"class":725},[204,1051,273],{"class":246},[204,1053,741],{"class":337},[204,1055,414],{"class":250},[204,1057,273],{"class":246},[204,1059,855],{"class":250},[204,1061,1063,1065,1067,1070,1073,1076,1078],{"class":84,"line":1062},18,[204,1064,1043],{"class":250},[204,1066,313],{"class":213},[204,1068,1069],{"class":250},").expand(",[204,1071,1072],{"class":213},"\"right\"",[204,1074,1075],{"class":250},").font.bold ",[204,1077,273],{"class":246},[204,1079,1080],{"class":337}," True\n",[204,1082,1084],{"class":84,"line":1083},19,[204,1085,1086],{"class":250},"    out.autofit()\n",[204,1088,1090,1093],{"class":84,"line":1089},20,[204,1091,1092],{"class":250},"wb.app.calculate()          ",[204,1094,1095],{"class":391},"# one recalculation at the end\n",[10,1097,1098],{},"Restoring the previous values — rather than forcing them back on — matters when the script runs inside a larger macro or a user session that had its own settings. Calculating once at the end replaces the hundreds of recalculations Excel would otherwise run while the ranges were being written.",[29,1100,38,1105,38,1108,38,1111,38,1114,38,1117,38,1123,38,1129,38,1134,38,1141,38,1145,38,1150,38,1153,38,1157,38,1164,38,1169,38,1172,38,1176,38,1178,38,1181,38,1184,38,1187,38,1192,38,1197,38,1199,38,1203,38,1206,38,1210,38,1213,38,1216,38,1222,38,1228,38,1232,38,1236,38,1240],{"viewBox":1101,"role":32,"ariaLabelledBy":1102,"xmlns":36,"style":37},"4 0 752 150",[1103,1104],"xw-fast-t","xw-fast-d",[40,1106,1107],{"id":1103},"The fast-mode sequence around a bulk update",[44,1109,1110],{"id":1104},"Save the current settings, switch off screen updating, events and automatic calculation, perform the bulk reads and writes, restore the saved settings in a finally block, and trigger one recalculation.",[48,1112],{"x":50,"y":51,"width":52,"height":1113,"fill":54},"150",[48,1115],{"x":57,"y":58,"width":656,"height":1116,"rx":580,"fill":600,"stroke":601,"style":64},"118.5",[66,1118,1120],{"transform":1119},"translate(84.5,41.8) scale(1.000)",[70,1121],{"d":1122,"fill":73,"stroke":608,"style":590},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5",[77,1124,1128],{"x":1125,"y":1126,"style":1127},"96.5","88.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Save settings",[77,1130,1133],{"x":1125,"y":1131,"style":1132},"107.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","updating, events, calc",[1135,1136],"circle",{"cx":86,"cy":1137,"r":1138,"fill":1139,"stroke":73,"style":1140},"32.0","11.0","#5b6780","stroke-width:0px",[77,1142,485],{"x":86,"y":1143,"style":1144},"36.5","font-size:12px;font-weight:700;fill:#ffffff;text-anchor:middle",[84,1146],{"x1":1147,"y1":1148,"x2":1149,"y2":1148,"stroke":582,"style":64},"178.0","75.2","200.0",[627,1151],{"points":1152,"fill":630},"204.0,75.2 195.0,80.2 195.0,70.2",[48,1154],{"x":1155,"y":58,"width":656,"height":1116,"rx":580,"fill":1156,"stroke":130,"style":64},"209.0","#fdefd8",[66,1158,1160],{"transform":1159},"translate(273.5,41.8) scale(1.000)",[70,1161],{"d":1162,"fill":73,"stroke":1163,"style":590},"M13 2L4 14h7l-1 8 9-12h-7z M3 3l18 18","var(--gold-ink,#7a4e06)",[77,1165,1168],{"x":1166,"y":1126,"style":1167},"285.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Switch off",[77,1170,1171],{"x":1166,"y":1131,"style":1132},"redraw and recalc",[1135,1173],{"cx":1174,"cy":1137,"r":1138,"fill":1175,"stroke":73,"style":1140},"225.0","#8a5808",[77,1177,344],{"x":1174,"y":1143,"style":1144},[84,1179],{"x1":1180,"y1":1148,"x2":133,"y2":1148,"stroke":582,"style":64},"367.0",[627,1182],{"points":1183,"fill":630},"393.0,75.2 384.0,80.2 384.0,70.2",[48,1185],{"x":1186,"y":58,"width":656,"height":1116,"rx":580,"fill":581,"stroke":582,"style":64},"398.0",[66,1188,1190],{"transform":1189},"translate(462.5,41.8) scale(1.000)",[70,1191],{"d":141,"fill":73,"stroke":589,"style":590},[77,1193,1196],{"x":1194,"y":1126,"style":1195},"474.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Bulk transfer",[77,1198,155],{"x":1194,"y":1131,"style":1132},[1135,1200],{"cx":1201,"cy":1137,"r":1138,"fill":1202,"stroke":73,"style":1140},"414.0","#4338ca",[77,1204,1205],{"x":1201,"y":1143,"style":1144},"3",[84,1207],{"x1":1208,"y1":1148,"x2":1209,"y2":1148,"stroke":582,"style":64},"556.0","578.0",[627,1211],{"points":1212,"fill":630},"582.0,75.2 573.0,80.2 573.0,70.2",[48,1214],{"x":1215,"y":58,"width":656,"height":1116,"rx":580,"fill":134,"stroke":135,"style":64},"587.0",[66,1217,1219],{"transform":1218},"translate(651.5,33.0) scale(1.000)",[70,1220],{"d":1221,"fill":73,"stroke":142,"style":590},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[77,1223,1227],{"x":1224,"y":1225,"style":1226},"663.5","79.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Restore +",[77,1229,1231],{"x":1224,"y":1230,"style":1226},"97.0","calculate",[77,1233,1235],{"x":1224,"y":1234,"style":1132},"116.5","in finally",[1135,1237],{"cx":1238,"cy":1137,"r":1138,"fill":1239,"stroke":73,"style":1140},"603.0","#0b6157",[77,1241,50],{"x":1238,"y":1143,"style":1144},[187,1243,1245],{"id":1244},"step-5-chunk-very-large-ranges","Step 5 — Chunk very large ranges",[10,1247,1248,1249,1252],{},"A single call moving hundreds of thousands of cells can run into memory or COM limits and fail with an unhelpful error. The ",[14,1250,1251],{},"chunksize"," option splits the transfer into blocks of rows while keeping your code unchanged:",[195,1254,1256],{"className":237,"code":1255,"language":239,"meta":200,"style":200},"big = sheet.range(\"A1\").options(pd.DataFrame, header=1, index=False,\n                                expand=\"table\", chunksize=10_000).value\nout.range(\"A1\").options(index=False, chunksize=10_000).value = big\n",[14,1257,1258,1288,1308],{"__ignoreMap":200},[204,1259,1260,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285],{"class":84,"line":206},[204,1261,1262],{"class":250},"big ",[204,1264,273],{"class":246},[204,1266,310],{"class":250},[204,1268,313],{"class":213},[204,1270,722],{"class":250},[204,1272,726],{"class":725},[204,1274,273],{"class":246},[204,1276,485],{"class":337},[204,1278,733],{"class":250},[204,1280,736],{"class":725},[204,1282,273],{"class":246},[204,1284,741],{"class":337},[204,1286,1287],{"class":250},",\n",[204,1289,1290,1293,1295,1297,1299,1301,1303,1306],{"class":84,"line":260},[204,1291,1292],{"class":725},"                                expand",[204,1294,273],{"class":246},[204,1296,751],{"class":213},[204,1298,733],{"class":250},[204,1300,1251],{"class":725},[204,1302,273],{"class":246},[204,1304,1305],{"class":337},"10_000",[204,1307,754],{"class":250},[204,1309,1310,1312,1314,1316,1318,1320,1322,1324,1326,1328,1330,1332,1334],{"class":84,"line":267},[204,1311,837],{"class":250},[204,1313,313],{"class":213},[204,1315,842],{"class":250},[204,1317,736],{"class":725},[204,1319,273],{"class":246},[204,1321,741],{"class":337},[204,1323,733],{"class":250},[204,1325,1251],{"class":725},[204,1327,273],{"class":246},[204,1329,1305],{"class":337},[204,1331,414],{"class":250},[204,1333,273],{"class":246},[204,1335,1336],{"class":250}," big\n",[10,1338,1339],{},"Ten thousand rows per chunk is a reasonable starting point; wide tables may need smaller chunks. Chunking still moves the data in a handful of calls rather than one per cell, so it remains fast.",[187,1341,1343],{"id":1342},"step-6-format-ranges-not-cells","Step 6 — Format ranges, not cells",[10,1345,1346],{},"Formatting has the same trap as values. Setting a colour on each cell in a loop is one call per cell; setting it on the whole range is one call. Build the target range once and style it:",[195,1348,1350],{"className":237,"code":1349,"language":239,"meta":200,"style":200},"last_row = out.range(\"A1\").end(\"down\").row\nmoney = out.range(f\"B2:B{last_row}\")\nmoney.number_format = \"#,##0.00\"\nout.range(f\"A1:B1\").color = (67, 56, 202)\nout.range(f\"A1:B1\").font.color = (255, 255, 255)\n",[14,1351,1352,1370,1395,1405,1437],{"__ignoreMap":200},[204,1353,1354,1357,1359,1362,1364,1366,1368],{"class":84,"line":206},[204,1355,1356],{"class":250},"last_row ",[204,1358,273],{"class":246},[204,1360,1361],{"class":250}," out.range(",[204,1363,313],{"class":213},[204,1365,316],{"class":250},[204,1367,319],{"class":213},[204,1369,322],{"class":250},[204,1371,1372,1375,1377,1379,1381,1384,1386,1389,1391,1393],{"class":84,"line":260},[204,1373,1374],{"class":250},"money ",[204,1376,273],{"class":246},[204,1378,1361],{"class":250},[204,1380,369],{"class":246},[204,1382,1383],{"class":213},"\"B2:B",[204,1385,376],{"class":375},[204,1387,1388],{"class":250},"last_row",[204,1390,382],{"class":375},[204,1392,385],{"class":213},[204,1394,282],{"class":250},[204,1396,1397,1400,1402],{"class":84,"line":267},[204,1398,1399],{"class":250},"money.number_format ",[204,1401,273],{"class":246},[204,1403,1404],{"class":213}," \"#,##0.00\"\n",[204,1406,1407,1409,1411,1414,1417,1419,1422,1425,1427,1430,1432,1435],{"class":84,"line":285},[204,1408,837],{"class":250},[204,1410,369],{"class":246},[204,1412,1413],{"class":213},"\"A1:B1\"",[204,1415,1416],{"class":250},").color ",[204,1418,273],{"class":246},[204,1420,1421],{"class":250}," (",[204,1423,1424],{"class":337},"67",[204,1426,733],{"class":250},[204,1428,1429],{"class":337},"56",[204,1431,733],{"class":250},[204,1433,1434],{"class":337},"202",[204,1436,282],{"class":250},[204,1438,1439,1441,1443,1445,1448,1450,1452,1455,1457,1459,1461,1463],{"class":84,"line":302},[204,1440,837],{"class":250},[204,1442,369],{"class":246},[204,1444,1413],{"class":213},[204,1446,1447],{"class":250},").font.color ",[204,1449,273],{"class":246},[204,1451,1421],{"class":250},[204,1453,1454],{"class":337},"255",[204,1456,733],{"class":250},[204,1458,1454],{"class":337},[204,1460,733],{"class":250},[204,1462,1454],{"class":337},[204,1464,282],{"class":250},[10,1466,1467],{},"For formatting that depends on the values — highlighting negative numbers, for example — prefer an Excel conditional formatting rule over a Python loop that colours cells one by one: one rule covers the range and stays correct when the data changes.",[187,1469,1471],{"id":1470},"step-7-measure-it-on-your-own-machine","Step 7 — Measure it on your own machine",[10,1473,1474],{},"The gap between the two approaches depends on the machine, the Excel version and whether Excel is visible, so measure it rather than trusting anyone's numbers — including a quoted speed-up from a blog. A small harness times both versions of the same work on a scratch sheet:",[195,1476,1478],{"className":237,"code":1477,"language":239,"meta":200,"style":200},"import time\n\ndef timed(label, fn):\n    start = time.perf_counter()\n    fn()\n    print(f\"{label:>14}: {time.perf_counter() - start:.2f} s\")\n\nscratch = wb.sheets.add(\"Scratch\")\nn = 2_000\n\ndef per_cell():\n    for i in range(1, n + 1):\n        scratch.range(f\"A{i}\").value = i\n\ndef bulk():\n    scratch.range(\"B1\").value = [[i] for i in range(1, n + 1)]\n\ntimed(\"cell by cell\", per_cell)\ntimed(\"one call\", bulk)\nscratch.delete()\n",[14,1479,1480,1487,1491,1501,1511,1516,1561,1565,1580,1590,1594,1604,1629,1655,1659,1668,1704,1708,1719,1729],{"__ignoreMap":200},[204,1481,1482,1484],{"class":84,"line":206},[204,1483,247],{"class":246},[204,1485,1486],{"class":250}," time\n",[204,1488,1489],{"class":84,"line":260},[204,1490,264],{"emptyLinePlaceholder":263},[204,1492,1493,1495,1498],{"class":84,"line":267},[204,1494,910],{"class":246},[204,1496,1497],{"class":904}," timed",[204,1499,1500],{"class":250},"(label, fn):\n",[204,1502,1503,1506,1508],{"class":84,"line":285},[204,1504,1505],{"class":250},"    start ",[204,1507,273],{"class":246},[204,1509,1510],{"class":250}," time.perf_counter()\n",[204,1512,1513],{"class":84,"line":302},[204,1514,1515],{"class":250},"    fn()\n",[204,1517,1518,1521,1523,1525,1527,1529,1532,1535,1537,1540,1542,1545,1548,1551,1554,1556,1559],{"class":84,"line":325},[204,1519,1520],{"class":337},"    print",[204,1522,341],{"class":250},[204,1524,369],{"class":246},[204,1526,385],{"class":213},[204,1528,376],{"class":375},[204,1530,1531],{"class":250},"label",[204,1533,1534],{"class":246},":>14",[204,1536,382],{"class":375},[204,1538,1539],{"class":213},": ",[204,1541,376],{"class":375},[204,1543,1544],{"class":250},"time.perf_counter() ",[204,1546,1547],{"class":246},"-",[204,1549,1550],{"class":250}," start",[204,1552,1553],{"class":246},":.2f",[204,1555,382],{"class":375},[204,1557,1558],{"class":213}," s\"",[204,1560,282],{"class":250},[204,1562,1563],{"class":84,"line":359},[204,1564,264],{"emptyLinePlaceholder":263},[204,1566,1567,1570,1572,1575,1578],{"class":84,"line":395},[204,1568,1569],{"class":250},"scratch ",[204,1571,273],{"class":246},[204,1573,1574],{"class":250}," wb.sheets.add(",[204,1576,1577],{"class":213},"\"Scratch\"",[204,1579,282],{"class":250},[204,1581,1582,1585,1587],{"class":84,"line":834},[204,1583,1584],{"class":250},"n ",[204,1586,273],{"class":246},[204,1588,1589],{"class":337}," 2_000\n",[204,1591,1592],{"class":84,"line":966},[204,1593,264],{"emptyLinePlaceholder":263},[204,1595,1596,1598,1601],{"class":84,"line":975},[204,1597,910],{"class":246},[204,1599,1600],{"class":904}," per_cell",[204,1602,1603],{"class":250},"():\n",[204,1605,1606,1609,1612,1614,1616,1618,1620,1623,1625,1627],{"class":84,"line":983},[204,1607,1608],{"class":246},"    for",[204,1610,1611],{"class":250}," i ",[204,1613,334],{"class":246},[204,1615,338],{"class":337},[204,1617,341],{"class":250},[204,1619,485],{"class":337},[204,1621,1622],{"class":250},", n ",[204,1624,350],{"class":246},[204,1626,353],{"class":337},[204,1628,356],{"class":250},[204,1630,1631,1634,1636,1639,1641,1644,1646,1648,1650,1652],{"class":84,"line":998},[204,1632,1633],{"class":250},"        scratch.range(",[204,1635,369],{"class":246},[204,1637,1638],{"class":213},"\"A",[204,1640,376],{"class":375},[204,1642,1643],{"class":250},"i",[204,1645,382],{"class":375},[204,1647,385],{"class":213},[204,1649,414],{"class":250},[204,1651,273],{"class":246},[204,1653,1654],{"class":250}," i\n",[204,1656,1657],{"class":84,"line":1012},[204,1658,264],{"emptyLinePlaceholder":263},[204,1660,1661,1663,1666],{"class":84,"line":1026},[204,1662,910],{"class":246},[204,1664,1665],{"class":904}," bulk",[204,1667,1603],{"class":250},[204,1669,1670,1673,1676,1678,1680,1683,1685,1687,1689,1691,1693,1695,1697,1699,1701],{"class":84,"line":1031},[204,1671,1672],{"class":250},"    scratch.range(",[204,1674,1675],{"class":213},"\"B1\"",[204,1677,414],{"class":250},[204,1679,273],{"class":246},[204,1681,1682],{"class":250}," [[i] ",[204,1684,328],{"class":246},[204,1686,1611],{"class":250},[204,1688,334],{"class":246},[204,1690,338],{"class":337},[204,1692,341],{"class":250},[204,1694,485],{"class":337},[204,1696,1622],{"class":250},[204,1698,350],{"class":246},[204,1700,353],{"class":337},[204,1702,1703],{"class":250},")]\n",[204,1705,1706],{"class":84,"line":1040},[204,1707,264],{"emptyLinePlaceholder":263},[204,1709,1710,1713,1716],{"class":84,"line":1062},[204,1711,1712],{"class":250},"timed(",[204,1714,1715],{"class":213},"\"cell by cell\"",[204,1717,1718],{"class":250},", per_cell)\n",[204,1720,1721,1723,1726],{"class":84,"line":1083},[204,1722,1712],{"class":250},[204,1724,1725],{"class":213},"\"one call\"",[204,1727,1728],{"class":250},", bulk)\n",[204,1730,1731],{"class":84,"line":1089},[204,1732,1733],{"class":250},"scratch.delete()\n",[10,1735,1736,1737,1740,1741,1743],{},"Run it with ",[14,1738,1739],{},"n"," at a few hundred first — the per-cell version grows linearly with the number of cells, and at a few thousand it is already slow enough to make the point. The bulk version barely changes as ",[14,1742,1739],{}," grows, because it is still one call. Keeping a harness like this next to a slow production script is the quickest way to find which loop is responsible before rewriting anything.",[187,1745,1747],{"id":1746},"when-xlwings-is-the-wrong-tool","When xlwings is the wrong tool",[10,1749,1750,1751,1755,1756,227],{},"If the workbook does not need to be open in Excel — no live recalculation, no macros, nobody watching — skip the application entirely. openpyxl and xlsxwriter write files directly without any cross-process calls, and pandas reads them far faster than any automation of Excel. xlwings earns its place when you need the running application: a user's open workbook, formulas only Excel can calculate, or a button in Excel that runs Python, as described in ",[23,1752,1754],{"href":1753},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frun-python-from-an-excel-button-with-xlwings\u002F","Run Python from an Excel Button with xlwings RunPython",". The trade-off is laid out in ",[23,1757,1759],{"href":1758},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fwhen-to-use-xlwings-instead-of-openpyxl\u002F","When to Use xlwings Instead of openpyxl",[187,1761,1763],{"id":1762},"common-pitfalls","Common pitfalls",[1765,1766,1767,1783,1792,1800],"ul",{},[1768,1769,1770,1774,1775,1778,1779,1782],"li",{},[1771,1772,1773],"strong",{},"Hidden loops."," ",[14,1776,1777],{},"for cell in rng:"," iterates cell objects and makes a call per cell just as surely as an explicit index loop. Read ",[14,1780,1781],{},"rng.value"," once instead.",[1768,1784,1785,1788,1789,227],{},[1771,1786,1787],{},"Forgetting to restore settings."," A script that crashes with calculation on manual leaves the user's Excel not recalculating. Always restore in ",[14,1790,1791],{},"finally",[1768,1793,1794,1774,1797,1799],{},[1771,1795,1796],{},"Expanding across gaps.",[14,1798,441],{}," stops at the first empty row or column, so a blank row in the middle of a table cuts the read short. Use an explicit address or a table object for data with gaps.",[1768,1801,1802,1805,1806,1809],{},[1771,1803,1804],{},"Dates and numbers."," Bulk reads return Python ",[14,1807,1808],{},"datetime"," objects for date cells and floats for numbers — including whole numbers — so cast integer columns explicitly after reading.",[187,1811,1813],{"id":1812},"conclusion","Conclusion",[10,1815,1816,1817,1819,1820,1822],{},"xlwings is slow only when it is used like a spreadsheet user: one cell at a time. Read whole ranges with ",[14,1818,441],{}," or straight into a DataFrame, do the work in Python, and write results back in one call per range. Switch off screen updating, events and automatic calculation around large updates and restore them in ",[14,1821,1791],{},", chunk very large transfers, and format ranges rather than cells.",[187,1824,1826],{"id":1825},"frequently-asked-questions","Frequently asked questions",[10,1828,1829,1832],{},[1771,1830,1831],{},"Why is my xlwings loop so slow?","\nEvery .value access on a cell is a separate call from Python into the Excel process. Thousands of cells mean thousands of cross-process calls, each with fixed overhead. Reading or writing the whole range in one call removes almost all of that cost.",[10,1834,1835,1838],{},[1771,1836,1837],{},"How do I read a whole table into pandas with xlwings?","\nsheet.range(\"A1\").options(pd.DataFrame, index=False, header=1, expand=\"table\").value reads the contiguous block starting at A1 into a DataFrame in one call.",[10,1840,1841,1844],{},[1771,1842,1843],{},"Does turning off screen updating really help?","\nYes, when you write many ranges or format cells, because Excel otherwise redraws the window after each change. It matters less for a single bulk write, but it is cheap to switch off and restore.",[10,1846,1847,1850],{},[1771,1848,1849],{},"What does chunksize do?","\nFor very large ranges, options(chunksize=10_000) splits the transfer into blocks of that many rows, avoiding memory and COM limits that can make a single enormous call fail.",[187,1852,1854],{"id":1853},"related","Related",[1765,1856,1857,1863,1870,1877],{},[1768,1858,1859,1860,1862],{},"Up one level: ",[23,1861,26],{"href":25}," — connecting to workbooks.",[1768,1864,1865,1869],{},[23,1866,1868],{"href":1867},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002F","Read and Write a Live Excel Workbook with xlwings"," — the basic read and write calls.",[1768,1871,1872,1876],{},[23,1873,1875],{"href":1874},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation\u002F","xlwings vs pywin32 for Excel Automation"," — the same bulk rule applies to COM.",[1768,1878,1879,1883],{},[23,1880,1882],{"href":1881},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fbenchmark-python-excel-read-and-write-speed\u002F","Benchmark Python Excel Read and Write Speed"," — measuring the alternatives.",[1885,1886,1887],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":200,"searchDepth":260,"depth":260,"links":1889},[1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902],{"id":189,"depth":260,"text":190},{"id":230,"depth":260,"text":231},{"id":434,"depth":260,"text":435},{"id":679,"depth":260,"text":680},{"id":873,"depth":260,"text":874},{"id":1244,"depth":260,"text":1245},{"id":1342,"depth":260,"text":1343},{"id":1470,"depth":260,"text":1471},{"id":1746,"depth":260,"text":1747},{"id":1762,"depth":260,"text":1763},{"id":1812,"depth":260,"text":1813},{"id":1825,"depth":260,"text":1826},{"id":1853,"depth":260,"text":1854},"2026-09-23","Make xlwings scripts fast by moving whole ranges in single calls instead of looping over cells — expand and options for DataFrames, 2D lists, turning off screen updating, events and automatic calculation, and chunksize for very large ranges.","md",[1907,1909,1911,1913],{"q":1831,"a":1908},"Every .value access on a cell is a separate call from Python into the Excel process. Thousands of cells mean thousands of cross-process calls, each with fixed overhead. Reading or writing the whole range in one call removes almost all of that cost.",{"q":1837,"a":1910},"sheet.range(\"A1\").options(pd.DataFrame, index=False, header=1, expand=\"table\").value reads the contiguous block starting at A1 into a DataFrame in one call.",{"q":1843,"a":1912},"Yes, when you write many ranges or format cells, because Excel otherwise redraws the window after each change. It matters less for a single bulk write, but it is cheap to switch off and restore.",{"q":1849,"a":1914},"For very large ranges, options(chunksize=10_000) splits the transfer into blocks of that many rows, avoiding memory and COM limits that can make a single enormous call fail.","speed up xlwings with bulk reads and writes",null,{"breadcrumb":1918},[1919,1922,1925],{"name":1920,"item":1921},"Home","\u002F",{"name":1923,"item":1924},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":26,"item":25},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes",{"title":1928,"description":1929},"Speed Up xlwings — Bulk Range Reads and Writes","Why xlwings loops over cells are slow and how to fix them — read and write whole ranges at once, DataFrame options, screen updating and manual calculation, chunksize for big ranges, and a reusable fast-mode context manager.","speed-up-xlwings-with-bulk-reads-and-writes","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes\u002Findex",[1933,1934],"xlwings","large-files","how-to","ABhPZQif9_-R9iS6vbsccap8psT-3gw5jDEkrcyHZwU",[1938,1941],{"title":1754,"path":1939,"stem":1940,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frun-python-from-an-excel-button-with-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frun-python-from-an-excel-button-with-xlwings\u002Findex",{"title":1942,"path":1943,"stem":1944,"children":-1},"Work with Named Ranges and Tables in xlwings","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fwork-with-named-ranges-and-tables-in-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fwork-with-named-ranges-and-tables-in-xlwings\u002Findex",[1946,1948],{"path":1947,"title":26},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics",{"path":1949,"title":1923},"\u002Fgetting-started-with-python-excel-automation",[],[1952,1955,1958,1961,1964,1967],{"path":1953,"title":1954,"kind":1935},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-win32com-and-xlwings-code-without-excel","Test win32com and xlwings Code Without Excel",{"path":1956,"title":1957,"kind":1935},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fautomate-excel-on-macos-with-python","Automate Excel on macOS with Python",{"path":1959,"title":1875,"kind":1960},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation","compare",{"path":1962,"title":1963,"kind":1935},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges","Control How xlwings Converts DataFrames and Ranges",{"path":1965,"title":1966,"kind":1935},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings","Format Cells in a Live Workbook with xlwings",{"path":1968,"title":1969,"kind":1935},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings","Insert a matplotlib Plot into a Live Workbook with xlwings",1790972057061]