[{"data":1,"prerenderedAt":2545},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode":2537},{"id":4,"title":5,"body":6,"dateModified":2505,"datePublished":2505,"description":2506,"extension":2507,"faq":2508,"meta":2520,"navigation":197,"path":2530,"seo":2531,"slug":2533,"stem":2534,"type":2535,"__hash__":2536},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002Findex.md","Write Large DataFrames to Excel with write_only Mode",{"type":7,"value":8,"toc":2490},"minimark",[9,28,130,135,165,169,356,386,390,400,703,714,718,725,1087,1098,1105,1109,1118,1224,1239,1327,1331,1334,1546,1558,1562,1565,1894,1897,1976,1980,2122,2126,2132,2338,2349,2353,2362,2366,2380,2384,2397,2422,2431,2437,2441,2444,2453,2456,2486],[10,11,12,13,17,18,21,22,27],"p",{},"Generating a large workbook has the same problem as reading one, in reverse: the default path builds every cell in memory before anything reaches disk. ",[14,15,16],"code",{},"openpyxl","'s ",[14,19,20],{},"write_only"," mode streams instead, appending each row to the file as it is produced. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002F","Working with Large Excel Files in Python",", covers the mode, its constraints, the pandas bridge, and the xlsxwriter alternative.",[29,30,38,39,38,43,38,47,38,54,38,61,38,70,38,78,38,81,38,84,38,89,38,94,38,98,38,103,38,107,38,112,38,118,38,123,38,126],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"0 0 740 234","img",[34,35],"wo-flow-t","wo-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:740px;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},"Normal writing versus write-only streaming",[44,45,46],"desc",{"id":35},"In normal mode every appended row is retained in a worksheet object until save, so memory grows with the output. In write-only mode each appended row is serialised and released, so memory stays at roughly one row plus the shared string table.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","740","234","#ffffff",[55,56,60],"text",{"x":57,"y":58,"style":59},"185","30","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Workbook()",[48,62],{"x":58,"y":63,"width":64,"height":65,"rx":66,"fill":67,"stroke":68,"style":69},"44","310","150","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[48,71],{"x":72,"y":73,"width":74,"height":75,"rx":76,"fill":77},"56","66","258","22","5","#ebebfd",[48,79],{"x":72,"y":80,"width":74,"height":75,"rx":76,"fill":77},"92",[48,82],{"x":72,"y":83,"width":74,"height":75,"rx":76,"fill":77},"118",[55,85,88],{"x":57,"y":86,"style":87},"164","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","every row kept until save()",[55,90,93],{"x":57,"y":91,"style":92},"184","font-size:11.5px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","memory grows with the sheet",[55,95,97],{"x":96,"y":58,"style":59},"555","Workbook(write_only=True)",[48,99],{"x":100,"y":63,"width":64,"height":65,"rx":66,"fill":101,"stroke":102,"style":69},"400","#d9f4f1","var(--teal,#0f9488)",[48,104],{"x":105,"y":73,"width":74,"height":75,"rx":76,"fill":53,"stroke":106},"426","var(--line,#cdd5e6)",[55,108,111],{"x":96,"y":109,"style":110},"82","font-size:11px;fill:var(--muted,#5b6780);text-anchor:middle","current row",[113,114],"line",{"x1":105,"y1":115,"x2":116,"y2":115,"stroke":102,"style":117},"102","684","stroke-width:1.5px;stroke-dasharray:4 4",[55,119,122],{"x":96,"y":120,"style":121},"126","font-size:11px;fill:var(--teal,#0f9488);text-anchor:middle","previous rows already written to disk",[55,124,125],{"x":96,"y":86,"style":87},"append, serialise, release",[55,127,129],{"x":96,"y":91,"style":128},"font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","memory stays flat",[131,132,134],"h2",{"id":133},"prerequisites","Prerequisites",[136,137,142],"pre",{"className":138,"code":139,"language":140,"meta":141,"style":141},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[14,143,144],{"__ignoreMap":141},[145,146,148,152,156,159,162],"span",{"class":113,"line":147},1,[145,149,151],{"class":150},"sMTad","pip",[145,153,155],{"class":154},"srMev"," install",[145,157,158],{"class":154}," pandas",[145,160,161],{"class":154}," openpyxl",[145,163,164],{"class":154}," xlsxwriter\n",[131,166,168],{"id":167},"the-minimal-streaming-write","The minimal streaming write",[136,170,174],{"className":171,"code":172,"language":173,"meta":141,"style":141},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook(write_only=True)\nws = wb.create_sheet(\"Export\")\n\nws.append([\"Order_ID\", \"Region\", \"Revenue\"])\nfor order_id in range(1, 300_001):\n    ws.append([order_id, \"North\", round(order_id * 1.5, 2)])\n\nwb.save(\"streamed.xlsx\")\nprint(\"done\")\n","python",[14,175,176,192,199,223,239,244,267,296,327,332,343],{"__ignoreMap":141},[145,177,178,182,186,189],{"class":113,"line":147},[145,179,181],{"class":180},"s-kum","from",[145,183,185],{"class":184},"skGVy"," openpyxl ",[145,187,188],{"class":180},"import",[145,190,191],{"class":184}," Workbook\n",[145,193,195],{"class":113,"line":194},2,[145,196,198],{"emptyLinePlaceholder":197},true,"\n",[145,200,202,205,208,211,214,216,220],{"class":113,"line":201},3,[145,203,204],{"class":184},"wb ",[145,206,207],{"class":180},"=",[145,209,210],{"class":184}," Workbook(",[145,212,20],{"class":213},"sa561",[145,215,207],{"class":180},[145,217,219],{"class":218},"sP0c6","True",[145,221,222],{"class":184},")\n",[145,224,226,229,231,234,237],{"class":113,"line":225},4,[145,227,228],{"class":184},"ws ",[145,230,207],{"class":180},[145,232,233],{"class":184}," wb.create_sheet(",[145,235,236],{"class":154},"\"Export\"",[145,238,222],{"class":184},[145,240,242],{"class":113,"line":241},5,[145,243,198],{"emptyLinePlaceholder":197},[145,245,247,250,253,256,259,261,264],{"class":113,"line":246},6,[145,248,249],{"class":184},"ws.append([",[145,251,252],{"class":154},"\"Order_ID\"",[145,254,255],{"class":184},", ",[145,257,258],{"class":154},"\"Region\"",[145,260,255],{"class":184},[145,262,263],{"class":154},"\"Revenue\"",[145,265,266],{"class":184},"])\n",[145,268,270,273,276,279,282,285,288,290,293],{"class":113,"line":269},7,[145,271,272],{"class":180},"for",[145,274,275],{"class":184}," order_id ",[145,277,278],{"class":180},"in",[145,280,281],{"class":218}," range",[145,283,284],{"class":184},"(",[145,286,287],{"class":218},"1",[145,289,255],{"class":184},[145,291,292],{"class":218},"300_001",[145,294,295],{"class":184},"):\n",[145,297,299,302,305,307,310,313,316,319,321,324],{"class":113,"line":298},8,[145,300,301],{"class":184},"    ws.append([order_id, ",[145,303,304],{"class":154},"\"North\"",[145,306,255],{"class":184},[145,308,309],{"class":218},"round",[145,311,312],{"class":184},"(order_id ",[145,314,315],{"class":180},"*",[145,317,318],{"class":218}," 1.5",[145,320,255],{"class":184},[145,322,323],{"class":218},"2",[145,325,326],{"class":184},")])\n",[145,328,330],{"class":113,"line":329},9,[145,331,198],{"emptyLinePlaceholder":197},[145,333,335,338,341],{"class":113,"line":334},10,[145,336,337],{"class":184},"wb.save(",[145,339,340],{"class":154},"\"streamed.xlsx\"",[145,342,222],{"class":184},[145,344,346,349,351,354],{"class":113,"line":345},11,[145,347,348],{"class":218},"print",[145,350,284],{"class":184},[145,352,353],{"class":154},"\"done\"",[145,355,222],{"class":184},[10,357,358,359,361,362,365,366,369,370,373,374,377,378,381,382,385],{},"Two differences from ordinary openpyxl matter immediately. ",[14,360,97],{}," starts with no sheets at all, so ",[14,363,364],{},"create_sheet"," is mandatory — ",[14,367,368],{},"wb.active"," is ",[14,371,372],{},"None",". And the only supported operation on the sheet is ",[14,375,376],{},"append",": there is no ",[14,379,380],{},"ws[\"A1\"]",", no ",[14,383,384],{},"ws.cell(...)",", and no way to go back.",[131,387,389],{"id":388},"feeding-it-a-dataframe","Feeding it a DataFrame",[10,391,392,395,396,399],{},[14,393,394],{},"to_excel"," needs a normal worksheet, so stream the frame yourself. ",[14,397,398],{},"itertuples"," is the fast iterator — it avoids building a Series per row:",[136,401,403],{"className":171,"code":402,"language":173,"meta":141,"style":141},"import numpy as np\nimport pandas as pd\nfrom openpyxl import Workbook\n\nrng = np.random.default_rng(1)\ndf = pd.DataFrame({\n    \"Order_ID\": np.arange(1, 250_001),\n    \"Region\": rng.choice([\"North\", \"South\", \"West\"], 250_000),\n    \"Quantity\": rng.integers(1, 20, 250_000),\n    \"Unit_Price\": rng.uniform(5, 200, 250_000).round(2),\n})\ndf[\"Revenue\"] = (df[\"Quantity\"] * df[\"Unit_Price\"]).round(2)\n\nwb = Workbook(write_only=True)\nws = wb.create_sheet(\"Orders\")\nws.append(list(df.columns))\nfor row in df.itertuples(index=False, name=None):\n    ws.append(list(row))\nwb.save(\"orders_streamed.xlsx\")\n",[14,404,405,418,430,440,444,458,468,486,514,535,561,566,602,607,624,638,650,682,693],{"__ignoreMap":141},[145,406,407,409,412,415],{"class":113,"line":147},[145,408,188],{"class":180},[145,410,411],{"class":184}," numpy ",[145,413,414],{"class":180},"as",[145,416,417],{"class":184}," np\n",[145,419,420,422,425,427],{"class":113,"line":194},[145,421,188],{"class":180},[145,423,424],{"class":184}," pandas ",[145,426,414],{"class":180},[145,428,429],{"class":184}," pd\n",[145,431,432,434,436,438],{"class":113,"line":201},[145,433,181],{"class":180},[145,435,185],{"class":184},[145,437,188],{"class":180},[145,439,191],{"class":184},[145,441,442],{"class":113,"line":225},[145,443,198],{"emptyLinePlaceholder":197},[145,445,446,449,451,454,456],{"class":113,"line":241},[145,447,448],{"class":184},"rng ",[145,450,207],{"class":180},[145,452,453],{"class":184}," np.random.default_rng(",[145,455,287],{"class":218},[145,457,222],{"class":184},[145,459,460,463,465],{"class":113,"line":246},[145,461,462],{"class":184},"df ",[145,464,207],{"class":180},[145,466,467],{"class":184}," pd.DataFrame({\n",[145,469,470,473,476,478,480,483],{"class":113,"line":269},[145,471,472],{"class":154},"    \"Order_ID\"",[145,474,475],{"class":184},": np.arange(",[145,477,287],{"class":218},[145,479,255],{"class":184},[145,481,482],{"class":218},"250_001",[145,484,485],{"class":184},"),\n",[145,487,488,491,494,496,498,501,503,506,509,512],{"class":113,"line":298},[145,489,490],{"class":154},"    \"Region\"",[145,492,493],{"class":184},": rng.choice([",[145,495,304],{"class":154},[145,497,255],{"class":184},[145,499,500],{"class":154},"\"South\"",[145,502,255],{"class":184},[145,504,505],{"class":154},"\"West\"",[145,507,508],{"class":184},"], ",[145,510,511],{"class":218},"250_000",[145,513,485],{"class":184},[145,515,516,519,522,524,526,529,531,533],{"class":113,"line":329},[145,517,518],{"class":154},"    \"Quantity\"",[145,520,521],{"class":184},": rng.integers(",[145,523,287],{"class":218},[145,525,255],{"class":184},[145,527,528],{"class":218},"20",[145,530,255],{"class":184},[145,532,511],{"class":218},[145,534,485],{"class":184},[145,536,537,540,543,545,547,550,552,554,557,559],{"class":113,"line":334},[145,538,539],{"class":154},"    \"Unit_Price\"",[145,541,542],{"class":184},": rng.uniform(",[145,544,76],{"class":218},[145,546,255],{"class":184},[145,548,549],{"class":218},"200",[145,551,255],{"class":184},[145,553,511],{"class":218},[145,555,556],{"class":184},").round(",[145,558,323],{"class":218},[145,560,485],{"class":184},[145,562,563],{"class":113,"line":345},[145,564,565],{"class":184},"})\n",[145,567,569,572,574,577,579,582,585,587,589,592,595,598,600],{"class":113,"line":568},12,[145,570,571],{"class":184},"df[",[145,573,263],{"class":154},[145,575,576],{"class":184},"] ",[145,578,207],{"class":180},[145,580,581],{"class":184}," (df[",[145,583,584],{"class":154},"\"Quantity\"",[145,586,576],{"class":184},[145,588,315],{"class":180},[145,590,591],{"class":184}," df[",[145,593,594],{"class":154},"\"Unit_Price\"",[145,596,597],{"class":184},"]).round(",[145,599,323],{"class":218},[145,601,222],{"class":184},[145,603,605],{"class":113,"line":604},13,[145,606,198],{"emptyLinePlaceholder":197},[145,608,610,612,614,616,618,620,622],{"class":113,"line":609},14,[145,611,204],{"class":184},[145,613,207],{"class":180},[145,615,210],{"class":184},[145,617,20],{"class":213},[145,619,207],{"class":180},[145,621,219],{"class":218},[145,623,222],{"class":184},[145,625,627,629,631,633,636],{"class":113,"line":626},15,[145,628,228],{"class":184},[145,630,207],{"class":180},[145,632,233],{"class":184},[145,634,635],{"class":154},"\"Orders\"",[145,637,222],{"class":184},[145,639,641,644,647],{"class":113,"line":640},16,[145,642,643],{"class":184},"ws.append(",[145,645,646],{"class":218},"list",[145,648,649],{"class":184},"(df.columns))\n",[145,651,653,655,658,660,663,666,668,671,673,676,678,680],{"class":113,"line":652},17,[145,654,272],{"class":180},[145,656,657],{"class":184}," row ",[145,659,278],{"class":180},[145,661,662],{"class":184}," df.itertuples(",[145,664,665],{"class":213},"index",[145,667,207],{"class":180},[145,669,670],{"class":218},"False",[145,672,255],{"class":184},[145,674,675],{"class":213},"name",[145,677,207],{"class":180},[145,679,372],{"class":218},[145,681,295],{"class":184},[145,683,685,688,690],{"class":113,"line":684},18,[145,686,687],{"class":184},"    ws.append(",[145,689,646],{"class":218},[145,691,692],{"class":184},"(row))\n",[145,694,696,698,701],{"class":113,"line":695},19,[145,697,337],{"class":184},[145,699,700],{"class":154},"\"orders_streamed.xlsx\"",[145,702,222],{"class":184},[10,704,705,708,709,713],{},[14,706,707],{},"name=None"," gives plain tuples rather than named tuples, which is measurably quicker over a quarter of a million rows. If the frame itself is the memory problem, combine this with a ",[23,710,712],{"href":711},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002F","chunked read"," so neither side is ever fully resident.",[131,715,717],{"id":716},"styling-as-you-go","Styling as you go",[10,719,720,721,724],{},"Formatting is still possible, but it has to be attached to the cell at the moment its row is appended. ",[14,722,723],{},"WriteOnlyCell"," is the vehicle:",[136,726,728],{"className":171,"code":727,"language":173,"meta":141,"style":141},"from openpyxl import Workbook\nfrom openpyxl.cell import WriteOnlyCell\nfrom openpyxl.styles import Font, PatternFill\n\nwb = Workbook(write_only=True)\nws = wb.create_sheet(\"Orders\")\n\nheader_font = Font(bold=True, color=\"FFFFFF\")\nheader_fill = PatternFill(\"solid\", start_color=\"4338CA\")\n\nheader = []\nfor name in [\"Order_ID\", \"Region\", \"Quantity\", \"Revenue\"]:\n    cell = WriteOnlyCell(ws, value=name)\n    cell.font = header_font\n    cell.fill = header_fill\n    header.append(cell)\nws.append(header)\n\nfor order_id, region, qty, revenue in [(1, \"North\", 4, 79.96), (2, \"South\", 2, 99.0)]:\n    money = WriteOnlyCell(ws, value=revenue)\n    money.number_format = \"#,##0.00\"\n    ws.append([order_id, region, qty, money])\n\nws.freeze_panes = \"A2\"                 # sheet-level settings are fine\nws.column_dimensions[\"B\"].width = 14   # set before the rows are flushed\n\nwb.save(\"styled_stream.xlsx\")\n",[14,729,730,740,752,764,768,784,796,800,829,854,858,868,897,915,925,935,940,945,949,998,1015,1026,1032,1037,1052,1072,1077],{"__ignoreMap":141},[145,731,732,734,736,738],{"class":113,"line":147},[145,733,181],{"class":180},[145,735,185],{"class":184},[145,737,188],{"class":180},[145,739,191],{"class":184},[145,741,742,744,747,749],{"class":113,"line":194},[145,743,181],{"class":180},[145,745,746],{"class":184}," openpyxl.cell ",[145,748,188],{"class":180},[145,750,751],{"class":184}," WriteOnlyCell\n",[145,753,754,756,759,761],{"class":113,"line":201},[145,755,181],{"class":180},[145,757,758],{"class":184}," openpyxl.styles ",[145,760,188],{"class":180},[145,762,763],{"class":184}," Font, PatternFill\n",[145,765,766],{"class":113,"line":225},[145,767,198],{"emptyLinePlaceholder":197},[145,769,770,772,774,776,778,780,782],{"class":113,"line":241},[145,771,204],{"class":184},[145,773,207],{"class":180},[145,775,210],{"class":184},[145,777,20],{"class":213},[145,779,207],{"class":180},[145,781,219],{"class":218},[145,783,222],{"class":184},[145,785,786,788,790,792,794],{"class":113,"line":246},[145,787,228],{"class":184},[145,789,207],{"class":180},[145,791,233],{"class":184},[145,793,635],{"class":154},[145,795,222],{"class":184},[145,797,798],{"class":113,"line":269},[145,799,198],{"emptyLinePlaceholder":197},[145,801,802,805,807,810,813,815,817,819,822,824,827],{"class":113,"line":298},[145,803,804],{"class":184},"header_font ",[145,806,207],{"class":180},[145,808,809],{"class":184}," Font(",[145,811,812],{"class":213},"bold",[145,814,207],{"class":180},[145,816,219],{"class":218},[145,818,255],{"class":184},[145,820,821],{"class":213},"color",[145,823,207],{"class":180},[145,825,826],{"class":154},"\"FFFFFF\"",[145,828,222],{"class":184},[145,830,831,834,836,839,842,844,847,849,852],{"class":113,"line":329},[145,832,833],{"class":184},"header_fill ",[145,835,207],{"class":180},[145,837,838],{"class":184}," PatternFill(",[145,840,841],{"class":154},"\"solid\"",[145,843,255],{"class":184},[145,845,846],{"class":213},"start_color",[145,848,207],{"class":180},[145,850,851],{"class":154},"\"4338CA\"",[145,853,222],{"class":184},[145,855,856],{"class":113,"line":334},[145,857,198],{"emptyLinePlaceholder":197},[145,859,860,863,865],{"class":113,"line":345},[145,861,862],{"class":184},"header ",[145,864,207],{"class":180},[145,866,867],{"class":184}," []\n",[145,869,870,872,875,877,880,882,884,886,888,890,892,894],{"class":113,"line":568},[145,871,272],{"class":180},[145,873,874],{"class":184}," name ",[145,876,278],{"class":180},[145,878,879],{"class":184}," [",[145,881,252],{"class":154},[145,883,255],{"class":184},[145,885,258],{"class":154},[145,887,255],{"class":184},[145,889,584],{"class":154},[145,891,255],{"class":184},[145,893,263],{"class":154},[145,895,896],{"class":184},"]:\n",[145,898,899,902,904,907,910,912],{"class":113,"line":604},[145,900,901],{"class":184},"    cell ",[145,903,207],{"class":180},[145,905,906],{"class":184}," WriteOnlyCell(ws, ",[145,908,909],{"class":213},"value",[145,911,207],{"class":180},[145,913,914],{"class":184},"name)\n",[145,916,917,920,922],{"class":113,"line":609},[145,918,919],{"class":184},"    cell.font ",[145,921,207],{"class":180},[145,923,924],{"class":184}," header_font\n",[145,926,927,930,932],{"class":113,"line":626},[145,928,929],{"class":184},"    cell.fill ",[145,931,207],{"class":180},[145,933,934],{"class":184}," header_fill\n",[145,936,937],{"class":113,"line":640},[145,938,939],{"class":184},"    header.append(cell)\n",[145,941,942],{"class":113,"line":652},[145,943,944],{"class":184},"ws.append(header)\n",[145,946,947],{"class":113,"line":684},[145,948,198],{"emptyLinePlaceholder":197},[145,950,951,953,956,958,961,963,965,967,969,972,974,977,980,982,984,986,988,990,992,995],{"class":113,"line":695},[145,952,272],{"class":180},[145,954,955],{"class":184}," order_id, region, qty, revenue ",[145,957,278],{"class":180},[145,959,960],{"class":184}," [(",[145,962,287],{"class":218},[145,964,255],{"class":184},[145,966,304],{"class":154},[145,968,255],{"class":184},[145,970,971],{"class":218},"4",[145,973,255],{"class":184},[145,975,976],{"class":218},"79.96",[145,978,979],{"class":184},"), (",[145,981,323],{"class":218},[145,983,255],{"class":184},[145,985,500],{"class":154},[145,987,255],{"class":184},[145,989,323],{"class":218},[145,991,255],{"class":184},[145,993,994],{"class":218},"99.0",[145,996,997],{"class":184},")]:\n",[145,999,1001,1004,1006,1008,1010,1012],{"class":113,"line":1000},20,[145,1002,1003],{"class":184},"    money ",[145,1005,207],{"class":180},[145,1007,906],{"class":184},[145,1009,909],{"class":213},[145,1011,207],{"class":180},[145,1013,1014],{"class":184},"revenue)\n",[145,1016,1018,1021,1023],{"class":113,"line":1017},21,[145,1019,1020],{"class":184},"    money.number_format ",[145,1022,207],{"class":180},[145,1024,1025],{"class":154}," \"#,##0.00\"\n",[145,1027,1029],{"class":113,"line":1028},22,[145,1030,1031],{"class":184},"    ws.append([order_id, region, qty, money])\n",[145,1033,1035],{"class":113,"line":1034},23,[145,1036,198],{"emptyLinePlaceholder":197},[145,1038,1040,1043,1045,1048],{"class":113,"line":1039},24,[145,1041,1042],{"class":184},"ws.freeze_panes ",[145,1044,207],{"class":180},[145,1046,1047],{"class":154}," \"A2\"",[145,1049,1051],{"class":1050},"s-wDw","                 # sheet-level settings are fine\n",[145,1053,1055,1058,1061,1064,1066,1069],{"class":113,"line":1054},25,[145,1056,1057],{"class":184},"ws.column_dimensions[",[145,1059,1060],{"class":154},"\"B\"",[145,1062,1063],{"class":184},"].width ",[145,1065,207],{"class":180},[145,1067,1068],{"class":218}," 14",[145,1070,1071],{"class":1050},"   # set before the rows are flushed\n",[145,1073,1075],{"class":113,"line":1074},26,[145,1076,198],{"emptyLinePlaceholder":197},[145,1078,1080,1082,1085],{"class":113,"line":1079},27,[145,1081,337],{"class":184},[145,1083,1084],{"class":154},"\"styled_stream.xlsx\"",[145,1086,222],{"class":184},[10,1088,1089,1090,1093,1094,1097],{},"Reuse the ",[14,1091,1092],{},"Font"," and ",[14,1095,1096],{},"PatternFill"," objects rather than constructing them per row: openpyxl deduplicates identical styles, and a fresh object per cell inflates the workbook's style table, which is the very thing that makes large files slow to open.",[10,1099,1100,1101,1104],{},"Sheet-level properties such as ",[14,1102,1103],{},"freeze_panes"," and column widths work because they are not per-cell data — but set them before saving, since the sheet is finalised at that point.",[131,1106,1108],{"id":1107},"the-xlsxwriter-alternative","The xlsxwriter alternative",[10,1110,1111,1114,1115,1117],{},[14,1112,1113],{},"xlsxwriter"," has an equivalent mode and is generally faster for pure generation. It also plugs into pandas directly, which is the reason to prefer it when you want ",[14,1116,394],{}," convenience:",[136,1119,1121],{"className":171,"code":1120,"language":173,"meta":141,"style":141},"import pandas as pd\n\nwith pd.ExcelWriter(\n    \"constant_memory.xlsx\",\n    engine=\"xlsxwriter\",\n    engine_kwargs={\"options\": {\"constant_memory\": True}},\n) as writer:\n    df.to_excel(writer, sheet_name=\"Orders\", index=False)\n",[14,1122,1123,1133,1137,1145,1153,1165,1192,1202],{"__ignoreMap":141},[145,1124,1125,1127,1129,1131],{"class":113,"line":147},[145,1126,188],{"class":180},[145,1128,424],{"class":184},[145,1130,414],{"class":180},[145,1132,429],{"class":184},[145,1134,1135],{"class":113,"line":194},[145,1136,198],{"emptyLinePlaceholder":197},[145,1138,1139,1142],{"class":113,"line":201},[145,1140,1141],{"class":180},"with",[145,1143,1144],{"class":184}," pd.ExcelWriter(\n",[145,1146,1147,1150],{"class":113,"line":225},[145,1148,1149],{"class":154},"    \"constant_memory.xlsx\"",[145,1151,1152],{"class":184},",\n",[145,1154,1155,1158,1160,1163],{"class":113,"line":241},[145,1156,1157],{"class":213},"    engine",[145,1159,207],{"class":180},[145,1161,1162],{"class":154},"\"xlsxwriter\"",[145,1164,1152],{"class":184},[145,1166,1167,1170,1172,1175,1178,1181,1184,1187,1189],{"class":113,"line":246},[145,1168,1169],{"class":213},"    engine_kwargs",[145,1171,207],{"class":180},[145,1173,1174],{"class":184},"{",[145,1176,1177],{"class":154},"\"options\"",[145,1179,1180],{"class":184},": {",[145,1182,1183],{"class":154},"\"constant_memory\"",[145,1185,1186],{"class":184},": ",[145,1188,219],{"class":218},[145,1190,1191],{"class":184},"}},\n",[145,1193,1194,1197,1199],{"class":113,"line":269},[145,1195,1196],{"class":184},") ",[145,1198,414],{"class":180},[145,1200,1201],{"class":184}," writer:\n",[145,1203,1204,1207,1210,1212,1214,1216,1218,1220,1222],{"class":113,"line":298},[145,1205,1206],{"class":184},"    df.to_excel(writer, ",[145,1208,1209],{"class":213},"sheet_name",[145,1211,207],{"class":180},[145,1213,635],{"class":154},[145,1215,255],{"class":184},[145,1217,665],{"class":213},[145,1219,207],{"class":180},[145,1221,670],{"class":218},[145,1223,222],{"class":184},[10,1225,1226,1229,1230,1233,1234,1238],{},[14,1227,1228],{},"constant_memory"," flushes each row as it is written, with the same in-order constraint: once a row is flushed you cannot revisit it, so ",[14,1231,1232],{},"set_column"," widths and any ",[23,1235,1237],{"href":1236},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002F","conditional formatting"," must be declared before the data goes in. The upside is that pandas does the iteration for you.",[29,1240,38,1245,38,1248,38,1251,38,1254,38,1259,38,1265,38,1270,38,1274,38,1279,38,1284,38,1287,38,1292,38,1295,38,1298,38,1302,38,1305,38,1310,38,1315,38,1318,38,1321,38,1324],{"viewBox":1241,"role":32,"ariaLabelledBy":1242,"xmlns":36,"style":37},"0 0 740 224",[1243,1244],"wo-pick-t","wo-pick-d",[40,1246,1247],{"id":1243},"Choosing between the three ways to write a big sheet",[44,1249,1250],{"id":1244},"Normal openpyxl suits sheets small enough to hold, and allows revisiting cells. openpyxl write-only suits large output where you control the loop. xlsxwriter constant memory suits large output written straight from a DataFrame.",[48,1252],{"x":50,"y":50,"width":51,"height":1253,"fill":53},"224",[48,1255],{"x":1256,"y":58,"width":1257,"height":1258,"rx":66,"fill":77,"stroke":106},"16","228","168",[55,1260,1264],{"x":1261,"y":1262,"style":1263},"130","58","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","openpyxl, normal",[55,1266,1269],{"x":1261,"y":1267,"style":1268},"88","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","revisit any cell",[55,1271,1273],{"x":1261,"y":1272,"style":1268},"110","totals added at the end",[55,1275,1278],{"x":1261,"y":1276,"style":1277},"132","font-size:11.5px;fill:var(--danger,#dc2626);text-anchor:middle","holds the whole sheet",[55,1280,1283],{"x":1261,"y":1281,"style":1282},"170","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","up to ~50k rows",[48,1285],{"x":1286,"y":58,"width":1257,"height":1258,"rx":66,"fill":101,"stroke":102,"style":69},"256",[55,1288,1291],{"x":1289,"y":1262,"style":1290},"370","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","openpyxl, write_only",[55,1293,1294],{"x":1289,"y":1267,"style":1268},"append in order only",[55,1296,1297],{"x":1289,"y":1272,"style":1268},"WriteOnlyCell for styling",[55,1299,1301],{"x":1289,"y":1276,"style":1300},"font-size:11.5px;fill:var(--teal,#0f9488);text-anchor:middle","flat memory",[55,1303,1304],{"x":1289,"y":1281,"style":1282},"you own the loop",[48,1306],{"x":1307,"y":58,"width":1257,"height":1258,"rx":66,"fill":1308,"stroke":1309,"style":69},"496","#fdefd8","var(--gold,#b4740a)",[55,1311,1314],{"x":1312,"y":1262,"style":1313},"610","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","xlsxwriter, constant",[55,1316,1317],{"x":1312,"y":1267,"style":1268},"works with to_excel",[55,1319,1320],{"x":1312,"y":1272,"style":1268},"fastest generation",[55,1322,1323],{"x":1312,"y":1276,"style":1277},"cannot read or edit files",[55,1325,1326],{"x":1312,"y":1281,"style":1282},"pandas does the loop",[131,1328,1330],{"id":1329},"plan-the-things-you-cannot-go-back-and-add","Plan the things you cannot go back and add",[10,1332,1333],{},"Everything that would normally be done \"afterwards\" has to move before or beside the loop. Totals are the usual casualty:",[136,1335,1337],{"className":171,"code":1336,"language":173,"meta":141,"style":141},"from openpyxl import Workbook\n\nrows = [(1, \"North\", 79.96), (2, \"South\", 99.0), (3, \"West\", 85.75)]\n\nwb = Workbook(write_only=True)\nws = wb.create_sheet(\"Orders\")\nws.append([\"Order_ID\", \"Region\", \"Revenue\"])\n\ntotal = 0.0\nfor order_id, region, revenue in rows:      # accumulate while streaming\n    total += revenue\n    ws.append([order_id, region, revenue])\n\nws.append([])                                # spacer\nws.append([\"\", \"Total\", round(total, 2)])    # written last, as a value\n\nwb.save(\"streamed_with_total.xlsx\")\n",[14,1338,1339,1349,1353,1401,1405,1421,1433,1449,1453,1463,1478,1489,1494,1498,1506,1533,1537],{"__ignoreMap":141},[145,1340,1341,1343,1345,1347],{"class":113,"line":147},[145,1342,181],{"class":180},[145,1344,185],{"class":184},[145,1346,188],{"class":180},[145,1348,191],{"class":184},[145,1350,1351],{"class":113,"line":194},[145,1352,198],{"emptyLinePlaceholder":197},[145,1354,1355,1358,1360,1362,1364,1366,1368,1370,1372,1374,1376,1378,1380,1382,1384,1386,1389,1391,1393,1395,1398],{"class":113,"line":201},[145,1356,1357],{"class":184},"rows ",[145,1359,207],{"class":180},[145,1361,960],{"class":184},[145,1363,287],{"class":218},[145,1365,255],{"class":184},[145,1367,304],{"class":154},[145,1369,255],{"class":184},[145,1371,976],{"class":218},[145,1373,979],{"class":184},[145,1375,323],{"class":218},[145,1377,255],{"class":184},[145,1379,500],{"class":154},[145,1381,255],{"class":184},[145,1383,994],{"class":218},[145,1385,979],{"class":184},[145,1387,1388],{"class":218},"3",[145,1390,255],{"class":184},[145,1392,505],{"class":154},[145,1394,255],{"class":184},[145,1396,1397],{"class":218},"85.75",[145,1399,1400],{"class":184},")]\n",[145,1402,1403],{"class":113,"line":225},[145,1404,198],{"emptyLinePlaceholder":197},[145,1406,1407,1409,1411,1413,1415,1417,1419],{"class":113,"line":241},[145,1408,204],{"class":184},[145,1410,207],{"class":180},[145,1412,210],{"class":184},[145,1414,20],{"class":213},[145,1416,207],{"class":180},[145,1418,219],{"class":218},[145,1420,222],{"class":184},[145,1422,1423,1425,1427,1429,1431],{"class":113,"line":246},[145,1424,228],{"class":184},[145,1426,207],{"class":180},[145,1428,233],{"class":184},[145,1430,635],{"class":154},[145,1432,222],{"class":184},[145,1434,1435,1437,1439,1441,1443,1445,1447],{"class":113,"line":269},[145,1436,249],{"class":184},[145,1438,252],{"class":154},[145,1440,255],{"class":184},[145,1442,258],{"class":154},[145,1444,255],{"class":184},[145,1446,263],{"class":154},[145,1448,266],{"class":184},[145,1450,1451],{"class":113,"line":298},[145,1452,198],{"emptyLinePlaceholder":197},[145,1454,1455,1458,1460],{"class":113,"line":329},[145,1456,1457],{"class":184},"total ",[145,1459,207],{"class":180},[145,1461,1462],{"class":218}," 0.0\n",[145,1464,1465,1467,1470,1472,1475],{"class":113,"line":334},[145,1466,272],{"class":180},[145,1468,1469],{"class":184}," order_id, region, revenue ",[145,1471,278],{"class":180},[145,1473,1474],{"class":184}," rows:      ",[145,1476,1477],{"class":1050},"# accumulate while streaming\n",[145,1479,1480,1483,1486],{"class":113,"line":345},[145,1481,1482],{"class":184},"    total ",[145,1484,1485],{"class":180},"+=",[145,1487,1488],{"class":184}," revenue\n",[145,1490,1491],{"class":113,"line":568},[145,1492,1493],{"class":184},"    ws.append([order_id, region, revenue])\n",[145,1495,1496],{"class":113,"line":604},[145,1497,198],{"emptyLinePlaceholder":197},[145,1499,1500,1503],{"class":113,"line":609},[145,1501,1502],{"class":184},"ws.append([])                                ",[145,1504,1505],{"class":1050},"# spacer\n",[145,1507,1508,1510,1513,1515,1518,1520,1522,1525,1527,1530],{"class":113,"line":626},[145,1509,249],{"class":184},[145,1511,1512],{"class":154},"\"\"",[145,1514,255],{"class":184},[145,1516,1517],{"class":154},"\"Total\"",[145,1519,255],{"class":184},[145,1521,309],{"class":218},[145,1523,1524],{"class":184},"(total, ",[145,1526,323],{"class":218},[145,1528,1529],{"class":184},")])    ",[145,1531,1532],{"class":1050},"# written last, as a value\n",[145,1534,1535],{"class":113,"line":640},[145,1536,198],{"emptyLinePlaceholder":197},[145,1538,1539,1541,1544],{"class":113,"line":652},[145,1540,337],{"class":184},[145,1542,1543],{"class":154},"\"streamed_with_total.xlsx\"",[145,1545,222],{"class":184},[10,1547,1548,1549,1552,1553,1557],{},"A ",[14,1550,1551],{},"=SUM(C2:C4)"," formula would work equally well here, because it can be appended as the last row — what you cannot do is put the total at the ",[1554,1555,1556],"em",{},"top",". When a summary must appear first, write it to its own sheet and make that sheet active, which is a better layout anyway for a file with hundreds of thousands of detail rows.",[131,1559,1561],{"id":1560},"streaming-from-source-to-output","Streaming from source to output",[10,1563,1564],{},"The two streaming modes compose. Reading a large workbook row by row and writing a transformed one row by row keeps peak memory at a couple of rows regardless of how big either file is:",[136,1566,1568],{"className":171,"code":1567,"language":173,"meta":141,"style":141},"from openpyxl import Workbook, load_workbook\n\nsrc = load_workbook(\"large.xlsx\", read_only=True, data_only=True)\nout = Workbook(write_only=True)\n\ntry:\n    ws_in = src[\"Orders\"]\n    ws_out = out.create_sheet(\"Enriched\")\n\n    rows = ws_in.iter_rows(values_only=True)\n    header = list(next(rows))\n    qty, price = header.index(\"Quantity\"), header.index(\"Unit_Price\")\n    ws_out.append(header + [\"Revenue\"])\n\n    written = 0\n    for row in rows:\n        revenue = round((row[qty] or 0) * (row[price] or 0), 2)\n        ws_out.append(list(row) + [revenue])\n        written += 1\nfinally:\n    src.close()\n\nout.save(\"enriched_streamed.xlsx\")\nprint(f\"{written:,} row(s) transformed without holding either sheet\")\n",[14,1569,1570,1581,1585,1618,1635,1639,1647,1662,1677,1681,1700,1718,1737,1751,1755,1765,1777,1814,1829,1839,1846,1851,1855,1865],{"__ignoreMap":141},[145,1571,1572,1574,1576,1578],{"class":113,"line":147},[145,1573,181],{"class":180},[145,1575,185],{"class":184},[145,1577,188],{"class":180},[145,1579,1580],{"class":184}," Workbook, load_workbook\n",[145,1582,1583],{"class":113,"line":194},[145,1584,198],{"emptyLinePlaceholder":197},[145,1586,1587,1590,1592,1595,1598,1600,1603,1605,1607,1609,1612,1614,1616],{"class":113,"line":201},[145,1588,1589],{"class":184},"src ",[145,1591,207],{"class":180},[145,1593,1594],{"class":184}," load_workbook(",[145,1596,1597],{"class":154},"\"large.xlsx\"",[145,1599,255],{"class":184},[145,1601,1602],{"class":213},"read_only",[145,1604,207],{"class":180},[145,1606,219],{"class":218},[145,1608,255],{"class":184},[145,1610,1611],{"class":213},"data_only",[145,1613,207],{"class":180},[145,1615,219],{"class":218},[145,1617,222],{"class":184},[145,1619,1620,1623,1625,1627,1629,1631,1633],{"class":113,"line":225},[145,1621,1622],{"class":184},"out ",[145,1624,207],{"class":180},[145,1626,210],{"class":184},[145,1628,20],{"class":213},[145,1630,207],{"class":180},[145,1632,219],{"class":218},[145,1634,222],{"class":184},[145,1636,1637],{"class":113,"line":241},[145,1638,198],{"emptyLinePlaceholder":197},[145,1640,1641,1644],{"class":113,"line":246},[145,1642,1643],{"class":180},"try",[145,1645,1646],{"class":184},":\n",[145,1648,1649,1652,1654,1657,1659],{"class":113,"line":269},[145,1650,1651],{"class":184},"    ws_in ",[145,1653,207],{"class":180},[145,1655,1656],{"class":184}," src[",[145,1658,635],{"class":154},[145,1660,1661],{"class":184},"]\n",[145,1663,1664,1667,1669,1672,1675],{"class":113,"line":298},[145,1665,1666],{"class":184},"    ws_out ",[145,1668,207],{"class":180},[145,1670,1671],{"class":184}," out.create_sheet(",[145,1673,1674],{"class":154},"\"Enriched\"",[145,1676,222],{"class":184},[145,1678,1679],{"class":113,"line":329},[145,1680,198],{"emptyLinePlaceholder":197},[145,1682,1683,1686,1688,1691,1694,1696,1698],{"class":113,"line":334},[145,1684,1685],{"class":184},"    rows ",[145,1687,207],{"class":180},[145,1689,1690],{"class":184}," ws_in.iter_rows(",[145,1692,1693],{"class":213},"values_only",[145,1695,207],{"class":180},[145,1697,219],{"class":218},[145,1699,222],{"class":184},[145,1701,1702,1705,1707,1710,1712,1715],{"class":113,"line":345},[145,1703,1704],{"class":184},"    header ",[145,1706,207],{"class":180},[145,1708,1709],{"class":218}," list",[145,1711,284],{"class":184},[145,1713,1714],{"class":218},"next",[145,1716,1717],{"class":184},"(rows))\n",[145,1719,1720,1723,1725,1728,1730,1733,1735],{"class":113,"line":568},[145,1721,1722],{"class":184},"    qty, price ",[145,1724,207],{"class":180},[145,1726,1727],{"class":184}," header.index(",[145,1729,584],{"class":154},[145,1731,1732],{"class":184},"), header.index(",[145,1734,594],{"class":154},[145,1736,222],{"class":184},[145,1738,1739,1742,1745,1747,1749],{"class":113,"line":604},[145,1740,1741],{"class":184},"    ws_out.append(header ",[145,1743,1744],{"class":180},"+",[145,1746,879],{"class":184},[145,1748,263],{"class":154},[145,1750,266],{"class":184},[145,1752,1753],{"class":113,"line":609},[145,1754,198],{"emptyLinePlaceholder":197},[145,1756,1757,1760,1762],{"class":113,"line":626},[145,1758,1759],{"class":184},"    written ",[145,1761,207],{"class":180},[145,1763,1764],{"class":218}," 0\n",[145,1766,1767,1770,1772,1774],{"class":113,"line":640},[145,1768,1769],{"class":180},"    for",[145,1771,657],{"class":184},[145,1773,278],{"class":180},[145,1775,1776],{"class":184}," rows:\n",[145,1778,1779,1782,1784,1787,1790,1793,1796,1798,1800,1803,1805,1807,1810,1812],{"class":113,"line":652},[145,1780,1781],{"class":184},"        revenue ",[145,1783,207],{"class":180},[145,1785,1786],{"class":218}," round",[145,1788,1789],{"class":184},"((row[qty] ",[145,1791,1792],{"class":180},"or",[145,1794,1795],{"class":218}," 0",[145,1797,1196],{"class":184},[145,1799,315],{"class":180},[145,1801,1802],{"class":184}," (row[price] ",[145,1804,1792],{"class":180},[145,1806,1795],{"class":218},[145,1808,1809],{"class":184},"), ",[145,1811,323],{"class":218},[145,1813,222],{"class":184},[145,1815,1816,1819,1821,1824,1826],{"class":113,"line":684},[145,1817,1818],{"class":184},"        ws_out.append(",[145,1820,646],{"class":218},[145,1822,1823],{"class":184},"(row) ",[145,1825,1744],{"class":180},[145,1827,1828],{"class":184}," [revenue])\n",[145,1830,1831,1834,1836],{"class":113,"line":695},[145,1832,1833],{"class":184},"        written ",[145,1835,1485],{"class":180},[145,1837,1838],{"class":218}," 1\n",[145,1840,1841,1844],{"class":113,"line":1000},[145,1842,1843],{"class":180},"finally",[145,1845,1646],{"class":184},[145,1847,1848],{"class":113,"line":1017},[145,1849,1850],{"class":184},"    src.close()\n",[145,1852,1853],{"class":113,"line":1028},[145,1854,198],{"emptyLinePlaceholder":197},[145,1856,1857,1860,1863],{"class":113,"line":1034},[145,1858,1859],{"class":184},"out.save(",[145,1861,1862],{"class":154},"\"enriched_streamed.xlsx\"",[145,1864,222],{"class":184},[145,1866,1867,1869,1871,1874,1877,1880,1883,1886,1889,1892],{"class":113,"line":1039},[145,1868,348],{"class":218},[145,1870,284],{"class":184},[145,1872,1873],{"class":180},"f",[145,1875,1876],{"class":154},"\"",[145,1878,1174],{"class":1879},"sSjpA",[145,1881,1882],{"class":184},"written",[145,1884,1885],{"class":180},":,",[145,1887,1888],{"class":1879},"}",[145,1890,1891],{"class":154}," row(s) transformed without holding either sheet\"",[145,1893,222],{"class":184},[10,1895,1896],{},"This is the shape most \"add a calculated column to a huge export\" jobs should take. Nothing accumulates, the transformation is a couple of arithmetic operations per row, and the job's memory profile is flat enough to run in a small container.",[29,1898,38,1903,38,1906,38,1909,38,1912,38,1918,38,1922,38,1926,38,1932,38,1937,38,1942,38,1947,38,1951,38,1955,38,1958,38,1961,38,1965,38,1968,38,1973],{"viewBox":1899,"role":32,"ariaLabelledBy":1900,"xmlns":36,"style":37},"0 0 740 194",[1901,1902],"wo-pipe-t","wo-pipe-d",[40,1904,1905],{"id":1901},"A read-only stream feeding a write-only stream",[44,1907,1908],{"id":1902},"Rows flow from the source workbook through a per-row transformation into the output workbook. Only the current row exists in memory at any moment, so the size of neither file affects peak memory.",[48,1910],{"x":50,"y":50,"width":51,"height":1911,"fill":53},"194",[48,1913],{"x":1914,"y":1262,"width":1915,"height":1916,"rx":1917,"fill":101,"stroke":102,"style":69},"24","180","76","12",[55,1919,1921],{"x":1920,"y":1267,"style":1290},"114","large.xlsx",[55,1923,1925],{"x":1920,"y":1924,"style":87},"112","read_only stream",[113,1927],{"x1":1928,"y1":1929,"x2":1930,"y2":1929,"stroke":1931,"style":69},"208","96","248","var(--muted,#5b6780)",[1933,1934],"polygon",{"points":1935,"fill":1936},"252,96 240,90 240,102","#5b6780",[48,1938],{"x":1939,"y":1262,"width":1940,"height":1916,"rx":1917,"fill":1941},"254","212","#5b5cf0",[55,1943,1946],{"x":1944,"y":1267,"style":1945},"360","font-size:12.5px;font-weight:700;fill:#ffffff;text-anchor:middle","one row in flight",[55,1948,1950],{"x":1944,"y":1924,"style":1949},"font-size:11.5px;fill:rgba(255,255,255,0.88);text-anchor:middle","compute Revenue",[113,1952],{"x1":1953,"y1":1929,"x2":1954,"y2":1929,"stroke":1931,"style":69},"470","510",[1933,1956],{"points":1957,"fill":1936},"514,96 502,90 502,102",[48,1959],{"x":1960,"y":1262,"width":549,"height":1916,"rx":1917,"fill":67,"stroke":68,"style":69},"516",[55,1962,1964],{"x":1963,"y":1267,"style":1263},"616","enriched.xlsx",[55,1966,1967],{"x":1963,"y":1924,"style":87},"write_only stream",[55,1969,1972],{"x":1289,"y":1970,"style":1971},"34","font-size:12.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","Neither file is ever fully resident.",[55,1974,1975],{"x":1289,"y":1258,"style":87},"peak memory ≈ two rows + the shared string table",[131,1977,1979],{"id":1978},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1981,1982,1983,1999],"table",{},[1984,1985,1986],"thead",{},[1987,1988,1989,1993,1996],"tr",{},[1990,1991,1992],"th",{},"Symptom",[1990,1994,1995],{},"Cause",[1990,1997,1998],{},"Fix",[2000,2001,2002,2024,2046,2060,2077,2093,2104],"tbody",{},[1987,2003,2004,2010,2017],{},[2005,2006,2007],"td",{},[14,2008,2009],{},"AttributeError: 'NoneType' object has no attribute 'append'",[2005,2011,2012,369,2014,2016],{},[14,2013,368],{},[14,2015,372],{}," in write-only mode",[2005,2018,2019,2020,2023],{},"Call ",[14,2021,2022],{},"wb.create_sheet(...)"," first",[1987,2025,2026,2032,2040],{},[2005,2027,2028,2031],{},[14,2029,2030],{},"Cell is read-only"," or index errors",[2005,2033,2034,2035,2037,2038],{},"Tried ",[14,2036,380],{}," or ",[14,2039,384],{},[2005,2041,2042,2043,2045],{},"Only ",[14,2044,376],{}," is supported",[1987,2047,2048,2051,2054],{},[2005,2049,2050],{},"Styling ignored",[2005,2052,2053],{},"Style applied after the row was appended",[2005,2055,2056,2057,2059],{},"Build ",[14,2058,723],{}," objects before appending",[1987,2061,2062,2065,2068],{},[2005,2063,2064],{},"Workbook still huge in memory",[2005,2066,2067],{},"A style object created per cell",[2005,2069,2070,2071,2073,2074,2076],{},"Reuse ",[14,2072,1092],{},"\u002F",[14,2075,1096],{}," instances",[1987,2078,2079,2084,2087],{},[2005,2080,2081,2083],{},[14,2082,394],{}," fails on the write-only book",[2005,2085,2086],{},"pandas needs a normal worksheet",[2005,2088,2089,2090,2092],{},"Iterate with ",[14,2091,398],{},", or use xlsxwriter",[1987,2094,2095,2098,2101],{},[2005,2096,2097],{},"Totals cannot be added at the top",[2005,2099,2100],{},"Rows cannot be revisited",[2005,2102,2103],{},"Put the summary on its own sheet",[1987,2105,2106,2109,2115],{},[2005,2107,2108],{},"Column widths missing",[2005,2110,2111,2112],{},"Set after ",[14,2113,2114],{},"save()",[2005,2116,2117,2118,2121],{},"Set ",[14,2119,2120],{},"column_dimensions"," before saving",[131,2123,2125],{"id":2124},"splitting-output-across-sheets","Splitting output across sheets",[10,2127,2128,2129,2131],{},"A single sheet caps at 1,048,576 rows, and long before that a reader gives up. Write-only mode handles the split naturally, because a new sheet is just another ",[14,2130,364],{}," before the next batch of appends:",[136,2133,2135],{"className":171,"code":2134,"language":173,"meta":141,"style":141},"from openpyxl import Workbook\n\nROWS_PER_SHEET = 200_000\nheader = [\"Order_ID\", \"Region\", \"Revenue\"]\n\nwb = Workbook(write_only=True)\nws = None\nfor i, row in enumerate(df.itertuples(index=False, name=None)):\n    if i % ROWS_PER_SHEET == 0:\n        ws = wb.create_sheet(f\"Part_{i \u002F\u002F ROWS_PER_SHEET + 1}\")\n        ws.append(header)\n    ws.append(list(row))\n\nwb.save(\"split_output.xlsx\")\nprint(\"sheets:\", wb.sheetnames)\n",[14,2136,2137,2147,2151,2162,2182,2186,2202,2211,2243,2264,2300,2305,2313,2317,2326],{"__ignoreMap":141},[145,2138,2139,2141,2143,2145],{"class":113,"line":147},[145,2140,181],{"class":180},[145,2142,185],{"class":184},[145,2144,188],{"class":180},[145,2146,191],{"class":184},[145,2148,2149],{"class":113,"line":194},[145,2150,198],{"emptyLinePlaceholder":197},[145,2152,2153,2156,2159],{"class":113,"line":201},[145,2154,2155],{"class":218},"ROWS_PER_SHEET",[145,2157,2158],{"class":180}," =",[145,2160,2161],{"class":218}," 200_000\n",[145,2163,2164,2166,2168,2170,2172,2174,2176,2178,2180],{"class":113,"line":225},[145,2165,862],{"class":184},[145,2167,207],{"class":180},[145,2169,879],{"class":184},[145,2171,252],{"class":154},[145,2173,255],{"class":184},[145,2175,258],{"class":154},[145,2177,255],{"class":184},[145,2179,263],{"class":154},[145,2181,1661],{"class":184},[145,2183,2184],{"class":113,"line":241},[145,2185,198],{"emptyLinePlaceholder":197},[145,2187,2188,2190,2192,2194,2196,2198,2200],{"class":113,"line":246},[145,2189,204],{"class":184},[145,2191,207],{"class":180},[145,2193,210],{"class":184},[145,2195,20],{"class":213},[145,2197,207],{"class":180},[145,2199,219],{"class":218},[145,2201,222],{"class":184},[145,2203,2204,2206,2208],{"class":113,"line":269},[145,2205,228],{"class":184},[145,2207,207],{"class":180},[145,2209,2210],{"class":218}," None\n",[145,2212,2213,2215,2218,2220,2223,2226,2228,2230,2232,2234,2236,2238,2240],{"class":113,"line":298},[145,2214,272],{"class":180},[145,2216,2217],{"class":184}," i, row ",[145,2219,278],{"class":180},[145,2221,2222],{"class":218}," enumerate",[145,2224,2225],{"class":184},"(df.itertuples(",[145,2227,665],{"class":213},[145,2229,207],{"class":180},[145,2231,670],{"class":218},[145,2233,255],{"class":184},[145,2235,675],{"class":213},[145,2237,207],{"class":180},[145,2239,372],{"class":218},[145,2241,2242],{"class":184},")):\n",[145,2244,2245,2248,2251,2254,2257,2260,2262],{"class":113,"line":329},[145,2246,2247],{"class":180},"    if",[145,2249,2250],{"class":184}," i ",[145,2252,2253],{"class":180},"%",[145,2255,2256],{"class":218}," ROWS_PER_SHEET",[145,2258,2259],{"class":180}," ==",[145,2261,1795],{"class":218},[145,2263,1646],{"class":184},[145,2265,2266,2269,2271,2273,2275,2278,2280,2283,2286,2288,2291,2294,2296,2298],{"class":113,"line":334},[145,2267,2268],{"class":184},"        ws ",[145,2270,207],{"class":180},[145,2272,233],{"class":184},[145,2274,1873],{"class":180},[145,2276,2277],{"class":154},"\"Part_",[145,2279,1174],{"class":1879},[145,2281,2282],{"class":184},"i ",[145,2284,2285],{"class":180},"\u002F\u002F",[145,2287,2256],{"class":218},[145,2289,2290],{"class":180}," +",[145,2292,2293],{"class":218}," 1",[145,2295,1888],{"class":1879},[145,2297,1876],{"class":154},[145,2299,222],{"class":184},[145,2301,2302],{"class":113,"line":345},[145,2303,2304],{"class":184},"        ws.append(header)\n",[145,2306,2307,2309,2311],{"class":113,"line":568},[145,2308,687],{"class":184},[145,2310,646],{"class":218},[145,2312,692],{"class":184},[145,2314,2315],{"class":113,"line":604},[145,2316,198],{"emptyLinePlaceholder":197},[145,2318,2319,2321,2324],{"class":113,"line":609},[145,2320,337],{"class":184},[145,2322,2323],{"class":154},"\"split_output.xlsx\"",[145,2325,222],{"class":184},[145,2327,2328,2330,2332,2335],{"class":113,"line":626},[145,2329,348],{"class":218},[145,2331,284],{"class":184},[145,2333,2334],{"class":154},"\"sheets:\"",[145,2336,2337],{"class":184},", wb.sheetnames)\n",[10,2339,2340,2341,2344,2345,2348],{},"Repeating the header on each part is what makes the split usable — a reader who opens ",[14,2342,2343],{},"Part_3"," should not have to go back to ",[14,2346,2347],{},"Part_1"," to learn what the columns are. If the consumer is a machine rather than a person, splitting into several files is usually kinder still, and each one can then be read independently.",[131,2350,2352],{"id":2351},"performance-and-scale-notes","Performance and scale notes",[10,2354,2355,2356,2358,2359,2361],{},"Streaming turns peak memory from \"the whole sheet\" into \"one row plus the shared string table\" — and that string table is the remaining growth term, so a column of a million distinct free-text values still costs. Categorical text is nearly free by comparison, since each distinct string is stored once. On raw speed, xlsxwriter in ",[14,2357,1228],{}," mode is typically the fastest generator, openpyxl ",[14,2360,20],{}," close behind, and both are several times faster than the ordinary path once you are past a hundred thousand rows. If the output is only ever read by machines, writing CSV or Parquet instead is faster still by an order of magnitude.",[131,2363,2365],{"id":2364},"conclusion","Conclusion",[10,2367,2368,2370,2371,2373,2374,2376,2377,2379],{},[14,2369,97],{}," streams rows straight to disk, so generating a 300,000-row workbook costs roughly the memory of one row. In exchange you append in order, style with ",[14,2372,723],{}," at append time, reuse style objects, and plan anything that would normally be added afterwards. When the data is already a DataFrame and you want ",[14,2375,394],{},", xlsxwriter's ",[14,2378,1228],{}," gives the same benefit with less code.",[131,2381,2383],{"id":2382},"frequently-asked-questions","Frequently asked questions",[10,2385,2386,2393,2394,2396],{},[2387,2388,2389,2390,2392],"strong",{},"Can I still style cells in ",[14,2391,20],{}," mode?","\nYes, per cell, using ",[14,2395,723],{}," — but you must style each cell as you append its row, because rows cannot be revisited afterwards.",[10,2398,2399,2408,2409,2411,2412,2414,2415,2417,2418,2421],{},[2387,2400,2401,2402,2404,2405,2407],{},"Can I use ",[14,2403,20],{}," with pandas ",[14,2406,394],{},"?","\nNot directly. ",[14,2410,394],{}," needs a normal worksheet. Either iterate the DataFrame yourself with ",[14,2413,398],{},", or use xlsxwriter's ",[14,2416,1228],{}," option through ",[14,2419,2420],{},"ExcelWriter",".",[10,2423,2424,2430],{},[2387,2425,2426,2427,2429],{},"Does ",[14,2428,20],{}," work with an existing file?","\nNo. A write-only workbook is created from scratch. To add a large sheet to an existing file, generate it separately and combine, or accept a normal load.",[10,2432,2433,2436],{},[2387,2434,2435],{},"How much memory does it actually save?","\nPeak memory becomes roughly one row plus the shared string table, instead of the whole sheet — the difference between hundreds of megabytes and a few.",[131,2438,2440],{"id":2439},"related","Related",[10,2442,2443],{},"Up to the parent guide:",[2445,2446,2447],"ul",{},[2448,2449,2450,2452],"li",{},[23,2451,26],{"href":25}," — reading, writing and format choices at scale.",[10,2454,2455],{},"Related guides:",[2445,2457,2458,2465,2472,2479],{},[2448,2459,2460,2464],{},[23,2461,2463],{"href":2462},"\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"," — the same streaming idea on the way in.",[2448,2466,2467,2471],{},[23,2468,2470],{"href":2469},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File"," — layout when the output is several sheets.",[2448,2473,2474,2478],{},[23,2475,2477],{"href":2476},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter\u002F","openpyxl vs xlsxwriter vs pandas.ExcelWriter"," — how the engines differ.",[2448,2480,2481,2485],{},[23,2482,2484],{"href":2483},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002F","Convert Excel to CSV with Python"," — when the output does not need to be a workbook.",[2487,2488,2489],"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 .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":141,"searchDepth":194,"depth":194,"links":2491},[2492,2493,2494,2495,2496,2497,2498,2499,2500,2501,2502,2503,2504],{"id":133,"depth":194,"text":134},{"id":167,"depth":194,"text":168},{"id":388,"depth":194,"text":389},{"id":716,"depth":194,"text":717},{"id":1107,"depth":194,"text":1108},{"id":1329,"depth":194,"text":1330},{"id":1560,"depth":194,"text":1561},{"id":1978,"depth":194,"text":1979},{"id":2124,"depth":194,"text":2125},{"id":2351,"depth":194,"text":2352},{"id":2364,"depth":194,"text":2365},{"id":2382,"depth":194,"text":2383},{"id":2439,"depth":194,"text":2440},"2026-08-01","Generate hundred-thousand-row workbooks without the memory — openpyxl write_only, xlsxwriter constant_memory, streaming a DataFrame row by row, and the formatting you must plan ahead.","md",[2509,2512,2515,2518],{"q":2510,"a":2511},"Can I still style cells in write_only mode?","Yes, per cell, using WriteOnlyCell — but you must style each cell as you append its row, because rows cannot be revisited afterwards.",{"q":2513,"a":2514},"Can I use write_only with pandas to_excel?","Not directly. to_excel needs a normal worksheet. Either iterate the DataFrame yourself with itertuples, or use xlsxwriter's constant_memory option through ExcelWriter.",{"q":2516,"a":2517},"Does write_only work with an existing file?","No. A write-only workbook is created from scratch. To add a large sheet to an existing file, generate it separately and combine, or accept a normal load.",{"q":2435,"a":2519},"Peak memory becomes roughly one row plus the shared string table, instead of the whole sheet — the difference between hundreds of megabytes and a few.",{"breadcrumb":2521},[2522,2524,2527,2528],{"name":2523,"item":2073},"Home",{"name":2525,"item":2526},"Advanced Data Transformation and Cleaning","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},{"name":5,"item":2529},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002F","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode",{"title":5,"description":2532},"Stream large Excel output from Python: openpyxl Workbook(write_only=True), WriteOnlyCell for styling, xlsxwriter constant_memory, and how to feed a DataFrame into either.","write-large-dataframes-to-excel-with-write-only-mode","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002Findex","how-to","Fz3bpuK_o1OxiTR_ybNw8OzMzUcvn9k_uULL_JP8QhQ",[2538,2541],{"title":2463,"path":2539,"stem":2540,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002Findex",{"title":2542,"path":2543,"stem":2544,"children":-1},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows","automating-reporting-workflows\u002Findex",1785584462692]