[{"data":1,"prerenderedAt":2569},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl":2561},{"id":4,"title":5,"body":6,"dateModified":2537,"datePublished":2537,"description":2538,"extension":2539,"faq":2540,"meta":2552,"navigation":193,"path":2553,"seo":2554,"slug":2557,"stem":2558,"type":2559,"__hash__":2560},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl\u002Findex.md","Hide Sheets, Rows and Columns with openpyxl",{"type":7,"value":8,"toc":2525},"minimark",[9,19,128,133,162,165,456,460,466,595,606,609,670,674,677,938,941,948,1067,1071,1074,1212,1215,1471,1489,1529,1533,1536,1645,1790,1803,1806,1845,1849,1981,1985,2111,2114,2324,2331,2395,2407,2411,2426,2430,2446,2452,2461,2467,2479,2483,2521],[10,11,12,13,18],"p",{},"A generated workbook usually contains more than the reader needs: a lookup sheet feeding data validation, a raw extract behind a summary, an intermediate calculation column, a technical key nobody wants to see. Leaving them visible makes the report look like a working file rather than a finished one. openpyxl hides all of them in a line each — with two traps worth knowing, one of which produces a workbook Excel refuses to open. This guide covers sheets, rows, columns and grouping. It is part of ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002F","Protecting and Sharing Excel Workbooks",".",[20,21,30,31,30,35,30,39,30,46,30,55,30,62,30,67,30,71,30,76,30,79,30,84,30,89,30,92,30,95,30,98,30,101,30,106,30,111,30,114,30,117,30,120,30,123],"svg",{"viewBox":22,"role":23,"ariaLabel":24,"ariaLabelledBy":25,"xmlns":28,"style":29},"0 0 800 244","img","Three sheet visibility states: visible sheets appear on the tab bar, hidden sheets are restorable from the unhide dialog, and veryHidden sheets require the VBA editor.",[26,27],"vis-t","vis-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:800px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[32,33,34],"title",{"id":26},"The three sheet visibility states and who can see each",[36,37,38],"desc",{"id":27},"Three columns describing sheet_state values. Visible sheets sit on the tab bar and everyone sees them. Hidden sheets are off the tab bar but listed in Excel's unhide dialog, so any reader can restore one. Very hidden sheets are absent from that dialog and can only be revealed through the VBA editor. A note underneath stresses that all three are equally readable by any Python library that opens the file.",[40,41],"rect",{"x":42,"y":42,"width":43,"height":44,"fill":45},"0","800","244","#ffffff",[40,47],{"x":48,"y":49,"width":50,"height":51,"rx":48,"fill":52,"stroke":53,"style":54},"14","28","248","164","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[56,57,61],"text",{"x":58,"y":59,"style":60},"138","58","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","\"visible\"",[56,63,66],{"x":58,"y":64,"style":65},"88","font-size:11px;fill:var(--text,#172033);text-anchor:middle","on the tab bar",[56,68,70],{"x":58,"y":69,"style":65},"112","everyone sees it",[56,72,75],{"x":58,"y":73,"style":74},"144","font-size:10.5px;fill:var(--muted,#5b6780);text-anchor:middle","the default for",[56,77,78],{"x":58,"y":51,"style":74},"every new sheet",[40,80],{"x":81,"y":49,"width":50,"height":51,"rx":48,"fill":82,"stroke":83,"style":54},"276","#fdefd8","var(--gold,#b4740a)",[56,85,88],{"x":86,"y":59,"style":87},"400","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","\"hidden\"",[56,90,91],{"x":86,"y":64,"style":65},"off the tab bar",[56,93,94],{"x":86,"y":69,"style":65},"listed under Unhide",[56,96,97],{"x":86,"y":73,"style":74},"any reader can",[56,99,100],{"x":86,"y":51,"style":74},"restore it in two clicks",[40,102],{"x":103,"y":49,"width":50,"height":51,"rx":48,"fill":104,"stroke":105,"style":54},"538","#ebebfd","var(--brand,#5b5cf0)",[56,107,110],{"x":108,"y":59,"style":109},"662","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","\"veryHidden\"",[56,112,113],{"x":108,"y":64,"style":65},"absent from Unhide",[56,115,116],{"x":108,"y":69,"style":65},"needs the VBA editor",[56,118,119],{"x":108,"y":73,"style":74},"stops casual browsing",[56,121,122],{"x":108,"y":51,"style":74},"and nothing more",[56,124,127],{"x":86,"y":125,"style":126},"222","font-size:11.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","all three read identically from Python — visibility is not confidentiality",[129,130,132],"h2",{"id":131},"prerequisites","Prerequisites",[134,135,140],"pre",{"className":136,"code":137,"language":138,"meta":139,"style":139},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[141,142,143],"code",{"__ignoreMap":139},[144,145,148,152,156,159],"span",{"class":146,"line":147},"line",1,[144,149,151],{"class":150},"sMTad","pip",[144,153,155],{"class":154},"srMev"," install",[144,157,158],{"class":154}," openpyxl",[144,160,161],{"class":154}," pandas\n",[10,163,164],{},"A workbook with something worth hiding — a summary sheet a reader wants, plus the lookup and raw sheets that support it:",[134,166,170],{"className":167,"code":168,"language":169,"meta":139,"style":139},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nsummary = pd.DataFrame({\"region\": [\"North\", \"South\"], \"revenue\": [159.92, 247.50]})\nlookups = pd.DataFrame({\"region_code\": [\"N\", \"S\", \"W\"],\n                        \"region\": [\"North\", \"South\", \"West\"]})\nraw = pd.DataFrame({\"order_id\": range(1, 21), \"amount\": range(20, 40)})\n\nwith pd.ExcelWriter(\"report.xlsx\", engine=\"xlsxwriter\") as writer:\n    summary.to_excel(writer, sheet_name=\"Summary\", index=False)\n    lookups.to_excel(writer, sheet_name=\"Lookups\", index=False)\n    raw.to_excel(writer, sheet_name=\"_raw\", index=False)\n","python",[141,171,172,188,195,242,273,294,347,352,383,410,433],{"__ignoreMap":139},[144,173,174,178,182,185],{"class":146,"line":147},[144,175,177],{"class":176},"s-kum","import",[144,179,181],{"class":180},"skGVy"," pandas ",[144,183,184],{"class":176},"as",[144,186,187],{"class":180}," pd\n",[144,189,191],{"class":146,"line":190},2,[144,192,194],{"emptyLinePlaceholder":193},true,"\n",[144,196,198,201,204,207,210,213,216,219,222,225,228,230,234,236,239],{"class":146,"line":197},3,[144,199,200],{"class":180},"summary ",[144,202,203],{"class":176},"=",[144,205,206],{"class":180}," pd.DataFrame({",[144,208,209],{"class":154},"\"region\"",[144,211,212],{"class":180},": [",[144,214,215],{"class":154},"\"North\"",[144,217,218],{"class":180},", ",[144,220,221],{"class":154},"\"South\"",[144,223,224],{"class":180},"], ",[144,226,227],{"class":154},"\"revenue\"",[144,229,212],{"class":180},[144,231,233],{"class":232},"sP0c6","159.92",[144,235,218],{"class":180},[144,237,238],{"class":232},"247.50",[144,240,241],{"class":180},"]})\n",[144,243,245,248,250,252,255,257,260,262,265,267,270],{"class":146,"line":244},4,[144,246,247],{"class":180},"lookups ",[144,249,203],{"class":176},[144,251,206],{"class":180},[144,253,254],{"class":154},"\"region_code\"",[144,256,212],{"class":180},[144,258,259],{"class":154},"\"N\"",[144,261,218],{"class":180},[144,263,264],{"class":154},"\"S\"",[144,266,218],{"class":180},[144,268,269],{"class":154},"\"W\"",[144,271,272],{"class":180},"],\n",[144,274,276,279,281,283,285,287,289,292],{"class":146,"line":275},5,[144,277,278],{"class":154},"                        \"region\"",[144,280,212],{"class":180},[144,282,215],{"class":154},[144,284,218],{"class":180},[144,286,221],{"class":154},[144,288,218],{"class":180},[144,290,291],{"class":154},"\"West\"",[144,293,241],{"class":180},[144,295,297,300,302,304,307,310,313,316,319,321,324,327,330,332,334,336,339,341,344],{"class":146,"line":296},6,[144,298,299],{"class":180},"raw ",[144,301,203],{"class":176},[144,303,206],{"class":180},[144,305,306],{"class":154},"\"order_id\"",[144,308,309],{"class":180},": ",[144,311,312],{"class":232},"range",[144,314,315],{"class":180},"(",[144,317,318],{"class":232},"1",[144,320,218],{"class":180},[144,322,323],{"class":232},"21",[144,325,326],{"class":180},"), ",[144,328,329],{"class":154},"\"amount\"",[144,331,309],{"class":180},[144,333,312],{"class":232},[144,335,315],{"class":180},[144,337,338],{"class":232},"20",[144,340,218],{"class":180},[144,342,343],{"class":232},"40",[144,345,346],{"class":180},")})\n",[144,348,350],{"class":146,"line":349},7,[144,351,194],{"emptyLinePlaceholder":193},[144,353,355,358,361,364,366,370,372,375,378,380],{"class":146,"line":354},8,[144,356,357],{"class":176},"with",[144,359,360],{"class":180}," pd.ExcelWriter(",[144,362,363],{"class":154},"\"report.xlsx\"",[144,365,218],{"class":180},[144,367,369],{"class":368},"sa561","engine",[144,371,203],{"class":176},[144,373,374],{"class":154},"\"xlsxwriter\"",[144,376,377],{"class":180},") ",[144,379,184],{"class":176},[144,381,382],{"class":180}," writer:\n",[144,384,386,389,392,394,397,399,402,404,407],{"class":146,"line":385},9,[144,387,388],{"class":180},"    summary.to_excel(writer, ",[144,390,391],{"class":368},"sheet_name",[144,393,203],{"class":176},[144,395,396],{"class":154},"\"Summary\"",[144,398,218],{"class":180},[144,400,401],{"class":368},"index",[144,403,203],{"class":176},[144,405,406],{"class":232},"False",[144,408,409],{"class":180},")\n",[144,411,413,416,418,420,423,425,427,429,431],{"class":146,"line":412},10,[144,414,415],{"class":180},"    lookups.to_excel(writer, ",[144,417,391],{"class":368},[144,419,203],{"class":176},[144,421,422],{"class":154},"\"Lookups\"",[144,424,218],{"class":180},[144,426,401],{"class":368},[144,428,203],{"class":176},[144,430,406],{"class":232},[144,432,409],{"class":180},[144,434,436,439,441,443,446,448,450,452,454],{"class":146,"line":435},11,[144,437,438],{"class":180},"    raw.to_excel(writer, ",[144,440,391],{"class":368},[144,442,203],{"class":176},[144,444,445],{"class":154},"\"_raw\"",[144,447,218],{"class":180},[144,449,401],{"class":368},[144,451,203],{"class":176},[144,453,406],{"class":232},[144,455,409],{"class":180},[129,457,459],{"id":458},"step-1-hide-a-sheet","Step 1 — Hide a sheet",[10,461,462,465],{},[141,463,464],{},"sheet_state"," takes one of three string values:",[134,467,469],{"className":167,"code":468,"language":169,"meta":139,"style":139},"from openpyxl import load_workbook\n\nwb = load_workbook(\"report.xlsx\")\n\nwb[\"Lookups\"].sheet_state = \"hidden\"\nwb[\"_raw\"].sheet_state = \"veryHidden\"\n\nwb.save(\"report_tidy.xlsx\")\nprint([f\"{ws.title}: {ws.sheet_state}\" for ws in wb.worksheets])\n",[141,470,471,484,488,502,506,521,534,538,548],{"__ignoreMap":139},[144,472,473,476,479,481],{"class":146,"line":147},[144,474,475],{"class":176},"from",[144,477,478],{"class":180}," openpyxl ",[144,480,177],{"class":176},[144,482,483],{"class":180}," load_workbook\n",[144,485,486],{"class":146,"line":190},[144,487,194],{"emptyLinePlaceholder":193},[144,489,490,493,495,498,500],{"class":146,"line":197},[144,491,492],{"class":180},"wb ",[144,494,203],{"class":176},[144,496,497],{"class":180}," load_workbook(",[144,499,363],{"class":154},[144,501,409],{"class":180},[144,503,504],{"class":146,"line":244},[144,505,194],{"emptyLinePlaceholder":193},[144,507,508,511,513,516,518],{"class":146,"line":275},[144,509,510],{"class":180},"wb[",[144,512,422],{"class":154},[144,514,515],{"class":180},"].sheet_state ",[144,517,203],{"class":176},[144,519,520],{"class":154}," \"hidden\"\n",[144,522,523,525,527,529,531],{"class":146,"line":296},[144,524,510],{"class":180},[144,526,445],{"class":154},[144,528,515],{"class":180},[144,530,203],{"class":176},[144,532,533],{"class":154}," \"veryHidden\"\n",[144,535,536],{"class":146,"line":349},[144,537,194],{"emptyLinePlaceholder":193},[144,539,540,543,546],{"class":146,"line":354},[144,541,542],{"class":180},"wb.save(",[144,544,545],{"class":154},"\"report_tidy.xlsx\"",[144,547,409],{"class":180},[144,549,550,553,556,559,562,566,569,572,574,576,579,581,583,586,589,592],{"class":146,"line":385},[144,551,552],{"class":232},"print",[144,554,555],{"class":180},"([",[144,557,558],{"class":176},"f",[144,560,561],{"class":154},"\"",[144,563,565],{"class":564},"sSjpA","{",[144,567,568],{"class":180},"ws.title",[144,570,571],{"class":564},"}",[144,573,309],{"class":154},[144,575,565],{"class":564},[144,577,578],{"class":180},"ws.sheet_state",[144,580,571],{"class":564},[144,582,561],{"class":154},[144,584,585],{"class":176}," for",[144,587,588],{"class":180}," ws ",[144,590,591],{"class":176},"in",[144,593,594],{"class":180}," wb.worksheets])\n",[10,596,597,598,601,602,605],{},"Choose between the two by asking whether a reader restoring the sheet would be a problem or a convenience. A lookup table somebody might reasonably want to check should be ",[141,599,600],{},"hidden",". A raw extract that would only confuse should be ",[141,603,604],{},"veryHidden"," — not because it is secret, but because an accidental unhide makes the report look broken.",[10,607,608],{},"Both are equally readable from Python, which is the point to keep clear in your head. Reading a very hidden sheet takes no special handling at all:",[134,610,612],{"className":167,"code":611,"language":169,"meta":139,"style":139},"import pandas as pd\n\n# The \"very hidden\" sheet is entirely ordinary to pandas.\nraw = pd.read_excel(\"report_tidy.xlsx\", sheet_name=\"_raw\")\nprint(len(raw))     # 20\n",[141,613,614,624,628,634,655],{"__ignoreMap":139},[144,615,616,618,620,622],{"class":146,"line":147},[144,617,177],{"class":176},[144,619,181],{"class":180},[144,621,184],{"class":176},[144,623,187],{"class":180},[144,625,626],{"class":146,"line":190},[144,627,194],{"emptyLinePlaceholder":193},[144,629,630],{"class":146,"line":197},[144,631,633],{"class":632},"s-wDw","# The \"very hidden\" sheet is entirely ordinary to pandas.\n",[144,635,636,638,640,643,645,647,649,651,653],{"class":146,"line":244},[144,637,299],{"class":180},[144,639,203],{"class":176},[144,641,642],{"class":180}," pd.read_excel(",[144,644,545],{"class":154},[144,646,218],{"class":180},[144,648,391],{"class":368},[144,650,203],{"class":176},[144,652,445],{"class":154},[144,654,409],{"class":180},[144,656,657,659,661,664,667],{"class":146,"line":275},[144,658,552],{"class":232},[144,660,315],{"class":180},[144,662,663],{"class":232},"len",[144,665,666],{"class":180},"(raw))     ",[144,668,669],{"class":632},"# 20\n",[129,671,673],{"id":672},"step-2-never-hide-the-last-visible-sheet","Step 2 — Never hide the last visible sheet",[10,675,676],{},"Excel requires at least one visible worksheet. Hide them all and the file opens with a repair prompt, or refuses to open at all — and openpyxl will write it happily, because the constraint is Excel's rather than the format's.",[134,678,680],{"className":167,"code":679,"language":169,"meta":139,"style":139},"def hide_sheets(wb, names, state=\"hidden\"):\n    \"\"\"Hide the named sheets, refusing to leave the workbook with none visible.\"\"\"\n    if state not in {\"hidden\", \"veryHidden\"}:\n        raise ValueError(f\"unknown state: {state}\")\n\n    targets = [n for n in names if n in wb.sheetnames]\n    still_visible = [\n        ws.title for ws in wb.worksheets\n        if ws.sheet_state == \"visible\" and ws.title not in targets\n    ]\n    if not still_visible:\n        raise ValueError(\n            \"at least one sheet must remain visible; \"\n            f\"hiding {targets} would hide them all\"\n        )\n\n    for name in targets:\n        wb[name].sheet_state = state\n    return targets\n",[141,681,682,701,706,732,758,762,793,803,817,844,849,859,869,875,894,900,905,919,930],{"__ignoreMap":139},[144,683,684,687,691,694,696,698],{"class":146,"line":147},[144,685,686],{"class":176},"def",[144,688,690],{"class":689},"s_Opv"," hide_sheets",[144,692,693],{"class":180},"(wb, names, state",[144,695,203],{"class":176},[144,697,88],{"class":154},[144,699,700],{"class":180},"):\n",[144,702,703],{"class":146,"line":190},[144,704,705],{"class":154},"    \"\"\"Hide the named sheets, refusing to leave the workbook with none visible.\"\"\"\n",[144,707,708,711,714,717,720,723,725,727,729],{"class":146,"line":197},[144,709,710],{"class":176},"    if",[144,712,713],{"class":180}," state ",[144,715,716],{"class":176},"not",[144,718,719],{"class":176}," in",[144,721,722],{"class":180}," {",[144,724,88],{"class":154},[144,726,218],{"class":180},[144,728,110],{"class":154},[144,730,731],{"class":180},"}:\n",[144,733,734,737,740,742,744,747,749,752,754,756],{"class":146,"line":244},[144,735,736],{"class":176},"        raise",[144,738,739],{"class":232}," ValueError",[144,741,315],{"class":180},[144,743,558],{"class":176},[144,745,746],{"class":154},"\"unknown state: ",[144,748,565],{"class":564},[144,750,751],{"class":180},"state",[144,753,571],{"class":564},[144,755,561],{"class":154},[144,757,409],{"class":180},[144,759,760],{"class":146,"line":275},[144,761,194],{"emptyLinePlaceholder":193},[144,763,764,767,769,772,775,778,780,783,786,788,790],{"class":146,"line":296},[144,765,766],{"class":180},"    targets ",[144,768,203],{"class":176},[144,770,771],{"class":180}," [n ",[144,773,774],{"class":176},"for",[144,776,777],{"class":180}," n ",[144,779,591],{"class":176},[144,781,782],{"class":180}," names ",[144,784,785],{"class":176},"if",[144,787,777],{"class":180},[144,789,591],{"class":176},[144,791,792],{"class":180}," wb.sheetnames]\n",[144,794,795,798,800],{"class":146,"line":349},[144,796,797],{"class":180},"    still_visible ",[144,799,203],{"class":176},[144,801,802],{"class":180}," [\n",[144,804,805,808,810,812,814],{"class":146,"line":354},[144,806,807],{"class":180},"        ws.title ",[144,809,774],{"class":176},[144,811,588],{"class":180},[144,813,591],{"class":176},[144,815,816],{"class":180}," wb.worksheets\n",[144,818,819,822,825,828,831,834,837,839,841],{"class":146,"line":385},[144,820,821],{"class":176},"        if",[144,823,824],{"class":180}," ws.sheet_state ",[144,826,827],{"class":176},"==",[144,829,830],{"class":154}," \"visible\"",[144,832,833],{"class":176}," and",[144,835,836],{"class":180}," ws.title ",[144,838,716],{"class":176},[144,840,719],{"class":176},[144,842,843],{"class":180}," targets\n",[144,845,846],{"class":146,"line":412},[144,847,848],{"class":180},"    ]\n",[144,850,851,853,856],{"class":146,"line":435},[144,852,710],{"class":176},[144,854,855],{"class":176}," not",[144,857,858],{"class":180}," still_visible:\n",[144,860,862,864,866],{"class":146,"line":861},12,[144,863,736],{"class":176},[144,865,739],{"class":232},[144,867,868],{"class":180},"(\n",[144,870,872],{"class":146,"line":871},13,[144,873,874],{"class":154},"            \"at least one sheet must remain visible; \"\n",[144,876,878,881,884,886,889,891],{"class":146,"line":877},14,[144,879,880],{"class":176},"            f",[144,882,883],{"class":154},"\"hiding ",[144,885,565],{"class":564},[144,887,888],{"class":180},"targets",[144,890,571],{"class":564},[144,892,893],{"class":154}," would hide them all\"\n",[144,895,897],{"class":146,"line":896},15,[144,898,899],{"class":180},"        )\n",[144,901,903],{"class":146,"line":902},16,[144,904,194],{"emptyLinePlaceholder":193},[144,906,908,911,914,916],{"class":146,"line":907},17,[144,909,910],{"class":176},"    for",[144,912,913],{"class":180}," name ",[144,915,591],{"class":176},[144,917,918],{"class":180}," targets:\n",[144,920,922,925,927],{"class":146,"line":921},18,[144,923,924],{"class":180},"        wb[name].sheet_state ",[144,926,203],{"class":176},[144,928,929],{"class":180}," state\n",[144,931,933,936],{"class":146,"line":932},19,[144,934,935],{"class":176},"    return",[144,937,843],{"class":180},[10,939,940],{},"This bites most often in a loop that hides \"every sheet matching a pattern\" against a workbook where the pattern happens to match everything — a naming convention change upstream is enough to trigger it.",[10,942,943,944,947],{},"There is a second, related rule: the active sheet should be visible. A workbook whose ",[141,945,946],{},"active"," index points at a hidden sheet opens on a blank view, which reads as a broken file:",[134,949,951],{"className":167,"code":950,"language":169,"meta":139,"style":139},"from openpyxl import load_workbook\n\nwb = load_workbook(\"report_tidy.xlsx\")\nhide_sheets(wb, [\"Lookups\", \"_raw\"], state=\"hidden\")\n\n# Point the workbook at a sheet the reader will actually see.\nvisible = [ws for ws in wb.worksheets if ws.sheet_state == \"visible\"]\nwb.active = wb.index(visible[0])\n\nwb.save(\"report_final.xlsx\")\n",[141,952,953,963,967,979,1000,1004,1009,1039,1054,1058],{"__ignoreMap":139},[144,954,955,957,959,961],{"class":146,"line":147},[144,956,475],{"class":176},[144,958,478],{"class":180},[144,960,177],{"class":176},[144,962,483],{"class":180},[144,964,965],{"class":146,"line":190},[144,966,194],{"emptyLinePlaceholder":193},[144,968,969,971,973,975,977],{"class":146,"line":197},[144,970,492],{"class":180},[144,972,203],{"class":176},[144,974,497],{"class":180},[144,976,545],{"class":154},[144,978,409],{"class":180},[144,980,981,984,986,988,990,992,994,996,998],{"class":146,"line":244},[144,982,983],{"class":180},"hide_sheets(wb, [",[144,985,422],{"class":154},[144,987,218],{"class":180},[144,989,445],{"class":154},[144,991,224],{"class":180},[144,993,751],{"class":368},[144,995,203],{"class":176},[144,997,88],{"class":154},[144,999,409],{"class":180},[144,1001,1002],{"class":146,"line":275},[144,1003,194],{"emptyLinePlaceholder":193},[144,1005,1006],{"class":146,"line":296},[144,1007,1008],{"class":632},"# Point the workbook at a sheet the reader will actually see.\n",[144,1010,1011,1014,1016,1019,1021,1023,1025,1028,1030,1032,1034,1036],{"class":146,"line":349},[144,1012,1013],{"class":180},"visible ",[144,1015,203],{"class":176},[144,1017,1018],{"class":180}," [ws ",[144,1020,774],{"class":176},[144,1022,588],{"class":180},[144,1024,591],{"class":176},[144,1026,1027],{"class":180}," wb.worksheets ",[144,1029,785],{"class":176},[144,1031,824],{"class":180},[144,1033,827],{"class":176},[144,1035,830],{"class":154},[144,1037,1038],{"class":180},"]\n",[144,1040,1041,1044,1046,1049,1051],{"class":146,"line":354},[144,1042,1043],{"class":180},"wb.active ",[144,1045,203],{"class":176},[144,1047,1048],{"class":180}," wb.index(visible[",[144,1050,42],{"class":232},[144,1052,1053],{"class":180},"])\n",[144,1055,1056],{"class":146,"line":385},[144,1057,194],{"emptyLinePlaceholder":193},[144,1059,1060,1062,1065],{"class":146,"line":412},[144,1061,542],{"class":180},[144,1063,1064],{"class":154},"\"report_final.xlsx\"",[144,1066,409],{"class":180},[129,1068,1070],{"id":1069},"step-3-hide-rows-and-columns","Step 3 — Hide rows and columns",[10,1072,1073],{},"Row and column visibility lives on the dimension objects, not on individual cells — so there is no loop:",[134,1075,1077],{"className":167,"code":1076,"language":169,"meta":139,"style":139},"from openpyxl import load_workbook\n\nwb = load_workbook(\"report.xlsx\")\nws = wb[\"Summary\"]\n\n# A single helper column.\nws.column_dimensions[\"D\"].hidden = True\n\n# A contiguous block of columns, in one call.\nws.column_dimensions.group(\"F\", \"J\", hidden=True)\n\n# Individual rows.\nws.row_dimensions[7].hidden = True\n\nwb.save(\"report_tidy.xlsx\")\n",[141,1078,1079,1089,1093,1105,1119,1123,1128,1144,1148,1153,1177,1181,1186,1200,1204],{"__ignoreMap":139},[144,1080,1081,1083,1085,1087],{"class":146,"line":147},[144,1082,475],{"class":176},[144,1084,478],{"class":180},[144,1086,177],{"class":176},[144,1088,483],{"class":180},[144,1090,1091],{"class":146,"line":190},[144,1092,194],{"emptyLinePlaceholder":193},[144,1094,1095,1097,1099,1101,1103],{"class":146,"line":197},[144,1096,492],{"class":180},[144,1098,203],{"class":176},[144,1100,497],{"class":180},[144,1102,363],{"class":154},[144,1104,409],{"class":180},[144,1106,1107,1110,1112,1115,1117],{"class":146,"line":244},[144,1108,1109],{"class":180},"ws ",[144,1111,203],{"class":176},[144,1113,1114],{"class":180}," wb[",[144,1116,396],{"class":154},[144,1118,1038],{"class":180},[144,1120,1121],{"class":146,"line":275},[144,1122,194],{"emptyLinePlaceholder":193},[144,1124,1125],{"class":146,"line":296},[144,1126,1127],{"class":632},"# A single helper column.\n",[144,1129,1130,1133,1136,1139,1141],{"class":146,"line":349},[144,1131,1132],{"class":180},"ws.column_dimensions[",[144,1134,1135],{"class":154},"\"D\"",[144,1137,1138],{"class":180},"].hidden ",[144,1140,203],{"class":176},[144,1142,1143],{"class":232}," True\n",[144,1145,1146],{"class":146,"line":354},[144,1147,194],{"emptyLinePlaceholder":193},[144,1149,1150],{"class":146,"line":385},[144,1151,1152],{"class":632},"# A contiguous block of columns, in one call.\n",[144,1154,1155,1158,1161,1163,1166,1168,1170,1172,1175],{"class":146,"line":412},[144,1156,1157],{"class":180},"ws.column_dimensions.group(",[144,1159,1160],{"class":154},"\"F\"",[144,1162,218],{"class":180},[144,1164,1165],{"class":154},"\"J\"",[144,1167,218],{"class":180},[144,1169,600],{"class":368},[144,1171,203],{"class":176},[144,1173,1174],{"class":232},"True",[144,1176,409],{"class":180},[144,1178,1179],{"class":146,"line":435},[144,1180,194],{"emptyLinePlaceholder":193},[144,1182,1183],{"class":146,"line":861},[144,1184,1185],{"class":632},"# Individual rows.\n",[144,1187,1188,1191,1194,1196,1198],{"class":146,"line":871},[144,1189,1190],{"class":180},"ws.row_dimensions[",[144,1192,1193],{"class":232},"7",[144,1195,1138],{"class":180},[144,1197,203],{"class":176},[144,1199,1143],{"class":232},[144,1201,1202],{"class":146,"line":877},[144,1203,194],{"emptyLinePlaceholder":193},[144,1205,1206,1208,1210],{"class":146,"line":896},[144,1207,542],{"class":180},[144,1209,545],{"class":154},[144,1211,409],{"class":180},[10,1213,1214],{},"Hiding many individual rows one at a time is the slow path, because each creates a dimension record. When you need to hide a computed set — say, every row whose status column is closed — collapse consecutive runs into groups:",[134,1216,1218],{"className":167,"code":1217,"language":169,"meta":139,"style":139},"from itertools import groupby\nfrom operator import itemgetter\nfrom openpyxl import load_workbook\n\nwb = load_workbook(\"report.xlsx\")\nws = wb[\"_raw\"]\n\n# Rows to hide: every closed order.\nto_hide = [\n    cell.row\n    for (cell,) in ws.iter_rows(min_row=2, min_col=3, max_col=3)\n    if cell.value == \"closed\"\n]\n\n# Collapse [4,5,6,9,10] into ranges 4-6 and 9-10.\nfor _, group in groupby(enumerate(to_hide), lambda p: p[1] - p[0]):\n    rows = list(map(itemgetter(1), group))\n    ws.row_dimensions.group(rows[0], rows[-1], hidden=True)\n\nwb.save(\"report_tidy.xlsx\")\n",[141,1219,1220,1232,1244,1254,1258,1270,1282,1286,1291,1300,1305,1346,1358,1362,1366,1371,1411,1434,1458,1462],{"__ignoreMap":139},[144,1221,1222,1224,1227,1229],{"class":146,"line":147},[144,1223,475],{"class":176},[144,1225,1226],{"class":180}," itertools ",[144,1228,177],{"class":176},[144,1230,1231],{"class":180}," groupby\n",[144,1233,1234,1236,1239,1241],{"class":146,"line":190},[144,1235,475],{"class":176},[144,1237,1238],{"class":180}," operator ",[144,1240,177],{"class":176},[144,1242,1243],{"class":180}," itemgetter\n",[144,1245,1246,1248,1250,1252],{"class":146,"line":197},[144,1247,475],{"class":176},[144,1249,478],{"class":180},[144,1251,177],{"class":176},[144,1253,483],{"class":180},[144,1255,1256],{"class":146,"line":244},[144,1257,194],{"emptyLinePlaceholder":193},[144,1259,1260,1262,1264,1266,1268],{"class":146,"line":275},[144,1261,492],{"class":180},[144,1263,203],{"class":176},[144,1265,497],{"class":180},[144,1267,363],{"class":154},[144,1269,409],{"class":180},[144,1271,1272,1274,1276,1278,1280],{"class":146,"line":296},[144,1273,1109],{"class":180},[144,1275,203],{"class":176},[144,1277,1114],{"class":180},[144,1279,445],{"class":154},[144,1281,1038],{"class":180},[144,1283,1284],{"class":146,"line":349},[144,1285,194],{"emptyLinePlaceholder":193},[144,1287,1288],{"class":146,"line":354},[144,1289,1290],{"class":632},"# Rows to hide: every closed order.\n",[144,1292,1293,1296,1298],{"class":146,"line":385},[144,1294,1295],{"class":180},"to_hide ",[144,1297,203],{"class":176},[144,1299,802],{"class":180},[144,1301,1302],{"class":146,"line":412},[144,1303,1304],{"class":180},"    cell.row\n",[144,1306,1307,1309,1312,1314,1317,1320,1322,1325,1327,1330,1332,1335,1337,1340,1342,1344],{"class":146,"line":435},[144,1308,910],{"class":176},[144,1310,1311],{"class":180}," (cell,) ",[144,1313,591],{"class":176},[144,1315,1316],{"class":180}," ws.iter_rows(",[144,1318,1319],{"class":368},"min_row",[144,1321,203],{"class":176},[144,1323,1324],{"class":232},"2",[144,1326,218],{"class":180},[144,1328,1329],{"class":368},"min_col",[144,1331,203],{"class":176},[144,1333,1334],{"class":232},"3",[144,1336,218],{"class":180},[144,1338,1339],{"class":368},"max_col",[144,1341,203],{"class":176},[144,1343,1334],{"class":232},[144,1345,409],{"class":180},[144,1347,1348,1350,1353,1355],{"class":146,"line":861},[144,1349,710],{"class":176},[144,1351,1352],{"class":180}," cell.value ",[144,1354,827],{"class":176},[144,1356,1357],{"class":154}," \"closed\"\n",[144,1359,1360],{"class":146,"line":871},[144,1361,1038],{"class":180},[144,1363,1364],{"class":146,"line":877},[144,1365,194],{"emptyLinePlaceholder":193},[144,1367,1368],{"class":146,"line":896},[144,1369,1370],{"class":632},"# Collapse [4,5,6,9,10] into ranges 4-6 and 9-10.\n",[144,1372,1373,1375,1378,1380,1383,1386,1389,1392,1395,1397,1400,1403,1406,1408],{"class":146,"line":902},[144,1374,774],{"class":176},[144,1376,1377],{"class":180}," _, group ",[144,1379,591],{"class":176},[144,1381,1382],{"class":180}," groupby(",[144,1384,1385],{"class":232},"enumerate",[144,1387,1388],{"class":180},"(to_hide), ",[144,1390,1391],{"class":176},"lambda",[144,1393,1394],{"class":180}," p: p[",[144,1396,318],{"class":232},[144,1398,1399],{"class":180},"] ",[144,1401,1402],{"class":176},"-",[144,1404,1405],{"class":180}," p[",[144,1407,42],{"class":232},[144,1409,1410],{"class":180},"]):\n",[144,1412,1413,1416,1418,1421,1423,1426,1429,1431],{"class":146,"line":907},[144,1414,1415],{"class":180},"    rows ",[144,1417,203],{"class":176},[144,1419,1420],{"class":232}," list",[144,1422,315],{"class":180},[144,1424,1425],{"class":232},"map",[144,1427,1428],{"class":180},"(itemgetter(",[144,1430,318],{"class":232},[144,1432,1433],{"class":180},"), group))\n",[144,1435,1436,1439,1441,1444,1446,1448,1450,1452,1454,1456],{"class":146,"line":921},[144,1437,1438],{"class":180},"    ws.row_dimensions.group(rows[",[144,1440,42],{"class":232},[144,1442,1443],{"class":180},"], rows[",[144,1445,1402],{"class":176},[144,1447,318],{"class":232},[144,1449,224],{"class":180},[144,1451,600],{"class":368},[144,1453,203],{"class":176},[144,1455,1174],{"class":232},[144,1457,409],{"class":180},[144,1459,1460],{"class":146,"line":932},[144,1461,194],{"emptyLinePlaceholder":193},[144,1463,1465,1467,1469],{"class":146,"line":1464},20,[144,1466,542],{"class":180},[144,1468,545],{"class":154},[144,1470,409],{"class":180},[10,1472,1473,1474,1477,1478,1481,1482,1485,1486,1488],{},"One thing hiding does ",[1475,1476,716],"em",{}," do: change any value. A hidden row still contributes to ",[141,1479,1480],{},"SUM",", and pandas reads it like any other row. If a total should exclude hidden rows, the formula has to say so — ",[141,1483,1484],{},"SUBTOTAL(109, ...)"," sums only visible rows, where the plain ",[141,1487,1480],{}," does not:",[134,1490,1492],{"className":167,"code":1491,"language":169,"meta":139,"style":139},"ws[\"D20\"] = \"=SUBTOTAL(109,D2:D19)\"    # visible rows only\nws[\"D21\"] = \"=SUM(D2:D19)\"             # every row, hidden included\n",[141,1493,1494,1512],{"__ignoreMap":139},[144,1495,1496,1499,1502,1504,1506,1509],{"class":146,"line":147},[144,1497,1498],{"class":180},"ws[",[144,1500,1501],{"class":154},"\"D20\"",[144,1503,1399],{"class":180},[144,1505,203],{"class":176},[144,1507,1508],{"class":154}," \"=SUBTOTAL(109,D2:D19)\"",[144,1510,1511],{"class":632},"    # visible rows only\n",[144,1513,1514,1516,1519,1521,1523,1526],{"class":146,"line":190},[144,1515,1498],{"class":180},[144,1517,1518],{"class":154},"\"D21\"",[144,1520,1399],{"class":180},[144,1522,203],{"class":176},[144,1524,1525],{"class":154}," \"=SUM(D2:D19)\"",[144,1527,1528],{"class":632},"             # every row, hidden included\n",[129,1530,1532],{"id":1531},"step-4-group-instead-of-hide-where-readers-might-want-the-detail","Step 4 — Group instead of hide, where readers might want the detail",[10,1534,1535],{},"Hiding gives the reader no signal that anything is there. Outline grouping does the same tidying but adds a plus and minus control in the margin, so the detail is one click away.",[20,1537,30,1543,30,1546,30,1549,30,1552,30,1559,30,1565,30,1569,30,1576,30,1581,30,1585,30,1588,30,1592,30,1596,30,1600,30,1603,30,1607,30,1611,30,1616,30,1619,30,1623,30,1626,30,1629,30,1632,30,1634,30,1636,30,1638,30,1642],{"viewBox":1538,"role":23,"ariaLabel":1539,"ariaLabelledBy":1540,"xmlns":28,"style":29},"0 0 800 240","Hiding versus grouping: hidden rows leave a jump in the row numbers with no indication anything is missing, while grouped rows add an expand control in the left margin.",[1541,1542],"grpvs-t","grpvs-d",[32,1544,1545],{"id":1541},"Hidden rows versus a collapsed outline group",[36,1547,1548],{"id":1542},"Two views of the same sheet. On the left, rows four to nine are hidden: the row numbers jump straight from three to ten and nothing tells the reader that six rows are missing. On the right the same rows are grouped and collapsed: a plus control sits in the left margin beside the summary row, so the reader can see detail exists and expand it themselves.",[40,1550],{"x":42,"y":42,"width":43,"height":1551,"fill":45},"240",[40,1553],{"x":48,"y":1554,"width":1555,"height":1556,"rx":48,"fill":1557,"stroke":1558,"style":54},"24","368","196","#fee8f2","var(--accent,#f43f8f)",[56,1560,1564],{"x":1561,"y":1562,"style":1563},"198","52","font-size:12.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","hidden rows",[56,1566,1334],{"x":1562,"y":1567,"style":1568},"84","font-size:11px;fill:var(--muted,#5b6780);text-anchor:middle",[40,1570],{"x":1571,"y":1572,"width":1573,"height":1554,"rx":1574,"fill":45,"stroke":1575},"72","70","286","5","var(--line,#cdd5e6)",[56,1577,1580],{"x":1578,"y":1579,"style":65},"215","87","North · regional total",[56,1582,1584],{"x":1562,"y":1583,"style":1568},"122","10",[40,1586],{"x":1571,"y":1587,"width":1573,"height":1554,"rx":1574,"fill":45,"stroke":1575},"108",[56,1589,1591],{"x":1578,"y":1590,"style":65},"125","South · regional total",[56,1593,1595],{"x":1561,"y":51,"style":1594},"font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","rows 4–9 are simply gone",[56,1597,1599],{"x":1561,"y":1598,"style":74},"188","only the number jump hints at it",[40,1601],{"x":1602,"y":1554,"width":1555,"height":1556,"rx":48,"fill":52,"stroke":53,"style":54},"418",[56,1604,1606],{"x":1605,"y":1562,"style":60},"602","collapsed outline group",[40,1608],{"x":1609,"y":1571,"width":338,"height":338,"rx":1610,"fill":45,"stroke":53,"style":54},"440","4",[56,1612,1615],{"x":1613,"y":64,"style":1614},"450","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","+",[56,1617,1334],{"x":1618,"y":1579,"style":1568},"476",[40,1620],{"x":1621,"y":1572,"width":1622,"height":1554,"rx":1574,"fill":45,"stroke":1575},"494","268",[56,1624,1580],{"x":1625,"y":1579,"style":65},"628",[40,1627],{"x":1609,"y":1628,"width":338,"height":338,"rx":1610,"fill":45,"stroke":53,"style":54},"110",[56,1630,1615],{"x":1613,"y":1631,"style":1614},"126",[56,1633,1584],{"x":1618,"y":1590,"style":1568},[40,1635],{"x":1621,"y":1587,"width":1622,"height":1554,"rx":1574,"fill":45,"stroke":1575},[56,1637,1591],{"x":1625,"y":1590,"style":65},[56,1639,1641],{"x":1605,"y":51,"style":1640},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","the detail is one click away",[56,1643,1644],{"x":1605,"y":1598,"style":74},"and visibly exists",[134,1646,1648],{"className":167,"code":1647,"language":169,"meta":139,"style":139},"from openpyxl import load_workbook\n\nwb = load_workbook(\"report.xlsx\")\nws = wb[\"_raw\"]\n\n# Collapse detail rows under their summary row.\nws.row_dimensions.group(4, 9, outline_level=1, hidden=True)\nws.row_dimensions.group(12, 17, outline_level=1, hidden=True)\n\n# Put the plus\u002Fminus control above the group rather than below it.\nws.sheet_properties.outlinePr.summaryBelow = False\n\nwb.save(\"report_grouped.xlsx\")\n",[141,1649,1650,1660,1664,1676,1688,1692,1697,1728,1758,1762,1767,1777,1781],{"__ignoreMap":139},[144,1651,1652,1654,1656,1658],{"class":146,"line":147},[144,1653,475],{"class":176},[144,1655,478],{"class":180},[144,1657,177],{"class":176},[144,1659,483],{"class":180},[144,1661,1662],{"class":146,"line":190},[144,1663,194],{"emptyLinePlaceholder":193},[144,1665,1666,1668,1670,1672,1674],{"class":146,"line":197},[144,1667,492],{"class":180},[144,1669,203],{"class":176},[144,1671,497],{"class":180},[144,1673,363],{"class":154},[144,1675,409],{"class":180},[144,1677,1678,1680,1682,1684,1686],{"class":146,"line":244},[144,1679,1109],{"class":180},[144,1681,203],{"class":176},[144,1683,1114],{"class":180},[144,1685,445],{"class":154},[144,1687,1038],{"class":180},[144,1689,1690],{"class":146,"line":275},[144,1691,194],{"emptyLinePlaceholder":193},[144,1693,1694],{"class":146,"line":296},[144,1695,1696],{"class":632},"# Collapse detail rows under their summary row.\n",[144,1698,1699,1702,1704,1706,1709,1711,1714,1716,1718,1720,1722,1724,1726],{"class":146,"line":349},[144,1700,1701],{"class":180},"ws.row_dimensions.group(",[144,1703,1610],{"class":232},[144,1705,218],{"class":180},[144,1707,1708],{"class":232},"9",[144,1710,218],{"class":180},[144,1712,1713],{"class":368},"outline_level",[144,1715,203],{"class":176},[144,1717,318],{"class":232},[144,1719,218],{"class":180},[144,1721,600],{"class":368},[144,1723,203],{"class":176},[144,1725,1174],{"class":232},[144,1727,409],{"class":180},[144,1729,1730,1732,1735,1737,1740,1742,1744,1746,1748,1750,1752,1754,1756],{"class":146,"line":354},[144,1731,1701],{"class":180},[144,1733,1734],{"class":232},"12",[144,1736,218],{"class":180},[144,1738,1739],{"class":232},"17",[144,1741,218],{"class":180},[144,1743,1713],{"class":368},[144,1745,203],{"class":176},[144,1747,318],{"class":232},[144,1749,218],{"class":180},[144,1751,600],{"class":368},[144,1753,203],{"class":176},[144,1755,1174],{"class":232},[144,1757,409],{"class":180},[144,1759,1760],{"class":146,"line":385},[144,1761,194],{"emptyLinePlaceholder":193},[144,1763,1764],{"class":146,"line":412},[144,1765,1766],{"class":632},"# Put the plus\u002Fminus control above the group rather than below it.\n",[144,1768,1769,1772,1774],{"class":146,"line":435},[144,1770,1771],{"class":180},"ws.sheet_properties.outlinePr.summaryBelow ",[144,1773,203],{"class":176},[144,1775,1776],{"class":232}," False\n",[144,1778,1779],{"class":146,"line":861},[144,1780,194],{"emptyLinePlaceholder":193},[144,1782,1783,1785,1788],{"class":146,"line":871},[144,1784,542],{"class":180},[144,1786,1787],{"class":154},"\"report_grouped.xlsx\"",[144,1789,409],{"class":180},[10,1791,1792,1795,1796,1799,1800,1802],{},[141,1793,1794],{},"summaryBelow"," is worth setting deliberately. Excel's default places the control on the row ",[1475,1797,1798],{},"after"," the group, which reads oddly when your summary row comes first — the usual layout for a regional breakdown. Setting it to ",[141,1801,406],{}," puts the control beside the summary, where readers expect it.",[10,1804,1805],{},"Columns group the same way, which is the neat way to fold away a block of monthly detail while leaving the annual totals visible:",[134,1807,1809],{"className":167,"code":1808,"language":169,"meta":139,"style":139},"# Months in D through O; the annual total sits in P.\nws.column_dimensions.group(\"D\", \"O\", outline_level=1, hidden=True)\n",[141,1810,1811,1816],{"__ignoreMap":139},[144,1812,1813],{"class":146,"line":147},[144,1814,1815],{"class":632},"# Months in D through O; the annual total sits in P.\n",[144,1817,1818,1820,1822,1824,1827,1829,1831,1833,1835,1837,1839,1841,1843],{"class":146,"line":190},[144,1819,1157],{"class":180},[144,1821,1135],{"class":154},[144,1823,218],{"class":180},[144,1825,1826],{"class":154},"\"O\"",[144,1828,218],{"class":180},[144,1830,1713],{"class":368},[144,1832,203],{"class":176},[144,1834,318],{"class":232},[144,1836,218],{"class":180},[144,1838,600],{"class":368},[144,1840,203],{"class":176},[144,1842,1174],{"class":232},[144,1844,409],{"class":180},[129,1846,1848],{"id":1847},"common-pitfalls-and-fixes","Common pitfalls and fixes",[1850,1851,1852,1868],"table",{},[1853,1854,1855],"thead",{},[1856,1857,1858,1862,1865],"tr",{},[1859,1860,1861],"th",{},"Symptom",[1859,1863,1864],{},"Cause",[1859,1866,1867],{},"Fix",[1869,1870,1871,1883,1900,1916,1930,1941,1956,1970],"tbody",{},[1856,1872,1873,1877,1880],{},[1874,1875,1876],"td",{},"Excel reports the file as corrupt",[1874,1878,1879],{},"Every sheet hidden",[1874,1881,1882],{},"Keep at least one visible; guard the hide step.",[1856,1884,1885,1888,1893],{},[1874,1886,1887],{},"Workbook opens on a blank view",[1874,1889,1890,1892],{},[141,1891,946],{}," points at a hidden sheet",[1874,1894,1895,1896,1899],{},"Set ",[141,1897,1898],{},"wb.active"," to a visible sheet's index.",[1856,1901,1902,1905,1910],{},[1874,1903,1904],{},"Hidden column reappears",[1874,1906,1895,1907,1909],{},[141,1908,600],{}," on cells rather than the dimension",[1874,1911,1912,1913,18],{},"Use ",[141,1914,1915],{},"ws.column_dimensions[\"D\"].hidden = True",[1856,1917,1918,1921,1926],{},[1874,1919,1920],{},"Totals include rows that are hidden",[1874,1922,1923,1925],{},[141,1924,1480],{}," ignores visibility",[1874,1927,1912,1928,18],{},[141,1929,1484],{},[1856,1931,1932,1935,1938],{},[1874,1933,1934],{},"pandas still returns hidden rows",[1874,1936,1937],{},"Hiding is display state only",[1874,1939,1940],{},"Filter in pandas; do not rely on visibility.",[1856,1942,1943,1946,1951],{},[1874,1944,1945],{},"Grouping control on the wrong side",[1874,1947,1948,1950],{},[141,1949,1794],{}," default",[1874,1952,1953,18],{},[141,1954,1955],{},"ws.sheet_properties.outlinePr.summaryBelow = False",[1856,1957,1958,1961,1964],{},[1874,1959,1960],{},"Hiding is very slow",[1874,1962,1963],{},"One dimension record per row",[1874,1965,1966,1967,18],{},"Group consecutive runs with ",[141,1968,1969],{},"row_dimensions.group",[1856,1971,1972,1975,1978],{},[1874,1973,1974],{},"Very hidden sheet visible again",[1874,1976,1977],{},"A later save through pandas rebuilt the workbook",[1874,1979,1980],{},"Apply visibility last, after all data is written.",[129,1982,1984],{"id":1983},"performance-and-scale-notes","Performance and scale notes",[20,1986,30,1993,30,1996,30,1999,30,2003,30,2007,30,2013,30,2019,30,2083,30,2089,30,2091,30,2095,30,2099,30,2104,30,2108],{"viewBox":1987,"role":23,"ariaLabel":1988,"ariaLabelledBy":1989,"xmlns":28,"style":1992},"4 1 583 214","Storage cost of hiding rows: one dimension record per row hidden individually, versus a single range record when consecutive runs are grouped.",[1990,1991],"dimcost-t","dimcost-d","width:100%;max-width:583px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[32,1994,1995],{"id":1990},"One record per row, or one record per run",[36,1997,1998],{"id":1991},"Two representations of hiding fifty thousand rows. Hiding each row individually creates fifty thousand row dimension records in the file, inflating both memory during the write and the size of the saved workbook. Grouping the same rows into contiguous runs produces a handful of range records instead. The saving grows with the number of rows, and the grouped form is also what Excel itself writes.",[40,2000],{"x":1610,"y":318,"width":2001,"height":2002,"fill":45},"583","214",[56,2004,2006],{"x":86,"y":49,"style":2005},"font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","hiding rows 2 through 50,001",[40,2008],{"x":338,"y":2009,"width":1583,"height":2010,"rx":2011,"fill":2012,"stroke":1575},"56","30","6","#f0f2f5",[56,2014,2018],{"x":2015,"y":2016,"style":2017},"81","76","font-size:11px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","row by row",[2020,2021,2022,2023,2022,2028,2022,2031,2022,2034,2022,2037,2022,2040,2022,2043,2022,2046,2022,2048,2022,2051,2022,2054,2022,2056,2022,2059,2022,2062,2022,2065,2022,2068,2022,2071,2022,2074,2022,2077,2022,2080,30],"g",{},"\n    ",[40,2024],{"x":2025,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"156","8","#f43f8f",[40,2029],{"x":2030,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"168",[40,2032],{"x":2033,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"180",[40,2035],{"x":2036,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"192",[40,2038],{"x":2039,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"204",[40,2041],{"x":2042,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"216",[40,2044],{"x":2045,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"228",[40,2047],{"x":1551,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},[40,2049],{"x":2050,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"252",[40,2052],{"x":2053,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"264",[40,2055],{"x":81,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},[40,2057],{"x":2058,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"288",[40,2060],{"x":2061,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"300",[40,2063],{"x":2064,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"312",[40,2066],{"x":2067,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"324",[40,2069],{"x":2070,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"336",[40,2072],{"x":2073,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"348",[40,2075],{"x":2076,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"360",[40,2078],{"x":2079,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"372",[40,2081],{"x":2082,"y":59,"width":2026,"height":2010,"rx":1324,"fill":2027},"384",[56,2084,2088],{"x":2085,"y":2086,"style":2087},"404","78","font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d)","… 50,000 dimension records",[40,2090],{"x":338,"y":1631,"width":1583,"height":2010,"rx":2011,"fill":2012,"stroke":1575},[56,2092,2094],{"x":2015,"y":2093,"style":2017},"146","grouped",[40,2096],{"x":2025,"y":2097,"width":2098,"height":2010,"rx":1574,"fill":52,"stroke":53,"style":54},"128","236",[56,2100,2103],{"x":2101,"y":2102,"style":1640},"274","148","one range record: 2–50,001",[56,2105,2107],{"x":2085,"y":2102,"style":2106},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157)","and that is the whole file cost",[56,2109,2110],{"x":86,"y":1556,"style":1568},"grouping is also what Excel writes itself, so the file stays idiomatic",[10,2112,2113],{},"Row dimensions are stored individually, so hiding 100,000 rows one at a time creates 100,000 records and inflates both memory and file size noticeably. Grouping consecutive runs collapses those into a handful of range records instead — the difference is easy to measure:",[134,2115,2117],{"className":167,"code":2116,"language":169,"meta":139,"style":139},"import time\nfrom openpyxl import Workbook\n\nrows = list(range(2, 50_002))\n\nwb = Workbook(); ws = wb.active\nstart = time.perf_counter()\nfor r in rows:\n    ws.row_dimensions[r].hidden = True\nprint(f\"per row : {time.perf_counter() - start:.2f}s\")\n\nwb2 = Workbook(); ws2 = wb2.active\nstart = time.perf_counter()\nws2.row_dimensions.group(rows[0], rows[-1], hidden=True)\nprint(f\"grouped : {time.perf_counter() - start:.4f}s\")\n",[141,2118,2119,2126,2137,2141,2166,2170,2184,2194,2206,2215,2246,2250,2265,2273,2296],{"__ignoreMap":139},[144,2120,2121,2123],{"class":146,"line":147},[144,2122,177],{"class":176},[144,2124,2125],{"class":180}," time\n",[144,2127,2128,2130,2132,2134],{"class":146,"line":190},[144,2129,475],{"class":176},[144,2131,478],{"class":180},[144,2133,177],{"class":176},[144,2135,2136],{"class":180}," Workbook\n",[144,2138,2139],{"class":146,"line":197},[144,2140,194],{"emptyLinePlaceholder":193},[144,2142,2143,2146,2148,2150,2152,2154,2156,2158,2160,2163],{"class":146,"line":244},[144,2144,2145],{"class":180},"rows ",[144,2147,203],{"class":176},[144,2149,1420],{"class":232},[144,2151,315],{"class":180},[144,2153,312],{"class":232},[144,2155,315],{"class":180},[144,2157,1324],{"class":232},[144,2159,218],{"class":180},[144,2161,2162],{"class":232},"50_002",[144,2164,2165],{"class":180},"))\n",[144,2167,2168],{"class":146,"line":275},[144,2169,194],{"emptyLinePlaceholder":193},[144,2171,2172,2174,2176,2179,2181],{"class":146,"line":296},[144,2173,492],{"class":180},[144,2175,203],{"class":176},[144,2177,2178],{"class":180}," Workbook(); ws ",[144,2180,203],{"class":176},[144,2182,2183],{"class":180}," wb.active\n",[144,2185,2186,2189,2191],{"class":146,"line":349},[144,2187,2188],{"class":180},"start ",[144,2190,203],{"class":176},[144,2192,2193],{"class":180}," time.perf_counter()\n",[144,2195,2196,2198,2201,2203],{"class":146,"line":354},[144,2197,774],{"class":176},[144,2199,2200],{"class":180}," r ",[144,2202,591],{"class":176},[144,2204,2205],{"class":180}," rows:\n",[144,2207,2208,2211,2213],{"class":146,"line":385},[144,2209,2210],{"class":180},"    ws.row_dimensions[r].hidden ",[144,2212,203],{"class":176},[144,2214,1143],{"class":232},[144,2216,2217,2219,2221,2223,2226,2228,2231,2233,2236,2239,2241,2244],{"class":146,"line":412},[144,2218,552],{"class":232},[144,2220,315],{"class":180},[144,2222,558],{"class":176},[144,2224,2225],{"class":154},"\"per row : ",[144,2227,565],{"class":564},[144,2229,2230],{"class":180},"time.perf_counter() ",[144,2232,1402],{"class":176},[144,2234,2235],{"class":180}," start",[144,2237,2238],{"class":176},":.2f",[144,2240,571],{"class":564},[144,2242,2243],{"class":154},"s\"",[144,2245,409],{"class":180},[144,2247,2248],{"class":146,"line":435},[144,2249,194],{"emptyLinePlaceholder":193},[144,2251,2252,2255,2257,2260,2262],{"class":146,"line":861},[144,2253,2254],{"class":180},"wb2 ",[144,2256,203],{"class":176},[144,2258,2259],{"class":180}," Workbook(); ws2 ",[144,2261,203],{"class":176},[144,2263,2264],{"class":180}," wb2.active\n",[144,2266,2267,2269,2271],{"class":146,"line":871},[144,2268,2188],{"class":180},[144,2270,203],{"class":176},[144,2272,2193],{"class":180},[144,2274,2275,2278,2280,2282,2284,2286,2288,2290,2292,2294],{"class":146,"line":877},[144,2276,2277],{"class":180},"ws2.row_dimensions.group(rows[",[144,2279,42],{"class":232},[144,2281,1443],{"class":180},[144,2283,1402],{"class":176},[144,2285,318],{"class":232},[144,2287,224],{"class":180},[144,2289,600],{"class":368},[144,2291,203],{"class":176},[144,2293,1174],{"class":232},[144,2295,409],{"class":180},[144,2297,2298,2300,2302,2304,2307,2309,2311,2313,2315,2318,2320,2322],{"class":146,"line":896},[144,2299,552],{"class":232},[144,2301,315],{"class":180},[144,2303,558],{"class":176},[144,2305,2306],{"class":154},"\"grouped : ",[144,2308,565],{"class":564},[144,2310,2230],{"class":180},[144,2312,1402],{"class":176},[144,2314,2235],{"class":180},[144,2316,2317],{"class":176},":.4f",[144,2319,571],{"class":564},[144,2321,2243],{"class":154},[144,2323,409],{"class":180},[10,2325,2326,2327,2330],{},"Three habits follow. Group contiguous runs rather than hiding row by row. Prefer hiding ",[1475,2328,2329],{},"columns"," to hiding rows where you have the choice — a sheet has at most a few dozen columns and potentially a million rows, so the column path is bounded. And ask whether the rows need to be in the file at all: filtering them out before writing produces a smaller, faster workbook than writing them and hiding them, and it removes the risk of somebody unhiding data you did not intend to ship.",[134,2332,2334],{"className":167,"code":2333,"language":169,"meta":139,"style":139},"import pandas as pd\n\n# Better than writing everything and hiding the closed orders.\nopen_orders = df.loc[df[\"status\"] != \"closed\"]\nopen_orders.to_excel(\"report.xlsx\", index=False)\n",[141,2335,2336,2346,2350,2355,2378],{"__ignoreMap":139},[144,2337,2338,2340,2342,2344],{"class":146,"line":147},[144,2339,177],{"class":176},[144,2341,181],{"class":180},[144,2343,184],{"class":176},[144,2345,187],{"class":180},[144,2347,2348],{"class":146,"line":190},[144,2349,194],{"emptyLinePlaceholder":193},[144,2351,2352],{"class":146,"line":197},[144,2353,2354],{"class":632},"# Better than writing everything and hiding the closed orders.\n",[144,2356,2357,2360,2362,2365,2368,2370,2373,2376],{"class":146,"line":244},[144,2358,2359],{"class":180},"open_orders ",[144,2361,203],{"class":176},[144,2363,2364],{"class":180}," df.loc[df[",[144,2366,2367],{"class":154},"\"status\"",[144,2369,1399],{"class":180},[144,2371,2372],{"class":176},"!=",[144,2374,2375],{"class":154}," \"closed\"",[144,2377,1038],{"class":180},[144,2379,2380,2383,2385,2387,2389,2391,2393],{"class":146,"line":275},[144,2381,2382],{"class":180},"open_orders.to_excel(",[144,2384,363],{"class":154},[144,2386,218],{"class":180},[144,2388,401],{"class":368},[144,2390,203],{"class":176},[144,2392,406],{"class":232},[144,2394,409],{"class":180},[10,2396,2397,2398,2401,2402,2406],{},"That last point is the one that matters most in practice. Hidden rows are still data in the file, still readable by anyone, and still counted by any total that does not use ",[141,2399,2400],{},"SUBTOTAL",". Hide for tidiness; filter for correctness. Where the volume is large enough that either choice affects runtime, the streaming techniques in ",[14,2403,2405],{"href":2404},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002F","writing large DataFrames with write-only mode"," apply — though note that mode cannot set row visibility, so filtering upstream becomes the only option.",[129,2408,2410],{"id":2409},"conclusion","Conclusion",[10,2412,2413,2414,2416,2417,2419,2420,2422,2423,2425],{},"Hiding is a presentation control: ",[141,2415,464],{}," for sheets, ",[141,2418,600],{}," on the row and column dimensions for everything else. Guard the last visible sheet, or Excel will call the file corrupt, and point ",[141,2421,1898],{}," at something a reader will see. Prefer outline grouping when the detail might genuinely be wanted, since it signals that something is there. And remember what hiding does not do — the values stay in the file, readable by any library and counted by any plain ",[141,2424,1480],{},". When data should not be in the report, filter it out rather than hiding it.",[129,2427,2429],{"id":2428},"frequently-asked-questions","Frequently asked questions",[10,2431,2432,2442,2443,2445],{},[2433,2434,2435,2436,2438,2439,2441],"strong",{},"What is the difference between ",[141,2437,600],{}," and ",[141,2440,604],{},"?","\nA hidden sheet appears in Excel's unhide dialog and any reader can restore it. A ",[141,2444,604],{}," sheet does not appear there at all and needs the VBA editor to reveal. Neither hides the data from a library reading the file.",[10,2447,2448,2451],{},[2433,2449,2450],{},"Why does Excel say my file is corrupt after hiding sheets?","\nYou hid every sheet. Excel requires at least one visible worksheet, so check that a visible sheet remains before applying the last hide.",[10,2453,2454,2457,2458,2460],{},[2433,2455,2456],{},"How do I hide a whole column?","\nSet ",[141,2459,1915],{},". Note that this is a column dimension property, not a per-cell one, so there is no need to loop over the cells.",[10,2462,2463,2466],{},[2433,2464,2465],{},"Is grouping better than hiding?","\nUsually, for detail rows. Grouping adds a plus and minus control in the margin so readers can expand the detail themselves, whereas hiding gives them no indication anything is there.",[10,2468,2469,2472,2473,2475,2476,2478],{},[2433,2470,2471],{},"Do hidden rows still count in formulas and exports?","\nYes. ",[141,2474,1480],{}," includes hidden rows unless you use ",[141,2477,2400],{},", and pandas reads them like any other row. Hiding is purely a display state.",[129,2480,2482],{"id":2481},"related","Related",[2484,2485,2486,2493,2500,2507,2514],"ul",{},[2487,2488,2489,2490,2492],"li",{},"Up to the parent: ",[14,2491,17],{"href":16}," — where hiding sits among the protection layers.",[2487,2494,2495,2499],{},[14,2496,2498],{"href":2497},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl\u002F","Lock Cells and Protect a Sheet with openpyxl"," — the step that usually follows hiding.",[2487,2501,2502,2506],{},[14,2503,2505],{"href":2504},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Frename-reorder-and-delete-excel-sheets-with-openpyxl\u002F","Rename, Reorder and Delete Excel Sheets with openpyxl"," — the other sheet-level operations.",[2487,2508,2509,2513],{},[14,2510,2512],{"href":2511},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002F","Add a Summary Sheet to an Excel Report"," — the visible sheet the hidden ones support.",[2487,2515,2516,2520],{},[14,2517,2519],{"href":2518},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fset-column-width-and-row-height-in-openpyxl\u002F","Set Column Width and Row Height in openpyxl"," — the sibling dimension properties.",[2522,2523,2524],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":139,"searchDepth":190,"depth":190,"links":2526},[2527,2528,2529,2530,2531,2532,2533,2534,2535,2536],{"id":131,"depth":190,"text":132},{"id":458,"depth":190,"text":459},{"id":672,"depth":190,"text":673},{"id":1069,"depth":190,"text":1070},{"id":1531,"depth":190,"text":1532},{"id":1847,"depth":190,"text":1848},{"id":1983,"depth":190,"text":1984},{"id":2409,"depth":190,"text":2410},{"id":2428,"depth":190,"text":2429},{"id":2481,"depth":190,"text":2482},"2026-08-15","Tidy a generated Excel report from Python: hide working sheets, use veryHidden, collapse rows and columns, add outline grouping, and avoid the last-visible-sheet corruption.","md",[2541,2544,2546,2548,2550],{"q":2542,"a":2543},"What is the difference between hidden and veryHidden?","A hidden sheet appears in Excel's unhide dialog and any reader can restore it. A veryHidden sheet does not appear there at all and needs the VBA editor to reveal. Neither hides the data from a library reading the file.",{"q":2450,"a":2545},"You hid every sheet. Excel requires at least one visible worksheet, so check that a visible sheet remains before applying the last hide.",{"q":2456,"a":2547},"Set ws.column_dimensions[\"D\"].hidden = True. Note that this is a column dimension property, not a per-cell one, so there is no need to loop over the cells.",{"q":2465,"a":2549},"Usually, for detail rows. Grouping adds a plus and minus control in the margin so readers can expand the detail themselves, whereas hiding gives them no indication anything is there.",{"q":2471,"a":2551},"Yes. SUM includes hidden rows unless you use SUBTOTAL, and pandas reads them like any other row. Hiding is purely a display state.",{},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl",{"title":2555,"description":2556},"Hide Sheets, Rows and Columns in openpyxl (Python)","Hide lookup sheets and helper columns in a generated workbook with openpyxl — sheet_state, hidden row and column dimensions, outline groups, and the pitfalls that corrupt a file.","hide-sheets-rows-and-columns-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl\u002Findex","how-to","dUdqAKVDgHzKBJdaLrXS2n1ZYBSJt1yFrRer4IG8k_I",[2562,2566],{"title":2563,"path":2564,"stem":2565,"children":-1},"Add Comments and Notes to Excel Cells with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fadd-comments-and-notes-to-excel-cells-with-python","formatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fadd-comments-and-notes-to-excel-cells-with-python\u002Findex",{"title":2498,"path":2567,"stem":2568,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl\u002Findex",1786800027196]