[{"data":1,"prerenderedAt":1394},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter":1387},{"id":4,"title":5,"body":6,"dateModified":1366,"datePublished":1366,"description":1367,"extension":1368,"faq":1369,"meta":1379,"navigation":432,"path":1380,"seo":1381,"slug":1383,"stem":1384,"type":1385,"__hash__":1386},"docs\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter\u002Findex.md","openpyxl vs xlsxwriter vs pandas.ExcelWriter",{"type":7,"value":8,"toc":1352},"minimark",[9,38,164,169,172,202,206,226,260,264,397,401,404,544,548,554,774,778,781,965,969,984,1149,1153,1233,1244,1248,1262,1266,1274,1289,1295,1311,1315,1324,1328,1348],[10,11,12,13,17,18,22,23,26,27,31,32,37],"p",{},"The three names look like competing libraries, but they sit at different layers. ",[14,15,16],"code",{},"pandas.ExcelWriter"," is not an engine at all — it's a thin wrapper that delegates to one. The real engines are ",[19,20,21],"strong",{},"openpyxl"," and ",[19,24,25],{},"xlsxwriter",", and they have opposite strengths: openpyxl can read ",[28,29,30],"em",{},"and"," write existing files and preserves styles, while xlsxwriter is write-only but faster and richer at formatting and charts. This guide, part of ",[33,34,36],"a",{"href":35},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with Pandas",", clears up the relationship, gives you a decision table, and writes the same DataFrame three ways.",[39,40,48,49,48,53,48,57,48,67,48,73,48,76,48,79,48,82,48,88,48,93,48,98,48,102,48,106,48,108,48,113,48,117,48,119,48,121,48,123,48,126,48,130,48,132,48,134,48,136,48,139,48,143,48,145,48,147,48,149,48,152,48,157,48,160],"svg",{"viewBox":41,"role":42,"ariaLabelledBy":43,"xmlns":46,"style":47},"0 0 760 310","img",[44,45],"cmp-t","cmp-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[50,51,52],"title",{"id":44},"openpyxl vs xlsxwriter vs pandas.ExcelWriter capabilities",[54,55,56],"desc",{"id":45},"A matrix comparing the three across four capabilities: reading existing files, writing styles, charts, and appending sheets. openpyxl reads and appends; xlsxwriter writes styles and charts; pandas.ExcelWriter appends through openpyxl.",[58,59],"rect",{"x":60,"y":61,"width":62,"height":63,"rx":64,"fill":65,"stroke":66},"250","20","160","44","8","var(--brand-soft,rgba(91,92,240,0.12))","var(--line,#cdd5e6)",[68,69,21],"text",{"x":70,"y":71,"style":72},"330","47","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[58,74],{"x":75,"y":61,"width":62,"height":63,"rx":64,"fill":65,"stroke":66},"420",[68,77,25],{"x":78,"y":71,"style":72},"500",[58,80],{"x":81,"y":61,"width":62,"height":63,"rx":64,"fill":65,"stroke":66},"590",[68,83,87],{"x":84,"y":85,"style":86},"670","42","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","pandas",[68,89,92],{"x":84,"y":90,"style":91},"58","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","ExcelWriter",[68,94,97],{"x":61,"y":95,"style":96},"98","font-size:13.5px;font-weight:600;fill:var(--text,#172033)","Reads existing?",[68,99,101],{"x":70,"y":95,"style":100},"font-size:18px;font-weight:700;fill:var(--teal,#0f9488);text-anchor:middle","✓",[68,103,105],{"x":78,"y":95,"style":104},"font-size:18px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","–",[68,107,105],{"x":84,"y":95,"style":104},[109,110],"line",{"x1":61,"y1":111,"x2":112,"y2":111,"stroke":66},"112","740",[68,114,116],{"x":61,"y":115,"style":96},"148","Writes styles?",[68,118,101],{"x":70,"y":115,"style":100},[68,120,101],{"x":78,"y":115,"style":100},[68,122,105],{"x":84,"y":115,"style":104},[109,124],{"x1":61,"y1":125,"x2":112,"y2":125,"stroke":66},"162",[68,127,129],{"x":61,"y":128,"style":96},"198","Charts?",[68,131,101],{"x":70,"y":128,"style":100},[68,133,101],{"x":78,"y":128,"style":100},[68,135,105],{"x":84,"y":128,"style":104},[109,137],{"x1":61,"y1":138,"x2":112,"y2":138,"stroke":66},"212",[68,140,142],{"x":61,"y":141,"style":96},"248","Append sheets?",[68,144,101],{"x":70,"y":141,"style":100},[68,146,105],{"x":78,"y":141,"style":104},[68,148,101],{"x":84,"y":141,"style":100},[109,150],{"x1":61,"y1":151,"x2":112,"y2":151,"stroke":66},"262",[68,153,156],{"x":61,"y":154,"style":155},"294","font-size:12px;fill:var(--muted,#5b6780)","✓ supported",[68,158,159],{"x":62,"y":154,"style":155},"– not available",[68,161,163],{"x":162,"y":154,"style":155},"400","pandas inherits append from the openpyxl engine",[165,166,168],"h2",{"id":167},"prerequisites","Prerequisites",[10,170,171],{},"Install pandas and both engines:",[173,174,179],"pre",{"className":175,"code":176,"language":177,"meta":178,"style":178},"language-bash shiki shiki-themes github-light github-dark","pip install pandas openpyxl xlsxwriter\n","bash","",[14,180,181],{"__ignoreMap":178},[182,183,185,189,193,196,199],"span",{"class":109,"line":184},1,[182,186,188],{"class":187},"sScJk","pip",[182,190,192],{"class":191},"sZZnC"," install",[182,194,195],{"class":191}," pandas",[182,197,198],{"class":191}," openpyxl",[182,200,201],{"class":191}," xlsxwriter\n",[165,203,205],{"id":204},"how-the-pieces-relate","How the pieces relate",[10,207,208,22,211,214,215,217,218,221,222,225],{},[14,209,210],{},"df.to_excel(path)",[14,212,213],{},"pd.ExcelWriter(path)"," both produce a file by handing your data to an engine. When you don't name one, pandas picks a default (",[14,216,21],{}," for ",[14,219,220],{},".xlsx","). You choose explicitly with ",[14,223,224],{},"engine=",":",[227,228,229,238,243],"ul",{},[230,231,232,234,235,237],"li",{},[19,233,21],{}," — read + write. Opens an existing ",[14,236,220],{},", edits cells in place, appends sheets, and keeps the styles already in the file. This is the only engine that can modify a workbook you already have.",[230,239,240,242],{},[19,241,25],{}," — write only. It builds a brand-new file fast and exposes the richest formatting, conditional formats, and native charts. It cannot open or read an existing workbook.",[230,244,245,247,248,251,252,255,256,259],{},[19,246,16],{}," — the wrapper. It exposes ",[14,249,250],{},"mode=\"a\""," (append) and a ",[14,253,254],{},"book","\u002F",[14,257,258],{},"sheets"," handle so you can reach the underlying engine object for formatting.",[165,261,263],{"id":262},"decision-table","Decision table",[265,266,267,287],"table",{},[268,269,270],"thead",{},[271,272,273,277,279,281],"tr",{},[274,275,276],"th",{},"Need",[274,278,21],{},[274,280,25],{},[274,282,283,284],{},"plain ",[14,285,286],{},"to_excel",[288,289,290,305,316,331,345,357,371,384],"tbody",{},[271,291,292,296,299,302],{},[293,294,295],"td",{},"Read an existing file",[293,297,298],{},"Yes",[293,300,301],{},"No",[293,303,304],{},"n\u002Fa (write-only)",[271,306,307,310,312,314],{},[293,308,309],{},"Edit a file in place",[293,311,298],{},[293,313,301],{},[293,315,301],{},[271,317,318,324,326,328],{},[293,319,320,321,323],{},"Append a sheet (",[14,322,250],{},")",[293,325,298],{},[293,327,301],{},[293,329,330],{},"Uses openpyxl",[271,332,333,336,339,342],{},[293,334,335],{},"Native charts",[293,337,338],{},"Limited",[293,340,341],{},"Yes (strong)",[293,343,344],{},"Whatever engine you pass",[271,346,347,350,352,354],{},[293,348,349],{},"Conditional formatting",[293,351,338],{},[293,353,298],{},[293,355,356],{},"Via engine",[271,358,359,362,365,368],{},[293,360,361],{},"Raw write speed on large files",[293,363,364],{},"Good",[293,366,367],{},"Fastest",[293,369,370],{},"Matches the engine",[271,372,373,376,378,381],{},[293,374,375],{},"Preserve styles in source file",[293,377,298],{},[293,379,380],{},"n\u002Fa",[293,382,383],{},"Only via openpyxl",[271,385,386,389,392,394],{},[293,387,388],{},"Quick one-off dump",[293,390,391],{},"Overkill",[293,393,391],{},[293,395,396],{},"Best fit",[165,398,400],{"id":399},"write-1-plain-to_excel-for-a-quick-dump","Write 1: plain to_excel for a quick dump",[10,402,403],{},"No engine argument, no formatting — the fastest way to get a DataFrame onto disk:",[173,405,409],{"className":406,"code":407,"language":408,"meta":178,"style":178},"language-python shiki shiki-themes github-light github-dark","import pandas as pd\n\ndf = pd.DataFrame({\n    \"Region\": [\"North\", \"South\", \"West\"],\n    \"Revenue\": [12000, 9800, 15400],\n})\ndf.to_excel(\"dump.xlsx\", index=False)   # defaults to openpyxl\nprint(\"wrote dump.xlsx\")\n","python",[14,410,411,427,434,446,472,496,502,529],{"__ignoreMap":178},[182,412,413,417,421,424],{"class":109,"line":184},[182,414,416],{"class":415},"szBVR","import",[182,418,420],{"class":419},"sVt8B"," pandas ",[182,422,423],{"class":415},"as",[182,425,426],{"class":419}," pd\n",[182,428,430],{"class":109,"line":429},2,[182,431,433],{"emptyLinePlaceholder":432},true,"\n",[182,435,437,440,443],{"class":109,"line":436},3,[182,438,439],{"class":419},"df ",[182,441,442],{"class":415},"=",[182,444,445],{"class":419}," pd.DataFrame({\n",[182,447,449,452,455,458,461,464,466,469],{"class":109,"line":448},4,[182,450,451],{"class":191},"    \"Region\"",[182,453,454],{"class":419},": [",[182,456,457],{"class":191},"\"North\"",[182,459,460],{"class":419},", ",[182,462,463],{"class":191},"\"South\"",[182,465,460],{"class":419},[182,467,468],{"class":191},"\"West\"",[182,470,471],{"class":419},"],\n",[182,473,475,478,480,484,486,489,491,494],{"class":109,"line":474},5,[182,476,477],{"class":191},"    \"Revenue\"",[182,479,454],{"class":419},[182,481,483],{"class":482},"sj4cs","12000",[182,485,460],{"class":419},[182,487,488],{"class":482},"9800",[182,490,460],{"class":419},[182,492,493],{"class":482},"15400",[182,495,471],{"class":419},[182,497,499],{"class":109,"line":498},6,[182,500,501],{"class":419},"})\n",[182,503,505,508,511,513,517,519,522,525],{"class":109,"line":504},7,[182,506,507],{"class":419},"df.to_excel(",[182,509,510],{"class":191},"\"dump.xlsx\"",[182,512,460],{"class":419},[182,514,516],{"class":515},"s4XuR","index",[182,518,442],{"class":415},[182,520,521],{"class":482},"False",[182,523,524],{"class":419},")   ",[182,526,528],{"class":527},"sJ8bj","# defaults to openpyxl\n",[182,530,532,535,538,541],{"class":109,"line":531},8,[182,533,534],{"class":482},"print",[182,536,537],{"class":419},"(",[182,539,540],{"class":191},"\"wrote dump.xlsx\"",[182,542,543],{"class":419},")\n",[165,545,547],{"id":546},"write-2-xlsxwriter-with-a-cell-format","Write 2: xlsxwriter with a cell format",[10,549,550,551,553],{},"Reach the workbook and worksheet objects through the ",[14,552,92],{},", then add a currency format. xlsxwriter shines here because formatting is first-class:",[173,555,557],{"className":406,"code":556,"language":408,"meta":178,"style":178},"import pandas as pd\n\ndf = pd.DataFrame({\n    \"Region\": [\"North\", \"South\", \"West\"],\n    \"Revenue\": [12000, 9800, 15400],\n})\n\nwith pd.ExcelWriter(\"styled.xlsx\", engine=\"xlsxwriter\") as writer:\n    df.to_excel(writer, sheet_name=\"Report\", index=False)\n    workbook = writer.book\n    worksheet = writer.sheets[\"Report\"]\n    money = workbook.add_format({\"num_format\": \"$#,##0\", \"bold\": True})\n    worksheet.set_column(\"B:B\", 14, money)   # format the Revenue column\n\nprint(\"wrote styled.xlsx\")\n",[14,558,559,569,573,581,599,617,621,625,654,678,689,705,737,757,762],{"__ignoreMap":178},[182,560,561,563,565,567],{"class":109,"line":184},[182,562,416],{"class":415},[182,564,420],{"class":419},[182,566,423],{"class":415},[182,568,426],{"class":419},[182,570,571],{"class":109,"line":429},[182,572,433],{"emptyLinePlaceholder":432},[182,574,575,577,579],{"class":109,"line":436},[182,576,439],{"class":419},[182,578,442],{"class":415},[182,580,445],{"class":419},[182,582,583,585,587,589,591,593,595,597],{"class":109,"line":448},[182,584,451],{"class":191},[182,586,454],{"class":419},[182,588,457],{"class":191},[182,590,460],{"class":419},[182,592,463],{"class":191},[182,594,460],{"class":419},[182,596,468],{"class":191},[182,598,471],{"class":419},[182,600,601,603,605,607,609,611,613,615],{"class":109,"line":474},[182,602,477],{"class":191},[182,604,454],{"class":419},[182,606,483],{"class":482},[182,608,460],{"class":419},[182,610,488],{"class":482},[182,612,460],{"class":419},[182,614,493],{"class":482},[182,616,471],{"class":419},[182,618,619],{"class":109,"line":498},[182,620,501],{"class":419},[182,622,623],{"class":109,"line":504},[182,624,433],{"emptyLinePlaceholder":432},[182,626,627,630,633,636,638,641,643,646,649,651],{"class":109,"line":531},[182,628,629],{"class":415},"with",[182,631,632],{"class":419}," pd.ExcelWriter(",[182,634,635],{"class":191},"\"styled.xlsx\"",[182,637,460],{"class":419},[182,639,640],{"class":515},"engine",[182,642,442],{"class":415},[182,644,645],{"class":191},"\"xlsxwriter\"",[182,647,648],{"class":419},") ",[182,650,423],{"class":415},[182,652,653],{"class":419}," writer:\n",[182,655,657,660,663,665,668,670,672,674,676],{"class":109,"line":656},9,[182,658,659],{"class":419},"    df.to_excel(writer, ",[182,661,662],{"class":515},"sheet_name",[182,664,442],{"class":415},[182,666,667],{"class":191},"\"Report\"",[182,669,460],{"class":419},[182,671,516],{"class":515},[182,673,442],{"class":415},[182,675,521],{"class":482},[182,677,543],{"class":419},[182,679,681,684,686],{"class":109,"line":680},10,[182,682,683],{"class":419},"    workbook ",[182,685,442],{"class":415},[182,687,688],{"class":419}," writer.book\n",[182,690,692,695,697,700,702],{"class":109,"line":691},11,[182,693,694],{"class":419},"    worksheet ",[182,696,442],{"class":415},[182,698,699],{"class":419}," writer.sheets[",[182,701,667],{"class":191},[182,703,704],{"class":419},"]\n",[182,706,708,711,713,716,719,722,725,727,730,732,735],{"class":109,"line":707},12,[182,709,710],{"class":419},"    money ",[182,712,442],{"class":415},[182,714,715],{"class":419}," workbook.add_format({",[182,717,718],{"class":191},"\"num_format\"",[182,720,721],{"class":419},": ",[182,723,724],{"class":191},"\"$#,##0\"",[182,726,460],{"class":419},[182,728,729],{"class":191},"\"bold\"",[182,731,721],{"class":419},[182,733,734],{"class":482},"True",[182,736,501],{"class":419},[182,738,740,743,746,748,751,754],{"class":109,"line":739},13,[182,741,742],{"class":419},"    worksheet.set_column(",[182,744,745],{"class":191},"\"B:B\"",[182,747,460],{"class":419},[182,749,750],{"class":482},"14",[182,752,753],{"class":419},", money)   ",[182,755,756],{"class":527},"# format the Revenue column\n",[182,758,760],{"class":109,"line":759},14,[182,761,433],{"emptyLinePlaceholder":432},[182,763,765,767,769,772],{"class":109,"line":764},15,[182,766,534],{"class":482},[182,768,537],{"class":419},[182,770,771],{"class":191},"\"wrote styled.xlsx\"",[182,773,543],{"class":419},[165,775,777],{"id":776},"write-3-openpyxl-direct-to-edit-an-existing-file","Write 3: openpyxl direct, to edit an existing file",[10,779,780],{},"This is where openpyxl is the only option: load a file that already exists, change a cell, and save. xlsxwriter physically cannot do this because it never reads:",[173,782,784],{"className":406,"code":783,"language":408,"meta":178,"style":178},"import pandas as pd\nfrom openpyxl import load_workbook\n\n# Start from a file written earlier\npd.DataFrame({\"Region\": [\"North\"], \"Revenue\": [12000]}).to_excel(\n    \"ledger.xlsx\", index=False\n)\n\nwb = load_workbook(\"ledger.xlsx\")   # read the existing workbook\nws = wb.active\nws[\"B2\"].value = 13500              # edit a cell in place\nws[\"C1\"] = \"Adjusted\"               # add a column header\nwb.save(\"ledger.xlsx\")\n\nprint(\"updated:\", load_workbook(\"ledger.xlsx\").active[\"B2\"].value)\n",[14,785,786,796,809,813,818,843,857,861,865,883,893,912,930,939,943],{"__ignoreMap":178},[182,787,788,790,792,794],{"class":109,"line":184},[182,789,416],{"class":415},[182,791,420],{"class":419},[182,793,423],{"class":415},[182,795,426],{"class":419},[182,797,798,801,804,806],{"class":109,"line":429},[182,799,800],{"class":415},"from",[182,802,803],{"class":419}," openpyxl ",[182,805,416],{"class":415},[182,807,808],{"class":419}," load_workbook\n",[182,810,811],{"class":109,"line":436},[182,812,433],{"emptyLinePlaceholder":432},[182,814,815],{"class":109,"line":448},[182,816,817],{"class":527},"# Start from a file written earlier\n",[182,819,820,823,826,828,830,833,836,838,840],{"class":109,"line":474},[182,821,822],{"class":419},"pd.DataFrame({",[182,824,825],{"class":191},"\"Region\"",[182,827,454],{"class":419},[182,829,457],{"class":191},[182,831,832],{"class":419},"], ",[182,834,835],{"class":191},"\"Revenue\"",[182,837,454],{"class":419},[182,839,483],{"class":482},[182,841,842],{"class":419},"]}).to_excel(\n",[182,844,845,848,850,852,854],{"class":109,"line":498},[182,846,847],{"class":191},"    \"ledger.xlsx\"",[182,849,460],{"class":419},[182,851,516],{"class":515},[182,853,442],{"class":415},[182,855,856],{"class":482},"False\n",[182,858,859],{"class":109,"line":504},[182,860,543],{"class":419},[182,862,863],{"class":109,"line":531},[182,864,433],{"emptyLinePlaceholder":432},[182,866,867,870,872,875,878,880],{"class":109,"line":656},[182,868,869],{"class":419},"wb ",[182,871,442],{"class":415},[182,873,874],{"class":419}," load_workbook(",[182,876,877],{"class":191},"\"ledger.xlsx\"",[182,879,524],{"class":419},[182,881,882],{"class":527},"# read the existing workbook\n",[182,884,885,888,890],{"class":109,"line":680},[182,886,887],{"class":419},"ws ",[182,889,442],{"class":415},[182,891,892],{"class":419}," wb.active\n",[182,894,895,898,901,904,906,909],{"class":109,"line":691},[182,896,897],{"class":419},"ws[",[182,899,900],{"class":191},"\"B2\"",[182,902,903],{"class":419},"].value ",[182,905,442],{"class":415},[182,907,908],{"class":482}," 13500",[182,910,911],{"class":527},"              # edit a cell in place\n",[182,913,914,916,919,922,924,927],{"class":109,"line":707},[182,915,897],{"class":419},[182,917,918],{"class":191},"\"C1\"",[182,920,921],{"class":419},"] ",[182,923,442],{"class":415},[182,925,926],{"class":191}," \"Adjusted\"",[182,928,929],{"class":527},"               # add a column header\n",[182,931,932,935,937],{"class":109,"line":739},[182,933,934],{"class":419},"wb.save(",[182,936,877],{"class":191},[182,938,543],{"class":419},[182,940,941],{"class":109,"line":759},[182,942,433],{"emptyLinePlaceholder":432},[182,944,945,947,949,952,955,957,960,962],{"class":109,"line":764},[182,946,534],{"class":482},[182,948,537],{"class":419},[182,950,951],{"class":191},"\"updated:\"",[182,953,954],{"class":419},", load_workbook(",[182,956,877],{"class":191},[182,958,959],{"class":419},").active[",[182,961,900],{"class":191},[182,963,964],{"class":419},"].value)\n",[165,966,968],{"id":967},"appending-a-sheet-needs-openpyxl","Appending a sheet needs openpyxl",[10,970,971,973,974,977,978,980,981,225],{},[14,972,250],{}," opens the existing file to add to it, so it requires the read-capable engine. Passing ",[14,975,976],{},"engine=\"xlsxwriter\""," with ",[14,979,250],{}," raises a ",[14,982,983],{},"ValueError",[173,985,987],{"className":406,"code":986,"language":408,"meta":178,"style":178},"import pandas as pd\n\npd.DataFrame({\"Region\": [\"North\"], \"Revenue\": [12000]}).to_excel(\n    \"book.xlsx\", sheet_name=\"Jan\", index=False\n)\n\nwith pd.ExcelWriter(\"book.xlsx\", engine=\"openpyxl\", mode=\"a\") as writer:\n    pd.DataFrame({\"Region\": [\"South\"], \"Revenue\": [9800]}).to_excel(\n        writer, sheet_name=\"Feb\", index=False\n    )\n\nprint(pd.ExcelFile(\"book.xlsx\").sheet_names)\n",[14,988,989,999,1003,1023,1045,1049,1053,1087,1108,1128,1133,1137],{"__ignoreMap":178},[182,990,991,993,995,997],{"class":109,"line":184},[182,992,416],{"class":415},[182,994,420],{"class":419},[182,996,423],{"class":415},[182,998,426],{"class":419},[182,1000,1001],{"class":109,"line":429},[182,1002,433],{"emptyLinePlaceholder":432},[182,1004,1005,1007,1009,1011,1013,1015,1017,1019,1021],{"class":109,"line":436},[182,1006,822],{"class":419},[182,1008,825],{"class":191},[182,1010,454],{"class":419},[182,1012,457],{"class":191},[182,1014,832],{"class":419},[182,1016,835],{"class":191},[182,1018,454],{"class":419},[182,1020,483],{"class":482},[182,1022,842],{"class":419},[182,1024,1025,1028,1030,1032,1034,1037,1039,1041,1043],{"class":109,"line":448},[182,1026,1027],{"class":191},"    \"book.xlsx\"",[182,1029,460],{"class":419},[182,1031,662],{"class":515},[182,1033,442],{"class":415},[182,1035,1036],{"class":191},"\"Jan\"",[182,1038,460],{"class":419},[182,1040,516],{"class":515},[182,1042,442],{"class":415},[182,1044,856],{"class":482},[182,1046,1047],{"class":109,"line":474},[182,1048,543],{"class":419},[182,1050,1051],{"class":109,"line":498},[182,1052,433],{"emptyLinePlaceholder":432},[182,1054,1055,1057,1059,1062,1064,1066,1068,1071,1073,1076,1078,1081,1083,1085],{"class":109,"line":504},[182,1056,629],{"class":415},[182,1058,632],{"class":419},[182,1060,1061],{"class":191},"\"book.xlsx\"",[182,1063,460],{"class":419},[182,1065,640],{"class":515},[182,1067,442],{"class":415},[182,1069,1070],{"class":191},"\"openpyxl\"",[182,1072,460],{"class":419},[182,1074,1075],{"class":515},"mode",[182,1077,442],{"class":415},[182,1079,1080],{"class":191},"\"a\"",[182,1082,648],{"class":419},[182,1084,423],{"class":415},[182,1086,653],{"class":419},[182,1088,1089,1092,1094,1096,1098,1100,1102,1104,1106],{"class":109,"line":531},[182,1090,1091],{"class":419},"    pd.DataFrame({",[182,1093,825],{"class":191},[182,1095,454],{"class":419},[182,1097,463],{"class":191},[182,1099,832],{"class":419},[182,1101,835],{"class":191},[182,1103,454],{"class":419},[182,1105,488],{"class":482},[182,1107,842],{"class":419},[182,1109,1110,1113,1115,1117,1120,1122,1124,1126],{"class":109,"line":656},[182,1111,1112],{"class":419},"        writer, ",[182,1114,662],{"class":515},[182,1116,442],{"class":415},[182,1118,1119],{"class":191},"\"Feb\"",[182,1121,460],{"class":419},[182,1123,516],{"class":515},[182,1125,442],{"class":415},[182,1127,856],{"class":482},[182,1129,1130],{"class":109,"line":680},[182,1131,1132],{"class":419},"    )\n",[182,1134,1135],{"class":109,"line":691},[182,1136,433],{"emptyLinePlaceholder":432},[182,1138,1139,1141,1144,1146],{"class":109,"line":707},[182,1140,534],{"class":482},[182,1142,1143],{"class":419},"(pd.ExcelFile(",[182,1145,1061],{"class":191},[182,1147,1148],{"class":419},").sheet_names)\n",[165,1150,1152],{"id":1151},"common-pitfalls","Common pitfalls",[265,1154,1155,1168],{},[268,1156,1157],{},[271,1158,1159,1162,1165],{},[274,1160,1161],{},"Symptom",[274,1163,1164],{},"Cause",[274,1166,1167],{},"Fix",[288,1169,1170,1188,1205,1222],{},[271,1171,1172,1177,1180],{},[293,1173,1174],{},[14,1175,1176],{},"ValueError: Append mode is not supported with xlsxwriter",[293,1178,1179],{},"xlsxwriter can't open existing files",[293,1181,1182,1183,217,1186],{},"Use ",[14,1184,1185],{},"engine=\"openpyxl\"",[14,1187,250],{},[271,1189,1190,1196,1199],{},[293,1191,1192,1195],{},[14,1193,1194],{},"FileNotFoundError","\u002Fempty file when \"editing\"",[293,1197,1198],{},"Tried to load an existing file with xlsxwriter",[293,1200,1182,1201,1204],{},[14,1202,1203],{},"load_workbook"," (openpyxl); xlsxwriter only creates new files",[271,1206,1207,1210,1213],{},[293,1208,1209],{},"Styles vanish after a pandas round-trip",[293,1211,1212],{},"Reading to a DataFrame keeps values, not formatting",[293,1214,1215,1216,1219,1220],{},"Edit with openpyxl directly instead of ",[14,1217,1218],{},"read_excel"," then ",[14,1221,286],{},[271,1223,1224,1227,1230],{},[293,1225,1226],{},"Chart or conditional format ignored",[293,1228,1229],{},"Tried to add it through the wrong engine",[293,1231,1232],{},"Build new styled reports with xlsxwriter",[10,1234,1235,1236,1239,1240,1243],{},"The styling-loss trap is the one that bites teams: ",[14,1237,1238],{},"pd.read_excel()"," returns only the data, so a ",[14,1241,1242],{},"read_excel → to_excel"," cycle silently drops every fill, border, and number format the source had. To keep formatting, never pass through a DataFrame — open the file with openpyxl and modify cells in place.",[165,1245,1247],{"id":1246},"performance-and-scale","Performance and scale",[10,1249,1250,1251,1254,1255,1258,1259,1261],{},"For large write-only jobs, xlsxwriter is the fastest and supports ",[14,1252,1253],{},"constant_memory"," mode to stream rows without holding the whole sheet in RAM. openpyxl is competitive and adds a ",[14,1256,1257],{},"write_only=True"," mode for the same reason, but it pays a cost to preserve existing content. For a quick dump under a few thousand rows, the engine choice is irrelevant — use plain ",[14,1260,286],{},".",[165,1263,1265],{"id":1264},"frequently-asked-questions","Frequently asked questions",[10,1267,1268,1271,1272,1261],{},[19,1269,1270],{},"Is pandas.ExcelWriter an engine?","\nNo. It's a context-manager wrapper that delegates to openpyxl or xlsxwriter. You pick the real engine with ",[14,1273,224],{},[10,1275,1276,1282,1283,1285,1286,1288],{},[19,1277,1278,1279,1281],{},"Which engine does ",[14,1280,286],{}," use by default?","\nFor ",[14,1284,220],{},", pandas defaults to openpyxl when it's installed. Name ",[14,1287,976],{}," explicitly when you want xlsxwriter's formatting.",[10,1290,1291,1294],{},[19,1292,1293],{},"Can xlsxwriter edit an existing workbook?","\nNever. It is write-only and creates a fresh file each run. Any in-place edit or append must go through openpyxl.",[10,1296,1297,1300,1301,1303,1304,22,1307,1310],{},[19,1298,1299],{},"How do I add a chart to a pandas export?","\nWrite with ",[14,1302,976],{},", grab ",[14,1305,1306],{},"writer.book",[14,1308,1309],{},"writer.sheets[...]",", then build the chart with xlsxwriter's chart API.",[165,1312,1314],{"id":1313},"conclusion","Conclusion",[10,1316,1317,1318,1320,1321,1323],{},"Think in layers: ",[14,1319,16],{}," is the wrapper, openpyxl and xlsxwriter are the engines. Reach for openpyxl when you must read, edit in place, or append to a file that already exists; reach for xlsxwriter to build new, heavily styled reports and charts from scratch; and use plain ",[14,1322,286],{}," for a quick dump where formatting doesn't matter. The single rule that prevents most surprises: a pandas round-trip keeps data but loses styling, so edit existing files with openpyxl directly.",[165,1325,1327],{"id":1326},"where-to-go-next","Where to go next",[10,1329,1330,1331,1333,1334,1338,1339,1343,1344,1261],{},"Return to ",[33,1332,36],{"href":35}," for the full export workflow. To drop the index column cleanly on any of these engines, see ",[33,1335,1337],{"href":1336},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-pandas-dataframe-to-excel-without-index\u002F","Write a Pandas DataFrame to Excel Without the Index",". For the openpyxl append pattern in depth, read ",[33,1340,1342],{"href":1341},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fopenpyxl-append-data-to-existing-excel-sheet\u002F","Append Data to an Existing Excel Sheet with openpyxl",". To take styling further, explore ",[33,1345,1347],{"href":1346},"\u002Fformatting-and-charting-excel-reports-with-python\u002F","Formatting and Charting Excel Reports with Python",[1349,1350,1351],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":178,"searchDepth":429,"depth":429,"links":1353},[1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365],{"id":167,"depth":429,"text":168},{"id":204,"depth":429,"text":205},{"id":262,"depth":429,"text":263},{"id":399,"depth":429,"text":400},{"id":546,"depth":429,"text":547},{"id":776,"depth":429,"text":777},{"id":967,"depth":429,"text":968},{"id":1151,"depth":429,"text":1152},{"id":1246,"depth":429,"text":1247},{"id":1264,"depth":429,"text":1265},{"id":1313,"depth":429,"text":1314},{"id":1326,"depth":429,"text":1327},"2026-06-18","Understand how pandas.ExcelWriter wraps the openpyxl and xlsxwriter engines, when each one wins, and write the same DataFrame three ways with a clear decision table.","md",[1370,1372,1375,1377],{"q":1270,"a":1371},"No. It's a context-manager wrapper that delegates to openpyxl or xlsxwriter. You pick the real engine with engine=.",{"q":1373,"a":1374},"Which engine does to_excel use by default?","For .xlsx, pandas defaults to openpyxl when it's installed. Name engine=\"xlsxwriter\" explicitly when you want xlsxwriter's formatting.",{"q":1293,"a":1376},"Never. It is write-only and creates a fresh file each run. Any in-place edit or append must go through openpyxl.",{"q":1299,"a":1378},"Write with engine=\"xlsxwriter\", grab writer.book and writer.sheets[...], then build the chart with xlsxwriter's chart API.",{},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter",{"title":5,"description":1382},"pandas.ExcelWriter wraps an engine — openpyxl reads, edits and appends; xlsxwriter is write-only but fast with rich formatting. A decision table and three runnable writes.","openpyxl-vs-xlsxwriter-vs-pandas-excelwriter","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter\u002Findex","long_tail","IUfylkvOyu4sh5LiVQ9ZMOiMdK7iTJLakgL980YRMJw",[1388,1391],{"title":36,"path":1389,"stem":1390,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Findex",{"title":1337,"path":1392,"stem":1393,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-pandas-dataframe-to-excel-without-index","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-pandas-dataframe-to-excel-without-index\u002Findex",1781795518789]