[{"data":1,"prerenderedAt":2534},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl":2499,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl":2508,"children:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl":2513,"related:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl":2514},{"id":4,"title":5,"body":6,"dateModified":2465,"datePublished":2465,"description":2466,"extension":2467,"faq":2468,"keyword":2477,"kind":2478,"meta":2479,"navigation":118,"path":2488,"seo":2489,"slug":2492,"stem":2493,"topics":2494,"type":2497,"__hash__":2498},"docs\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl\u002Findex.md","Delete Empty Rows and Columns with openpyxl",{"type":7,"value":8,"toc":2453},"minimark",[9,36,41,66,86,90,346,373,377,391,585,923,930,934,943,1055,1069,1073,1080,1273,1399,1410,1863,1885,1889,1895,2183,2203,2324,2328,2373,2377,2388,2392,2398,2404,2410,2416,2420,2449],[10,11,12,13,17,18,21,22,25,26,29,30,35],"p",{},"Exports from accounting and ERP systems love blank rows: a spacer after every group, an empty column where a field used to be, a block of formatted-but-empty rows at the bottom that makes ",[14,15,16],"code",{},"max_row"," report 5,000 when the data stops at 800. With pandas, dropping blank rows is one ",[14,19,20],{},"dropna(how=\"all\")"," — but pandas rewrites the file and throws away the fonts, fills, widths and number formats someone set up. When the formatting has to stay, the edit has to happen in openpyxl, on the worksheet itself. openpyxl has ",[14,23,24],{},"delete_rows"," and ",[14,27,28],{},"delete_cols",", and they work, but they are slow when called row by row and they leave several things unadjusted. This page covers detection, deletion in the right order, the repairs, and a much faster rebuild for large sheets. It sits under ",[31,32,34],"a",{"href":33},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",".",[37,38,40],"h2",{"id":39},"prerequisites","Prerequisites",[42,43,48],"pre",{"className":44,"code":45,"language":46,"meta":47,"style":47},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,49,50],{"__ignoreMap":47},[51,52,55,59,63],"span",{"class":53,"line":54},"line",1,[51,56,58],{"class":57},"sMTad","pip",[51,60,62],{"class":61},"srMev"," install",[51,64,65],{"class":61}," openpyxl\n",[10,67,68,71,72,75,76,79,80,82,83,85],{},[14,69,70],{},"export.xlsx"," has a styled header, six data rows separated by blank spacer rows, an empty column C (only its header cell exists, and it is blank), a ",[14,73,74],{},"Total"," row with a ",[14,77,78],{},"SUM"," formula, a tall row, a ",[14,81,74],{}," label merged across A:B, and 39 formatted-but-empty rows below the data — so ",[14,84,16],{}," overstates the size of the sheet.",[37,87,89],{"id":88},"step-1-find-the-empty-rows-without-creating-cells","Step 1 — Find the empty rows without creating cells",[42,91,95],{"className":92,"code":93,"language":94,"meta":47,"style":47},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nwb = load_workbook(\"export.xlsx\")\nws = wb[\"Export\"]\nprint(\"max_row before:\", ws.max_row)\n\ndef is_blank(v):\n    return v is None or (isinstance(v, str) and v.strip() == \"\")\n\nempty_rows = [i for i, row in enumerate(ws.iter_rows(values_only=True), start=1)\n              if all(is_blank(v) for v in row)]\nprint(\"empty rows:\", empty_rows[:6], \"...\", len(empty_rows), \"in total\")\n","python",[14,96,97,113,120,138,155,171,176,189,236,241,289,310],{"__ignoreMap":47},[51,98,99,103,107,110],{"class":53,"line":54},[51,100,102],{"class":101},"s-kum","from",[51,104,106],{"class":105},"skGVy"," openpyxl ",[51,108,109],{"class":101},"import",[51,111,112],{"class":105}," load_workbook\n",[51,114,116],{"class":53,"line":115},2,[51,117,119],{"emptyLinePlaceholder":118},true,"\n",[51,121,123,126,129,132,135],{"class":53,"line":122},3,[51,124,125],{"class":105},"wb ",[51,127,128],{"class":101},"=",[51,130,131],{"class":105}," load_workbook(",[51,133,134],{"class":61},"\"export.xlsx\"",[51,136,137],{"class":105},")\n",[51,139,141,144,146,149,152],{"class":53,"line":140},4,[51,142,143],{"class":105},"ws ",[51,145,128],{"class":101},[51,147,148],{"class":105}," wb[",[51,150,151],{"class":61},"\"Export\"",[51,153,154],{"class":105},"]\n",[51,156,158,162,165,168],{"class":53,"line":157},5,[51,159,161],{"class":160},"sP0c6","print",[51,163,164],{"class":105},"(",[51,166,167],{"class":61},"\"max_row before:\"",[51,169,170],{"class":105},", ws.max_row)\n",[51,172,174],{"class":53,"line":173},6,[51,175,119],{"emptyLinePlaceholder":118},[51,177,179,182,186],{"class":53,"line":178},7,[51,180,181],{"class":101},"def",[51,183,185],{"class":184},"s_Opv"," is_blank",[51,187,188],{"class":105},"(v):\n",[51,190,192,195,198,201,204,207,210,213,216,219,222,225,228,231,234],{"class":53,"line":191},8,[51,193,194],{"class":101},"    return",[51,196,197],{"class":105}," v ",[51,199,200],{"class":101},"is",[51,202,203],{"class":160}," None",[51,205,206],{"class":101}," or",[51,208,209],{"class":105}," (",[51,211,212],{"class":160},"isinstance",[51,214,215],{"class":105},"(v, ",[51,217,218],{"class":160},"str",[51,220,221],{"class":105},") ",[51,223,224],{"class":101},"and",[51,226,227],{"class":105}," v.strip() ",[51,229,230],{"class":101},"==",[51,232,233],{"class":61}," \"\"",[51,235,137],{"class":105},[51,237,239],{"class":53,"line":238},9,[51,240,119],{"emptyLinePlaceholder":118},[51,242,244,247,249,252,255,258,261,264,267,271,273,276,279,282,284,287],{"class":53,"line":243},10,[51,245,246],{"class":105},"empty_rows ",[51,248,128],{"class":101},[51,250,251],{"class":105}," [i ",[51,253,254],{"class":101},"for",[51,256,257],{"class":105}," i, row ",[51,259,260],{"class":101},"in",[51,262,263],{"class":160}," enumerate",[51,265,266],{"class":105},"(ws.iter_rows(",[51,268,270],{"class":269},"sa561","values_only",[51,272,128],{"class":101},[51,274,275],{"class":160},"True",[51,277,278],{"class":105},"), ",[51,280,281],{"class":269},"start",[51,283,128],{"class":101},[51,285,286],{"class":160},"1",[51,288,137],{"class":105},[51,290,292,295,298,301,303,305,307],{"class":53,"line":291},11,[51,293,294],{"class":101},"              if",[51,296,297],{"class":160}," all",[51,299,300],{"class":105},"(is_blank(v) ",[51,302,254],{"class":101},[51,304,197],{"class":105},[51,306,260],{"class":101},[51,308,309],{"class":105}," row)]\n",[51,311,313,315,317,320,323,326,329,332,335,338,341,344],{"class":53,"line":312},12,[51,314,161],{"class":160},[51,316,164],{"class":105},[51,318,319],{"class":61},"\"empty rows:\"",[51,321,322],{"class":105},", empty_rows[:",[51,324,325],{"class":160},"6",[51,327,328],{"class":105},"], ",[51,330,331],{"class":61},"\"...\"",[51,333,334],{"class":105},", ",[51,336,337],{"class":160},"len",[51,339,340],{"class":105},"(empty_rows), ",[51,342,343],{"class":61},"\"in total\"",[51,345,137],{"class":105},[10,347,348,349,352,353,356,357,360,361,365,366,368,369,372],{},"Use ",[14,350,351],{},"iter_rows(values_only=True)"," for the scan. The tempting alternative, ",[14,354,355],{},"ws[r]"," or ",[14,358,359],{},"ws.cell(r, c)"," inside a loop, ",[362,363,364],"em",{},"creates"," any cell it touches, so checking whether a row is empty quietly fills it with empty cell objects and makes ",[14,367,16],{}," worse. ",[14,370,371],{},"is_blank"," treats whitespace-only strings as empty, which is usually what you want for exported data; drop the second condition if a space is meaningful. Rows that carry formatting but no value count as empty here — that is what lets the formatted tail at the bottom go too.",[37,374,376],{"id":375},"step-2-delete-in-runs-from-the-bottom-up","Step 2 — Delete in runs, from the bottom up",[10,378,379,382,383,386,387,390],{},[14,380,381],{},"delete_rows(idx, amount)"," shifts every row below ",[14,384,385],{},"idx"," up by ",[14,388,389],{},"amount",". Deleting from the top would make every remaining index in the list wrong after the first deletion; deleting from the bottom keeps the rows above untouched. Grouping consecutive rows into runs means one call per run instead of one per row:",[42,392,394],{"className":92,"code":393,"language":94,"meta":47,"style":47},"def runs(indices):\n    out = []\n    for i in indices:\n        if out and out[-1][0] + out[-1][1] == i:\n            out[-1][1] += 1\n        else:\n            out.append([i, 1])\n    return out\n\nblocks = runs(empty_rows)\nprint(\"delete calls needed:\", len(blocks))\nfor start, amount in reversed(blocks):\n    ws.delete_rows(start, amount)\nprint(\"max_row after:\", ws.max_row)\n",[14,395,396,406,416,429,476,497,505,515,522,526,536,552,567,573],{"__ignoreMap":47},[51,397,398,400,403],{"class":53,"line":54},[51,399,181],{"class":101},[51,401,402],{"class":184}," runs",[51,404,405],{"class":105},"(indices):\n",[51,407,408,411,413],{"class":53,"line":115},[51,409,410],{"class":105},"    out ",[51,412,128],{"class":101},[51,414,415],{"class":105}," []\n",[51,417,418,421,424,426],{"class":53,"line":122},[51,419,420],{"class":101},"    for",[51,422,423],{"class":105}," i ",[51,425,260],{"class":101},[51,427,428],{"class":105}," indices:\n",[51,430,431,434,437,439,442,445,447,450,453,456,459,461,463,465,467,469,471,473],{"class":53,"line":140},[51,432,433],{"class":101},"        if",[51,435,436],{"class":105}," out ",[51,438,224],{"class":101},[51,440,441],{"class":105}," out[",[51,443,444],{"class":101},"-",[51,446,286],{"class":160},[51,448,449],{"class":105},"][",[51,451,452],{"class":160},"0",[51,454,455],{"class":105},"] ",[51,457,458],{"class":101},"+",[51,460,441],{"class":105},[51,462,444],{"class":101},[51,464,286],{"class":160},[51,466,449],{"class":105},[51,468,286],{"class":160},[51,470,455],{"class":105},[51,472,230],{"class":101},[51,474,475],{"class":105}," i:\n",[51,477,478,481,483,485,487,489,491,494],{"class":53,"line":157},[51,479,480],{"class":105},"            out[",[51,482,444],{"class":101},[51,484,286],{"class":160},[51,486,449],{"class":105},[51,488,286],{"class":160},[51,490,455],{"class":105},[51,492,493],{"class":101},"+=",[51,495,496],{"class":160}," 1\n",[51,498,499,502],{"class":53,"line":173},[51,500,501],{"class":101},"        else",[51,503,504],{"class":105},":\n",[51,506,507,510,512],{"class":53,"line":178},[51,508,509],{"class":105},"            out.append([i, ",[51,511,286],{"class":160},[51,513,514],{"class":105},"])\n",[51,516,517,519],{"class":53,"line":191},[51,518,194],{"class":101},[51,520,521],{"class":105}," out\n",[51,523,524],{"class":53,"line":238},[51,525,119],{"emptyLinePlaceholder":118},[51,527,528,531,533],{"class":53,"line":243},[51,529,530],{"class":105},"blocks ",[51,532,128],{"class":101},[51,534,535],{"class":105}," runs(empty_rows)\n",[51,537,538,540,542,545,547,549],{"class":53,"line":291},[51,539,161],{"class":160},[51,541,164],{"class":105},[51,543,544],{"class":61},"\"delete calls needed:\"",[51,546,334],{"class":105},[51,548,337],{"class":160},[51,550,551],{"class":105},"(blocks))\n",[51,553,554,556,559,561,564],{"class":53,"line":312},[51,555,254],{"class":101},[51,557,558],{"class":105}," start, amount ",[51,560,260],{"class":101},[51,562,563],{"class":160}," reversed",[51,565,566],{"class":105},"(blocks):\n",[51,568,570],{"class":53,"line":569},13,[51,571,572],{"class":105},"    ws.delete_rows(start, amount)\n",[51,574,576,578,580,583],{"class":53,"line":575},14,[51,577,161],{"class":160},[51,579,164],{"class":105},[51,581,582],{"class":61},"\"max_row after:\"",[51,584,170],{"class":105},[586,587,595,596,595,600,595,604,595,611,595,620,595,624,595,631,595,635,595,639,595,643,595,647,595,651,595,655,595,660,595,664,595,666,595,669,595,671,595,674,595,677,595,681,595,683,595,687,595,689,595,692,595,694,595,697,595,700,595,704,595,706,595,708,595,710,595,713,595,715,595,718,595,721,595,724,595,728,595,730,595,732,595,735,595,739,595,741,595,744,595,746,595,748,595,750,595,753,595,756,595,759,595,761,595,763,595,765,595,768,595,772,595,774,595,777,595,779,595,781,595,783,595,786,595,789,595,792,595,795,595,798,595,801,595,804,595,807,595,809,595,812,595,814,595,816,595,818,595,820,595,822,595,824,595,826,595,828,595,830,595,832,595,834,595,836,595,838,595,840,595,842,595,844,595,846,595,848,595,850,595,852,595,854,595,856,595,858,595,860,595,862,595,864,595,866,595,868,595,870,595,872,595,874,595,876,595,878,595,880,595,882,595,884,595,886,595,888,595,894,595,898,595,904,595,909,595,917],"svg",{"viewBox":588,"role":589,"ariaLabelledBy":590,"xmlns":593,"style":594},"4 0 751 276","img",[591,592],"de-rows-t","de-rows-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:751px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[597,598,599],"title",{"id":591},"Spacer rows removed, the styled header and data kept",[601,602,603],"desc",{"id":592},"Before, the sheet has data rows for North, South and East separated by blank rows, and blank rows below the total. After deleting the empty rows in runs from the bottom, the data rows and the total sit directly under the header.",[605,606],"rect",{"x":607,"y":452,"width":608,"height":609,"fill":610},"4","751","276","#ffffff",[605,612],{"x":613,"y":614,"width":615,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"20.8","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[605,621],{"x":622,"y":614,"width":623,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"52.8","61.4",[625,626,630],"text",{"x":627,"y":628,"style":629},"83.5","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[605,632],{"x":633,"y":614,"width":634,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"114.2","71.1",[625,636,638],{"x":637,"y":628,"style":629},"149.7","B",[605,640],{"x":641,"y":614,"width":642,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"185.3","66.8",[625,644,646],{"x":645,"y":628,"style":629},"218.7","C",[605,648],{"x":613,"y":649,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"64.0","28.0",[625,652,286],{"x":653,"y":654,"style":629},"36.8","83.0",[605,656],{"x":622,"y":649,"width":623,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[625,661,663],{"x":627,"y":654,"style":662},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Region",[605,665],{"x":633,"y":649,"width":634,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},[625,667,668],{"x":637,"y":654,"style":662},"Account",[605,670],{"x":641,"y":649,"width":642,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},[625,672,673],{"x":645,"y":654,"style":662},"Amount",[605,675],{"x":613,"y":676,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"92.0",[625,678,680],{"x":653,"y":679,"style":629},"111.0","2",[605,682],{"x":622,"y":676,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,684,686],{"x":627,"y":679,"style":685},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","North",[605,688],{"x":633,"y":676,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,690,691],{"x":637,"y":679,"style":685},"Sales",[605,693],{"x":641,"y":676,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,695,696],{"x":645,"y":679,"style":685},"1,200",[605,698],{"x":613,"y":699,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"120.0",[625,701,703],{"x":653,"y":702,"style":629},"139.0","3",[605,705],{"x":622,"y":699,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,707,686],{"x":627,"y":702,"style":685},[605,709],{"x":633,"y":699,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,711,712],{"x":637,"y":702,"style":685},"Services",[605,714],{"x":641,"y":699,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,716,717],{"x":645,"y":702,"style":685},"340",[605,719],{"x":613,"y":720,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"148.0",[625,722,607],{"x":653,"y":723,"style":629},"167.0",[605,725],{"x":622,"y":720,"width":623,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},"#fdefd8","var(--gold,#b4740a)",[605,729],{"x":633,"y":720,"width":634,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},[605,731],{"x":641,"y":720,"width":642,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},[605,733],{"x":613,"y":734,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"176.0",[625,736,738],{"x":653,"y":737,"style":629},"195.0","5",[605,740],{"x":622,"y":734,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,742,743],{"x":627,"y":737,"style":685},"South",[605,745],{"x":633,"y":734,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,747,691],{"x":637,"y":737,"style":685},[605,749],{"x":641,"y":734,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,751,752],{"x":645,"y":737,"style":685},"980",[605,754],{"x":613,"y":755,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"204.0",[625,757,325],{"x":653,"y":758,"style":629},"223.0",[605,760],{"x":622,"y":755,"width":623,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},[605,762],{"x":633,"y":755,"width":634,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},[605,764],{"x":641,"y":755,"width":642,"height":650,"rx":452,"fill":726,"stroke":727,"style":659},[605,766],{"x":613,"y":767,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},"232.0",[625,769,771],{"x":653,"y":770,"style":629},"251.0","7",[605,773],{"x":622,"y":767,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,775,776],{"x":627,"y":770,"style":685},"East",[605,778],{"x":633,"y":767,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,780,691],{"x":637,"y":770,"style":685},[605,782],{"x":641,"y":767,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,784,785],{"x":645,"y":770,"style":685},"1,500",[605,787],{"x":788,"y":614,"width":615,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"507.9",[605,790],{"x":791,"y":614,"width":623,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"539.9",[625,793,630],{"x":794,"y":628,"style":629},"570.6",[605,796],{"x":797,"y":614,"width":634,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"601.3",[625,799,638],{"x":800,"y":628,"style":629},"636.9",[605,802],{"x":803,"y":614,"width":642,"height":616,"rx":452,"fill":617,"stroke":618,"style":619},"672.4",[625,805,646],{"x":806,"y":628,"style":629},"705.8",[605,808],{"x":788,"y":649,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},[625,810,286],{"x":811,"y":654,"style":629},"523.9",[605,813],{"x":791,"y":649,"width":623,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},[625,815,663],{"x":794,"y":654,"style":662},[605,817],{"x":797,"y":649,"width":634,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},[625,819,668],{"x":800,"y":654,"style":662},[605,821],{"x":803,"y":649,"width":642,"height":650,"rx":452,"fill":657,"stroke":658,"style":659},[625,823,673],{"x":806,"y":654,"style":662},[605,825],{"x":788,"y":676,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},[625,827,680],{"x":811,"y":679,"style":629},[605,829],{"x":791,"y":676,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,831,686],{"x":794,"y":679,"style":685},[605,833],{"x":797,"y":676,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,835,691],{"x":800,"y":679,"style":685},[605,837],{"x":803,"y":676,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,839,696],{"x":806,"y":679,"style":685},[605,841],{"x":788,"y":699,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},[625,843,703],{"x":811,"y":702,"style":629},[605,845],{"x":791,"y":699,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,847,686],{"x":794,"y":702,"style":685},[605,849],{"x":797,"y":699,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,851,712],{"x":800,"y":702,"style":685},[605,853],{"x":803,"y":699,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,855,717],{"x":806,"y":702,"style":685},[605,857],{"x":788,"y":720,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},[625,859,607],{"x":811,"y":723,"style":629},[605,861],{"x":791,"y":720,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,863,743],{"x":794,"y":723,"style":685},[605,865],{"x":797,"y":720,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,867,691],{"x":800,"y":723,"style":685},[605,869],{"x":803,"y":720,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,871,752],{"x":806,"y":723,"style":685},[605,873],{"x":788,"y":734,"width":615,"height":650,"rx":452,"fill":617,"stroke":618,"style":619},[625,875,738],{"x":811,"y":737,"style":629},[605,877],{"x":791,"y":734,"width":623,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,879,776],{"x":794,"y":737,"style":685},[605,881],{"x":797,"y":734,"width":634,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,883,691],{"x":800,"y":737,"style":685},[605,885],{"x":803,"y":734,"width":642,"height":650,"rx":452,"fill":610,"stroke":618,"style":619},[625,887,785],{"x":806,"y":737,"style":685},[625,889,893],{"x":890,"y":891,"style":892},"136.4","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[625,895,897],{"x":896,"y":891,"style":892},"623.6","after",[53,899],{"x1":900,"y1":901,"x2":902,"y2":901,"stroke":658,"style":903},"262.1","122.0","493.9","stroke-width:2px",[905,906],"polygon",{"points":907,"fill":908},"497.9,122.0 488.9,127.0 488.9,117.0","#5b5cf0",[605,910],{"x":911,"y":912,"width":913,"height":914,"rx":915,"fill":610,"stroke":618,"style":916},"293.7","89.0","172.7","22.0","11","stroke-width:1.2px",[625,918,922],{"x":919,"y":920,"style":921},"380.0","104.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","delete_rows(start, n)",[10,924,925,926,929],{},"The scan found the spacer rows and the formatted tail; the tail is one long run, so it goes in a single call. The header keeps its bold white-on-indigo style, and the amount cells keep their ",[14,927,928],{},"#,##0"," format, because deleting rows moves the remaining cell objects rather than rewriting them.",[37,931,933],{"id":932},"step-3-delete-empty-columns-the-same-way","Step 3 — Delete empty columns the same way",[10,935,936,937,25,940,942],{},"Columns work identically with ",[14,938,939],{},"iter_cols",[14,941,28],{},". Decide whether a column with only a header counts as empty — here column C has a header cell that is blank, so it qualifies; if your headers are filled in, skip the first row in the test:",[42,944,946],{"className":92,"code":945,"language":94,"meta":47,"style":47},"empty_cols = [j for j, col in enumerate(ws.iter_cols(values_only=True), start=1)\n              if all(is_blank(v) for v in col)]\nprint(\"empty columns:\", empty_cols)\nfor start, amount in reversed(runs(empty_cols)):\n    ws.delete_cols(start, amount)\nprint([c.value for c in ws[1]])\n",[14,947,948,986,1003,1015,1028,1033],{"__ignoreMap":47},[51,949,950,953,955,958,960,963,965,967,970,972,974,976,978,980,982,984],{"class":53,"line":54},[51,951,952],{"class":105},"empty_cols ",[51,954,128],{"class":101},[51,956,957],{"class":105}," [j ",[51,959,254],{"class":101},[51,961,962],{"class":105}," j, col ",[51,964,260],{"class":101},[51,966,263],{"class":160},[51,968,969],{"class":105},"(ws.iter_cols(",[51,971,270],{"class":269},[51,973,128],{"class":101},[51,975,275],{"class":160},[51,977,278],{"class":105},[51,979,281],{"class":269},[51,981,128],{"class":101},[51,983,286],{"class":160},[51,985,137],{"class":105},[51,987,988,990,992,994,996,998,1000],{"class":53,"line":115},[51,989,294],{"class":101},[51,991,297],{"class":160},[51,993,300],{"class":105},[51,995,254],{"class":101},[51,997,197],{"class":105},[51,999,260],{"class":101},[51,1001,1002],{"class":105}," col)]\n",[51,1004,1005,1007,1009,1012],{"class":53,"line":122},[51,1006,161],{"class":160},[51,1008,164],{"class":105},[51,1010,1011],{"class":61},"\"empty columns:\"",[51,1013,1014],{"class":105},", empty_cols)\n",[51,1016,1017,1019,1021,1023,1025],{"class":53,"line":140},[51,1018,254],{"class":101},[51,1020,558],{"class":105},[51,1022,260],{"class":101},[51,1024,563],{"class":160},[51,1026,1027],{"class":105},"(runs(empty_cols)):\n",[51,1029,1030],{"class":53,"line":157},[51,1031,1032],{"class":105},"    ws.delete_cols(start, amount)\n",[51,1034,1035,1037,1040,1042,1045,1047,1050,1052],{"class":53,"line":173},[51,1036,161],{"class":160},[51,1038,1039],{"class":105},"([c.value ",[51,1041,254],{"class":101},[51,1043,1044],{"class":105}," c ",[51,1046,260],{"class":101},[51,1048,1049],{"class":105}," ws[",[51,1051,286],{"class":160},[51,1053,1054],{"class":105},"]])\n",[10,1056,1057,1058,1061,1062,1064,1065,1068],{},"To ignore the header when testing, use ",[14,1059,1060],{},"ws.iter_cols(min_row=2, values_only=True)",". Column widths have the same problem as row heights, covered next: ",[14,1063,28],{}," does not move ",[14,1066,1067],{},"column_dimensions",", so the width set for column D stays on the letter D even though the Amount data now lives in column C.",[37,1070,1072],{"id":1071},"step-4-repair-what-delete_rows-does-not-move","Step 4 — Repair what delete_rows does not move",[10,1074,1075,25,1077,1079],{},[14,1076,24],{},[14,1078,28],{}," move cell values and cell styles. Several other things stay where they were. Check them on a tiny example:",[42,1081,1083],{"className":92,"code":1082,"language":94,"meta":47,"style":47},"from openpyxl import Workbook\n\ndemo = Workbook().active\ndemo[\"A1\"], demo[\"A2\"], demo[\"A4\"] = \"Header\", 1, 2\ndemo[\"A5\"] = \"=SUM(A2:A4)\"\ndemo.row_dimensions[4].height = 30\ndemo.merge_cells(\"B4:C4\")\ndemo[\"B4\"] = \"merged\"\ndemo.delete_rows(3)\nprint(\"formula:\", demo[\"A4\"].value)\nprint(\"height on row 3:\", demo.row_dimensions[3].height, \"| row 4:\", demo.row_dimensions[4].height)\nprint(\"merged ranges:\", demo.merged_cells.ranges, \"| value now in B3:\", demo[\"B3\"].value)\n",[14,1084,1085,1096,1100,1110,1145,1159,1174,1184,1198,1207,1224,1251],{"__ignoreMap":47},[51,1086,1087,1089,1091,1093],{"class":53,"line":54},[51,1088,102],{"class":101},[51,1090,106],{"class":105},[51,1092,109],{"class":101},[51,1094,1095],{"class":105}," Workbook\n",[51,1097,1098],{"class":53,"line":115},[51,1099,119],{"emptyLinePlaceholder":118},[51,1101,1102,1105,1107],{"class":53,"line":122},[51,1103,1104],{"class":105},"demo ",[51,1106,128],{"class":101},[51,1108,1109],{"class":105}," Workbook().active\n",[51,1111,1112,1115,1118,1121,1124,1126,1129,1131,1133,1136,1138,1140,1142],{"class":53,"line":140},[51,1113,1114],{"class":105},"demo[",[51,1116,1117],{"class":61},"\"A1\"",[51,1119,1120],{"class":105},"], demo[",[51,1122,1123],{"class":61},"\"A2\"",[51,1125,1120],{"class":105},[51,1127,1128],{"class":61},"\"A4\"",[51,1130,455],{"class":105},[51,1132,128],{"class":101},[51,1134,1135],{"class":61}," \"Header\"",[51,1137,334],{"class":105},[51,1139,286],{"class":160},[51,1141,334],{"class":105},[51,1143,1144],{"class":160},"2\n",[51,1146,1147,1149,1152,1154,1156],{"class":53,"line":157},[51,1148,1114],{"class":105},[51,1150,1151],{"class":61},"\"A5\"",[51,1153,455],{"class":105},[51,1155,128],{"class":101},[51,1157,1158],{"class":61}," \"=SUM(A2:A4)\"\n",[51,1160,1161,1164,1166,1169,1171],{"class":53,"line":173},[51,1162,1163],{"class":105},"demo.row_dimensions[",[51,1165,607],{"class":160},[51,1167,1168],{"class":105},"].height ",[51,1170,128],{"class":101},[51,1172,1173],{"class":160}," 30\n",[51,1175,1176,1179,1182],{"class":53,"line":178},[51,1177,1178],{"class":105},"demo.merge_cells(",[51,1180,1181],{"class":61},"\"B4:C4\"",[51,1183,137],{"class":105},[51,1185,1186,1188,1191,1193,1195],{"class":53,"line":191},[51,1187,1114],{"class":105},[51,1189,1190],{"class":61},"\"B4\"",[51,1192,455],{"class":105},[51,1194,128],{"class":101},[51,1196,1197],{"class":61}," \"merged\"\n",[51,1199,1200,1203,1205],{"class":53,"line":238},[51,1201,1202],{"class":105},"demo.delete_rows(",[51,1204,703],{"class":160},[51,1206,137],{"class":105},[51,1208,1209,1211,1213,1216,1219,1221],{"class":53,"line":243},[51,1210,161],{"class":160},[51,1212,164],{"class":105},[51,1214,1215],{"class":61},"\"formula:\"",[51,1217,1218],{"class":105},", demo[",[51,1220,1128],{"class":61},[51,1222,1223],{"class":105},"].value)\n",[51,1225,1226,1228,1230,1233,1236,1238,1241,1244,1246,1248],{"class":53,"line":291},[51,1227,161],{"class":160},[51,1229,164],{"class":105},[51,1231,1232],{"class":61},"\"height on row 3:\"",[51,1234,1235],{"class":105},", demo.row_dimensions[",[51,1237,703],{"class":160},[51,1239,1240],{"class":105},"].height, ",[51,1242,1243],{"class":61},"\"| row 4:\"",[51,1245,1235],{"class":105},[51,1247,607],{"class":160},[51,1249,1250],{"class":105},"].height)\n",[51,1252,1253,1255,1257,1260,1263,1266,1268,1271],{"class":53,"line":312},[51,1254,161],{"class":160},[51,1256,164],{"class":105},[51,1258,1259],{"class":61},"\"merged ranges:\"",[51,1261,1262],{"class":105},", demo.merged_cells.ranges, ",[51,1264,1265],{"class":61},"\"| value now in B3:\"",[51,1267,1218],{"class":105},[51,1269,1270],{"class":61},"\"B3\"",[51,1272,1223],{"class":105},[586,1274,595,1280,595,1283,595,1286,595,1289,595,1294,595,1298,595,1304,595,1308,595,1313,595,1315,595,1326,595,1329,595,1333,595,1335,595,1340,595,1343,595,1347,595,1349,595,1357,595,1360,595,1364,595,1366,595,1371,595,1374,595,1378,595,1380,595,1385,595,1388,595,1392,595,1394],{"viewBox":1275,"role":589,"ariaLabelledBy":1276,"xmlns":593,"style":1279},"4 0 752 276",[1277,1278],"de-moves-t","de-moves-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[597,1281,1282],{"id":1277},"What delete_rows updates and what it leaves behind",[601,1284,1285],{"id":1278},"A grid of worksheet features. Cell values and cell styles move with the rows. Formulas, row heights, merged cell ranges, conditional formatting ranges, data validation, defined names and charts keep their old positions.",[605,1287],{"x":607,"y":452,"width":1288,"height":609,"fill":610},"752",[605,1290],{"x":1291,"y":1292,"width":1293,"height":614,"rx":452,"fill":610,"stroke":618,"style":619},"20.0","16.0","300.0",[605,1295],{"x":1296,"y":1292,"width":1297,"height":614,"rx":452,"fill":657,"stroke":658,"style":916},"320.0","420.0",[625,1299,1303],{"x":1300,"y":1301,"style":1302},"530.0","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Moves with the rows",[605,1305],{"x":1291,"y":1306,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"56.0","34.0",[625,1309,1312],{"x":615,"y":1310,"style":1311},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Cell values",[605,1314],{"x":1296,"y":1306,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1317,1319],"g",{"transform":1318},"translate(521.0,64.0) scale(0.750)",[1320,1321],"path",{"d":1322,"fill":1323,"stroke":1324,"style":1325},"M5 12.5l4.5 4.5L19 7","none","var(--teal,#0f9488)","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[605,1327],{"x":1291,"y":1328,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"90.0",[625,1330,1332],{"x":615,"y":1331,"style":1311},"112.0","Cell styles (font, fill, number format)",[605,1334],{"x":1296,"y":1328,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1336,1338],{"transform":1337},"translate(521.0,98.0) scale(0.750)",[1320,1339],{"d":1322,"fill":1323,"stroke":1324,"style":1325},[605,1341],{"x":1291,"y":1342,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"124.0",[625,1344,1346],{"x":615,"y":1345,"style":1311},"146.0","Formula references",[605,1348],{"x":1296,"y":1342,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1350,1352],{"transform":1351},"translate(522.0,133.0) scale(0.667)",[1320,1353],{"d":1354,"fill":1323,"stroke":1355,"style":1356},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[605,1358],{"x":1291,"y":1359,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"158.0",[625,1361,1363],{"x":615,"y":1362,"style":1311},"180.0","Row heights and column widths",[605,1365],{"x":1296,"y":1359,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1367,1369],{"transform":1368},"translate(522.0,167.0) scale(0.667)",[1320,1370],{"d":1354,"fill":1323,"stroke":1355,"style":1356},[605,1372],{"x":1291,"y":1373,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"192.0",[625,1375,1377],{"x":615,"y":1376,"style":1311},"214.0","Merged cell ranges",[605,1379],{"x":1296,"y":1373,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1381,1383],{"transform":1382},"translate(522.0,201.0) scale(0.667)",[1320,1384],{"d":1354,"fill":1323,"stroke":1355,"style":1356},[605,1386],{"x":1291,"y":1387,"width":1293,"height":1307,"rx":452,"fill":617,"stroke":618,"style":619},"226.0",[625,1389,1391],{"x":615,"y":1390,"style":1311},"248.0","Conditional formats, validation, names, charts",[605,1393],{"x":1296,"y":1387,"width":1297,"height":1307,"rx":452,"fill":610,"stroke":618,"style":619},[1316,1395,1397],{"transform":1396},"translate(522.0,235.0) scale(0.667)",[1320,1398],{"d":1354,"fill":1323,"stroke":1355,"style":1356},[10,1400,1401,1402,1405,1406,1409],{},"The formula still reads ",[14,1403,1404],{},"=SUM(A2:A4)"," although its range should now end at A3; the 30-point height stayed on row 4 instead of moving to row 3; and the merged range is still ",[14,1407,1408],{},"B4:C4"," while the merged cell's value moved to B3 — a recipe for a broken-looking sheet or a repair prompt in Excel. The fix is to remember these features before deleting and re-apply them afterwards with the row numbers mapped to their new positions:",[42,1411,1413],{"className":92,"code":1412,"language":94,"meta":47,"style":47},"def remap_rows(ws, deleted):\n    deleted = set(deleted)\n    new_index, n = {}, 0\n    for r in range(1, ws.max_row + len(deleted) + 1):\n        if r not in deleted:\n            n += 1\n            new_index[r] = n\n    return new_index\n\nwb2 = load_workbook(\"export.xlsx\")\nws2 = wb2[\"Export\"]\nblank = [i for i, row in enumerate(ws2.iter_rows(values_only=True), 1) if all(is_blank(v) for v in row)]\nmapping = remap_rows(ws2, blank)\nheights = {r: d.height for r, d in ws2.row_dimensions.items() if d.height}\nmerges = [(m.min_row, m.min_col, m.max_row, m.max_col) for m in ws2.merged_cells.ranges]\nfor m in list(ws2.merged_cells.ranges):\n    ws2.unmerge_cells(str(m))\nfor start, amount in reversed(runs(blank)):\n    ws2.delete_rows(start, amount)\nfor r in list(ws2.row_dimensions):\n    ws2.row_dimensions[r].height = None\nfor r, h in heights.items():\n    if r in mapping:\n        ws2.row_dimensions[mapping[r]].height = h\nfor r1, c1, r2, c2 in merges:\n    if r1 in mapping and r2 in mapping:\n        ws2.merge_cells(start_row=mapping[r1], start_column=c1, end_row=mapping[r2], end_column=c2)\nprint({r: d.height for r, d in ws2.row_dimensions.items() if d.height}, ws2.merged_cells.ranges)\n",[14,1414,1415,1425,1438,1451,1486,1501,1510,1520,1527,1531,1544,1558,1605,1615,1640,1661,1676,1687,1701,1707,1721,1732,1745,1758,1769,1782,1804,1842],{"__ignoreMap":47},[51,1416,1417,1419,1422],{"class":53,"line":54},[51,1418,181],{"class":101},[51,1420,1421],{"class":184}," remap_rows",[51,1423,1424],{"class":105},"(ws, deleted):\n",[51,1426,1427,1430,1432,1435],{"class":53,"line":115},[51,1428,1429],{"class":105},"    deleted ",[51,1431,128],{"class":101},[51,1433,1434],{"class":160}," set",[51,1436,1437],{"class":105},"(deleted)\n",[51,1439,1440,1443,1445,1448],{"class":53,"line":122},[51,1441,1442],{"class":105},"    new_index, n ",[51,1444,128],{"class":101},[51,1446,1447],{"class":105}," {}, ",[51,1449,1450],{"class":160},"0\n",[51,1452,1453,1455,1458,1460,1463,1465,1467,1470,1472,1475,1478,1480,1483],{"class":53,"line":140},[51,1454,420],{"class":101},[51,1456,1457],{"class":105}," r ",[51,1459,260],{"class":101},[51,1461,1462],{"class":160}," range",[51,1464,164],{"class":105},[51,1466,286],{"class":160},[51,1468,1469],{"class":105},", ws.max_row ",[51,1471,458],{"class":101},[51,1473,1474],{"class":160}," len",[51,1476,1477],{"class":105},"(deleted) ",[51,1479,458],{"class":101},[51,1481,1482],{"class":160}," 1",[51,1484,1485],{"class":105},"):\n",[51,1487,1488,1490,1492,1495,1498],{"class":53,"line":157},[51,1489,433],{"class":101},[51,1491,1457],{"class":105},[51,1493,1494],{"class":101},"not",[51,1496,1497],{"class":101}," in",[51,1499,1500],{"class":105}," deleted:\n",[51,1502,1503,1506,1508],{"class":53,"line":173},[51,1504,1505],{"class":105},"            n ",[51,1507,493],{"class":101},[51,1509,496],{"class":160},[51,1511,1512,1515,1517],{"class":53,"line":178},[51,1513,1514],{"class":105},"            new_index[r] ",[51,1516,128],{"class":101},[51,1518,1519],{"class":105}," n\n",[51,1521,1522,1524],{"class":53,"line":191},[51,1523,194],{"class":101},[51,1525,1526],{"class":105}," new_index\n",[51,1528,1529],{"class":53,"line":238},[51,1530,119],{"emptyLinePlaceholder":118},[51,1532,1533,1536,1538,1540,1542],{"class":53,"line":243},[51,1534,1535],{"class":105},"wb2 ",[51,1537,128],{"class":101},[51,1539,131],{"class":105},[51,1541,134],{"class":61},[51,1543,137],{"class":105},[51,1545,1546,1549,1551,1554,1556],{"class":53,"line":291},[51,1547,1548],{"class":105},"ws2 ",[51,1550,128],{"class":101},[51,1552,1553],{"class":105}," wb2[",[51,1555,151],{"class":61},[51,1557,154],{"class":105},[51,1559,1560,1563,1565,1567,1569,1571,1573,1575,1578,1580,1582,1584,1586,1588,1590,1593,1595,1597,1599,1601,1603],{"class":53,"line":312},[51,1561,1562],{"class":105},"blank ",[51,1564,128],{"class":101},[51,1566,251],{"class":105},[51,1568,254],{"class":101},[51,1570,257],{"class":105},[51,1572,260],{"class":101},[51,1574,263],{"class":160},[51,1576,1577],{"class":105},"(ws2.iter_rows(",[51,1579,270],{"class":269},[51,1581,128],{"class":101},[51,1583,275],{"class":160},[51,1585,278],{"class":105},[51,1587,286],{"class":160},[51,1589,221],{"class":105},[51,1591,1592],{"class":101},"if",[51,1594,297],{"class":160},[51,1596,300],{"class":105},[51,1598,254],{"class":101},[51,1600,197],{"class":105},[51,1602,260],{"class":101},[51,1604,309],{"class":105},[51,1606,1607,1610,1612],{"class":53,"line":569},[51,1608,1609],{"class":105},"mapping ",[51,1611,128],{"class":101},[51,1613,1614],{"class":105}," remap_rows(ws2, blank)\n",[51,1616,1617,1620,1622,1625,1627,1630,1632,1635,1637],{"class":53,"line":575},[51,1618,1619],{"class":105},"heights ",[51,1621,128],{"class":101},[51,1623,1624],{"class":105}," {r: d.height ",[51,1626,254],{"class":101},[51,1628,1629],{"class":105}," r, d ",[51,1631,260],{"class":101},[51,1633,1634],{"class":105}," ws2.row_dimensions.items() ",[51,1636,1592],{"class":101},[51,1638,1639],{"class":105}," d.height}\n",[51,1641,1643,1646,1648,1651,1653,1656,1658],{"class":53,"line":1642},15,[51,1644,1645],{"class":105},"merges ",[51,1647,128],{"class":101},[51,1649,1650],{"class":105}," [(m.min_row, m.min_col, m.max_row, m.max_col) ",[51,1652,254],{"class":101},[51,1654,1655],{"class":105}," m ",[51,1657,260],{"class":101},[51,1659,1660],{"class":105}," ws2.merged_cells.ranges]\n",[51,1662,1664,1666,1668,1670,1673],{"class":53,"line":1663},16,[51,1665,254],{"class":101},[51,1667,1655],{"class":105},[51,1669,260],{"class":101},[51,1671,1672],{"class":160}," list",[51,1674,1675],{"class":105},"(ws2.merged_cells.ranges):\n",[51,1677,1679,1682,1684],{"class":53,"line":1678},17,[51,1680,1681],{"class":105},"    ws2.unmerge_cells(",[51,1683,218],{"class":160},[51,1685,1686],{"class":105},"(m))\n",[51,1688,1690,1692,1694,1696,1698],{"class":53,"line":1689},18,[51,1691,254],{"class":101},[51,1693,558],{"class":105},[51,1695,260],{"class":101},[51,1697,563],{"class":160},[51,1699,1700],{"class":105},"(runs(blank)):\n",[51,1702,1704],{"class":53,"line":1703},19,[51,1705,1706],{"class":105},"    ws2.delete_rows(start, amount)\n",[51,1708,1710,1712,1714,1716,1718],{"class":53,"line":1709},20,[51,1711,254],{"class":101},[51,1713,1457],{"class":105},[51,1715,260],{"class":101},[51,1717,1672],{"class":160},[51,1719,1720],{"class":105},"(ws2.row_dimensions):\n",[51,1722,1724,1727,1729],{"class":53,"line":1723},21,[51,1725,1726],{"class":105},"    ws2.row_dimensions[r].height ",[51,1728,128],{"class":101},[51,1730,1731],{"class":160}," None\n",[51,1733,1735,1737,1740,1742],{"class":53,"line":1734},22,[51,1736,254],{"class":101},[51,1738,1739],{"class":105}," r, h ",[51,1741,260],{"class":101},[51,1743,1744],{"class":105}," heights.items():\n",[51,1746,1748,1751,1753,1755],{"class":53,"line":1747},23,[51,1749,1750],{"class":101},"    if",[51,1752,1457],{"class":105},[51,1754,260],{"class":101},[51,1756,1757],{"class":105}," mapping:\n",[51,1759,1761,1764,1766],{"class":53,"line":1760},24,[51,1762,1763],{"class":105},"        ws2.row_dimensions[mapping[r]].height ",[51,1765,128],{"class":101},[51,1767,1768],{"class":105}," h\n",[51,1770,1772,1774,1777,1779],{"class":53,"line":1771},25,[51,1773,254],{"class":101},[51,1775,1776],{"class":105}," r1, c1, r2, c2 ",[51,1778,260],{"class":101},[51,1780,1781],{"class":105}," merges:\n",[51,1783,1785,1787,1790,1792,1795,1797,1800,1802],{"class":53,"line":1784},26,[51,1786,1750],{"class":101},[51,1788,1789],{"class":105}," r1 ",[51,1791,260],{"class":101},[51,1793,1794],{"class":105}," mapping ",[51,1796,224],{"class":101},[51,1798,1799],{"class":105}," r2 ",[51,1801,260],{"class":101},[51,1803,1757],{"class":105},[51,1805,1807,1810,1813,1815,1818,1821,1823,1826,1829,1831,1834,1837,1839],{"class":53,"line":1806},27,[51,1808,1809],{"class":105},"        ws2.merge_cells(",[51,1811,1812],{"class":269},"start_row",[51,1814,128],{"class":101},[51,1816,1817],{"class":105},"mapping[r1], ",[51,1819,1820],{"class":269},"start_column",[51,1822,128],{"class":101},[51,1824,1825],{"class":105},"c1, ",[51,1827,1828],{"class":269},"end_row",[51,1830,128],{"class":101},[51,1832,1833],{"class":105},"mapping[r2], ",[51,1835,1836],{"class":269},"end_column",[51,1838,128],{"class":101},[51,1840,1841],{"class":105},"c2)\n",[51,1843,1845,1847,1850,1852,1854,1856,1858,1860],{"class":53,"line":1844},28,[51,1846,161],{"class":160},[51,1848,1849],{"class":105},"({r: d.height ",[51,1851,254],{"class":101},[51,1853,1629],{"class":105},[51,1855,260],{"class":101},[51,1857,1634],{"class":105},[51,1859,1592],{"class":101},[51,1861,1862],{"class":105}," d.height}, ws2.merged_cells.ranges)\n",[10,1864,1865,1866,1868,1869,1871,1872,1875,1876,1880,1881,35],{},"Merges are removed before the deletion and recreated afterwards at their mapped rows, so the ",[14,1867,74],{}," label spans A:B again on its new row 8, and the 28-point height that belonged to old row 9 (East · Sales) lands on that row's new position, row 6. Formulas are the hard part: openpyxl does not parse references, so a ",[14,1870,78],{}," that spans deleted rows must be rewritten. For a known layout, regenerate the total after the deletion (",[14,1873,1874],{},"ws2.cell(total_row, 4, f\"=SUM(D2:D{total_row - 1})\")","); for unknown workbooks, list every formula first with the approach in ",[31,1877,1879],{"href":1878},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Faudit-every-formula-in-a-workbook-with-python\u002F","Audit Every Formula in a Workbook with Python",", or let LibreOffice do the deletion through its own engine, which does update references — see ",[31,1882,1884],{"href":1883},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fautomate-libreoffice-calc-from-python-with-uno\u002F","Automate LibreOffice Calc from Python with UNO",[37,1886,1888],{"id":1887},"step-5-rebuild-instead-of-deleting-when-the-sheet-is-large","Step 5 — Rebuild instead of deleting when the sheet is large",[10,1890,1891,1892,1894],{},"Each ",[14,1893,24],{}," call moves every cell below the deletion point, so the total cost grows with the number of runs times the number of rows. On a 10,000-row test sheet with 1,622 separate blank runs, the grouped deletion took 14 seconds; on 3,000 rows it took 1.2 seconds. Copying the non-empty rows to a fresh sheet is linear and took 0.03 seconds for the same 10,000 rows:",[42,1896,1898],{"className":92,"code":1897,"language":94,"meta":47,"style":47},"from copy import copy\n\ndef compact_copy(wb, ws):\n    blank = {i for i, row in enumerate(ws.iter_rows(values_only=True), 1) if all(is_blank(v) for v in row)}\n    new = wb.create_sheet(ws.title + \"_clean\")\n    out = 0\n    for row in ws.iter_rows():\n        if row[0].row in blank:\n            continue\n        out += 1\n        for cell in row:\n            if cell.value is None and not cell.has_style:\n                continue\n            target = new.cell(out, cell.column, cell.value)\n            if cell.has_style:\n                target._style = copy(cell._style)\n    for key, dim in ws.column_dimensions.items():\n        new.column_dimensions[key].width = dim.width\n    return new\n\nwb3 = load_workbook(\"export.xlsx\")\nclean = compact_copy(wb3, wb3[\"Export\"])\nprint(clean.title, clean.max_row, [c.value for c in clean[clean.max_row]])\n",[14,1899,1900,1912,1916,1926,1973,1990,1999,2011,2028,2033,2042,2055,2076,2081,2091,2097,2107,2119,2129,2136,2140,2153,2167],{"__ignoreMap":47},[51,1901,1902,1904,1907,1909],{"class":53,"line":54},[51,1903,102],{"class":101},[51,1905,1906],{"class":105}," copy ",[51,1908,109],{"class":101},[51,1910,1911],{"class":105}," copy\n",[51,1913,1914],{"class":53,"line":115},[51,1915,119],{"emptyLinePlaceholder":118},[51,1917,1918,1920,1923],{"class":53,"line":122},[51,1919,181],{"class":101},[51,1921,1922],{"class":184}," compact_copy",[51,1924,1925],{"class":105},"(wb, ws):\n",[51,1927,1928,1931,1933,1936,1938,1940,1942,1944,1946,1948,1950,1952,1954,1956,1958,1960,1962,1964,1966,1968,1970],{"class":53,"line":140},[51,1929,1930],{"class":105},"    blank ",[51,1932,128],{"class":101},[51,1934,1935],{"class":105}," {i ",[51,1937,254],{"class":101},[51,1939,257],{"class":105},[51,1941,260],{"class":101},[51,1943,263],{"class":160},[51,1945,266],{"class":105},[51,1947,270],{"class":269},[51,1949,128],{"class":101},[51,1951,275],{"class":160},[51,1953,278],{"class":105},[51,1955,286],{"class":160},[51,1957,221],{"class":105},[51,1959,1592],{"class":101},[51,1961,297],{"class":160},[51,1963,300],{"class":105},[51,1965,254],{"class":101},[51,1967,197],{"class":105},[51,1969,260],{"class":101},[51,1971,1972],{"class":105}," row)}\n",[51,1974,1975,1978,1980,1983,1985,1988],{"class":53,"line":157},[51,1976,1977],{"class":105},"    new ",[51,1979,128],{"class":101},[51,1981,1982],{"class":105}," wb.create_sheet(ws.title ",[51,1984,458],{"class":101},[51,1986,1987],{"class":61}," \"_clean\"",[51,1989,137],{"class":105},[51,1991,1992,1994,1996],{"class":53,"line":173},[51,1993,410],{"class":105},[51,1995,128],{"class":101},[51,1997,1998],{"class":160}," 0\n",[51,2000,2001,2003,2006,2008],{"class":53,"line":178},[51,2002,420],{"class":101},[51,2004,2005],{"class":105}," row ",[51,2007,260],{"class":101},[51,2009,2010],{"class":105}," ws.iter_rows():\n",[51,2012,2013,2015,2018,2020,2023,2025],{"class":53,"line":191},[51,2014,433],{"class":101},[51,2016,2017],{"class":105}," row[",[51,2019,452],{"class":160},[51,2021,2022],{"class":105},"].row ",[51,2024,260],{"class":101},[51,2026,2027],{"class":105}," blank:\n",[51,2029,2030],{"class":53,"line":238},[51,2031,2032],{"class":101},"            continue\n",[51,2034,2035,2038,2040],{"class":53,"line":243},[51,2036,2037],{"class":105},"        out ",[51,2039,493],{"class":101},[51,2041,496],{"class":160},[51,2043,2044,2047,2050,2052],{"class":53,"line":291},[51,2045,2046],{"class":101},"        for",[51,2048,2049],{"class":105}," cell ",[51,2051,260],{"class":101},[51,2053,2054],{"class":105}," row:\n",[51,2056,2057,2060,2063,2065,2067,2070,2073],{"class":53,"line":312},[51,2058,2059],{"class":101},"            if",[51,2061,2062],{"class":105}," cell.value ",[51,2064,200],{"class":101},[51,2066,203],{"class":160},[51,2068,2069],{"class":101}," and",[51,2071,2072],{"class":101}," not",[51,2074,2075],{"class":105}," cell.has_style:\n",[51,2077,2078],{"class":53,"line":569},[51,2079,2080],{"class":101},"                continue\n",[51,2082,2083,2086,2088],{"class":53,"line":575},[51,2084,2085],{"class":105},"            target ",[51,2087,128],{"class":101},[51,2089,2090],{"class":105}," new.cell(out, cell.column, cell.value)\n",[51,2092,2093,2095],{"class":53,"line":1642},[51,2094,2059],{"class":101},[51,2096,2075],{"class":105},[51,2098,2099,2102,2104],{"class":53,"line":1663},[51,2100,2101],{"class":105},"                target._style ",[51,2103,128],{"class":101},[51,2105,2106],{"class":105}," copy(cell._style)\n",[51,2108,2109,2111,2114,2116],{"class":53,"line":1678},[51,2110,420],{"class":101},[51,2112,2113],{"class":105}," key, dim ",[51,2115,260],{"class":101},[51,2117,2118],{"class":105}," ws.column_dimensions.items():\n",[51,2120,2121,2124,2126],{"class":53,"line":1689},[51,2122,2123],{"class":105},"        new.column_dimensions[key].width ",[51,2125,128],{"class":101},[51,2127,2128],{"class":105}," dim.width\n",[51,2130,2131,2133],{"class":53,"line":1703},[51,2132,194],{"class":101},[51,2134,2135],{"class":105}," new\n",[51,2137,2138],{"class":53,"line":1709},[51,2139,119],{"emptyLinePlaceholder":118},[51,2141,2142,2145,2147,2149,2151],{"class":53,"line":1723},[51,2143,2144],{"class":105},"wb3 ",[51,2146,128],{"class":101},[51,2148,131],{"class":105},[51,2150,134],{"class":61},[51,2152,137],{"class":105},[51,2154,2155,2158,2160,2163,2165],{"class":53,"line":1734},[51,2156,2157],{"class":105},"clean ",[51,2159,128],{"class":101},[51,2161,2162],{"class":105}," compact_copy(wb3, wb3[",[51,2164,151],{"class":61},[51,2166,514],{"class":105},[51,2168,2169,2171,2174,2176,2178,2180],{"class":53,"line":1747},[51,2170,161],{"class":160},[51,2172,2173],{"class":105},"(clean.title, clean.max_row, [c.value ",[51,2175,254],{"class":101},[51,2177,1044],{"class":105},[51,2179,260],{"class":101},[51,2181,2182],{"class":105}," clean[clean.max_row]])\n",[10,2184,2185,2186,2189,2190,2194,2195,2198,2199,2202],{},"Copying ",[14,2187,2188],{},"_style"," carries the font, fill, border, alignment and number format in one assignment, the same technique explained in ",[31,2191,2193],{"href":2192},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fcopy-cell-styles-with-openpyxl\u002F","Copy Cell Styles with openpyxl",". The printout shows the catch that applies to both methods: the total row arrived on row 8 still reading ",[14,2196,2197],{},"=SUM(D2:D11)",", so regenerate it as in Step 4. Column widths are copied explicitly; heights and merges can be carried over with the mapping from Step 4. Once the copy looks right, remove the original with ",[14,2200,2201],{},"wb3.remove(wb3[\"Export\"])"," and rename the new sheet.",[586,2204,595,2210,595,2213,595,2216,595,2220,595,2224,595,2230,595,2234,595,2237,595,2242,595,2248,595,2252,595,2256,595,2259,595,2263,595,2266,595,2269,595,2272,595,2278,595,2282,595,2288,595,2294,595,2298,595,2303,595,2308,595,2314,595,2319],{"viewBox":2205,"role":589,"ariaLabelledBy":2206,"xmlns":593,"style":2209},"4 0 748 280",[2207,2208],"de-speed-t","de-speed-d","width:100%;max-width:748px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[597,2211,2212],{"id":2207},"Seconds to remove blank rows from a sheet",[601,2214,2215],{"id":2208},"A column chart comparing grouped delete_rows calls with copying the non-empty rows to a new sheet. On 3,000 rows delete_rows took 1.2 seconds and copying 0.01; on 10,000 rows delete_rows took 14.3 seconds and copying 0.03.",[605,2217],{"x":607,"y":452,"width":2218,"height":2219,"fill":610},"748","280",[605,2221],{"x":649,"y":2222,"width":2223,"height":2223,"rx":703,"fill":726,"stroke":727,"style":659},"18.0","14.0",[625,2225,2229],{"x":2226,"y":2227,"style":2228},"84.0","29.5","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","grouped delete_rows",[605,2231],{"x":2232,"y":2222,"width":2223,"height":2223,"rx":703,"fill":2233,"stroke":1324,"style":659},"242.1","#d9f4f1",[625,2235,2236],{"x":900,"y":2227,"style":2228},"copy to a new sheet",[53,2238],{"x1":2239,"y1":2240,"x2":2241,"y2":2240,"stroke":618,"style":619},"53.3","242.0","736.0",[625,2243,2247],{"x":2244,"y":2245,"style":2246},"30.7","246.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","0 s",[53,2249],{"x1":2239,"y1":2250,"x2":2241,"y2":2250,"stroke":618,"style":2251},"178.7","stroke-width:1px;stroke-dasharray:5 4",[625,2253,2255],{"x":2244,"y":2254,"style":2246},"182.7","5 s",[53,2257],{"x1":2239,"y1":2258,"x2":2241,"y2":2258,"stroke":618,"style":2251},"115.3",[625,2260,2262],{"x":2244,"y":2261,"style":2246},"119.3","10 s",[53,2264],{"x1":2239,"y1":2265,"x2":2241,"y2":2265,"stroke":618,"style":2251},"52.0",[625,2267,2268],{"x":2244,"y":1306,"style":2246},"15 s",[53,2270],{"x1":2239,"y1":2240,"x2":2241,"y2":2240,"stroke":2271,"style":659},"var(--muted,#5b6780)",[625,2273,2277],{"x":2274,"y":2275,"style":2276},"224.0","260.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","3,000 rows",[625,2279,2281],{"x":2280,"y":2275,"style":2276},"565.3","10,000 rows",[605,2283],{"x":2284,"y":2285,"width":2286,"height":2287,"rx":703,"fill":726,"stroke":727,"style":659},"187.0","226.7","36.0","15.3",[625,2289,2293],{"x":2290,"y":2291,"style":2292},"205.0","220.7","font-size:10.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","1.21",[605,2295],{"x":2296,"y":623,"width":2286,"height":2297,"rx":703,"fill":726,"stroke":727,"style":659},"528.3","180.6",[625,2299,2302],{"x":2300,"y":2301,"style":2292},"546.3","55.4","14.26",[605,2304],{"x":2305,"y":2306,"width":2286,"height":2307,"rx":703,"fill":2233,"stroke":1324,"style":659},"225.0","241.9","0.1",[625,2309,2313],{"x":2310,"y":2311,"style":2312},"243.0","235.9","font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","0.01",[605,2315],{"x":2316,"y":2317,"width":2286,"height":2318,"rx":703,"fill":2233,"stroke":1324,"style":659},"566.3","241.6","0.4",[625,2320,2323],{"x":2321,"y":2322,"style":2312},"584.3","235.6","0.03",[37,2325,2327],{"id":2326},"common-pitfalls","Common pitfalls",[2329,2330,2331,2346,2352,2358,2367],"ul",{},[2332,2333,2334,2343,2344,35],"li",{},[2335,2336,2337,2338,356,2341,35],"strong",{},"Scanning with ",[14,2339,2340],{},"ws.cell()",[14,2342,355],{}," Both create cells; scan with ",[14,2345,351],{},[2332,2347,2348,2351],{},[2335,2349,2350],{},"Deleting top-down."," After the first deletion every later index is off by one; always go bottom-up.",[2332,2353,2354,2357],{},[2335,2355,2356],{},"Forgetting the side effects."," Heights, widths, merges, conditional-format ranges, validations and formulas stay put; re-apply them or check them after saving.",[2332,2359,2360,2363,2364,2366],{},[2335,2361,2362],{},"Treating formatted cells as data."," A filled-but-empty cell still counts towards ",[14,2365,16],{},"; deciding emptiness on values alone is what removes the phantom tail.",[2332,2368,2369,2372],{},[2335,2370,2371],{},"Running on the only copy."," Save to a new file name until the result has been opened and checked.",[37,2374,2376],{"id":2375},"conclusion","Conclusion",[10,2378,2379,2380,2382,2383,25,2385,2387],{},"Find empty rows and columns with a ",[14,2381,270],{}," scan, group them into runs and delete from the bottom up with ",[14,2384,24],{},[14,2386,28],{},"; the values and styles move with the rows. Then repair what openpyxl leaves in place — heights, widths, merged ranges and especially formula references — or, for large sheets, copy the non-empty rows to a fresh sheet, which is hundreds of times faster and makes the remapping explicit.",[37,2389,2391],{"id":2390},"frequently-asked-questions","Frequently asked questions",[10,2393,2394,2397],{},[2335,2395,2396],{},"How do I delete blank rows in Excel with openpyxl?","\nCollect the indexes of rows whose values are all None (or whitespace) with ws.iter_rows(values_only=True), group consecutive indexes into runs, and call ws.delete_rows(start, amount) for each run starting from the bottom of the sheet.",[10,2399,2400,2403],{},[2335,2401,2402],{},"Why does openpyxl delete_rows break my formulas?","\nopenpyxl does not parse or adjust formula references when rows move. A formula such as =SUM(A2:A10) keeps its text after rows above row 10 are deleted. Rewrite affected formulas yourself, or perform the deletion in Excel or LibreOffice, which update references.",[10,2405,2406,2409],{},[2335,2407,2408],{},"Why is deleting rows with openpyxl so slow?","\nEvery delete_rows call shifts all cells below the deletion point, so deleting many separate rows costs roughly rows times deletions. Group consecutive rows into one call, or copy the non-empty rows to a new sheet, which takes a single pass.",[10,2411,2412,2415],{},[2335,2413,2414],{},"Does delete_rows keep cell formatting?","\nYes for the cells themselves: fonts, fills, borders and number formats move with the rows. Row heights, column widths, merged ranges, conditional formatting ranges and data validation do not move and need to be re-applied.",[37,2417,2419],{"id":2418},"related","Related",[2329,2421,2422,2428,2435,2442],{},[2332,2423,2424,2425,2427],{},"Up one level: ",[31,2426,34],{"href":33}," — editing workbooks cell by cell.",[2332,2429,2430,2434],{},[31,2431,2433],{"href":2432},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Finsert-and-delete-rows-and-columns-with-openpyxl\u002F","Insert and Delete Rows and Columns with openpyxl"," — the basics of both operations.",[2332,2436,2437,2441],{},[31,2438,2440],{"href":2439},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fremove-blank-rows-from-excel-with-pandas\u002F","Remove Blank Rows from Excel with pandas"," — the quicker route when formatting does not matter.",[2332,2443,2444,2448],{},[31,2445,2447],{"href":2446},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-max-row-counting-empty-rows\u002F","Fix openpyxl max_row Counting Empty Rows"," — why the phantom tail exists.",[2450,2451,2452],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":47,"searchDepth":115,"depth":115,"links":2454},[2455,2456,2457,2458,2459,2460,2461,2462,2463,2464],{"id":39,"depth":115,"text":40},{"id":88,"depth":115,"text":89},{"id":375,"depth":115,"text":376},{"id":932,"depth":115,"text":933},{"id":1071,"depth":115,"text":1072},{"id":1887,"depth":115,"text":1888},{"id":2326,"depth":115,"text":2327},{"id":2375,"depth":115,"text":2376},{"id":2390,"depth":115,"text":2391},{"id":2418,"depth":115,"text":2419},"2026-10-02","Remove blank rows and columns from a worksheet in place while keeping its formatting: detect empty rows without creating cells, delete them in runs from the bottom up, handle empty columns, repair row heights, merged cells and formulas that delete_rows leaves behind, and switch to a copy-based rebuild when the sheet is large.","md",[2469,2471,2473,2475],{"q":2396,"a":2470},"Collect the indexes of rows whose values are all None (or whitespace) with ws.iter_rows(values_only=True), group consecutive indexes into runs, and call ws.delete_rows(start, amount) for each run starting from the bottom of the sheet.",{"q":2402,"a":2472},"openpyxl does not parse or adjust formula references when rows move. A formula such as =SUM(A2:A10) keeps its text after rows above row 10 are deleted. Rewrite affected formulas yourself, or perform the deletion in Excel or LibreOffice, which update references.",{"q":2408,"a":2474},"Every delete_rows call shifts all cells below the deletion point, so deleting many separate rows costs roughly rows times deletions. Group consecutive rows into one call, or copy the non-empty rows to a new sheet, which takes a single pass.",{"q":2414,"a":2476},"Yes for the cells themselves: fonts, fills, borders and number formats move with the rows. Row heights, column widths, merged ranges, conditional formatting ranges and data validation do not move and need to be re-applied.","delete empty rows and columns with openpyxl",null,{"breadcrumb":2480},[2481,2484,2487],{"name":2482,"item":2483},"Home","\u002F",{"name":2485,"item":2486},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":34,"item":33},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl",{"title":2490,"description":2491},"Delete Empty Rows and Columns in Excel with openpyxl","Delete blank rows and columns with openpyxl without losing formatting: values_only detection, grouped delete_rows from the bottom, delete_cols, what delete_rows does not update (formulas, heights, merges) and a fast rebuild for big sheets.","delete-empty-rows-and-columns-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl\u002Findex",[2495,2496],"openpyxl","data-cleaning","how-to","7SnvThsX4aISvXkFlwkOeQT0u5Nsdo_hp_uSHvR6iLI",[2500,2504],{"title":2501,"path":2502,"stem":2503,"children":-1},"Copy Cell Styles from One Cell to Another with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fcopy-cell-styles-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fcopy-cell-styles-with-openpyxl\u002Findex",{"title":2505,"path":2506,"stem":2507,"children":-1},"Find and Replace Text in Excel with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl\u002Findex",[2509,2511],{"path":2510,"title":2485},"\u002Fgetting-started-with-python-excel-automation",{"path":2512,"title":34},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation",[],[2515,2519,2522,2525,2528,2531],{"path":2516,"title":2517,"kind":2518},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl","Applying Conditional Formatting With openpyxl","guide",{"path":2520,"title":2521,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fadd-data-bars-and-colour-scales-with-openpyxl","Add Data Bars and Colour Scales with openpyxl",{"path":2523,"title":2524,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fadd-icon-sets-with-openpyxl","Add Icon Sets to Excel Cells with openpyxl",{"path":2526,"title":2527,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fapply-a-colour-scale-per-row-or-per-column-with-openpyxl","Apply a Colour Scale per Row or per Column with openpyxl",{"path":2529,"title":2530,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fcontrol-conditional-formatting-rule-order-and-stop-if-true","Control Conditional Formatting Rule Order and Stop If True",{"path":2532,"title":2533,"kind":2497},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl","Highlight an Entire Row Based on a Cell Value with openpyxl",1790972069811]