[{"data":1,"prerenderedAt":2154},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python":2146},{"id":4,"title":5,"body":6,"description":2137,"extension":2138,"meta":2139,"navigation":364,"path":2140,"seo":2141,"stem":2144,"__hash__":2145},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Findex.md","Formatting and Charting Excel Reports with Python",{"type":7,"value":8,"toc":2126},"minimark",[9,34,37,42,45,78,81,85,100,200,203,237,256,259,287,291,309,910,915,919,942,1328,1333,1337,1352,1679,1687,1691,1698,1715,1951,1964,1968,1984,1996,2006,2019,2043,2047,2076,2080,2083,2104,2107,2123],[10,11,12,16,17,21,22,25,26,29,30,33],"p",{},[13,14,15],"code",{},"df.to_excel(\"report.xlsx\")"," produces a workbook that is ",[18,19,20],"em",{},"correct"," and ",[18,23,24],{},"ugly",": black Calibri on white, raw ",[13,27,28],{},"0.4815"," instead of ",[13,31,32],{},"48.15%",", dates rendered as five-digit serial numbers, and columns too narrow to read. Nobody trusts a report that looks like a database dump. This track is about the last mile — turning that plain grid into something a finance director will open, scan, and act on without complaining.",[10,35,36],{},"Everything here is for working Python developers who already write DataFrames to Excel and now need the output to look deliberate: a styled header row, money formatted as money, a chart that summarizes the table above it, and the company logo in the top-left corner. Almost all of it is plain openpyxl, with a note on where xlsxwriter is the faster tool. Every code block below builds its own tiny workbook and runs top to bottom, so you can paste and execute without any data of your own.",[38,39,41],"h2",{"id":40},"what-you-will-learn","What you will learn",[10,43,44],{},"This pillar links four clusters, each going deep on one piece of the polished-report problem:",[46,47,48,57,64,71],"ul",{},[49,50,51,56],"li",{},[52,53,55],"a",{"href":54},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002F","Styling Excel Cells with openpyxl"," — fonts, fills, borders, alignment, column widths, and freeze panes.",[49,58,59,63],{},[52,60,62],{"href":61},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","Applying Number and Date Formats in Excel"," — currency, percentage, thousands separators, and date format codes.",[49,65,66,70],{},[52,67,69],{"href":68},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl"," — bar, line, and pie charts driven by worksheet ranges.",[49,72,73,77],{},[52,74,76],{"href":75},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002F","Inserting Images and Logos into Excel"," — embedding logos and images for branded output.",[10,79,80],{},"The four runnable sections below are a tour of all four at once: style a header, format money and dates, add a bar chart, and drop in a logo.",[38,82,84],{"id":83},"the-styling-library-landscape","The styling library landscape",[10,86,87,88,91,92,95,96,99],{},"Three libraries write ",[13,89,90],{},".xlsx"," files, but they make different trade-offs around ",[18,93,94],{},"editing existing files"," versus ",[18,97,98],{},"building new ones fast",". Picking the wrong one is the most common reason a styling script becomes painful.",[101,102,103,125],"table",{},[104,105,106],"thead",{},[107,108,109,113,116,119,122],"tr",{},[110,111,112],"th",{},"Library",[110,114,115],{},"Role",[110,117,118],{},"Reads existing files?",[110,120,121],{},"Strengths",[110,123,124],{},"Limits",[126,127,128,156,178],"tbody",{},[107,129,130,141,144,150,153],{},[131,132,133,137,138],"td",{},[134,135,136],"strong",{},"pandas"," ",[13,139,140],{},"to_excel",[131,142,143],{},"Dump raw tabular data",[131,145,146,147],{},"Via ",[13,148,149],{},"read_excel",[131,151,152],{},"One line from DataFrame to sheet; multi-sheet writes",[131,154,155],{},"No real styling control beyond a header bold via the engine",[107,157,158,163,166,172,175],{},[131,159,160],{},[134,161,162],{},"openpyxl",[131,164,165],{},"Style and edit workbooks cell by cell",[131,167,168,171],{},[134,169,170],{},"Yes"," — loads and preserves existing styles",[131,173,174],{},"Charts, images, formats, conditional formatting; can re-open a pandas file and decorate it",[131,176,177],{},"Slower on very large writes",[107,179,180,185,188,194,197],{},[131,181,182],{},[134,183,184],{},"xlsxwriter",[131,186,187],{},"Build new styled files in one pass",[131,189,190,193],{},[134,191,192],{},"No"," — write-only",[131,195,196],{},"Fastest styled writes; rich chart and format API",[131,198,199],{},"Cannot open or modify an existing file",[10,201,202],{},"The decision is mechanical:",[46,204,205,214,227],{},[49,206,207,210,211,213],{},[134,208,209],{},"Already have a file"," (a pandas export, a template, last month's report)? Use ",[134,212,162],{}," — it is the only one of the three that can open a workbook, keep its existing formatting, and add to it.",[49,215,216,219,220,222,223,226],{},[134,217,218],{},"Generating a fresh report from scratch"," and want maximum speed on a big sheet? Use ",[134,221,184],{},", either directly or as the pandas engine: ",[13,224,225],{},"df.to_excel(\"out.xlsx\", engine=\"xlsxwriter\")",".",[49,228,229,232,233,236],{},[134,230,231],{},"Just need the data on a sheet"," with no styling? Plain ",[13,234,235],{},"pandas.to_excel"," is fine.",[10,238,239,240,243,244,247,248,21,252,226],{},"The realistic pattern for a weekly report is ",[134,241,242],{},"pandas to write the data, then openpyxl to dress it",": pandas turns your DataFrame into rows, you re-open the file with ",[13,245,246],{},"load_workbook",", and you apply styles, formats, charts, and a logo. That two-step flow is what most of this track teaches, and it builds on ",[52,249,251],{"href":250},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",[52,253,255],{"href":254},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with Pandas",[10,257,258],{},"Install both engines so every example below runs:",[260,261,266],"pre",{"className":262,"code":263,"language":264,"meta":265,"style":265},"language-bash shiki shiki-themes github-light github-dark","pip install pandas openpyxl\n","bash","",[13,267,268],{"__ignoreMap":265},[269,270,273,277,281,284],"span",{"class":271,"line":272},"line",1,[269,274,276],{"class":275},"sScJk","pip",[269,278,280],{"class":279},"sZZnC"," install",[269,282,283],{"class":279}," pandas",[269,285,286],{"class":279}," openpyxl\n",[38,288,290],{"id":289},"style-a-header-row-and-autosize-columns","Style a header row and autosize columns",[10,292,293,294,297,298,297,301,304,305,308],{},"The single biggest visual upgrade is a styled header: bold white text on a colored fill, centered, with a thin border under it, and columns wide enough to read. Here we write a small regional sales table with pandas, then re-open it with openpyxl to apply ",[13,295,296],{},"Font",", ",[13,299,300],{},"PatternFill",[13,302,303],{},"Alignment",", and ",[13,306,307],{},"Border",", and fit each column to its widest value.",[260,310,314],{"className":311,"code":312,"language":313,"meta":265,"style":265},"language-python shiki shiki-themes github-light github-dark","import pandas as pd\nfrom openpyxl import load_workbook\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side\n\n# 1. pandas writes the raw data\nsales = pd.DataFrame({\n    \"Region\": [\"North\", \"South\", \"East\", \"West\"],\n    \"Orders\": [128, 94, 156, 73],\n    \"Revenue\": [25640.50, 18890.00, 31200.75, 14005.25],\n})\nsales.to_excel(\"sales_report.xlsx\", sheet_name=\"Sales\", index=False)\n\n# 2. openpyxl re-opens it and styles the header\nwb = load_workbook(\"sales_report.xlsx\")\nws = wb[\"Sales\"]\n\nheader_font = Font(bold=True, color=\"FFFFFF\", size=12)\nheader_fill = PatternFill(\"solid\", fgColor=\"305496\")\ncenter = Alignment(horizontal=\"center\", vertical=\"center\")\nthin_bottom = Border(bottom=Side(style=\"thin\", color=\"1F3864\"))\n\nfor cell in ws[1]:\n    cell.font = header_font\n    cell.fill = header_fill\n    cell.alignment = center\n    cell.border = thin_bottom\n\n# Autosize: widen each column to its longest value\nfor column_cells in ws.columns:\n    longest = max(len(str(c.value)) for c in column_cells if c.value is not None)\n    ws.column_dimensions[column_cells[0].column_letter].width = longest + 3\n\nws.freeze_panes = \"A2\"  # keep the header visible while scrolling\nwb.save(\"sales_report.xlsx\")\nprint(\"Styled header and fitted\", ws.max_column, \"columns\")\n","python",[13,315,316,332,346,359,366,373,385,415,444,472,478,511,516,522,537,553,558,599,625,655,694,699,720,731,742,753,764,769,775,788,839,862,867,881,891],{"__ignoreMap":265},[269,317,318,322,326,329],{"class":271,"line":272},[269,319,321],{"class":320},"szBVR","import",[269,323,325],{"class":324},"sVt8B"," pandas ",[269,327,328],{"class":320},"as",[269,330,331],{"class":324}," pd\n",[269,333,335,338,341,343],{"class":271,"line":334},2,[269,336,337],{"class":320},"from",[269,339,340],{"class":324}," openpyxl ",[269,342,321],{"class":320},[269,344,345],{"class":324}," load_workbook\n",[269,347,349,351,354,356],{"class":271,"line":348},3,[269,350,337],{"class":320},[269,352,353],{"class":324}," openpyxl.styles ",[269,355,321],{"class":320},[269,357,358],{"class":324}," Font, PatternFill, Alignment, Border, Side\n",[269,360,362],{"class":271,"line":361},4,[269,363,365],{"emptyLinePlaceholder":364},true,"\n",[269,367,369],{"class":271,"line":368},5,[269,370,372],{"class":371},"sJ8bj","# 1. pandas writes the raw data\n",[269,374,376,379,382],{"class":271,"line":375},6,[269,377,378],{"class":324},"sales ",[269,380,381],{"class":320},"=",[269,383,384],{"class":324}," pd.DataFrame({\n",[269,386,388,391,394,397,399,402,404,407,409,412],{"class":271,"line":387},7,[269,389,390],{"class":279},"    \"Region\"",[269,392,393],{"class":324},": [",[269,395,396],{"class":279},"\"North\"",[269,398,297],{"class":324},[269,400,401],{"class":279},"\"South\"",[269,403,297],{"class":324},[269,405,406],{"class":279},"\"East\"",[269,408,297],{"class":324},[269,410,411],{"class":279},"\"West\"",[269,413,414],{"class":324},"],\n",[269,416,418,421,423,427,429,432,434,437,439,442],{"class":271,"line":417},8,[269,419,420],{"class":279},"    \"Orders\"",[269,422,393],{"class":324},[269,424,426],{"class":425},"sj4cs","128",[269,428,297],{"class":324},[269,430,431],{"class":425},"94",[269,433,297],{"class":324},[269,435,436],{"class":425},"156",[269,438,297],{"class":324},[269,440,441],{"class":425},"73",[269,443,414],{"class":324},[269,445,447,450,452,455,457,460,462,465,467,470],{"class":271,"line":446},9,[269,448,449],{"class":279},"    \"Revenue\"",[269,451,393],{"class":324},[269,453,454],{"class":425},"25640.50",[269,456,297],{"class":324},[269,458,459],{"class":425},"18890.00",[269,461,297],{"class":324},[269,463,464],{"class":425},"31200.75",[269,466,297],{"class":324},[269,468,469],{"class":425},"14005.25",[269,471,414],{"class":324},[269,473,475],{"class":271,"line":474},10,[269,476,477],{"class":324},"})\n",[269,479,481,484,487,489,493,495,498,500,503,505,508],{"class":271,"line":480},11,[269,482,483],{"class":324},"sales.to_excel(",[269,485,486],{"class":279},"\"sales_report.xlsx\"",[269,488,297],{"class":324},[269,490,492],{"class":491},"s4XuR","sheet_name",[269,494,381],{"class":320},[269,496,497],{"class":279},"\"Sales\"",[269,499,297],{"class":324},[269,501,502],{"class":491},"index",[269,504,381],{"class":320},[269,506,507],{"class":425},"False",[269,509,510],{"class":324},")\n",[269,512,514],{"class":271,"line":513},12,[269,515,365],{"emptyLinePlaceholder":364},[269,517,519],{"class":271,"line":518},13,[269,520,521],{"class":371},"# 2. openpyxl re-opens it and styles the header\n",[269,523,525,528,530,533,535],{"class":271,"line":524},14,[269,526,527],{"class":324},"wb ",[269,529,381],{"class":320},[269,531,532],{"class":324}," load_workbook(",[269,534,486],{"class":279},[269,536,510],{"class":324},[269,538,540,543,545,548,550],{"class":271,"line":539},15,[269,541,542],{"class":324},"ws ",[269,544,381],{"class":320},[269,546,547],{"class":324}," wb[",[269,549,497],{"class":279},[269,551,552],{"class":324},"]\n",[269,554,556],{"class":271,"line":555},16,[269,557,365],{"emptyLinePlaceholder":364},[269,559,561,564,566,569,572,574,577,579,582,584,587,589,592,594,597],{"class":271,"line":560},17,[269,562,563],{"class":324},"header_font ",[269,565,381],{"class":320},[269,567,568],{"class":324}," Font(",[269,570,571],{"class":491},"bold",[269,573,381],{"class":320},[269,575,576],{"class":425},"True",[269,578,297],{"class":324},[269,580,581],{"class":491},"color",[269,583,381],{"class":320},[269,585,586],{"class":279},"\"FFFFFF\"",[269,588,297],{"class":324},[269,590,591],{"class":491},"size",[269,593,381],{"class":320},[269,595,596],{"class":425},"12",[269,598,510],{"class":324},[269,600,602,605,607,610,613,615,618,620,623],{"class":271,"line":601},18,[269,603,604],{"class":324},"header_fill ",[269,606,381],{"class":320},[269,608,609],{"class":324}," PatternFill(",[269,611,612],{"class":279},"\"solid\"",[269,614,297],{"class":324},[269,616,617],{"class":491},"fgColor",[269,619,381],{"class":320},[269,621,622],{"class":279},"\"305496\"",[269,624,510],{"class":324},[269,626,628,631,633,636,639,641,644,646,649,651,653],{"class":271,"line":627},19,[269,629,630],{"class":324},"center ",[269,632,381],{"class":320},[269,634,635],{"class":324}," Alignment(",[269,637,638],{"class":491},"horizontal",[269,640,381],{"class":320},[269,642,643],{"class":279},"\"center\"",[269,645,297],{"class":324},[269,647,648],{"class":491},"vertical",[269,650,381],{"class":320},[269,652,643],{"class":279},[269,654,510],{"class":324},[269,656,658,661,663,666,669,671,674,677,679,682,684,686,688,691],{"class":271,"line":657},20,[269,659,660],{"class":324},"thin_bottom ",[269,662,381],{"class":320},[269,664,665],{"class":324}," Border(",[269,667,668],{"class":491},"bottom",[269,670,381],{"class":320},[269,672,673],{"class":324},"Side(",[269,675,676],{"class":491},"style",[269,678,381],{"class":320},[269,680,681],{"class":279},"\"thin\"",[269,683,297],{"class":324},[269,685,581],{"class":491},[269,687,381],{"class":320},[269,689,690],{"class":279},"\"1F3864\"",[269,692,693],{"class":324},"))\n",[269,695,697],{"class":271,"line":696},21,[269,698,365],{"emptyLinePlaceholder":364},[269,700,702,705,708,711,714,717],{"class":271,"line":701},22,[269,703,704],{"class":320},"for",[269,706,707],{"class":324}," cell ",[269,709,710],{"class":320},"in",[269,712,713],{"class":324}," ws[",[269,715,716],{"class":425},"1",[269,718,719],{"class":324},"]:\n",[269,721,723,726,728],{"class":271,"line":722},23,[269,724,725],{"class":324},"    cell.font ",[269,727,381],{"class":320},[269,729,730],{"class":324}," header_font\n",[269,732,734,737,739],{"class":271,"line":733},24,[269,735,736],{"class":324},"    cell.fill ",[269,738,381],{"class":320},[269,740,741],{"class":324}," header_fill\n",[269,743,745,748,750],{"class":271,"line":744},25,[269,746,747],{"class":324},"    cell.alignment ",[269,749,381],{"class":320},[269,751,752],{"class":324}," center\n",[269,754,756,759,761],{"class":271,"line":755},26,[269,757,758],{"class":324},"    cell.border ",[269,760,381],{"class":320},[269,762,763],{"class":324}," thin_bottom\n",[269,765,767],{"class":271,"line":766},27,[269,768,365],{"emptyLinePlaceholder":364},[269,770,772],{"class":271,"line":771},28,[269,773,774],{"class":371},"# Autosize: widen each column to its longest value\n",[269,776,778,780,783,785],{"class":271,"line":777},29,[269,779,704],{"class":320},[269,781,782],{"class":324}," column_cells ",[269,784,710],{"class":320},[269,786,787],{"class":324}," ws.columns:\n",[269,789,791,794,796,799,802,805,807,810,813,815,818,820,822,825,828,831,834,837],{"class":271,"line":790},30,[269,792,793],{"class":324},"    longest ",[269,795,381],{"class":320},[269,797,798],{"class":425}," max",[269,800,801],{"class":324},"(",[269,803,804],{"class":425},"len",[269,806,801],{"class":324},[269,808,809],{"class":425},"str",[269,811,812],{"class":324},"(c.value)) ",[269,814,704],{"class":320},[269,816,817],{"class":324}," c ",[269,819,710],{"class":320},[269,821,782],{"class":324},[269,823,824],{"class":320},"if",[269,826,827],{"class":324}," c.value ",[269,829,830],{"class":320},"is",[269,832,833],{"class":320}," not",[269,835,836],{"class":425}," None",[269,838,510],{"class":324},[269,840,842,845,848,851,853,856,859],{"class":271,"line":841},31,[269,843,844],{"class":324},"    ws.column_dimensions[column_cells[",[269,846,847],{"class":425},"0",[269,849,850],{"class":324},"].column_letter].width ",[269,852,381],{"class":320},[269,854,855],{"class":324}," longest ",[269,857,858],{"class":320},"+",[269,860,861],{"class":425}," 3\n",[269,863,865],{"class":271,"line":864},32,[269,866,365],{"emptyLinePlaceholder":364},[269,868,870,873,875,878],{"class":271,"line":869},33,[269,871,872],{"class":324},"ws.freeze_panes ",[269,874,381],{"class":320},[269,876,877],{"class":279}," \"A2\"",[269,879,880],{"class":371},"  # keep the header visible while scrolling\n",[269,882,884,887,889],{"class":271,"line":883},34,[269,885,886],{"class":324},"wb.save(",[269,888,486],{"class":279},[269,890,510],{"class":324},[269,892,894,897,899,902,905,908],{"class":271,"line":893},35,[269,895,896],{"class":425},"print",[269,898,801],{"class":324},[269,900,901],{"class":279},"\"Styled header and fitted\"",[269,903,904],{"class":324},", ws.max_column, ",[269,906,907],{"class":279},"\"columns\"",[269,909,510],{"class":324},[10,911,912,913,226],{},"Excel has no \"autofit\" you can call from a file writer — the fitted width comes from measuring the text yourself, which is exactly what the loop does. The full set of styling primitives lives in ",[52,914,55],{"href":54},[38,916,918],{"id":917},"apply-currency-and-date-number-formats","Apply currency and date number formats",[10,920,921,922,925,926,929,930,933,934,937,938,941],{},"A number format is a ",[18,923,924],{},"display"," rule: it changes how a value looks, not the value itself, so ",[13,927,928],{},"1234.5"," stays a number you can sum while showing as ",[13,931,932],{},"$1,234.50",". You set it with ",[13,935,936],{},"cell.number_format"," using Excel's format codes. Here we add an order date and a price to a tiny table, then format the date as ",[13,939,940],{},"dd-mmm-yyyy"," and the price as currency.",[260,943,945],{"className":311,"code":944,"language":313,"meta":265,"style":265},"import datetime as dt\nfrom openpyxl import Workbook\nfrom openpyxl.styles import Font\n\nwb = Workbook()\nws = wb.active\nws.title = \"Orders\"\n\nws.append([\"Order\", \"Order Date\", \"Amount\"])\nfor cell in ws[1]:\n    cell.font = Font(bold=True)\n\nrows = [\n    (\"A-1001\", dt.date(2026, 6, 1), 1234.50),\n    (\"A-1002\", dt.date(2026, 6, 3),  879.00),\n    (\"A-1003\", dt.date(2026, 6, 4), 2410.75),\n]\nfor order, order_date, amount in rows:\n    ws.append([order, order_date, amount])\n\n# Format the date column (B) and currency column (C)\nfor row in ws.iter_rows(min_row=2, max_row=ws.max_row):\n    row[1].number_format = \"dd-mmm-yyyy\"          # 01-Jun-2026\n    row[2].number_format = '\"$\"#,##0.00'          # $1,234.50\n\nwb.save(\"orders_formatted.xlsx\")\ntotal = sum(amount for _, _, amount in rows)\nprint(f\"Wrote 3 orders, total still summable: ${total:,.2f}\")\n",[13,946,947,959,970,981,985,994,1003,1013,1017,1038,1052,1068,1072,1082,1114,1142,1169,1173,1185,1190,1194,1199,1229,1247,1263,1267,1276,1299],{"__ignoreMap":265},[269,948,949,951,954,956],{"class":271,"line":272},[269,950,321],{"class":320},[269,952,953],{"class":324}," datetime ",[269,955,328],{"class":320},[269,957,958],{"class":324}," dt\n",[269,960,961,963,965,967],{"class":271,"line":334},[269,962,337],{"class":320},[269,964,340],{"class":324},[269,966,321],{"class":320},[269,968,969],{"class":324}," Workbook\n",[269,971,972,974,976,978],{"class":271,"line":348},[269,973,337],{"class":320},[269,975,353],{"class":324},[269,977,321],{"class":320},[269,979,980],{"class":324}," Font\n",[269,982,983],{"class":271,"line":361},[269,984,365],{"emptyLinePlaceholder":364},[269,986,987,989,991],{"class":271,"line":368},[269,988,527],{"class":324},[269,990,381],{"class":320},[269,992,993],{"class":324}," Workbook()\n",[269,995,996,998,1000],{"class":271,"line":375},[269,997,542],{"class":324},[269,999,381],{"class":320},[269,1001,1002],{"class":324}," wb.active\n",[269,1004,1005,1008,1010],{"class":271,"line":387},[269,1006,1007],{"class":324},"ws.title ",[269,1009,381],{"class":320},[269,1011,1012],{"class":279}," \"Orders\"\n",[269,1014,1015],{"class":271,"line":417},[269,1016,365],{"emptyLinePlaceholder":364},[269,1018,1019,1022,1025,1027,1030,1032,1035],{"class":271,"line":446},[269,1020,1021],{"class":324},"ws.append([",[269,1023,1024],{"class":279},"\"Order\"",[269,1026,297],{"class":324},[269,1028,1029],{"class":279},"\"Order Date\"",[269,1031,297],{"class":324},[269,1033,1034],{"class":279},"\"Amount\"",[269,1036,1037],{"class":324},"])\n",[269,1039,1040,1042,1044,1046,1048,1050],{"class":271,"line":474},[269,1041,704],{"class":320},[269,1043,707],{"class":324},[269,1045,710],{"class":320},[269,1047,713],{"class":324},[269,1049,716],{"class":425},[269,1051,719],{"class":324},[269,1053,1054,1056,1058,1060,1062,1064,1066],{"class":271,"line":480},[269,1055,725],{"class":324},[269,1057,381],{"class":320},[269,1059,568],{"class":324},[269,1061,571],{"class":491},[269,1063,381],{"class":320},[269,1065,576],{"class":425},[269,1067,510],{"class":324},[269,1069,1070],{"class":271,"line":513},[269,1071,365],{"emptyLinePlaceholder":364},[269,1073,1074,1077,1079],{"class":271,"line":518},[269,1075,1076],{"class":324},"rows ",[269,1078,381],{"class":320},[269,1080,1081],{"class":324}," [\n",[269,1083,1084,1087,1090,1093,1096,1098,1101,1103,1105,1108,1111],{"class":271,"line":524},[269,1085,1086],{"class":324},"    (",[269,1088,1089],{"class":279},"\"A-1001\"",[269,1091,1092],{"class":324},", dt.date(",[269,1094,1095],{"class":425},"2026",[269,1097,297],{"class":324},[269,1099,1100],{"class":425},"6",[269,1102,297],{"class":324},[269,1104,716],{"class":425},[269,1106,1107],{"class":324},"), ",[269,1109,1110],{"class":425},"1234.50",[269,1112,1113],{"class":324},"),\n",[269,1115,1116,1118,1121,1123,1125,1127,1129,1131,1134,1137,1140],{"class":271,"line":539},[269,1117,1086],{"class":324},[269,1119,1120],{"class":279},"\"A-1002\"",[269,1122,1092],{"class":324},[269,1124,1095],{"class":425},[269,1126,297],{"class":324},[269,1128,1100],{"class":425},[269,1130,297],{"class":324},[269,1132,1133],{"class":425},"3",[269,1135,1136],{"class":324},"),  ",[269,1138,1139],{"class":425},"879.00",[269,1141,1113],{"class":324},[269,1143,1144,1146,1149,1151,1153,1155,1157,1159,1162,1164,1167],{"class":271,"line":555},[269,1145,1086],{"class":324},[269,1147,1148],{"class":279},"\"A-1003\"",[269,1150,1092],{"class":324},[269,1152,1095],{"class":425},[269,1154,297],{"class":324},[269,1156,1100],{"class":425},[269,1158,297],{"class":324},[269,1160,1161],{"class":425},"4",[269,1163,1107],{"class":324},[269,1165,1166],{"class":425},"2410.75",[269,1168,1113],{"class":324},[269,1170,1171],{"class":271,"line":560},[269,1172,552],{"class":324},[269,1174,1175,1177,1180,1182],{"class":271,"line":601},[269,1176,704],{"class":320},[269,1178,1179],{"class":324}," order, order_date, amount ",[269,1181,710],{"class":320},[269,1183,1184],{"class":324}," rows:\n",[269,1186,1187],{"class":271,"line":627},[269,1188,1189],{"class":324},"    ws.append([order, order_date, amount])\n",[269,1191,1192],{"class":271,"line":657},[269,1193,365],{"emptyLinePlaceholder":364},[269,1195,1196],{"class":271,"line":696},[269,1197,1198],{"class":371},"# Format the date column (B) and currency column (C)\n",[269,1200,1201,1203,1206,1208,1211,1214,1216,1219,1221,1224,1226],{"class":271,"line":701},[269,1202,704],{"class":320},[269,1204,1205],{"class":324}," row ",[269,1207,710],{"class":320},[269,1209,1210],{"class":324}," ws.iter_rows(",[269,1212,1213],{"class":491},"min_row",[269,1215,381],{"class":320},[269,1217,1218],{"class":425},"2",[269,1220,297],{"class":324},[269,1222,1223],{"class":491},"max_row",[269,1225,381],{"class":320},[269,1227,1228],{"class":324},"ws.max_row):\n",[269,1230,1231,1234,1236,1239,1241,1244],{"class":271,"line":722},[269,1232,1233],{"class":324},"    row[",[269,1235,716],{"class":425},[269,1237,1238],{"class":324},"].number_format ",[269,1240,381],{"class":320},[269,1242,1243],{"class":279}," \"dd-mmm-yyyy\"",[269,1245,1246],{"class":371},"          # 01-Jun-2026\n",[269,1248,1249,1251,1253,1255,1257,1260],{"class":271,"line":733},[269,1250,1233],{"class":324},[269,1252,1218],{"class":425},[269,1254,1238],{"class":324},[269,1256,381],{"class":320},[269,1258,1259],{"class":279}," '\"$\"#,##0.00'",[269,1261,1262],{"class":371},"          # $1,234.50\n",[269,1264,1265],{"class":271,"line":744},[269,1266,365],{"emptyLinePlaceholder":364},[269,1268,1269,1271,1274],{"class":271,"line":755},[269,1270,886],{"class":324},[269,1272,1273],{"class":279},"\"orders_formatted.xlsx\"",[269,1275,510],{"class":324},[269,1277,1278,1281,1283,1286,1289,1291,1294,1296],{"class":271,"line":766},[269,1279,1280],{"class":324},"total ",[269,1282,381],{"class":320},[269,1284,1285],{"class":425}," sum",[269,1287,1288],{"class":324},"(amount ",[269,1290,704],{"class":320},[269,1292,1293],{"class":324}," _, _, amount ",[269,1295,710],{"class":320},[269,1297,1298],{"class":324}," rows)\n",[269,1300,1301,1303,1305,1308,1311,1314,1317,1320,1323,1326],{"class":271,"line":771},[269,1302,896],{"class":425},[269,1304,801],{"class":324},[269,1306,1307],{"class":320},"f",[269,1309,1310],{"class":279},"\"Wrote 3 orders, total still summable: $",[269,1312,1313],{"class":425},"{",[269,1315,1316],{"class":324},"total",[269,1318,1319],{"class":320},":,.2f",[269,1321,1322],{"class":425},"}",[269,1324,1325],{"class":279},"\"",[269,1327,510],{"class":324},[10,1329,1330,1331,226],{},"The amounts remain real numbers — Excel can total column C even though it displays dollar signs. For percentages, thousands separators, and locale-aware currency codes, see ",[52,1332,62],{"href":61},[38,1334,1336],{"id":1335},"add-a-bar-chart-from-worksheet-data","Add a bar chart from worksheet data",[10,1338,1339,1340,1343,1344,1347,1348,1351],{},"A chart in openpyxl points at ",[18,1341,1342],{},"ranges already on the sheet"," — you give it a ",[13,1345,1346],{},"Reference"," to the data and to the category labels, and Excel renders it live, so the chart updates if the cells change. Here we write four regions of revenue and anchor a ",[13,1349,1350],{},"BarChart"," next to the table.",[260,1353,1355],{"className":311,"code":1354,"language":313,"meta":265,"style":265},"from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, Reference\n\nwb = Workbook()\nws = wb.active\nws.title = \"Revenue\"\n\nws.append([\"Region\", \"Revenue\"])\nfor region, revenue in [(\"North\", 25640), (\"South\", 18890),\n                        (\"East\", 31200), (\"West\", 14005)]:\n    ws.append([region, revenue])\n\nchart = BarChart()\nchart.type = \"col\"\nchart.title = \"Revenue by Region\"\nchart.y_axis.title = \"Revenue ($)\"\nchart.x_axis.title = \"Region\"\n\ndata = Reference(ws, min_col=2, min_row=1, max_row=5)   # include header for series name\ncats = Reference(ws, min_col=1, min_row=2, max_row=5)\nchart.add_data(data, titles_from_data=True)\nchart.set_categories(cats)\n\nws.add_chart(chart, \"D2\")   # top-left corner of the chart\nwb.save(\"revenue_chart.xlsx\")\nprint(\"Embedded a\", chart.type, \"chart anchored at D2\")\n",[13,1356,1357,1367,1379,1383,1391,1399,1408,1412,1426,1457,1481,1486,1490,1500,1510,1520,1530,1540,1544,1584,1617,1631,1636,1640,1653,1662],{"__ignoreMap":265},[269,1358,1359,1361,1363,1365],{"class":271,"line":272},[269,1360,337],{"class":320},[269,1362,340],{"class":324},[269,1364,321],{"class":320},[269,1366,969],{"class":324},[269,1368,1369,1371,1374,1376],{"class":271,"line":334},[269,1370,337],{"class":320},[269,1372,1373],{"class":324}," openpyxl.chart ",[269,1375,321],{"class":320},[269,1377,1378],{"class":324}," BarChart, Reference\n",[269,1380,1381],{"class":271,"line":348},[269,1382,365],{"emptyLinePlaceholder":364},[269,1384,1385,1387,1389],{"class":271,"line":361},[269,1386,527],{"class":324},[269,1388,381],{"class":320},[269,1390,993],{"class":324},[269,1392,1393,1395,1397],{"class":271,"line":368},[269,1394,542],{"class":324},[269,1396,381],{"class":320},[269,1398,1002],{"class":324},[269,1400,1401,1403,1405],{"class":271,"line":375},[269,1402,1007],{"class":324},[269,1404,381],{"class":320},[269,1406,1407],{"class":279}," \"Revenue\"\n",[269,1409,1410],{"class":271,"line":387},[269,1411,365],{"emptyLinePlaceholder":364},[269,1413,1414,1416,1419,1421,1424],{"class":271,"line":417},[269,1415,1021],{"class":324},[269,1417,1418],{"class":279},"\"Region\"",[269,1420,297],{"class":324},[269,1422,1423],{"class":279},"\"Revenue\"",[269,1425,1037],{"class":324},[269,1427,1428,1430,1433,1435,1438,1440,1442,1445,1448,1450,1452,1455],{"class":271,"line":446},[269,1429,704],{"class":320},[269,1431,1432],{"class":324}," region, revenue ",[269,1434,710],{"class":320},[269,1436,1437],{"class":324}," [(",[269,1439,396],{"class":279},[269,1441,297],{"class":324},[269,1443,1444],{"class":425},"25640",[269,1446,1447],{"class":324},"), (",[269,1449,401],{"class":279},[269,1451,297],{"class":324},[269,1453,1454],{"class":425},"18890",[269,1456,1113],{"class":324},[269,1458,1459,1462,1464,1466,1469,1471,1473,1475,1478],{"class":271,"line":474},[269,1460,1461],{"class":324},"                        (",[269,1463,406],{"class":279},[269,1465,297],{"class":324},[269,1467,1468],{"class":425},"31200",[269,1470,1447],{"class":324},[269,1472,411],{"class":279},[269,1474,297],{"class":324},[269,1476,1477],{"class":425},"14005",[269,1479,1480],{"class":324},")]:\n",[269,1482,1483],{"class":271,"line":480},[269,1484,1485],{"class":324},"    ws.append([region, revenue])\n",[269,1487,1488],{"class":271,"line":513},[269,1489,365],{"emptyLinePlaceholder":364},[269,1491,1492,1495,1497],{"class":271,"line":518},[269,1493,1494],{"class":324},"chart ",[269,1496,381],{"class":320},[269,1498,1499],{"class":324}," BarChart()\n",[269,1501,1502,1505,1507],{"class":271,"line":524},[269,1503,1504],{"class":324},"chart.type ",[269,1506,381],{"class":320},[269,1508,1509],{"class":279}," \"col\"\n",[269,1511,1512,1515,1517],{"class":271,"line":539},[269,1513,1514],{"class":324},"chart.title ",[269,1516,381],{"class":320},[269,1518,1519],{"class":279}," \"Revenue by Region\"\n",[269,1521,1522,1525,1527],{"class":271,"line":555},[269,1523,1524],{"class":324},"chart.y_axis.title ",[269,1526,381],{"class":320},[269,1528,1529],{"class":279}," \"Revenue ($)\"\n",[269,1531,1532,1535,1537],{"class":271,"line":560},[269,1533,1534],{"class":324},"chart.x_axis.title ",[269,1536,381],{"class":320},[269,1538,1539],{"class":279}," \"Region\"\n",[269,1541,1542],{"class":271,"line":601},[269,1543,365],{"emptyLinePlaceholder":364},[269,1545,1546,1549,1551,1554,1557,1559,1561,1563,1565,1567,1569,1571,1573,1575,1578,1581],{"class":271,"line":627},[269,1547,1548],{"class":324},"data ",[269,1550,381],{"class":320},[269,1552,1553],{"class":324}," Reference(ws, ",[269,1555,1556],{"class":491},"min_col",[269,1558,381],{"class":320},[269,1560,1218],{"class":425},[269,1562,297],{"class":324},[269,1564,1213],{"class":491},[269,1566,381],{"class":320},[269,1568,716],{"class":425},[269,1570,297],{"class":324},[269,1572,1223],{"class":491},[269,1574,381],{"class":320},[269,1576,1577],{"class":425},"5",[269,1579,1580],{"class":324},")   ",[269,1582,1583],{"class":371},"# include header for series name\n",[269,1585,1586,1589,1591,1593,1595,1597,1599,1601,1603,1605,1607,1609,1611,1613,1615],{"class":271,"line":657},[269,1587,1588],{"class":324},"cats ",[269,1590,381],{"class":320},[269,1592,1553],{"class":324},[269,1594,1556],{"class":491},[269,1596,381],{"class":320},[269,1598,716],{"class":425},[269,1600,297],{"class":324},[269,1602,1213],{"class":491},[269,1604,381],{"class":320},[269,1606,1218],{"class":425},[269,1608,297],{"class":324},[269,1610,1223],{"class":491},[269,1612,381],{"class":320},[269,1614,1577],{"class":425},[269,1616,510],{"class":324},[269,1618,1619,1622,1625,1627,1629],{"class":271,"line":696},[269,1620,1621],{"class":324},"chart.add_data(data, ",[269,1623,1624],{"class":491},"titles_from_data",[269,1626,381],{"class":320},[269,1628,576],{"class":425},[269,1630,510],{"class":324},[269,1632,1633],{"class":271,"line":701},[269,1634,1635],{"class":324},"chart.set_categories(cats)\n",[269,1637,1638],{"class":271,"line":722},[269,1639,365],{"emptyLinePlaceholder":364},[269,1641,1642,1645,1648,1650],{"class":271,"line":733},[269,1643,1644],{"class":324},"ws.add_chart(chart, ",[269,1646,1647],{"class":279},"\"D2\"",[269,1649,1580],{"class":324},[269,1651,1652],{"class":371},"# top-left corner of the chart\n",[269,1654,1655,1657,1660],{"class":271,"line":744},[269,1656,886],{"class":324},[269,1658,1659],{"class":279},"\"revenue_chart.xlsx\"",[269,1661,510],{"class":324},[269,1663,1664,1666,1668,1671,1674,1677],{"class":271,"line":755},[269,1665,896],{"class":425},[269,1667,801],{"class":324},[269,1669,1670],{"class":279},"\"Embedded a\"",[269,1672,1673],{"class":324},", chart.type, ",[269,1675,1676],{"class":279},"\"chart anchored at D2\"",[269,1678,510],{"class":324},[10,1680,1681,1682,1684,1685,226],{},"The chart is a native Excel object, not a pasted image, so users can restyle it in Excel and it redraws if the data changes. Line and pie variants follow the same ",[13,1683,1346],{}," pattern in ",[52,1686,69],{"href":68},[38,1688,1690],{"id":1689},"insert-a-logo-image","Insert a logo image",[10,1692,1693,1694,1697],{},"Branding a report usually means a logo in the top-left corner. openpyxl embeds raster images (PNG, JPEG) through ",[13,1695,1696],{},"openpyxl.drawing.image.Image",", anchored to a cell. This needs Pillow, which openpyxl uses to read the image — the example generates a tiny PNG inline so it runs with nothing to download.",[260,1699,1701],{"className":262,"code":1700,"language":264,"meta":265,"style":265},"pip install openpyxl pillow\n",[13,1702,1703],{"__ignoreMap":265},[269,1704,1705,1707,1709,1712],{"class":271,"line":272},[269,1706,276],{"class":275},[269,1708,280],{"class":279},[269,1710,1711],{"class":279}," openpyxl",[269,1713,1714],{"class":279}," pillow\n",[260,1716,1718],{"className":311,"code":1717,"language":313,"meta":265,"style":265},"from openpyxl import Workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom PIL import Image as PILImage\n\n# Build a small placeholder logo so the example is self-contained\nPILImage.new(\"RGB\", (120, 40), color=\"#305496\").save(\"logo.png\")\n\nwb = Workbook()\nws = wb.active\nws.title = \"Report\"\n\n# Reserve space for the logo, then start the title below it\nws.row_dimensions[1].height = 34\nws[\"A3\"] = \"Weekly Sales Report\"\n\nlogo = XLImage(\"logo.png\")\nlogo.width, logo.height = 120, 40   # in pixels\nws.add_image(logo, \"A1\")            # anchor top-left\n\nwb.save(\"branded_report.xlsx\")\nprint(\"Embedded logo.png at cell A1\")\n",[13,1719,1720,1730,1747,1764,1768,1773,1809,1813,1821,1829,1838,1842,1847,1862,1878,1882,1896,1913,1927,1931,1940],{"__ignoreMap":265},[269,1721,1722,1724,1726,1728],{"class":271,"line":272},[269,1723,337],{"class":320},[269,1725,340],{"class":324},[269,1727,321],{"class":320},[269,1729,969],{"class":324},[269,1731,1732,1734,1737,1739,1742,1744],{"class":271,"line":334},[269,1733,337],{"class":320},[269,1735,1736],{"class":324}," openpyxl.drawing.image ",[269,1738,321],{"class":320},[269,1740,1741],{"class":324}," Image ",[269,1743,328],{"class":320},[269,1745,1746],{"class":324}," XLImage\n",[269,1748,1749,1751,1754,1757,1759,1761],{"class":271,"line":348},[269,1750,337],{"class":320},[269,1752,1753],{"class":425}," PIL",[269,1755,1756],{"class":320}," import",[269,1758,1741],{"class":324},[269,1760,328],{"class":320},[269,1762,1763],{"class":324}," PILImage\n",[269,1765,1766],{"class":271,"line":361},[269,1767,365],{"emptyLinePlaceholder":364},[269,1769,1770],{"class":271,"line":368},[269,1771,1772],{"class":371},"# Build a small placeholder logo so the example is self-contained\n",[269,1774,1775,1778,1781,1784,1787,1789,1792,1794,1796,1798,1801,1804,1807],{"class":271,"line":375},[269,1776,1777],{"class":324},"PILImage.new(",[269,1779,1780],{"class":279},"\"RGB\"",[269,1782,1783],{"class":324},", (",[269,1785,1786],{"class":425},"120",[269,1788,297],{"class":324},[269,1790,1791],{"class":425},"40",[269,1793,1107],{"class":324},[269,1795,581],{"class":491},[269,1797,381],{"class":320},[269,1799,1800],{"class":279},"\"#305496\"",[269,1802,1803],{"class":324},").save(",[269,1805,1806],{"class":279},"\"logo.png\"",[269,1808,510],{"class":324},[269,1810,1811],{"class":271,"line":387},[269,1812,365],{"emptyLinePlaceholder":364},[269,1814,1815,1817,1819],{"class":271,"line":417},[269,1816,527],{"class":324},[269,1818,381],{"class":320},[269,1820,993],{"class":324},[269,1822,1823,1825,1827],{"class":271,"line":446},[269,1824,542],{"class":324},[269,1826,381],{"class":320},[269,1828,1002],{"class":324},[269,1830,1831,1833,1835],{"class":271,"line":474},[269,1832,1007],{"class":324},[269,1834,381],{"class":320},[269,1836,1837],{"class":279}," \"Report\"\n",[269,1839,1840],{"class":271,"line":480},[269,1841,365],{"emptyLinePlaceholder":364},[269,1843,1844],{"class":271,"line":513},[269,1845,1846],{"class":371},"# Reserve space for the logo, then start the title below it\n",[269,1848,1849,1852,1854,1857,1859],{"class":271,"line":518},[269,1850,1851],{"class":324},"ws.row_dimensions[",[269,1853,716],{"class":425},[269,1855,1856],{"class":324},"].height ",[269,1858,381],{"class":320},[269,1860,1861],{"class":425}," 34\n",[269,1863,1864,1867,1870,1873,1875],{"class":271,"line":524},[269,1865,1866],{"class":324},"ws[",[269,1868,1869],{"class":279},"\"A3\"",[269,1871,1872],{"class":324},"] ",[269,1874,381],{"class":320},[269,1876,1877],{"class":279}," \"Weekly Sales Report\"\n",[269,1879,1880],{"class":271,"line":539},[269,1881,365],{"emptyLinePlaceholder":364},[269,1883,1884,1887,1889,1892,1894],{"class":271,"line":555},[269,1885,1886],{"class":324},"logo ",[269,1888,381],{"class":320},[269,1890,1891],{"class":324}," XLImage(",[269,1893,1806],{"class":279},[269,1895,510],{"class":324},[269,1897,1898,1901,1903,1906,1908,1910],{"class":271,"line":560},[269,1899,1900],{"class":324},"logo.width, logo.height ",[269,1902,381],{"class":320},[269,1904,1905],{"class":425}," 120",[269,1907,297],{"class":324},[269,1909,1791],{"class":425},[269,1911,1912],{"class":371},"   # in pixels\n",[269,1914,1915,1918,1921,1924],{"class":271,"line":601},[269,1916,1917],{"class":324},"ws.add_image(logo, ",[269,1919,1920],{"class":279},"\"A1\"",[269,1922,1923],{"class":324},")            ",[269,1925,1926],{"class":371},"# anchor top-left\n",[269,1928,1929],{"class":271,"line":627},[269,1930,365],{"emptyLinePlaceholder":364},[269,1932,1933,1935,1938],{"class":271,"line":657},[269,1934,886],{"class":324},[269,1936,1937],{"class":279},"\"branded_report.xlsx\"",[269,1939,510],{"class":324},[269,1941,1942,1944,1946,1949],{"class":271,"line":696},[269,1943,896],{"class":425},[269,1945,801],{"class":324},[269,1947,1948],{"class":279},"\"Embedded logo.png at cell A1\"",[269,1950,510],{"class":324},[10,1952,1953,1954,1957,1958,1960,1961,1963],{},"The image is copied ",[18,1955,1956],{},"into"," the workbook, so the saved ",[13,1959,90],{}," is self-contained — you can email it and the logo travels with it. See ",[52,1962,76],{"href":75}," for sizing, aspect ratio, and headers\u002Ffooters.",[38,1965,1967],{"id":1966},"frequently-asked-questions","Frequently asked questions",[10,1969,1970,1976,1977,1979,1980,1983],{},[134,1971,1972,1973,1975],{},"Does styling survive ",[13,1974,235],{},"?","\nOnly minimally. ",[13,1978,140],{}," writes data and a basic bold header; it gives you no handle on fonts, fills, borders, number formats, charts, or images. The standard approach is to write the data with pandas, then re-open the file with ",[13,1981,1982],{},"openpyxl.load_workbook"," and apply everything else. openpyxl preserves any styling already in the file when it loads.",[10,1985,1986,1989,1990,1992,1993,1995],{},[134,1987,1988],{},"openpyxl or xlsxwriter for charts and styling?","\nUse ",[134,1991,162],{}," when you need to open and modify an existing file — it is the only one that can read a workbook and keep its formatting. Use ",[134,1994,184],{}," when you are generating a brand-new file from scratch and want the fastest styled write; it has a rich chart and format API but is strictly write-only, so it cannot touch a file that already exists.",[10,1997,1998,2001,2002,2005],{},[134,1999,2000],{},"How do I autosize columns?","\nThere is no file-level \"autofit.\" Measure the longest string in each column yourself and set ",[13,2003,2004],{},"ws.column_dimensions[letter].width"," to that length plus a small pad (the header-row example above does this). Width units are roughly the count of default-font characters, not pixels.",[10,2007,2008,2011,2012,2014,2015,2018],{},[134,2009,2010],{},"Do I need Excel installed to format and chart?","\nNo. openpyxl and xlsxwriter are pure Python and write the ",[13,2013,90],{}," file format directly, so they run on a headless Linux server or CI runner with no Excel anywhere. You only need Excel (via xlwings) when a ",[18,2016,2017],{},"live"," application must run macros or recalculate — which is not the case for formatting, number formats, charts, or images.",[10,2020,2021,2024,2025,2028,2029,2032,2033,2035,2036,2038,2039,2042],{},[134,2022,2023],{},"Will a number format break my totals?","\nNo. A number format only changes how a value is ",[18,2026,2027],{},"displayed",". ",[13,2030,2031],{},"'\"$\"#,##0.00'"," makes ",[13,2034,928],{}," show as ",[13,2037,932],{},", but Excel still stores a number and sums it normally. Formatting a real ",[13,2040,2041],{},"datetime.date"," with a date code is likewise display-only.",[38,2044,2046],{"id":2045},"key-takeaways","Key takeaways",[46,2048,2049,2052,2064,2067,2070,2073],{},[49,2050,2051],{},"pandas writes correct but unstyled sheets; the polish comes from re-opening the file with openpyxl.",[49,2053,2054,2055,2057,2058,2060,2061,2063],{},"Use ",[134,2056,162],{}," to edit existing files (it preserves styles), ",[134,2059,184],{}," for fast new-file builds, plain ",[134,2062,136],{}," for raw data.",[49,2065,2066],{},"Header styling and fitted column widths are the cheapest, highest-impact upgrades.",[49,2068,2069],{},"Number formats are display-only — your values stay summable and your dates stay real dates.",[49,2071,2072],{},"Charts reference live worksheet ranges; logos embed into the file and travel with it.",[49,2074,2075],{},"None of this needs Excel installed, so it runs unattended on a server.",[38,2077,2079],{"id":2078},"where-to-go-next","Where to go next",[10,2081,2082],{},"Go deep on each piece of the polished report:",[46,2084,2085,2089,2094,2099],{},[49,2086,2087,56],{},[52,2088,55],{"href":54},[49,2090,2091,2093],{},[52,2092,62],{"href":61}," — currency, percentage, thousands, and date format codes.",[49,2095,2096,2098],{},[52,2097,69],{"href":68}," — bar, line, and pie charts from worksheet ranges.",[49,2100,2101,2103],{},[52,2102,76],{"href":75}," — embedding logos and images into branded reports.",[10,2105,2106],{},"Related tracks:",[46,2108,2109,2116],{},[49,2110,2111,2115],{},[52,2112,2114],{"href":2113},"\u002Fautomating-reporting-workflows\u002F","Automating Reporting Workflows"," — once a report looks right, put it on a schedule and email it.",[49,2117,2118,2122],{},[52,2119,2121],{"href":2120},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002F","Applying Conditional Formatting with openpyxl"," — rule-based styling that reacts to the data, such as highlighting low stock.",[676,2124,2125],{},"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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}",{"title":265,"searchDepth":334,"depth":334,"links":2127},[2128,2129,2130,2131,2132,2133,2134,2135,2136],{"id":40,"depth":334,"text":41},{"id":83,"depth":334,"text":84},{"id":289,"depth":334,"text":290},{"id":917,"depth":334,"text":918},{"id":1335,"depth":334,"text":1336},{"id":1689,"depth":334,"text":1690},{"id":1966,"depth":334,"text":1967},{"id":2045,"depth":334,"text":2046},{"id":2078,"depth":334,"text":2079},"Turn plain pandas output into branded, stakeholder-ready Excel reports: cell styling, number and date formats, charts, and embedded logos with openpyxl.","md",{},"\u002Fformatting-and-charting-excel-reports-with-python",{"title":2142,"description":2143},"Format & Chart Excel Reports in Python","Make Excel reports people trust: style headers, autosize columns, apply currency and date formats, add charts, and embed logos using openpyxl and xlsxwriter.","formatting-and-charting-excel-reports-with-python\u002Findex","ccwnU3UpjDIj0IXxdtisVGYjoAa7J1hhTIYAS72uCps",[2147,2151],{"title":2148,"path":2149,"stem":2150,"children":-1},"Schedule Recurring Excel Reports with APScheduler","\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-recurring-excel-reports-with-apscheduler","automating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-recurring-excel-reports-with-apscheduler\u002Findex",{"title":62,"path":2152,"stem":2153,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel","formatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Findex",1781773160495]