[{"data":1,"prerenderedAt":2226},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python":2219},{"id":4,"title":5,"body":6,"dateModified":2198,"datePublished":2198,"description":2199,"extension":2200,"faq":2201,"meta":2210,"navigation":279,"path":2211,"seo":2212,"slug":2215,"stem":2216,"type":2217,"__hash__":2218},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python\u002Findex.md","Add a Line Chart to an Excel Report with Python",{"type":7,"value":8,"toc":2186},"minimark",[9,24,180,185,210,220,224,231,603,606,610,616,1004,1013,1017,1027,1395,1417,1421,1431,1921,1930,1934,2065,2069,2072,2076,2085,2106,2123,2137,2141,2156,2160,2182],[10,11,12,13,18,19,23],"p",{},"This page adds a line chart to an Excel report from Python with openpyxl, the right choice when a metric moves over time. You will write a dated time-series, plot a single line, add a second series, switch the x-axis to read real dates, and turn on markers — all producing a native, editable Excel chart. It is the time-series companion to the parent cluster, ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",", and to its ",[14,20,22],{"href":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002F","bar chart guide",".",[25,26,34,35,34,39,34,43,34,50,34,60,34,65,34,68,34,72,34,78,34,83,34,88,34,93,34,97,34,101,34,105,34,109,34,116,34,120,34,126,34,131,34,137,34,140,34,147,34,151,34,156,34,160,34,164,34,168,34,172,34,176,34,178],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"0 0 760 250","img",[30,31],"line-t","line-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  ",[36,37,38],"title",{"id":30},"A dated time-series column rendered as a two-series line chart",[40,41,42],"desc",{"id":31},"A column of dates with two value series is bound into a line chart whose x-axis reads real dates, plotting two lines over time.",[44,45,49],"text",{"x":46,"y":47,"style":48},"115","32","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","Time-series data",[51,52],"rect",{"x":53,"y":54,"width":55,"height":56,"rx":57,"fill":58,"stroke":59},"30","44","170","158","10","var(--surface-muted,#eef2ff)","var(--line,#cdd5e6)",[61,62],"line",{"x1":63,"y1":54,"x2":63,"y2":64,"stroke":59},"100","202",[61,66],{"x1":67,"y1":54,"x2":67,"y2":64,"stroke":59},"150",[61,69],{"x1":53,"y1":70,"x2":71,"y2":70,"stroke":59},"72","200",[44,73,77],{"x":74,"y":75,"style":76},"65","63","font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Date",[44,79,82],{"x":80,"y":75,"style":81},"125","font-size:10.5px;font-weight:700;fill:var(--teal,#0f9488);text-anchor:middle","A",[44,84,87],{"x":85,"y":75,"style":86},"175","font-size:10.5px;font-weight:700;fill:var(--accent,#f43f8f);text-anchor:middle","B",[44,89,92],{"x":74,"y":90,"style":91},"95","font-size:10px;fill:var(--text,#172033);text-anchor:middle","Jan 1",[44,94,96],{"x":74,"y":95,"style":91},"121","Jan 8",[44,98,100],{"x":74,"y":99,"style":91},"147","Jan 15",[44,102,104],{"x":74,"y":103,"style":91},"173","Jan 22",[44,106,108],{"x":74,"y":107,"style":91},"195","Jan 29",[61,110],{"x1":111,"y1":112,"x2":113,"y2":112,"stroke":114,"style":115},"204","123","290","var(--brand,#5b5cf0)","stroke-width:2px",[117,118],"polygon",{"points":119,"fill":114},"290,123 280,118 280,128",[44,121,125],{"x":122,"y":123,"style":124},"247","112","font-size:11.5px;font-weight:600;fill:var(--brand,#5b5cf0);text-anchor:middle","Reference",[51,127],{"x":128,"y":54,"width":129,"height":56,"rx":57,"fill":130,"stroke":114},"296","434","var(--brand-soft,rgba(91,92,240,0.12))",[61,132],{"x1":133,"y1":134,"x2":133,"y2":135,"stroke":59,"style":136},"340","60","178","stroke-width:1.5px",[61,138],{"x1":133,"y1":135,"x2":139,"y2":135,"stroke":59,"style":136},"710",[141,142],"polyline",{"points":143,"fill":144,"stroke":145,"style":146},"360,150 440,128 520,140 600,98 680,108","none","var(--teal,#0f9488)","stroke-width:2.5px",[141,148],{"points":149,"fill":144,"stroke":150,"style":146},"360,165 440,158 520,120 600,148 680,84","var(--accent,#f43f8f)",[152,153],"circle",{"cx":154,"cy":67,"r":155,"fill":145},"360","3",[152,157],{"cx":158,"cy":159,"r":155,"fill":145},"440","128",[152,161],{"cx":162,"cy":163,"r":155,"fill":145},"520","140",[152,165],{"cx":166,"cy":167,"r":155,"fill":145},"600","98",[152,169],{"cx":170,"cy":171,"r":155,"fill":145},"680","108",[44,173,92],{"x":154,"y":174,"style":175},"194","font-size:9.5px;fill:var(--muted,#5b6780);text-anchor:middle",[44,177,100],{"x":162,"y":174,"style":175},[44,179,108],{"x":170,"y":174,"style":175},[181,182,184],"h2",{"id":183},"prerequisites","Prerequisites",[186,187,192],"pre",{"className":188,"code":189,"language":190,"meta":191,"style":191},"language-bash shiki shiki-themes github-light github-dark","pip install openpyxl\n","bash","",[193,194,195],"code",{"__ignoreMap":191},[196,197,199,203,207],"span",{"class":61,"line":198},1,[196,200,202],{"class":201},"sScJk","pip",[196,204,206],{"class":205},"sZZnC"," install",[196,208,209],{"class":205}," openpyxl\n",[10,211,212,215,216,219],{},[193,213,214],{},"datetime"," is used to write real date values into the sheet and comes with the standard library. Any current openpyxl release has the ",[193,217,218],{},"openpyxl.chart"," API below.",[181,221,223],{"id":222},"step-1-write-time-series-data","Step 1 — Write time-series data",[10,225,226,227,230],{},"Write a date column and one or more metric columns, with a header row so each series can take its name from it. Use real ",[193,228,229],{},"datetime.date"," objects, not strings — that is what lets the x-axis behave as dates later.",[186,232,236],{"className":233,"code":234,"language":235,"meta":191,"style":191},"language-python shiki shiki-themes github-light github-dark","# pip install openpyxl\nfrom datetime import date\nfrom openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Traffic\"\n\nws.append([\"Date\", \"Visitors\", \"Signups\"])     # row 1: header\nrows = [\n    (date(2026, 1, 1), 1200, 90),\n    (date(2026, 1, 8), 1340, 110),\n    (date(2026, 1, 15), 1290, 105),\n    (date(2026, 1, 22), 1510, 130),\n    (date(2026, 1, 29), 1625, 142),\n]\nfor r in rows:\n    ws.append(r)\n\nfor cell in ws[\"A\"][1:]:        # format the date column so Excel shows dates\n    cell.number_format = \"yyyy-mm-dd\"\n\nwb.save(\"line_step1.xlsx\")\nprint(\"Wrote 5 weekly rows: Date, Visitors, Signups\")\n","python",[193,237,238,244,261,274,281,293,304,315,320,346,357,390,418,446,474,502,508,523,529,534,561,572,577,589],{"__ignoreMap":191},[196,239,240],{"class":61,"line":198},[196,241,243],{"class":242},"sJ8bj","# pip install openpyxl\n",[196,245,247,251,255,258],{"class":61,"line":246},2,[196,248,250],{"class":249},"szBVR","from",[196,252,254],{"class":253},"sVt8B"," datetime ",[196,256,257],{"class":249},"import",[196,259,260],{"class":253}," date\n",[196,262,264,266,269,271],{"class":61,"line":263},3,[196,265,250],{"class":249},[196,267,268],{"class":253}," openpyxl ",[196,270,257],{"class":249},[196,272,273],{"class":253}," Workbook\n",[196,275,277],{"class":61,"line":276},4,[196,278,280],{"emptyLinePlaceholder":279},true,"\n",[196,282,284,287,290],{"class":61,"line":283},5,[196,285,286],{"class":253},"wb ",[196,288,289],{"class":249},"=",[196,291,292],{"class":253}," Workbook()\n",[196,294,296,299,301],{"class":61,"line":295},6,[196,297,298],{"class":253},"ws ",[196,300,289],{"class":249},[196,302,303],{"class":253}," wb.active\n",[196,305,307,310,312],{"class":61,"line":306},7,[196,308,309],{"class":253},"ws.title ",[196,311,289],{"class":249},[196,313,314],{"class":205}," \"Traffic\"\n",[196,316,318],{"class":61,"line":317},8,[196,319,280],{"emptyLinePlaceholder":279},[196,321,323,326,329,332,335,337,340,343],{"class":61,"line":322},9,[196,324,325],{"class":253},"ws.append([",[196,327,328],{"class":205},"\"Date\"",[196,330,331],{"class":253},", ",[196,333,334],{"class":205},"\"Visitors\"",[196,336,331],{"class":253},[196,338,339],{"class":205},"\"Signups\"",[196,341,342],{"class":253},"])     ",[196,344,345],{"class":242},"# row 1: header\n",[196,347,349,352,354],{"class":61,"line":348},10,[196,350,351],{"class":253},"rows ",[196,353,289],{"class":249},[196,355,356],{"class":253}," [\n",[196,358,360,363,367,369,372,374,376,379,382,384,387],{"class":61,"line":359},11,[196,361,362],{"class":253},"    (date(",[196,364,366],{"class":365},"sj4cs","2026",[196,368,331],{"class":253},[196,370,371],{"class":365},"1",[196,373,331],{"class":253},[196,375,371],{"class":365},[196,377,378],{"class":253},"), ",[196,380,381],{"class":365},"1200",[196,383,331],{"class":253},[196,385,386],{"class":365},"90",[196,388,389],{"class":253},"),\n",[196,391,393,395,397,399,401,403,406,408,411,413,416],{"class":61,"line":392},12,[196,394,362],{"class":253},[196,396,366],{"class":365},[196,398,331],{"class":253},[196,400,371],{"class":365},[196,402,331],{"class":253},[196,404,405],{"class":365},"8",[196,407,378],{"class":253},[196,409,410],{"class":365},"1340",[196,412,331],{"class":253},[196,414,415],{"class":365},"110",[196,417,389],{"class":253},[196,419,421,423,425,427,429,431,434,436,439,441,444],{"class":61,"line":420},13,[196,422,362],{"class":253},[196,424,366],{"class":365},[196,426,331],{"class":253},[196,428,371],{"class":365},[196,430,331],{"class":253},[196,432,433],{"class":365},"15",[196,435,378],{"class":253},[196,437,438],{"class":365},"1290",[196,440,331],{"class":253},[196,442,443],{"class":365},"105",[196,445,389],{"class":253},[196,447,449,451,453,455,457,459,462,464,467,469,472],{"class":61,"line":448},14,[196,450,362],{"class":253},[196,452,366],{"class":365},[196,454,331],{"class":253},[196,456,371],{"class":365},[196,458,331],{"class":253},[196,460,461],{"class":365},"22",[196,463,378],{"class":253},[196,465,466],{"class":365},"1510",[196,468,331],{"class":253},[196,470,471],{"class":365},"130",[196,473,389],{"class":253},[196,475,477,479,481,483,485,487,490,492,495,497,500],{"class":61,"line":476},15,[196,478,362],{"class":253},[196,480,366],{"class":365},[196,482,331],{"class":253},[196,484,371],{"class":365},[196,486,331],{"class":253},[196,488,489],{"class":365},"29",[196,491,378],{"class":253},[196,493,494],{"class":365},"1625",[196,496,331],{"class":253},[196,498,499],{"class":365},"142",[196,501,389],{"class":253},[196,503,505],{"class":61,"line":504},16,[196,506,507],{"class":253},"]\n",[196,509,511,514,517,520],{"class":61,"line":510},17,[196,512,513],{"class":249},"for",[196,515,516],{"class":253}," r ",[196,518,519],{"class":249},"in",[196,521,522],{"class":253}," rows:\n",[196,524,526],{"class":61,"line":525},18,[196,527,528],{"class":253},"    ws.append(r)\n",[196,530,532],{"class":61,"line":531},19,[196,533,280],{"emptyLinePlaceholder":279},[196,535,537,539,542,544,547,550,553,555,558],{"class":61,"line":536},20,[196,538,513],{"class":249},[196,540,541],{"class":253}," cell ",[196,543,519],{"class":249},[196,545,546],{"class":253}," ws[",[196,548,549],{"class":205},"\"A\"",[196,551,552],{"class":253},"][",[196,554,371],{"class":365},[196,556,557],{"class":253},":]:        ",[196,559,560],{"class":242},"# format the date column so Excel shows dates\n",[196,562,564,567,569],{"class":61,"line":563},21,[196,565,566],{"class":253},"    cell.number_format ",[196,568,289],{"class":249},[196,570,571],{"class":205}," \"yyyy-mm-dd\"\n",[196,573,575],{"class":61,"line":574},22,[196,576,280],{"emptyLinePlaceholder":279},[196,578,580,583,586],{"class":61,"line":579},23,[196,581,582],{"class":253},"wb.save(",[196,584,585],{"class":205},"\"line_step1.xlsx\"",[196,587,588],{"class":253},")\n",[196,590,592,595,598,601],{"class":61,"line":591},24,[196,593,594],{"class":365},"print",[196,596,597],{"class":253},"(",[196,599,600],{"class":205},"\"Wrote 5 weekly rows: Date, Visitors, Signups\"",[196,602,588],{"class":253},[10,604,605],{},"The data sits in A1:C6 — header in row 1, five data rows below.",[181,607,609],{"id":608},"step-2-plot-a-single-line","Step 2 — Plot a single line",[10,611,612,613,615],{},"Bind one metric column with ",[193,614,125],{},", include the header for the series name, and set the date column as categories.",[186,617,619],{"className":233,"code":618,"language":235,"meta":191,"style":191},"# pip install openpyxl\nfrom datetime import date\nfrom openpyxl import Workbook\nfrom openpyxl.chart import LineChart, Reference\n\nwb = Workbook()\nws = wb.active\nws.append([\"Date\", \"Visitors\", \"Signups\"])\nfor r in [(date(2026, 1, 1), 1200, 90), (date(2026, 1, 8), 1340, 110),\n          (date(2026, 1, 15), 1290, 105), (date(2026, 1, 22), 1510, 130),\n          (date(2026, 1, 29), 1625, 142)]:\n    ws.append(r)\n\nchart = LineChart()\nchart.title = \"Weekly Visitors\"\nchart.y_axis.title = \"Visitors\"\nchart.x_axis.title = \"Week\"\n\ndata = Reference(ws, min_col=2, min_row=1, max_row=6)   # Visitors + header\nchart.add_data(data, titles_from_data=True)\ncats = Reference(ws, min_col=1, min_row=2, max_row=6)   # dates, no header\nchart.set_categories(cats)\n\nws.add_chart(chart, \"E2\")\nwb.save(\"line_single.xlsx\")\nprint(\"Saved line_single.xlsx\")\n",[193,620,621,625,635,645,657,661,669,677,694,746,791,816,820,824,834,844,854,864,868,912,927,963,968,972,982,992],{"__ignoreMap":191},[196,622,623],{"class":61,"line":198},[196,624,243],{"class":242},[196,626,627,629,631,633],{"class":61,"line":246},[196,628,250],{"class":249},[196,630,254],{"class":253},[196,632,257],{"class":249},[196,634,260],{"class":253},[196,636,637,639,641,643],{"class":61,"line":263},[196,638,250],{"class":249},[196,640,268],{"class":253},[196,642,257],{"class":249},[196,644,273],{"class":253},[196,646,647,649,652,654],{"class":61,"line":276},[196,648,250],{"class":249},[196,650,651],{"class":253}," openpyxl.chart ",[196,653,257],{"class":249},[196,655,656],{"class":253}," LineChart, Reference\n",[196,658,659],{"class":61,"line":283},[196,660,280],{"emptyLinePlaceholder":279},[196,662,663,665,667],{"class":61,"line":295},[196,664,286],{"class":253},[196,666,289],{"class":249},[196,668,292],{"class":253},[196,670,671,673,675],{"class":61,"line":306},[196,672,298],{"class":253},[196,674,289],{"class":249},[196,676,303],{"class":253},[196,678,679,681,683,685,687,689,691],{"class":61,"line":317},[196,680,325],{"class":253},[196,682,328],{"class":205},[196,684,331],{"class":253},[196,686,334],{"class":205},[196,688,331],{"class":253},[196,690,339],{"class":205},[196,692,693],{"class":253},"])\n",[196,695,696,698,700,702,705,707,709,711,713,715,717,719,721,723,726,728,730,732,734,736,738,740,742,744],{"class":61,"line":322},[196,697,513],{"class":249},[196,699,516],{"class":253},[196,701,519],{"class":249},[196,703,704],{"class":253}," [(date(",[196,706,366],{"class":365},[196,708,331],{"class":253},[196,710,371],{"class":365},[196,712,331],{"class":253},[196,714,371],{"class":365},[196,716,378],{"class":253},[196,718,381],{"class":365},[196,720,331],{"class":253},[196,722,386],{"class":365},[196,724,725],{"class":253},"), (date(",[196,727,366],{"class":365},[196,729,331],{"class":253},[196,731,371],{"class":365},[196,733,331],{"class":253},[196,735,405],{"class":365},[196,737,378],{"class":253},[196,739,410],{"class":365},[196,741,331],{"class":253},[196,743,415],{"class":365},[196,745,389],{"class":253},[196,747,748,751,753,755,757,759,761,763,765,767,769,771,773,775,777,779,781,783,785,787,789],{"class":61,"line":348},[196,749,750],{"class":253},"          (date(",[196,752,366],{"class":365},[196,754,331],{"class":253},[196,756,371],{"class":365},[196,758,331],{"class":253},[196,760,433],{"class":365},[196,762,378],{"class":253},[196,764,438],{"class":365},[196,766,331],{"class":253},[196,768,443],{"class":365},[196,770,725],{"class":253},[196,772,366],{"class":365},[196,774,331],{"class":253},[196,776,371],{"class":365},[196,778,331],{"class":253},[196,780,461],{"class":365},[196,782,378],{"class":253},[196,784,466],{"class":365},[196,786,331],{"class":253},[196,788,471],{"class":365},[196,790,389],{"class":253},[196,792,793,795,797,799,801,803,805,807,809,811,813],{"class":61,"line":359},[196,794,750],{"class":253},[196,796,366],{"class":365},[196,798,331],{"class":253},[196,800,371],{"class":365},[196,802,331],{"class":253},[196,804,489],{"class":365},[196,806,378],{"class":253},[196,808,494],{"class":365},[196,810,331],{"class":253},[196,812,499],{"class":365},[196,814,815],{"class":253},")]:\n",[196,817,818],{"class":61,"line":392},[196,819,528],{"class":253},[196,821,822],{"class":61,"line":420},[196,823,280],{"emptyLinePlaceholder":279},[196,825,826,829,831],{"class":61,"line":448},[196,827,828],{"class":253},"chart ",[196,830,289],{"class":249},[196,832,833],{"class":253}," LineChart()\n",[196,835,836,839,841],{"class":61,"line":476},[196,837,838],{"class":253},"chart.title ",[196,840,289],{"class":249},[196,842,843],{"class":205}," \"Weekly Visitors\"\n",[196,845,846,849,851],{"class":61,"line":504},[196,847,848],{"class":253},"chart.y_axis.title ",[196,850,289],{"class":249},[196,852,853],{"class":205}," \"Visitors\"\n",[196,855,856,859,861],{"class":61,"line":510},[196,857,858],{"class":253},"chart.x_axis.title ",[196,860,289],{"class":249},[196,862,863],{"class":205}," \"Week\"\n",[196,865,866],{"class":61,"line":525},[196,867,280],{"emptyLinePlaceholder":279},[196,869,870,873,875,878,882,884,887,889,892,894,896,898,901,903,906,909],{"class":61,"line":531},[196,871,872],{"class":253},"data ",[196,874,289],{"class":249},[196,876,877],{"class":253}," Reference(ws, ",[196,879,881],{"class":880},"s4XuR","min_col",[196,883,289],{"class":249},[196,885,886],{"class":365},"2",[196,888,331],{"class":253},[196,890,891],{"class":880},"min_row",[196,893,289],{"class":249},[196,895,371],{"class":365},[196,897,331],{"class":253},[196,899,900],{"class":880},"max_row",[196,902,289],{"class":249},[196,904,905],{"class":365},"6",[196,907,908],{"class":253},")   ",[196,910,911],{"class":242},"# Visitors + header\n",[196,913,914,917,920,922,925],{"class":61,"line":536},[196,915,916],{"class":253},"chart.add_data(data, ",[196,918,919],{"class":880},"titles_from_data",[196,921,289],{"class":249},[196,923,924],{"class":365},"True",[196,926,588],{"class":253},[196,928,929,932,934,936,938,940,942,944,946,948,950,952,954,956,958,960],{"class":61,"line":563},[196,930,931],{"class":253},"cats ",[196,933,289],{"class":249},[196,935,877],{"class":253},[196,937,881],{"class":880},[196,939,289],{"class":249},[196,941,371],{"class":365},[196,943,331],{"class":253},[196,945,891],{"class":880},[196,947,289],{"class":249},[196,949,886],{"class":365},[196,951,331],{"class":253},[196,953,900],{"class":880},[196,955,289],{"class":249},[196,957,905],{"class":365},[196,959,908],{"class":253},[196,961,962],{"class":242},"# dates, no header\n",[196,964,965],{"class":61,"line":574},[196,966,967],{"class":253},"chart.set_categories(cats)\n",[196,969,970],{"class":61,"line":579},[196,971,280],{"emptyLinePlaceholder":279},[196,973,974,977,980],{"class":61,"line":591},[196,975,976],{"class":253},"ws.add_chart(chart, ",[196,978,979],{"class":205},"\"E2\"",[196,981,588],{"class":253},[196,983,985,987,990],{"class":61,"line":984},25,[196,986,582],{"class":253},[196,988,989],{"class":205},"\"line_single.xlsx\"",[196,991,588],{"class":253},[196,993,995,997,999,1002],{"class":61,"line":994},26,[196,996,594],{"class":365},[196,998,597],{"class":253},[196,1000,1001],{"class":205},"\"Saved line_single.xlsx\"",[196,1003,588],{"class":253},[10,1005,1006,1007,1009,1010,1012],{},"As with bar charts, the value ",[193,1008,125],{}," includes row 1 (for the name) and the category ",[193,1011,125],{}," starts at row 2.",[181,1014,1016],{"id":1015},"step-3-plot-multiple-series","Step 3 — Plot multiple series",[10,1018,1019,1020,1022,1023,1026],{},"To plot Visitors and Signups together, widen the value ",[193,1021,125],{}," to cover both columns. ",[193,1024,1025],{},"add_data"," creates one series per column, and the header row names them.",[186,1028,1030],{"className":233,"code":1029,"language":235,"meta":191,"style":191},"# pip install openpyxl\nfrom datetime import date\nfrom openpyxl import Workbook\nfrom openpyxl.chart import LineChart, Reference\n\nwb = Workbook()\nws = wb.active\nws.append([\"Date\", \"Visitors\", \"Signups\"])\nfor r in [(date(2026, 1, 1), 1200, 90), (date(2026, 1, 8), 1340, 110),\n          (date(2026, 1, 15), 1290, 105), (date(2026, 1, 22), 1510, 130),\n          (date(2026, 1, 29), 1625, 142)]:\n    ws.append(r)\n\nchart = LineChart()\nchart.title = \"Visitors vs Signups\"\nchart.y_axis.title = \"Count\"\nchart.x_axis.title = \"Week\"\n\n# Two metric columns (B and C) -> two series.\ndata = Reference(ws, min_col=2, max_col=3, min_row=1, max_row=6)\nchart.add_data(data, titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=6))\n\nprint(\"series:\", len(chart.series))   # -> 2, legend appears automatically\nws.add_chart(chart, \"E2\")\nwb.save(\"line_multi.xlsx\")\n",[193,1031,1032,1036,1046,1056,1066,1070,1078,1086,1102,1152,1196,1220,1224,1228,1236,1245,1254,1262,1266,1271,1312,1324,1354,1358,1378,1386],{"__ignoreMap":191},[196,1033,1034],{"class":61,"line":198},[196,1035,243],{"class":242},[196,1037,1038,1040,1042,1044],{"class":61,"line":246},[196,1039,250],{"class":249},[196,1041,254],{"class":253},[196,1043,257],{"class":249},[196,1045,260],{"class":253},[196,1047,1048,1050,1052,1054],{"class":61,"line":263},[196,1049,250],{"class":249},[196,1051,268],{"class":253},[196,1053,257],{"class":249},[196,1055,273],{"class":253},[196,1057,1058,1060,1062,1064],{"class":61,"line":276},[196,1059,250],{"class":249},[196,1061,651],{"class":253},[196,1063,257],{"class":249},[196,1065,656],{"class":253},[196,1067,1068],{"class":61,"line":283},[196,1069,280],{"emptyLinePlaceholder":279},[196,1071,1072,1074,1076],{"class":61,"line":295},[196,1073,286],{"class":253},[196,1075,289],{"class":249},[196,1077,292],{"class":253},[196,1079,1080,1082,1084],{"class":61,"line":306},[196,1081,298],{"class":253},[196,1083,289],{"class":249},[196,1085,303],{"class":253},[196,1087,1088,1090,1092,1094,1096,1098,1100],{"class":61,"line":317},[196,1089,325],{"class":253},[196,1091,328],{"class":205},[196,1093,331],{"class":253},[196,1095,334],{"class":205},[196,1097,331],{"class":253},[196,1099,339],{"class":205},[196,1101,693],{"class":253},[196,1103,1104,1106,1108,1110,1112,1114,1116,1118,1120,1122,1124,1126,1128,1130,1132,1134,1136,1138,1140,1142,1144,1146,1148,1150],{"class":61,"line":322},[196,1105,513],{"class":249},[196,1107,516],{"class":253},[196,1109,519],{"class":249},[196,1111,704],{"class":253},[196,1113,366],{"class":365},[196,1115,331],{"class":253},[196,1117,371],{"class":365},[196,1119,331],{"class":253},[196,1121,371],{"class":365},[196,1123,378],{"class":253},[196,1125,381],{"class":365},[196,1127,331],{"class":253},[196,1129,386],{"class":365},[196,1131,725],{"class":253},[196,1133,366],{"class":365},[196,1135,331],{"class":253},[196,1137,371],{"class":365},[196,1139,331],{"class":253},[196,1141,405],{"class":365},[196,1143,378],{"class":253},[196,1145,410],{"class":365},[196,1147,331],{"class":253},[196,1149,415],{"class":365},[196,1151,389],{"class":253},[196,1153,1154,1156,1158,1160,1162,1164,1166,1168,1170,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190,1192,1194],{"class":61,"line":348},[196,1155,750],{"class":253},[196,1157,366],{"class":365},[196,1159,331],{"class":253},[196,1161,371],{"class":365},[196,1163,331],{"class":253},[196,1165,433],{"class":365},[196,1167,378],{"class":253},[196,1169,438],{"class":365},[196,1171,331],{"class":253},[196,1173,443],{"class":365},[196,1175,725],{"class":253},[196,1177,366],{"class":365},[196,1179,331],{"class":253},[196,1181,371],{"class":365},[196,1183,331],{"class":253},[196,1185,461],{"class":365},[196,1187,378],{"class":253},[196,1189,466],{"class":365},[196,1191,331],{"class":253},[196,1193,471],{"class":365},[196,1195,389],{"class":253},[196,1197,1198,1200,1202,1204,1206,1208,1210,1212,1214,1216,1218],{"class":61,"line":359},[196,1199,750],{"class":253},[196,1201,366],{"class":365},[196,1203,331],{"class":253},[196,1205,371],{"class":365},[196,1207,331],{"class":253},[196,1209,489],{"class":365},[196,1211,378],{"class":253},[196,1213,494],{"class":365},[196,1215,331],{"class":253},[196,1217,499],{"class":365},[196,1219,815],{"class":253},[196,1221,1222],{"class":61,"line":392},[196,1223,528],{"class":253},[196,1225,1226],{"class":61,"line":420},[196,1227,280],{"emptyLinePlaceholder":279},[196,1229,1230,1232,1234],{"class":61,"line":448},[196,1231,828],{"class":253},[196,1233,289],{"class":249},[196,1235,833],{"class":253},[196,1237,1238,1240,1242],{"class":61,"line":476},[196,1239,838],{"class":253},[196,1241,289],{"class":249},[196,1243,1244],{"class":205}," \"Visitors vs Signups\"\n",[196,1246,1247,1249,1251],{"class":61,"line":504},[196,1248,848],{"class":253},[196,1250,289],{"class":249},[196,1252,1253],{"class":205}," \"Count\"\n",[196,1255,1256,1258,1260],{"class":61,"line":510},[196,1257,858],{"class":253},[196,1259,289],{"class":249},[196,1261,863],{"class":205},[196,1263,1264],{"class":61,"line":525},[196,1265,280],{"emptyLinePlaceholder":279},[196,1267,1268],{"class":61,"line":531},[196,1269,1270],{"class":242},"# Two metric columns (B and C) -> two series.\n",[196,1272,1273,1275,1277,1279,1281,1283,1285,1287,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310],{"class":61,"line":536},[196,1274,872],{"class":253},[196,1276,289],{"class":249},[196,1278,877],{"class":253},[196,1280,881],{"class":880},[196,1282,289],{"class":249},[196,1284,886],{"class":365},[196,1286,331],{"class":253},[196,1288,1289],{"class":880},"max_col",[196,1291,289],{"class":249},[196,1293,155],{"class":365},[196,1295,331],{"class":253},[196,1297,891],{"class":880},[196,1299,289],{"class":249},[196,1301,371],{"class":365},[196,1303,331],{"class":253},[196,1305,900],{"class":880},[196,1307,289],{"class":249},[196,1309,905],{"class":365},[196,1311,588],{"class":253},[196,1313,1314,1316,1318,1320,1322],{"class":61,"line":563},[196,1315,916],{"class":253},[196,1317,919],{"class":880},[196,1319,289],{"class":249},[196,1321,924],{"class":365},[196,1323,588],{"class":253},[196,1325,1326,1329,1331,1333,1335,1337,1339,1341,1343,1345,1347,1349,1351],{"class":61,"line":574},[196,1327,1328],{"class":253},"chart.set_categories(Reference(ws, ",[196,1330,881],{"class":880},[196,1332,289],{"class":249},[196,1334,371],{"class":365},[196,1336,331],{"class":253},[196,1338,891],{"class":880},[196,1340,289],{"class":249},[196,1342,886],{"class":365},[196,1344,331],{"class":253},[196,1346,900],{"class":880},[196,1348,289],{"class":249},[196,1350,905],{"class":365},[196,1352,1353],{"class":253},"))\n",[196,1355,1356],{"class":61,"line":579},[196,1357,280],{"emptyLinePlaceholder":279},[196,1359,1360,1362,1364,1367,1369,1372,1375],{"class":61,"line":591},[196,1361,594],{"class":365},[196,1363,597],{"class":253},[196,1365,1366],{"class":205},"\"series:\"",[196,1368,331],{"class":253},[196,1370,1371],{"class":365},"len",[196,1373,1374],{"class":253},"(chart.series))   ",[196,1376,1377],{"class":242},"# -> 2, legend appears automatically\n",[196,1379,1380,1382,1384],{"class":61,"line":984},[196,1381,976],{"class":253},[196,1383,979],{"class":205},[196,1385,588],{"class":253},[196,1387,1388,1390,1393],{"class":61,"line":994},[196,1389,582],{"class":253},[196,1391,1392],{"class":205},"\"line_multi.xlsx\"",[196,1394,588],{"class":253},[10,1396,1397,1398,1400,1401,1404,1405,1408,1409,1413,1414,1416],{},"With more than one series the legend appears on its own. The key is that the value ",[193,1399,125],{}," spans ",[193,1402,1403],{},"min_col=2"," to ",[193,1406,1407],{},"max_col=3"," while the ",[1410,1411,1412],"strong",{},"category"," ",[193,1415,125],{}," stays on the single date column.",[181,1418,1420],{"id":1419},"step-4-a-date-axis-markers-and-smoothing","Step 4 — A date axis, markers, and smoothing",[10,1422,1423,1426,1427,1430],{},[193,1424,1425],{},"set_categories"," treats your dates as text labels. To get a true date-scaled axis — where uneven gaps between dates are spaced proportionally — switch the x-axis to ",[193,1428,1429],{},"dateAx",". Add markers so each data point is visible, and decide on smoothing.",[186,1432,1434],{"className":233,"code":1433,"language":235,"meta":191,"style":191},"# pip install openpyxl\nfrom datetime import date\nfrom openpyxl import Workbook\nfrom openpyxl.chart import LineChart, Reference\nfrom openpyxl.chart.axis import DateAxis\n\nwb = Workbook()\nws = wb.active\nws.append([\"Date\", \"Visitors\", \"Signups\"])\nfor r in [(date(2026, 1, 1), 1200, 90), (date(2026, 1, 8), 1340, 110),\n          (date(2026, 1, 15), 1290, 105), (date(2026, 1, 22), 1510, 130),\n          (date(2026, 1, 29), 1625, 142)]:\n    ws.append(r)\nfor cell in ws[\"A\"][1:]:\n    cell.number_format = \"yyyy-mm-dd\"\n\nchart = LineChart()\nchart.title = \"Weekly Traffic\"\nchart.x_axis = DateAxis(crossAx=100)   # treat categories as real dates\nchart.x_axis.number_format = \"yyyy-mm-dd\"\nchart.x_axis.majorTimeUnit = \"days\"\nchart.x_axis.title = \"Date\"\nchart.y_axis.title = \"Count\"\n\ndata = Reference(ws, min_col=2, max_col=3, min_row=1, max_row=6)\nchart.add_data(data, titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=6))\n\nfor s in chart.series:\n    s.smooth = False          # straight segments; True for curved\n    s.marker.symbol = \"circle\"\n    s.marker.size = 7\n\nws.add_chart(chart, \"E2\")\nwb.save(\"line_dateaxis.xlsx\")\nprint(\"Saved line_dateaxis.xlsx with a date axis and markers\")\n",[193,1435,1436,1440,1450,1460,1470,1482,1486,1494,1502,1518,1568,1612,1636,1640,1659,1667,1671,1679,1688,1710,1719,1729,1738,1746,1750,1790,1802,1831,1836,1849,1863,1874,1885,1890,1899,1909],{"__ignoreMap":191},[196,1437,1438],{"class":61,"line":198},[196,1439,243],{"class":242},[196,1441,1442,1444,1446,1448],{"class":61,"line":246},[196,1443,250],{"class":249},[196,1445,254],{"class":253},[196,1447,257],{"class":249},[196,1449,260],{"class":253},[196,1451,1452,1454,1456,1458],{"class":61,"line":263},[196,1453,250],{"class":249},[196,1455,268],{"class":253},[196,1457,257],{"class":249},[196,1459,273],{"class":253},[196,1461,1462,1464,1466,1468],{"class":61,"line":276},[196,1463,250],{"class":249},[196,1465,651],{"class":253},[196,1467,257],{"class":249},[196,1469,656],{"class":253},[196,1471,1472,1474,1477,1479],{"class":61,"line":283},[196,1473,250],{"class":249},[196,1475,1476],{"class":253}," openpyxl.chart.axis ",[196,1478,257],{"class":249},[196,1480,1481],{"class":253}," DateAxis\n",[196,1483,1484],{"class":61,"line":295},[196,1485,280],{"emptyLinePlaceholder":279},[196,1487,1488,1490,1492],{"class":61,"line":306},[196,1489,286],{"class":253},[196,1491,289],{"class":249},[196,1493,292],{"class":253},[196,1495,1496,1498,1500],{"class":61,"line":317},[196,1497,298],{"class":253},[196,1499,289],{"class":249},[196,1501,303],{"class":253},[196,1503,1504,1506,1508,1510,1512,1514,1516],{"class":61,"line":322},[196,1505,325],{"class":253},[196,1507,328],{"class":205},[196,1509,331],{"class":253},[196,1511,334],{"class":205},[196,1513,331],{"class":253},[196,1515,339],{"class":205},[196,1517,693],{"class":253},[196,1519,1520,1522,1524,1526,1528,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548,1550,1552,1554,1556,1558,1560,1562,1564,1566],{"class":61,"line":348},[196,1521,513],{"class":249},[196,1523,516],{"class":253},[196,1525,519],{"class":249},[196,1527,704],{"class":253},[196,1529,366],{"class":365},[196,1531,331],{"class":253},[196,1533,371],{"class":365},[196,1535,331],{"class":253},[196,1537,371],{"class":365},[196,1539,378],{"class":253},[196,1541,381],{"class":365},[196,1543,331],{"class":253},[196,1545,386],{"class":365},[196,1547,725],{"class":253},[196,1549,366],{"class":365},[196,1551,331],{"class":253},[196,1553,371],{"class":365},[196,1555,331],{"class":253},[196,1557,405],{"class":365},[196,1559,378],{"class":253},[196,1561,410],{"class":365},[196,1563,331],{"class":253},[196,1565,415],{"class":365},[196,1567,389],{"class":253},[196,1569,1570,1572,1574,1576,1578,1580,1582,1584,1586,1588,1590,1592,1594,1596,1598,1600,1602,1604,1606,1608,1610],{"class":61,"line":359},[196,1571,750],{"class":253},[196,1573,366],{"class":365},[196,1575,331],{"class":253},[196,1577,371],{"class":365},[196,1579,331],{"class":253},[196,1581,433],{"class":365},[196,1583,378],{"class":253},[196,1585,438],{"class":365},[196,1587,331],{"class":253},[196,1589,443],{"class":365},[196,1591,725],{"class":253},[196,1593,366],{"class":365},[196,1595,331],{"class":253},[196,1597,371],{"class":365},[196,1599,331],{"class":253},[196,1601,461],{"class":365},[196,1603,378],{"class":253},[196,1605,466],{"class":365},[196,1607,331],{"class":253},[196,1609,471],{"class":365},[196,1611,389],{"class":253},[196,1613,1614,1616,1618,1620,1622,1624,1626,1628,1630,1632,1634],{"class":61,"line":392},[196,1615,750],{"class":253},[196,1617,366],{"class":365},[196,1619,331],{"class":253},[196,1621,371],{"class":365},[196,1623,331],{"class":253},[196,1625,489],{"class":365},[196,1627,378],{"class":253},[196,1629,494],{"class":365},[196,1631,331],{"class":253},[196,1633,499],{"class":365},[196,1635,815],{"class":253},[196,1637,1638],{"class":61,"line":420},[196,1639,528],{"class":253},[196,1641,1642,1644,1646,1648,1650,1652,1654,1656],{"class":61,"line":448},[196,1643,513],{"class":249},[196,1645,541],{"class":253},[196,1647,519],{"class":249},[196,1649,546],{"class":253},[196,1651,549],{"class":205},[196,1653,552],{"class":253},[196,1655,371],{"class":365},[196,1657,1658],{"class":253},":]:\n",[196,1660,1661,1663,1665],{"class":61,"line":476},[196,1662,566],{"class":253},[196,1664,289],{"class":249},[196,1666,571],{"class":205},[196,1668,1669],{"class":61,"line":504},[196,1670,280],{"emptyLinePlaceholder":279},[196,1672,1673,1675,1677],{"class":61,"line":510},[196,1674,828],{"class":253},[196,1676,289],{"class":249},[196,1678,833],{"class":253},[196,1680,1681,1683,1685],{"class":61,"line":525},[196,1682,838],{"class":253},[196,1684,289],{"class":249},[196,1686,1687],{"class":205}," \"Weekly Traffic\"\n",[196,1689,1690,1693,1695,1698,1701,1703,1705,1707],{"class":61,"line":531},[196,1691,1692],{"class":253},"chart.x_axis ",[196,1694,289],{"class":249},[196,1696,1697],{"class":253}," DateAxis(",[196,1699,1700],{"class":880},"crossAx",[196,1702,289],{"class":249},[196,1704,63],{"class":365},[196,1706,908],{"class":253},[196,1708,1709],{"class":242},"# treat categories as real dates\n",[196,1711,1712,1715,1717],{"class":61,"line":536},[196,1713,1714],{"class":253},"chart.x_axis.number_format ",[196,1716,289],{"class":249},[196,1718,571],{"class":205},[196,1720,1721,1724,1726],{"class":61,"line":563},[196,1722,1723],{"class":253},"chart.x_axis.majorTimeUnit ",[196,1725,289],{"class":249},[196,1727,1728],{"class":205}," \"days\"\n",[196,1730,1731,1733,1735],{"class":61,"line":574},[196,1732,858],{"class":253},[196,1734,289],{"class":249},[196,1736,1737],{"class":205}," \"Date\"\n",[196,1739,1740,1742,1744],{"class":61,"line":579},[196,1741,848],{"class":253},[196,1743,289],{"class":249},[196,1745,1253],{"class":205},[196,1747,1748],{"class":61,"line":591},[196,1749,280],{"emptyLinePlaceholder":279},[196,1751,1752,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778,1780,1782,1784,1786,1788],{"class":61,"line":984},[196,1753,872],{"class":253},[196,1755,289],{"class":249},[196,1757,877],{"class":253},[196,1759,881],{"class":880},[196,1761,289],{"class":249},[196,1763,886],{"class":365},[196,1765,331],{"class":253},[196,1767,1289],{"class":880},[196,1769,289],{"class":249},[196,1771,155],{"class":365},[196,1773,331],{"class":253},[196,1775,891],{"class":880},[196,1777,289],{"class":249},[196,1779,371],{"class":365},[196,1781,331],{"class":253},[196,1783,900],{"class":880},[196,1785,289],{"class":249},[196,1787,905],{"class":365},[196,1789,588],{"class":253},[196,1791,1792,1794,1796,1798,1800],{"class":61,"line":994},[196,1793,916],{"class":253},[196,1795,919],{"class":880},[196,1797,289],{"class":249},[196,1799,924],{"class":365},[196,1801,588],{"class":253},[196,1803,1805,1807,1809,1811,1813,1815,1817,1819,1821,1823,1825,1827,1829],{"class":61,"line":1804},27,[196,1806,1328],{"class":253},[196,1808,881],{"class":880},[196,1810,289],{"class":249},[196,1812,371],{"class":365},[196,1814,331],{"class":253},[196,1816,891],{"class":880},[196,1818,289],{"class":249},[196,1820,886],{"class":365},[196,1822,331],{"class":253},[196,1824,900],{"class":880},[196,1826,289],{"class":249},[196,1828,905],{"class":365},[196,1830,1353],{"class":253},[196,1832,1834],{"class":61,"line":1833},28,[196,1835,280],{"emptyLinePlaceholder":279},[196,1837,1839,1841,1844,1846],{"class":61,"line":1838},29,[196,1840,513],{"class":249},[196,1842,1843],{"class":253}," s ",[196,1845,519],{"class":249},[196,1847,1848],{"class":253}," chart.series:\n",[196,1850,1852,1855,1857,1860],{"class":61,"line":1851},30,[196,1853,1854],{"class":253},"    s.smooth ",[196,1856,289],{"class":249},[196,1858,1859],{"class":365}," False",[196,1861,1862],{"class":242},"          # straight segments; True for curved\n",[196,1864,1866,1869,1871],{"class":61,"line":1865},31,[196,1867,1868],{"class":253},"    s.marker.symbol ",[196,1870,289],{"class":249},[196,1872,1873],{"class":205}," \"circle\"\n",[196,1875,1877,1880,1882],{"class":61,"line":1876},32,[196,1878,1879],{"class":253},"    s.marker.size ",[196,1881,289],{"class":249},[196,1883,1884],{"class":365}," 7\n",[196,1886,1888],{"class":61,"line":1887},33,[196,1889,280],{"emptyLinePlaceholder":279},[196,1891,1893,1895,1897],{"class":61,"line":1892},34,[196,1894,976],{"class":253},[196,1896,979],{"class":205},[196,1898,588],{"class":253},[196,1900,1902,1904,1907],{"class":61,"line":1901},35,[196,1903,582],{"class":253},[196,1905,1906],{"class":205},"\"line_dateaxis.xlsx\"",[196,1908,588],{"class":253},[196,1910,1912,1914,1916,1919],{"class":61,"line":1911},36,[196,1913,594],{"class":365},[196,1915,597],{"class":253},[196,1917,1918],{"class":205},"\"Saved line_dateaxis.xlsx with a date axis and markers\"",[196,1920,588],{"class":253},[10,1922,1923,1926,1927,1929],{},[193,1924,1925],{},"DateAxis"," requires the category cells to hold real dates (Step 1) — that is why the data was written with ",[193,1928,229],{}," objects rather than strings. With a plain text axis, January 1 and January 29 would be equally spaced regardless of the real gap; the date axis spaces them by actual time.",[181,1931,1933],{"id":1932},"common-pitfalls","Common pitfalls",[1935,1936,1937,1953],"table",{},[1938,1939,1940],"thead",{},[1941,1942,1943,1947,1950],"tr",{},[1944,1945,1946],"th",{},"Symptom",[1944,1948,1949],{},"Cause",[1944,1951,1952],{},"Fix",[1954,1955,1956,1974,1991,2006,2030,2050],"tbody",{},[1941,1957,1958,1962,1965],{},[1959,1960,1961],"td",{},"X-axis ignores date spacing",[1959,1963,1964],{},"Dates stored as strings, or axis left as default category axis",[1959,1966,1967,1968,1970,1971,23],{},"Write ",[193,1969,229],{}," values and assign ",[193,1972,1973],{},"chart.x_axis = DateAxis(crossAx=100)",[1941,1975,1976,1979,1985],{},[1959,1977,1978],{},"Only one line appears for two metrics",[1959,1980,1981,1982,1984],{},"Value ",[193,1983,125],{}," covers one column",[1959,1986,1987,1988,1990],{},"Widen it with ",[193,1989,1289],{}," to span every metric column.",[1941,1992,1993,1996,2001],{},[1959,1994,1995],{},"The date column itself plotted as a line",[1959,1997,1998,1999],{},"Date column included in the value ",[193,2000,125],{},[1959,2002,2003,2004,23],{},"Keep dates out of the values — put them only in ",[193,2005,1425],{},[1941,2007,2008,2011,2017],{},[1959,2009,2010],{},"Series named \"Series 1\", \"Series 2\"",[1959,2012,2013,2014,2016],{},"Header row omitted or ",[193,2015,919],{}," not set",[1959,2018,2019,2020,2022,2023,2026,2027,23],{},"Start the value ",[193,2021,125],{}," at ",[193,2024,2025],{},"min_row=1"," and pass ",[193,2028,2029],{},"titles_from_data=True",[1941,2031,2032,2035,2040],{},[1959,2033,2034],{},"Last data point missing",[1959,2036,2037,2039],{},[193,2038,900],{}," off by one",[1959,2041,2042,2043,2046,2047,23],{},"With a header plus N rows, ",[193,2044,2045],{},"max_row = N + 1",". Five rows → ",[193,2048,2049],{},"max_row=6",[1941,2051,2052,2055,2058],{},[1959,2053,2054],{},"No markers on the line",[1959,2056,2057],{},"Markers off by default in openpyxl",[1959,2059,2060,2061,2064],{},"Set ",[193,2062,2063],{},"series.marker.symbol = \"circle\""," per series.",[181,2066,2068],{"id":2067},"performance-and-scale","Performance and scale",[10,2070,2071],{},"The chart definition is tiny and fixed in size; only the underlying cells scale with the data. A line chart stays readable far longer than a bar chart — hundreds of points per series are fine — but past a few thousand points the rendered line turns to noise, so downsample or aggregate (for example to weekly or monthly buckets) before plotting. Writing the series data is the only per-row cost, and it is negligible at report scale.",[181,2073,2075],{"id":2074},"frequently-asked-questions","Frequently asked questions",[10,2077,2078,2081,2082,2084],{},[1410,2079,2080],{},"Why are my dates evenly spaced even though the real gaps differ?"," You are on a category axis, which treats every label as equal. Switch to ",[193,2083,1925],{}," and make sure the cells hold real date values.",[10,2086,2087,2090,2091,2094,2095,2098,2099,2102,2103,23],{},[1410,2088,2089],{},"How do I add a second y-axis for a series on a different scale?"," Create a second ",[193,2092,2093],{},"LineChart",", add the other series to it, set its ",[193,2096,2097],{},"y_axis.axId"," to a new value and ",[193,2100,2101],{},"y_axis.crosses = \"max\"",", then combine with ",[193,2104,2105],{},"chart += chart2",[10,2107,2108,2111,2112,2115,2116,2118,2119,2122],{},[1410,2109,2110],{},"Can I mix a line and bars in one chart?"," Yes — build a ",[193,2113,2114],{},"BarChart"," and a ",[193,2117,2093],{},", then add them together with ",[193,2120,2121],{},"chart1 += chart2",". The combined chart keeps both renderings.",[10,2124,2125,2128,2129,2132,2133,2136],{},[1410,2126,2127],{},"Should I use smoothing?"," Usually no. Smoothing (",[193,2130,2131],{},"series.smooth = True",") draws curves between points that imply values you don't have. Leave it ",[193,2134,2135],{},"False"," for honest reporting.",[181,2138,2140],{"id":2139},"conclusion","Conclusion",[10,2142,2143,2144,2146,2147,2149,2150,2152,2153,2155],{},"A line chart for a report is the same Reference-and-categories pattern as any openpyxl chart, with two time-series specifics: write real ",[193,2145,229],{}," values so you can promote the x-axis to a ",[193,2148,1925],{},", and widen the value ",[193,2151,125],{}," across columns to get one line per metric. Keep the date column out of the values, get ",[193,2154,900],{}," right, and add markers so individual points stay visible.",[181,2157,2159],{"id":2158},"where-to-go-next","Where to go next",[2161,2162,2163,2169,2175],"ul",{},[2164,2165,2166,2167],"li",{},"Main guide: ",[14,2168,17],{"href":16},[2164,2170,2171,2172],{},"The sibling guide: ",[14,2173,2174],{"href":21},"Create a Bar Chart in Excel with openpyxl",[2164,2176,2177,2178],{},"Related: ",[14,2179,2181],{"href":2180},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002F","Building Multi-Sheet Excel Dashboards",[2183,2184,2185],"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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}",{"title":191,"searchDepth":246,"depth":246,"links":2187},[2188,2189,2190,2191,2192,2193,2194,2195,2196,2197],{"id":183,"depth":246,"text":184},{"id":222,"depth":246,"text":223},{"id":608,"depth":246,"text":609},{"id":1015,"depth":246,"text":1016},{"id":1419,"depth":246,"text":1420},{"id":1932,"depth":246,"text":1933},{"id":2067,"depth":246,"text":2068},{"id":2074,"depth":246,"text":2075},{"id":2139,"depth":246,"text":2140},{"id":2158,"depth":246,"text":2159},"2026-06-18","Add an editable line chart to an Excel report with openpyxl: write time-series data, plot multiple series, set a date x-axis, add markers, label axes, and anchor it.","md",[2202,2204,2206,2208],{"q":2080,"a":2203},"You are on a category axis, which treats every label as equal. Switch to DateAxis and make sure the cells hold real date values.",{"q":2089,"a":2205},"Create a second LineChart, add the other series to it, set its y_axis.axId to a new value and y_axis.crosses = \"max\", then combine with chart += chart2.",{"q":2110,"a":2207},"Yes — build a BarChart and a LineChart, then add them together with chart1 += chart2. The combined chart keeps both renderings.",{"q":2127,"a":2209},"Usually no. Smoothing (series.smooth = True) draws curves between points that imply values you don't have. Leave it False for honest reporting.",{},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python",{"title":2213,"description":2214},"Add a Line Chart to an Excel Report in Python","Build an openpyxl line chart for time-series data: plot multiple series from Reference ranges, set a date x-axis, add markers and smoothing, label axes, and anchor it.","add-line-chart-to-excel-report-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python\u002Findex","long_tail","e-oLJ6-9fPBmgUNu5QabGbhlyJEMv6mulr4KK6RbI48",[2220,2223],{"title":17,"path":2221,"stem":2222,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Findex",{"title":2174,"path":2224,"stem":2225,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002Findex",1781795518883]