[{"data":1,"prerenderedAt":2448},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl":2415,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl":2424,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl":2429,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl":2430},{"id":4,"title":5,"body":6,"dateModified":2381,"datePublished":2381,"description":2382,"extension":2383,"faq":2384,"keyword":2393,"kind":2394,"meta":2395,"navigation":301,"path":2404,"seo":2405,"slug":2408,"stem":2409,"topics":2410,"type":2413,"__hash__":2414},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl\u002Findex.md","Create a Stacked Column Chart with openpyxl",{"type":7,"value":8,"toc":2368},"minimark",[9,24,215,220,244,247,523,527,545,762,772,776,779,969,977,981,984,1112,1118,1219,1223,1226,1326,1333,1337,1348,1618,1625,1759,1763,1766,2227,2238,2242,2278,2282,2303,2307,2313,2319,2325,2331,2335,2364],[10,11,12,13,17,18,23],"p",{},"A stacked column chart answers two questions in one picture: how big is each total, and what is it made of. Quarterly sales by region, headcount by department, costs by category — each column is a period, each coloured layer a component. openpyxl builds it from the ordinary ",[14,15,16],"code",{},"BarChart"," with a few settings that are easy to miss, and a small trick adds the one thing readers always ask for: the total on top of every stack. This guide, part of ",[19,20,22],"a",{"href":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",", builds the chart step by step and checks the result by rendering it.",[25,26,34,35,34,39,34,43,34,51,34,60,34,67,34,72,34,76,34,81,34,85,34,93,34,98,34,102,34,106,34,109,34,113,34,116,34,120,34,123,34,127,34,130,34,136,34,140,34,144,34,148,34,154,34,157,34,161,34,166,34,171,34,175,34,179,34,183,34,188,34,192,34,196,34,199,34,204,34,208,34,211],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"4 0 748 280","img",[30,31],"sc-chart-t","sc-chart-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:748px;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},"Quarterly sales stacked by region",[40,41,42],"desc",{"id":31},"Four stacked columns for Q1 to Q4. North contributes 120, 140, 110 and 160, South 80, 90, 100 and 95, and East 40, 55, 70 and 80, giving totals of 240, 285, 280 and 335 shown above each stack.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"4","0","748","280","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":55,"rx":56,"fill":57,"stroke":58,"style":59},"64.0","18.0","14.0","3","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[61,62,66],"text",{"x":63,"y":64,"style":65},"84.0","29.5","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","North",[44,68],{"x":69,"y":54,"width":55,"height":55,"rx":56,"fill":70,"stroke":71,"style":59},"146.9","#d9f4f1","var(--teal,#0f9488)",[61,73,75],{"x":74,"y":64,"style":65},"166.9","South",[44,77],{"x":78,"y":54,"width":55,"height":55,"rx":56,"fill":79,"stroke":80,"style":59},"231.2","#fdefd8","var(--gold,#b4740a)",[61,82,84],{"x":83,"y":64,"style":65},"251.2","East",[86,87],"line",{"x1":88,"y1":89,"x2":90,"y2":89,"stroke":91,"style":92},"54.0","242.0","736.0","var(--line,#cdd5e6)","stroke-width:1px",[61,94,47],{"x":95,"y":96,"style":97},"31.0","246.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[86,99],{"x1":88,"y1":100,"x2":90,"y2":100,"stroke":91,"style":101},"194.5","stroke-width:1px;stroke-dasharray:5 4",[61,103,105],{"x":95,"y":104,"style":97},"198.5","100",[86,107],{"x1":88,"y1":108,"x2":90,"y2":108,"stroke":91,"style":101},"147.0",[61,110,112],{"x":95,"y":111,"style":97},"151.0","200",[86,114],{"x1":88,"y1":115,"x2":90,"y2":115,"stroke":91,"style":101},"99.5",[61,117,119],{"x":95,"y":118,"style":97},"103.5","300",[86,121],{"x1":88,"y1":122,"x2":90,"y2":122,"stroke":91,"style":101},"52.0",[61,124,126],{"x":95,"y":125,"style":97},"56.0","400",[86,128],{"x1":88,"y1":89,"x2":90,"y2":89,"stroke":129,"style":59},"var(--muted,#5b6780)",[61,131,135],{"x":132,"y":133,"style":134},"139.3","260.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Q1",[61,137,139],{"x":138,"y":133,"style":134},"309.8","Q2",[61,141,143],{"x":142,"y":133,"style":134},"480.3","Q3",[61,145,147],{"x":146,"y":133,"style":134},"650.8","Q4",[44,149],{"x":150,"y":151,"width":152,"height":153,"rx":56,"fill":57,"stroke":58,"style":59},"110.3","185.0","58.0","57.0",[44,155],{"x":150,"y":108,"width":152,"height":156,"rx":56,"fill":70,"stroke":71,"style":59},"38.0",[44,158],{"x":150,"y":159,"width":152,"height":160,"rx":56,"fill":79,"stroke":80,"style":59},"128.0","19.0",[61,162,165],{"x":132,"y":163,"style":164},"122.0","font-size:10.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","240",[44,167],{"x":168,"y":169,"width":152,"height":170,"rx":56,"fill":57,"stroke":58,"style":59},"280.8","175.5","66.5",[44,172],{"x":168,"y":173,"width":152,"height":174,"rx":56,"fill":70,"stroke":71,"style":59},"132.8","42.8",[44,176],{"x":168,"y":177,"width":152,"height":178,"rx":56,"fill":79,"stroke":80,"style":59},"106.6","26.1",[61,180,182],{"x":138,"y":181,"style":164},"100.6","285",[44,184],{"x":185,"y":186,"width":152,"height":187,"rx":56,"fill":57,"stroke":58,"style":59},"451.3","189.8","52.3",[44,189],{"x":185,"y":190,"width":152,"height":191,"rx":56,"fill":70,"stroke":71,"style":59},"142.2","47.5",[44,193],{"x":185,"y":194,"width":152,"height":195,"rx":56,"fill":79,"stroke":80,"style":59},"109.0","33.2",[61,197,49],{"x":142,"y":198,"style":164},"103.0",[44,200],{"x":201,"y":202,"width":152,"height":203,"rx":56,"fill":57,"stroke":58,"style":59},"621.8","166.0","76.0",[44,205],{"x":201,"y":206,"width":152,"height":207,"rx":56,"fill":70,"stroke":71,"style":59},"120.9","45.1",[44,209],{"x":201,"y":210,"width":152,"height":156,"rx":56,"fill":79,"stroke":80,"style":59},"82.9",[61,212,214],{"x":146,"y":213,"style":164},"76.9","335",[216,217,219],"h2",{"id":218},"prerequisites","Prerequisites",[221,222,227],"pre",{"className":223,"code":224,"language":225,"meta":226,"style":226},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,228,229],{"__ignoreMap":226},[230,231,233,237,241],"span",{"class":86,"line":232},1,[230,234,236],{"class":235},"sMTad","pip",[230,238,240],{"class":239},"srMev"," install",[230,242,243],{"class":239}," openpyxl\n",[10,245,246],{},"The data, with a total column that the chart will use for its labels:",[221,248,252],{"className":249,"code":250,"language":251,"meta":226,"style":226},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, LineChart, Reference\nfrom openpyxl.chart.label import DataLabelList\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\nws.append([\"Quarter\", \"North\", \"South\", \"East\", \"Total\"])\nfor row, (q, north, south, east) in enumerate(\n        [(\"Q1\", 120, 80, 40), (\"Q2\", 140, 90, 55), (\"Q3\", 110, 100, 70), (\"Q4\", 160, 95, 80)], start=2):\n    ws.append([q, north, south, east, f\"=SUM(B{row}:D{row})\"])\n","python",[14,253,254,270,283,296,303,315,326,337,370,389,487],{"__ignoreMap":226},[230,255,256,260,264,267],{"class":86,"line":232},[230,257,259],{"class":258},"s-kum","from",[230,261,263],{"class":262},"skGVy"," openpyxl ",[230,265,266],{"class":258},"import",[230,268,269],{"class":262}," Workbook\n",[230,271,273,275,278,280],{"class":86,"line":272},2,[230,274,259],{"class":258},[230,276,277],{"class":262}," openpyxl.chart ",[230,279,266],{"class":258},[230,281,282],{"class":262}," BarChart, LineChart, Reference\n",[230,284,286,288,291,293],{"class":86,"line":285},3,[230,287,259],{"class":258},[230,289,290],{"class":262}," openpyxl.chart.label ",[230,292,266],{"class":258},[230,294,295],{"class":262}," DataLabelList\n",[230,297,299],{"class":86,"line":298},4,[230,300,302],{"emptyLinePlaceholder":301},true,"\n",[230,304,306,309,312],{"class":86,"line":305},5,[230,307,308],{"class":262},"wb ",[230,310,311],{"class":258},"=",[230,313,314],{"class":262}," Workbook()\n",[230,316,318,321,323],{"class":86,"line":317},6,[230,319,320],{"class":262},"ws ",[230,322,311],{"class":258},[230,324,325],{"class":262}," wb.active\n",[230,327,329,332,334],{"class":86,"line":328},7,[230,330,331],{"class":262},"ws.title ",[230,333,311],{"class":258},[230,335,336],{"class":239}," \"Sales\"\n",[230,338,340,343,346,349,352,354,357,359,362,364,367],{"class":86,"line":339},8,[230,341,342],{"class":262},"ws.append([",[230,344,345],{"class":239},"\"Quarter\"",[230,347,348],{"class":262},", ",[230,350,351],{"class":239},"\"North\"",[230,353,348],{"class":262},[230,355,356],{"class":239},"\"South\"",[230,358,348],{"class":262},[230,360,361],{"class":239},"\"East\"",[230,363,348],{"class":262},[230,365,366],{"class":239},"\"Total\"",[230,368,369],{"class":262},"])\n",[230,371,373,376,379,382,386],{"class":86,"line":372},9,[230,374,375],{"class":258},"for",[230,377,378],{"class":262}," row, (q, north, south, east) ",[230,380,381],{"class":258},"in",[230,383,385],{"class":384},"sP0c6"," enumerate",[230,387,388],{"class":262},"(\n",[230,390,392,395,398,400,403,405,408,410,413,416,419,421,424,426,429,431,434,436,439,441,444,446,448,450,453,455,458,460,463,465,468,470,472,475,479,481,484],{"class":86,"line":391},10,[230,393,394],{"class":262},"        [(",[230,396,397],{"class":239},"\"Q1\"",[230,399,348],{"class":262},[230,401,402],{"class":384},"120",[230,404,348],{"class":262},[230,406,407],{"class":384},"80",[230,409,348],{"class":262},[230,411,412],{"class":384},"40",[230,414,415],{"class":262},"), (",[230,417,418],{"class":239},"\"Q2\"",[230,420,348],{"class":262},[230,422,423],{"class":384},"140",[230,425,348],{"class":262},[230,427,428],{"class":384},"90",[230,430,348],{"class":262},[230,432,433],{"class":384},"55",[230,435,415],{"class":262},[230,437,438],{"class":239},"\"Q3\"",[230,440,348],{"class":262},[230,442,443],{"class":384},"110",[230,445,348],{"class":262},[230,447,105],{"class":384},[230,449,348],{"class":262},[230,451,452],{"class":384},"70",[230,454,415],{"class":262},[230,456,457],{"class":239},"\"Q4\"",[230,459,348],{"class":262},[230,461,462],{"class":384},"160",[230,464,348],{"class":262},[230,466,467],{"class":384},"95",[230,469,348],{"class":262},[230,471,407],{"class":384},[230,473,474],{"class":262},")], ",[230,476,478],{"class":477},"sa561","start",[230,480,311],{"class":258},[230,482,483],{"class":384},"2",[230,485,486],{"class":262},"):\n",[230,488,490,493,496,499,503,506,509,512,514,516,518,521],{"class":86,"line":489},11,[230,491,492],{"class":262},"    ws.append([q, north, south, east, ",[230,494,495],{"class":258},"f",[230,497,498],{"class":239},"\"=SUM(B",[230,500,502],{"class":501},"sSjpA","{",[230,504,505],{"class":262},"row",[230,507,508],{"class":501},"}",[230,510,511],{"class":239},":D",[230,513,502],{"class":501},[230,515,505],{"class":262},[230,517,508],{"class":501},[230,519,520],{"class":239},")\"",[230,522,369],{"class":262},[216,524,526],{"id":525},"step-1-the-stacked-chart","Step 1 — The stacked chart",[10,528,529,530,532,533,536,537,540,541,544],{},"A stacked column chart is a ",[14,531,16],{}," with three settings: ",[14,534,535],{},"type=\"col\""," for vertical columns, ",[14,538,539],{},"grouping=\"stacked\""," to stack the series, and ",[14,542,543],{},"overlap=100"," so the series share one position instead of sitting side by side:",[221,546,548],{"className":249,"code":547,"language":251,"meta":226,"style":226},"chart = BarChart()\nchart.x_axis.delete = False  # Excel hides axes unless this is set\nchart.y_axis.delete = False\nchart.type = \"col\"\nchart.grouping = \"stacked\"\nchart.overlap = 100\nchart.gapWidth = 60\ndata = Reference(ws, min_col=2, max_col=4, min_row=1, max_row=5)\nchart.add_data(data, titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=5))\nchart.title = \"Sales by region\"\nchart.height, chart.width = 8, 14\nprint(len(chart.series))              # 3 — one layer per region\n",[14,549,550,560,574,584,594,604,614,624,673,688,718,728,744],{"__ignoreMap":226},[230,551,552,555,557],{"class":86,"line":232},[230,553,554],{"class":262},"chart ",[230,556,311],{"class":258},[230,558,559],{"class":262}," BarChart()\n",[230,561,562,565,567,570],{"class":86,"line":272},[230,563,564],{"class":262},"chart.x_axis.delete ",[230,566,311],{"class":258},[230,568,569],{"class":384}," False",[230,571,573],{"class":572},"s-wDw","  # Excel hides axes unless this is set\n",[230,575,576,579,581],{"class":86,"line":285},[230,577,578],{"class":262},"chart.y_axis.delete ",[230,580,311],{"class":258},[230,582,583],{"class":384}," False\n",[230,585,586,589,591],{"class":86,"line":298},[230,587,588],{"class":262},"chart.type ",[230,590,311],{"class":258},[230,592,593],{"class":239}," \"col\"\n",[230,595,596,599,601],{"class":86,"line":305},[230,597,598],{"class":262},"chart.grouping ",[230,600,311],{"class":258},[230,602,603],{"class":239}," \"stacked\"\n",[230,605,606,609,611],{"class":86,"line":317},[230,607,608],{"class":262},"chart.overlap ",[230,610,311],{"class":258},[230,612,613],{"class":384}," 100\n",[230,615,616,619,621],{"class":86,"line":328},[230,617,618],{"class":262},"chart.gapWidth ",[230,620,311],{"class":258},[230,622,623],{"class":384}," 60\n",[230,625,626,629,631,634,637,639,641,643,646,648,650,652,655,657,660,662,665,667,670],{"class":86,"line":339},[230,627,628],{"class":262},"data ",[230,630,311],{"class":258},[230,632,633],{"class":262}," Reference(ws, ",[230,635,636],{"class":477},"min_col",[230,638,311],{"class":258},[230,640,483],{"class":384},[230,642,348],{"class":262},[230,644,645],{"class":477},"max_col",[230,647,311],{"class":258},[230,649,46],{"class":384},[230,651,348],{"class":262},[230,653,654],{"class":477},"min_row",[230,656,311],{"class":258},[230,658,659],{"class":384},"1",[230,661,348],{"class":262},[230,663,664],{"class":477},"max_row",[230,666,311],{"class":258},[230,668,669],{"class":384},"5",[230,671,672],{"class":262},")\n",[230,674,675,678,681,683,686],{"class":86,"line":372},[230,676,677],{"class":262},"chart.add_data(data, ",[230,679,680],{"class":477},"titles_from_data",[230,682,311],{"class":258},[230,684,685],{"class":384},"True",[230,687,672],{"class":262},[230,689,690,693,695,697,699,701,703,705,707,709,711,713,715],{"class":86,"line":391},[230,691,692],{"class":262},"chart.set_categories(Reference(ws, ",[230,694,636],{"class":477},[230,696,311],{"class":258},[230,698,659],{"class":384},[230,700,348],{"class":262},[230,702,654],{"class":477},[230,704,311],{"class":258},[230,706,483],{"class":384},[230,708,348],{"class":262},[230,710,664],{"class":477},[230,712,311],{"class":258},[230,714,669],{"class":384},[230,716,717],{"class":262},"))\n",[230,719,720,723,725],{"class":86,"line":489},[230,721,722],{"class":262},"chart.title ",[230,724,311],{"class":258},[230,726,727],{"class":239}," \"Sales by region\"\n",[230,729,731,734,736,739,741],{"class":86,"line":730},12,[230,732,733],{"class":262},"chart.height, chart.width ",[230,735,311],{"class":258},[230,737,738],{"class":384}," 8",[230,740,348],{"class":262},[230,742,743],{"class":384},"14\n",[230,745,747,750,753,756,759],{"class":86,"line":746},13,[230,748,749],{"class":384},"print",[230,751,752],{"class":262},"(",[230,754,755],{"class":384},"len",[230,757,758],{"class":262},"(chart.series))              ",[230,760,761],{"class":572},"# 3 — one layer per region\n",[10,763,764,767,768,771],{},[14,765,766],{},"titles_from_data=True"," takes each series name from the header row, which is why the data reference starts at row 1. Series are stacked in the order they are added, first at the bottom, so put the most important or most stable component first — it then sits on the common baseline, where lengths are easiest to compare. ",[14,769,770],{},"gapWidth"," is the space between stacks as a percentage of a column's width; 60 gives chunkier columns than the default 150.",[216,773,775],{"id":774},"step-2-colours-and-labels-inside-each-segment","Step 2 — Colours and labels inside each segment",[10,777,778],{},"Give each series a fill and a value label centred in its segment. The labels need the other label parts switched off, or Excel may show series and category names as well:",[221,780,782],{"className":249,"code":781,"language":251,"meta":226,"style":226},"COLOURS = [\"A5B4FC\", \"5EEAD4\", \"FCD34D\"]\n\ndef value_labels(position):\n    labels = DataLabelList()\n    labels.showVal = True\n    labels.position = position\n    for part in (\"showSerName\", \"showCatName\", \"showLegendKey\", \"showPercent\"):\n        setattr(labels, part, False)\n    return labels\n\nfor series, colour in zip(chart.series, COLOURS):\n    series.graphicalProperties.solidFill = colour\n    series.graphicalProperties.line.solidFill = colour\n    series.dLbls = value_labels(\"ctr\")\n",[14,783,784,811,815,827,837,847,857,890,903,911,915,934,944,953],{"__ignoreMap":226},[230,785,786,789,792,795,798,800,803,805,808],{"class":86,"line":232},[230,787,788],{"class":384},"COLOURS",[230,790,791],{"class":258}," =",[230,793,794],{"class":262}," [",[230,796,797],{"class":239},"\"A5B4FC\"",[230,799,348],{"class":262},[230,801,802],{"class":239},"\"5EEAD4\"",[230,804,348],{"class":262},[230,806,807],{"class":239},"\"FCD34D\"",[230,809,810],{"class":262},"]\n",[230,812,813],{"class":86,"line":272},[230,814,302],{"emptyLinePlaceholder":301},[230,816,817,820,824],{"class":86,"line":285},[230,818,819],{"class":258},"def",[230,821,823],{"class":822},"s_Opv"," value_labels",[230,825,826],{"class":262},"(position):\n",[230,828,829,832,834],{"class":86,"line":298},[230,830,831],{"class":262},"    labels ",[230,833,311],{"class":258},[230,835,836],{"class":262}," DataLabelList()\n",[230,838,839,842,844],{"class":86,"line":305},[230,840,841],{"class":262},"    labels.showVal ",[230,843,311],{"class":258},[230,845,846],{"class":384}," True\n",[230,848,849,852,854],{"class":86,"line":317},[230,850,851],{"class":262},"    labels.position ",[230,853,311],{"class":258},[230,855,856],{"class":262}," position\n",[230,858,859,862,865,867,870,873,875,878,880,883,885,888],{"class":86,"line":328},[230,860,861],{"class":258},"    for",[230,863,864],{"class":262}," part ",[230,866,381],{"class":258},[230,868,869],{"class":262}," (",[230,871,872],{"class":239},"\"showSerName\"",[230,874,348],{"class":262},[230,876,877],{"class":239},"\"showCatName\"",[230,879,348],{"class":262},[230,881,882],{"class":239},"\"showLegendKey\"",[230,884,348],{"class":262},[230,886,887],{"class":239},"\"showPercent\"",[230,889,486],{"class":262},[230,891,892,895,898,901],{"class":86,"line":339},[230,893,894],{"class":384},"        setattr",[230,896,897],{"class":262},"(labels, part, ",[230,899,900],{"class":384},"False",[230,902,672],{"class":262},[230,904,905,908],{"class":86,"line":372},[230,906,907],{"class":258},"    return",[230,909,910],{"class":262}," labels\n",[230,912,913],{"class":86,"line":391},[230,914,302],{"emptyLinePlaceholder":301},[230,916,917,919,922,924,927,930,932],{"class":86,"line":489},[230,918,375],{"class":258},[230,920,921],{"class":262}," series, colour ",[230,923,381],{"class":258},[230,925,926],{"class":384}," zip",[230,928,929],{"class":262},"(chart.series, ",[230,931,788],{"class":384},[230,933,486],{"class":262},[230,935,936,939,941],{"class":86,"line":730},[230,937,938],{"class":262},"    series.graphicalProperties.solidFill ",[230,940,311],{"class":258},[230,942,943],{"class":262}," colour\n",[230,945,946,949,951],{"class":86,"line":746},[230,947,948],{"class":262},"    series.graphicalProperties.line.solidFill ",[230,950,311],{"class":258},[230,952,943],{"class":262},[230,954,956,959,961,964,967],{"class":86,"line":955},14,[230,957,958],{"class":262},"    series.dLbls ",[230,960,311],{"class":258},[230,962,963],{"class":262}," value_labels(",[230,965,966],{"class":239},"\"ctr\"",[230,968,672],{"class":262},[10,970,971,972,976],{},"Light fills keep the default black label text readable; with dark fills, the labels need a white font, which means setting the label text properties as well. Choosing and styling chart colours is covered further in ",[19,973,975],{"href":974},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl\u002F","Style Chart Colours and Data Labels with openpyxl",".",[216,978,980],{"id":979},"step-3-a-total-above-every-stack","Step 3 — A total above every stack",[10,982,983],{},"Stacked charts have no built-in total label. The standard trick is to plot the total column as a line series on the same chart, hide the line, and label its points from above. Because the total equals the height of the stack, each label lands just on top of its column:",[221,985,987],{"className":249,"code":986,"language":251,"meta":226,"style":226},"totals = LineChart()\ntotals.x_axis.delete = False\ntotals.y_axis.delete = False\ntotals.add_data(Reference(ws, min_col=5, min_row=1, max_row=5), titles_from_data=True)\nline = totals.series[0]\nline.graphicalProperties.line.noFill = True\nline.marker.symbol = \"none\"\nline.dLbls = value_labels(\"t\")\nchart += totals\n",[14,988,989,999,1008,1017,1055,1069,1078,1088,1102],{"__ignoreMap":226},[230,990,991,994,996],{"class":86,"line":232},[230,992,993],{"class":262},"totals ",[230,995,311],{"class":258},[230,997,998],{"class":262}," LineChart()\n",[230,1000,1001,1004,1006],{"class":86,"line":272},[230,1002,1003],{"class":262},"totals.x_axis.delete ",[230,1005,311],{"class":258},[230,1007,583],{"class":384},[230,1009,1010,1013,1015],{"class":86,"line":285},[230,1011,1012],{"class":262},"totals.y_axis.delete ",[230,1014,311],{"class":258},[230,1016,583],{"class":384},[230,1018,1019,1022,1024,1026,1028,1030,1032,1034,1036,1038,1040,1042,1044,1047,1049,1051,1053],{"class":86,"line":298},[230,1020,1021],{"class":262},"totals.add_data(Reference(ws, ",[230,1023,636],{"class":477},[230,1025,311],{"class":258},[230,1027,669],{"class":384},[230,1029,348],{"class":262},[230,1031,654],{"class":477},[230,1033,311],{"class":258},[230,1035,659],{"class":384},[230,1037,348],{"class":262},[230,1039,664],{"class":477},[230,1041,311],{"class":258},[230,1043,669],{"class":384},[230,1045,1046],{"class":262},"), ",[230,1048,680],{"class":477},[230,1050,311],{"class":258},[230,1052,685],{"class":384},[230,1054,672],{"class":262},[230,1056,1057,1060,1062,1065,1067],{"class":86,"line":305},[230,1058,1059],{"class":262},"line ",[230,1061,311],{"class":258},[230,1063,1064],{"class":262}," totals.series[",[230,1066,47],{"class":384},[230,1068,810],{"class":262},[230,1070,1071,1074,1076],{"class":86,"line":317},[230,1072,1073],{"class":262},"line.graphicalProperties.line.noFill ",[230,1075,311],{"class":258},[230,1077,846],{"class":384},[230,1079,1080,1083,1085],{"class":86,"line":328},[230,1081,1082],{"class":262},"line.marker.symbol ",[230,1084,311],{"class":258},[230,1086,1087],{"class":239}," \"none\"\n",[230,1089,1090,1093,1095,1097,1100],{"class":86,"line":339},[230,1091,1092],{"class":262},"line.dLbls ",[230,1094,311],{"class":258},[230,1096,963],{"class":262},[230,1098,1099],{"class":239},"\"t\"",[230,1101,672],{"class":262},[230,1103,1104,1106,1109],{"class":86,"line":372},[230,1105,554],{"class":262},[230,1107,1108],{"class":258},"+=",[230,1110,1111],{"class":262}," totals\n",[10,1113,1114,1117],{},[14,1115,1116],{},"chart += totals"," combines the two charts on the same axes. The line series draws nothing — no line, no markers — and only its labels remain. The total column in the sheet is a formula, so the labels stay correct if a regional figure is edited.",[25,1119,34,1125,34,1128,34,1131,34,1136,34,1144,34,1155,34,1161,34,1165,34,1168,34,1175,34,1180,34,1184,34,1187,34,1193,34,1198,34,1201,34,1204,34,1210,34,1215],{"viewBox":1120,"role":28,"ariaLabelledBy":1121,"xmlns":32,"style":1124},"84 0 592 434",[1122,1123],"sc-layers-t","sc-layers-d","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,1126,1127],{"id":1122},"The layers that make one labelled stack",[40,1129,1130],{"id":1123},"The stack for Q4 is built from a North segment of 160, a South segment of 95 and an East segment of 80, each labelled in the centre, with an invisible line series whose point sits at the stack's total of 335 and carries the label above it.",[44,1132],{"x":1133,"y":47,"width":1134,"height":1135,"fill":50},"84","592","434",[44,1137],{"x":1138,"y":1139,"width":1140,"height":1141,"rx":1142,"fill":50,"stroke":91,"style":1143},"100.0","16.0","560.0","93.0","12","stroke-width:2px",[1145,1146,1148],"g",{"transform":1147},"translate(368.0,29.0) scale(1.000)",[1149,1150],"path",{"d":1151,"fill":1152,"stroke":1153,"style":1154},"M4 20h16 M4 20V4 M6 16l4-5 4 3 6-7","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[61,1156,1160],{"x":1157,"y":1158,"style":1159},"380.0","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Total line series (invisible)",[61,1162,1164],{"x":1157,"y":1163,"style":134},"95.0","label 335 above the point",[44,1166],{"x":1138,"y":1167,"width":1140,"height":1141,"rx":1142,"fill":79,"stroke":80,"style":1143},"119.0",[1145,1169,1171],{"transform":1170},"translate(368.0,132.0) scale(1.000)",[1149,1172],{"d":1173,"fill":1152,"stroke":1174,"style":1154},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--gold-ink,#7a4e06)",[61,1176,1179],{"x":1157,"y":1177,"style":1178},"178.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","East: 80",[61,1181,1183],{"x":1157,"y":1182,"style":134},"198.0","label centred",[44,1185],{"x":1138,"y":1186,"width":1140,"height":1141,"rx":1142,"fill":70,"stroke":71,"style":1143},"222.0",[1145,1188,1190],{"transform":1189},"translate(368.0,235.0) scale(1.000)",[1149,1191],{"d":1173,"fill":1152,"stroke":1192,"style":1154},"var(--teal-ink,#0b6157)",[61,1194,1197],{"x":1157,"y":1195,"style":1196},"281.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","South: 95",[61,1199,1183],{"x":1157,"y":1200,"style":134},"301.0",[44,1202],{"x":1138,"y":1203,"width":1140,"height":1141,"rx":1142,"fill":57,"stroke":58,"style":1143},"325.0",[1145,1205,1207],{"transform":1206},"translate(368.0,338.0) scale(1.000)",[1149,1208],{"d":1173,"fill":1152,"stroke":1209,"style":1154},"var(--brand-strong,#4338ca)",[61,1211,1214],{"x":1157,"y":1212,"style":1213},"384.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","North: 160",[61,1216,1218],{"x":1157,"y":1217,"style":134},"404.0","on the baseline",[216,1220,1222],{"id":1221},"step-4-hide-the-total-from-the-legend","Step 4 — Hide the total from the legend",[10,1224,1225],{},"The invisible series still appears in the legend as \"Total\" with no symbol. Delete its legend entry by index — the fourth series, index 3:",[221,1227,1229],{"className":249,"code":1228,"language":251,"meta":226,"style":226},"from openpyxl.chart.legend import LegendEntry\n\nchart.legend.position = \"b\"\nchart.legend.legendEntry = [LegendEntry(idx=3, delete=True)]\nws.add_chart(chart, \"G2\")\nwb.save(\"stacked.xlsx\")\nprint([e.idx for e in chart.legend.legendEntry])      # [3]\n",[14,1230,1231,1243,1247,1257,1286,1296,1306],{"__ignoreMap":226},[230,1232,1233,1235,1238,1240],{"class":86,"line":232},[230,1234,259],{"class":258},[230,1236,1237],{"class":262}," openpyxl.chart.legend ",[230,1239,266],{"class":258},[230,1241,1242],{"class":262}," LegendEntry\n",[230,1244,1245],{"class":86,"line":272},[230,1246,302],{"emptyLinePlaceholder":301},[230,1248,1249,1252,1254],{"class":86,"line":285},[230,1250,1251],{"class":262},"chart.legend.position ",[230,1253,311],{"class":258},[230,1255,1256],{"class":239}," \"b\"\n",[230,1258,1259,1262,1264,1267,1270,1272,1274,1276,1279,1281,1283],{"class":86,"line":298},[230,1260,1261],{"class":262},"chart.legend.legendEntry ",[230,1263,311],{"class":258},[230,1265,1266],{"class":262}," [LegendEntry(",[230,1268,1269],{"class":477},"idx",[230,1271,311],{"class":258},[230,1273,56],{"class":384},[230,1275,348],{"class":262},[230,1277,1278],{"class":477},"delete",[230,1280,311],{"class":258},[230,1282,685],{"class":384},[230,1284,1285],{"class":262},")]\n",[230,1287,1288,1291,1294],{"class":86,"line":305},[230,1289,1290],{"class":262},"ws.add_chart(chart, ",[230,1292,1293],{"class":239},"\"G2\"",[230,1295,672],{"class":262},[230,1297,1298,1301,1304],{"class":86,"line":317},[230,1299,1300],{"class":262},"wb.save(",[230,1302,1303],{"class":239},"\"stacked.xlsx\"",[230,1305,672],{"class":262},[230,1307,1308,1310,1313,1315,1318,1320,1323],{"class":86,"line":328},[230,1309,749],{"class":384},[230,1311,1312],{"class":262},"([e.idx ",[230,1314,375],{"class":258},[230,1316,1317],{"class":262}," e ",[230,1319,381],{"class":258},[230,1321,1322],{"class":262}," chart.legend.legendEntry])      ",[230,1324,1325],{"class":572},"# [3]\n",[10,1327,1328,1329,1332],{},"Rendering the saved workbook with LibreOffice shows four stacks with centred values — 120, 80 and 40 in Q1 — and totals of 240, 285, 280 and 335 above them. Put the legend at the bottom (",[14,1330,1331],{},"\"b\"",") so the plot area keeps its full width.",[216,1334,1336],{"id":1335},"step-5-percent-stacks-and-horizontal-bars","Step 5 — Percent stacks and horizontal bars",[10,1338,1339,1340,1343,1344,1347],{},"Two variations use the same code. ",[14,1341,1342],{},"grouping=\"percentStacked\""," scales every stack to 100% and shows each region's share, at the cost of hiding the totals — drop the total series for this version. ",[14,1345,1346],{},"type=\"bar\""," turns the columns into horizontal bars, which read better when category labels are long:",[221,1349,1351],{"className":249,"code":1350,"language":251,"meta":226,"style":226},"share = BarChart()\nshare.x_axis.delete = False\nshare.y_axis.delete = False\nshare.type = \"col\"\nshare.grouping = \"percentStacked\"\nshare.overlap = 100\nshare.add_data(data, titles_from_data=True)\nshare.set_categories(Reference(ws, min_col=1, min_row=2, max_row=5))\nshare.title = \"Share of sales by region\"\nshare.y_axis.number_format = \"0%\"\nws.add_chart(share, \"G20\")\n\nbars = BarChart()\nbars.x_axis.delete = False\nbars.y_axis.delete = False\nbars.type = \"bar\"\nbars.grouping = \"stacked\"\nbars.overlap = 100\nbars.add_data(data, titles_from_data=True)\nbars.set_categories(Reference(ws, min_col=1, min_row=2, max_row=5))\nws.add_chart(bars, \"G38\")\nwb.save(\"stacked.xlsx\")\nprint(share.grouping, bars.type)     # percentStacked bar\n",[14,1352,1353,1362,1371,1380,1389,1399,1408,1421,1450,1460,1470,1480,1484,1493,1502,1512,1523,1533,1543,1557,1587,1598,1607],{"__ignoreMap":226},[230,1354,1355,1358,1360],{"class":86,"line":232},[230,1356,1357],{"class":262},"share ",[230,1359,311],{"class":258},[230,1361,559],{"class":262},[230,1363,1364,1367,1369],{"class":86,"line":272},[230,1365,1366],{"class":262},"share.x_axis.delete ",[230,1368,311],{"class":258},[230,1370,583],{"class":384},[230,1372,1373,1376,1378],{"class":86,"line":285},[230,1374,1375],{"class":262},"share.y_axis.delete ",[230,1377,311],{"class":258},[230,1379,583],{"class":384},[230,1381,1382,1385,1387],{"class":86,"line":298},[230,1383,1384],{"class":262},"share.type ",[230,1386,311],{"class":258},[230,1388,593],{"class":239},[230,1390,1391,1394,1396],{"class":86,"line":305},[230,1392,1393],{"class":262},"share.grouping ",[230,1395,311],{"class":258},[230,1397,1398],{"class":239}," \"percentStacked\"\n",[230,1400,1401,1404,1406],{"class":86,"line":317},[230,1402,1403],{"class":262},"share.overlap ",[230,1405,311],{"class":258},[230,1407,613],{"class":384},[230,1409,1410,1413,1415,1417,1419],{"class":86,"line":328},[230,1411,1412],{"class":262},"share.add_data(data, ",[230,1414,680],{"class":477},[230,1416,311],{"class":258},[230,1418,685],{"class":384},[230,1420,672],{"class":262},[230,1422,1423,1426,1428,1430,1432,1434,1436,1438,1440,1442,1444,1446,1448],{"class":86,"line":339},[230,1424,1425],{"class":262},"share.set_categories(Reference(ws, ",[230,1427,636],{"class":477},[230,1429,311],{"class":258},[230,1431,659],{"class":384},[230,1433,348],{"class":262},[230,1435,654],{"class":477},[230,1437,311],{"class":258},[230,1439,483],{"class":384},[230,1441,348],{"class":262},[230,1443,664],{"class":477},[230,1445,311],{"class":258},[230,1447,669],{"class":384},[230,1449,717],{"class":262},[230,1451,1452,1455,1457],{"class":86,"line":372},[230,1453,1454],{"class":262},"share.title ",[230,1456,311],{"class":258},[230,1458,1459],{"class":239}," \"Share of sales by region\"\n",[230,1461,1462,1465,1467],{"class":86,"line":391},[230,1463,1464],{"class":262},"share.y_axis.number_format ",[230,1466,311],{"class":258},[230,1468,1469],{"class":239}," \"0%\"\n",[230,1471,1472,1475,1478],{"class":86,"line":489},[230,1473,1474],{"class":262},"ws.add_chart(share, ",[230,1476,1477],{"class":239},"\"G20\"",[230,1479,672],{"class":262},[230,1481,1482],{"class":86,"line":730},[230,1483,302],{"emptyLinePlaceholder":301},[230,1485,1486,1489,1491],{"class":86,"line":746},[230,1487,1488],{"class":262},"bars ",[230,1490,311],{"class":258},[230,1492,559],{"class":262},[230,1494,1495,1498,1500],{"class":86,"line":955},[230,1496,1497],{"class":262},"bars.x_axis.delete ",[230,1499,311],{"class":258},[230,1501,583],{"class":384},[230,1503,1505,1508,1510],{"class":86,"line":1504},15,[230,1506,1507],{"class":262},"bars.y_axis.delete ",[230,1509,311],{"class":258},[230,1511,583],{"class":384},[230,1513,1515,1518,1520],{"class":86,"line":1514},16,[230,1516,1517],{"class":262},"bars.type ",[230,1519,311],{"class":258},[230,1521,1522],{"class":239}," \"bar\"\n",[230,1524,1526,1529,1531],{"class":86,"line":1525},17,[230,1527,1528],{"class":262},"bars.grouping ",[230,1530,311],{"class":258},[230,1532,603],{"class":239},[230,1534,1536,1539,1541],{"class":86,"line":1535},18,[230,1537,1538],{"class":262},"bars.overlap ",[230,1540,311],{"class":258},[230,1542,613],{"class":384},[230,1544,1546,1549,1551,1553,1555],{"class":86,"line":1545},19,[230,1547,1548],{"class":262},"bars.add_data(data, ",[230,1550,680],{"class":477},[230,1552,311],{"class":258},[230,1554,685],{"class":384},[230,1556,672],{"class":262},[230,1558,1560,1563,1565,1567,1569,1571,1573,1575,1577,1579,1581,1583,1585],{"class":86,"line":1559},20,[230,1561,1562],{"class":262},"bars.set_categories(Reference(ws, ",[230,1564,636],{"class":477},[230,1566,311],{"class":258},[230,1568,659],{"class":384},[230,1570,348],{"class":262},[230,1572,654],{"class":477},[230,1574,311],{"class":258},[230,1576,483],{"class":384},[230,1578,348],{"class":262},[230,1580,664],{"class":477},[230,1582,311],{"class":258},[230,1584,669],{"class":384},[230,1586,717],{"class":262},[230,1588,1590,1593,1596],{"class":86,"line":1589},21,[230,1591,1592],{"class":262},"ws.add_chart(bars, ",[230,1594,1595],{"class":239},"\"G38\"",[230,1597,672],{"class":262},[230,1599,1601,1603,1605],{"class":86,"line":1600},22,[230,1602,1300],{"class":262},[230,1604,1303],{"class":239},[230,1606,672],{"class":262},[230,1608,1610,1612,1615],{"class":86,"line":1609},23,[230,1611,749],{"class":384},[230,1613,1614],{"class":262},"(share.grouping, bars.type)     ",[230,1616,1617],{"class":572},"# percentStacked bar\n",[10,1619,1620,1621,1624],{},"With horizontal bars the first category is drawn at the bottom; set ",[14,1622,1623],{},"bars.y_axis.scaling.orientation = \"maxMin\""," on the category axis if Q1 should appear at the top, as it does in the table.",[25,1626,34,1632,34,1635,34,1638,34,1642,34,1648,34,1655,34,1661,34,1666,34,1672,34,1679,34,1683,34,1688,34,1692,34,1699,34,1703,34,1708,34,1711,34,1716,34,1721,34,1725,34,1729,34,1734,34,1737,34,1742,34,1745,34,1750,34,1753],{"viewBox":1627,"role":28,"ariaLabelledBy":1628,"xmlns":32,"style":1631},"4 0 752 233",[1629,1630],"sc-modes-t","sc-modes-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,1633,1634],{"id":1629},"Stacked or 100% stacked",[40,1636,1637],{"id":1630},"A stacked chart shows absolute values, so both the totals and the components can be compared, but only the bottom segment sits on a common baseline. A 100 percent stacked chart shows each component's share of every total, making mix changes visible, but hides whether the totals grew or shrank.",[44,1639],{"x":46,"y":47,"width":1640,"height":1641,"fill":50},"752","233",[44,1643],{"x":1644,"y":1139,"width":1645,"height":1646,"rx":1647,"fill":57,"stroke":58,"style":1143},"20.0","351.0","201.0","14",[1145,1649,1651],{"transform":1650},"translate(182.5,29.0) scale(1.083)",[1149,1652],{"d":1653,"fill":1152,"stroke":1209,"style":1654},"M4 20h16 M7 17v-5 M12 17V7 M17 17v-8","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[61,1656,1660],{"x":1657,"y":1658,"style":1659},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","grouping = stacked",[86,1662],{"x1":1663,"y1":1664,"x2":1665,"y2":1664,"stroke":58,"style":92},"36.0","91.0","355.0",[61,1667,1671],{"x":1668,"y":1669,"style":1670},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","totals are visible",[1145,1673,1675],{"transform":1674},"translate(38.5,99.5) scale(0.625)",[1149,1676],{"d":1677,"fill":1152,"stroke":71,"style":1678},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[61,1680,1682],{"x":1668,"y":1681,"style":1670},"137.0","components in real units",[1145,1684,1686],{"transform":1685},"translate(38.5,125.5) scale(0.625)",[1149,1687],{"d":1677,"fill":1152,"stroke":71,"style":1678},[61,1689,1691],{"x":1668,"y":1690,"style":1670},"163.0","upper segments hard to compare",[1145,1693,1695],{"transform":1694},"translate(38.5,151.5) scale(0.625)",[1149,1696],{"d":1697,"fill":1152,"stroke":1698,"style":1678},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[61,1700,1702],{"x":1668,"y":1701,"style":1670},"189.0","total labels make sense",[1145,1704,1706],{"transform":1705},"translate(38.5,177.5) scale(0.625)",[1149,1707],{"d":1677,"fill":1152,"stroke":71,"style":1678},[44,1709],{"x":1710,"y":1139,"width":1645,"height":1646,"rx":1647,"fill":70,"stroke":71,"style":1143},"389.0",[1145,1712,1714],{"transform":1713},"translate(551.5,29.0) scale(1.083)",[1149,1715],{"d":1173,"fill":1152,"stroke":1192,"style":1654},[61,1717,1720],{"x":1718,"y":1658,"style":1719},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","grouping = percentStacked",[86,1722],{"x1":1723,"y1":1664,"x2":1724,"y2":1664,"stroke":71,"style":92},"405.0","724.0",[61,1726,1728],{"x":1727,"y":1669,"style":1670},"429.0","shares easy to compare",[1145,1730,1732],{"transform":1731},"translate(407.5,99.5) scale(0.625)",[1149,1733],{"d":1677,"fill":1152,"stroke":71,"style":1678},[61,1735,1736],{"x":1727,"y":1681,"style":1670},"mix changes stand out",[1145,1738,1740],{"transform":1739},"translate(407.5,125.5) scale(0.625)",[1149,1741],{"d":1677,"fill":1152,"stroke":71,"style":1678},[61,1743,1744],{"x":1727,"y":1690,"style":1670},"totals are hidden",[1145,1746,1748],{"transform":1747},"translate(407.5,151.5) scale(0.625)",[1149,1749],{"d":1697,"fill":1152,"stroke":1698,"style":1678},[61,1751,1752],{"x":1727,"y":1701,"style":1670},"format the axis as 0%",[1145,1754,1756],{"transform":1755},"translate(407.5,177.5) scale(0.625)",[1149,1757],{"d":1758,"fill":1152,"stroke":80,"style":1678},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4",[216,1760,1762],{"id":1761},"step-6-from-long-data-in-pandas-to-a-stacked-chart","Step 6 — From long data in pandas to a stacked chart",[10,1764,1765],{},"Sales data usually arrives long — one row per quarter and region — while a stacked chart needs one column per component. Pivot first, add the total, write the table, then point the chart at it:",[221,1767,1769],{"className":249,"code":1768,"language":251,"meta":226,"style":226},"import pandas as pd\n\nlong = pd.DataFrame({\n    \"quarter\": [\"Q1\", \"Q1\", \"Q1\", \"Q2\", \"Q2\", \"Q2\"],\n    \"region\": [\"North\", \"South\", \"East\"] * 2,\n    \"sales\": [120, 80, 40, 140, 90, 55],\n})\nwide = long.pivot_table(index=\"quarter\", columns=\"region\", values=\"sales\", aggfunc=\"sum\")\nwide = wide[[\"North\", \"South\", \"East\"]]                  # stacking order, bottom first\nwide[\"Total\"] = wide.sum(axis=1)\n\nwith pd.ExcelWriter(\"stacked_pd.xlsx\", engine=\"openpyxl\") as writer:\n    wide.to_excel(writer, sheet_name=\"Sales\")\n    sheet = writer.sheets[\"Sales\"]\n    rows = len(wide) + 1\n    pd_chart = BarChart()\n    pd_chart.x_axis.delete = False\n    pd_chart.y_axis.delete = False\n    pd_chart.type, pd_chart.grouping, pd_chart.overlap = \"col\", \"stacked\", 100\n    pd_chart.add_data(Reference(sheet, min_col=2, max_col=4, min_row=1, max_row=rows), titles_from_data=True)\n    pd_chart.set_categories(Reference(sheet, min_col=1, min_row=2, max_row=rows))\n    sheet.add_chart(pd_chart, \"G2\")\nprint(wide.to_dict(\"index\")[\"Q2\"])      # {'North': 140, 'South': 90, 'East': 55, 'Total': 285}\n",[14,1770,1771,1784,1788,1798,1831,1860,1891,1896,1949,1974,1997,2001,2030,2045,2059,2078,2087,2096,2105,2125,2169,2197,2206],{"__ignoreMap":226},[230,1772,1773,1775,1778,1781],{"class":86,"line":232},[230,1774,266],{"class":258},[230,1776,1777],{"class":262}," pandas ",[230,1779,1780],{"class":258},"as",[230,1782,1783],{"class":262}," pd\n",[230,1785,1786],{"class":86,"line":272},[230,1787,302],{"emptyLinePlaceholder":301},[230,1789,1790,1793,1795],{"class":86,"line":285},[230,1791,1792],{"class":477},"long",[230,1794,791],{"class":258},[230,1796,1797],{"class":262}," pd.DataFrame({\n",[230,1799,1800,1803,1806,1808,1810,1812,1814,1816,1818,1820,1822,1824,1826,1828],{"class":86,"line":298},[230,1801,1802],{"class":239},"    \"quarter\"",[230,1804,1805],{"class":262},": [",[230,1807,397],{"class":239},[230,1809,348],{"class":262},[230,1811,397],{"class":239},[230,1813,348],{"class":262},[230,1815,397],{"class":239},[230,1817,348],{"class":262},[230,1819,418],{"class":239},[230,1821,348],{"class":262},[230,1823,418],{"class":239},[230,1825,348],{"class":262},[230,1827,418],{"class":239},[230,1829,1830],{"class":262},"],\n",[230,1832,1833,1836,1838,1840,1842,1844,1846,1848,1851,1854,1857],{"class":86,"line":305},[230,1834,1835],{"class":239},"    \"region\"",[230,1837,1805],{"class":262},[230,1839,351],{"class":239},[230,1841,348],{"class":262},[230,1843,356],{"class":239},[230,1845,348],{"class":262},[230,1847,361],{"class":239},[230,1849,1850],{"class":262},"] ",[230,1852,1853],{"class":258},"*",[230,1855,1856],{"class":384}," 2",[230,1858,1859],{"class":262},",\n",[230,1861,1862,1865,1867,1869,1871,1873,1875,1877,1879,1881,1883,1885,1887,1889],{"class":86,"line":317},[230,1863,1864],{"class":239},"    \"sales\"",[230,1866,1805],{"class":262},[230,1868,402],{"class":384},[230,1870,348],{"class":262},[230,1872,407],{"class":384},[230,1874,348],{"class":262},[230,1876,412],{"class":384},[230,1878,348],{"class":262},[230,1880,423],{"class":384},[230,1882,348],{"class":262},[230,1884,428],{"class":384},[230,1886,348],{"class":262},[230,1888,433],{"class":384},[230,1890,1830],{"class":262},[230,1892,1893],{"class":86,"line":328},[230,1894,1895],{"class":262},"})\n",[230,1897,1898,1901,1903,1906,1909,1912,1914,1917,1919,1922,1924,1927,1929,1932,1934,1937,1939,1942,1944,1947],{"class":86,"line":339},[230,1899,1900],{"class":262},"wide ",[230,1902,311],{"class":258},[230,1904,1905],{"class":477}," long",[230,1907,1908],{"class":262},".pivot_table(",[230,1910,1911],{"class":477},"index",[230,1913,311],{"class":258},[230,1915,1916],{"class":239},"\"quarter\"",[230,1918,348],{"class":262},[230,1920,1921],{"class":477},"columns",[230,1923,311],{"class":258},[230,1925,1926],{"class":239},"\"region\"",[230,1928,348],{"class":262},[230,1930,1931],{"class":477},"values",[230,1933,311],{"class":258},[230,1935,1936],{"class":239},"\"sales\"",[230,1938,348],{"class":262},[230,1940,1941],{"class":477},"aggfunc",[230,1943,311],{"class":258},[230,1945,1946],{"class":239},"\"sum\"",[230,1948,672],{"class":262},[230,1950,1951,1953,1955,1958,1960,1962,1964,1966,1968,1971],{"class":86,"line":372},[230,1952,1900],{"class":262},[230,1954,311],{"class":258},[230,1956,1957],{"class":262}," wide[[",[230,1959,351],{"class":239},[230,1961,348],{"class":262},[230,1963,356],{"class":239},[230,1965,348],{"class":262},[230,1967,361],{"class":239},[230,1969,1970],{"class":262},"]]                  ",[230,1972,1973],{"class":572},"# stacking order, bottom first\n",[230,1975,1976,1979,1981,1983,1985,1988,1991,1993,1995],{"class":86,"line":391},[230,1977,1978],{"class":262},"wide[",[230,1980,366],{"class":239},[230,1982,1850],{"class":262},[230,1984,311],{"class":258},[230,1986,1987],{"class":262}," wide.sum(",[230,1989,1990],{"class":477},"axis",[230,1992,311],{"class":258},[230,1994,659],{"class":384},[230,1996,672],{"class":262},[230,1998,1999],{"class":86,"line":489},[230,2000,302],{"emptyLinePlaceholder":301},[230,2002,2003,2006,2009,2012,2014,2017,2019,2022,2025,2027],{"class":86,"line":730},[230,2004,2005],{"class":258},"with",[230,2007,2008],{"class":262}," pd.ExcelWriter(",[230,2010,2011],{"class":239},"\"stacked_pd.xlsx\"",[230,2013,348],{"class":262},[230,2015,2016],{"class":477},"engine",[230,2018,311],{"class":258},[230,2020,2021],{"class":239},"\"openpyxl\"",[230,2023,2024],{"class":262},") ",[230,2026,1780],{"class":258},[230,2028,2029],{"class":262}," writer:\n",[230,2031,2032,2035,2038,2040,2043],{"class":86,"line":746},[230,2033,2034],{"class":262},"    wide.to_excel(writer, ",[230,2036,2037],{"class":477},"sheet_name",[230,2039,311],{"class":258},[230,2041,2042],{"class":239},"\"Sales\"",[230,2044,672],{"class":262},[230,2046,2047,2050,2052,2055,2057],{"class":86,"line":955},[230,2048,2049],{"class":262},"    sheet ",[230,2051,311],{"class":258},[230,2053,2054],{"class":262}," writer.sheets[",[230,2056,2042],{"class":239},[230,2058,810],{"class":262},[230,2060,2061,2064,2066,2069,2072,2075],{"class":86,"line":1504},[230,2062,2063],{"class":262},"    rows ",[230,2065,311],{"class":258},[230,2067,2068],{"class":384}," len",[230,2070,2071],{"class":262},"(wide) ",[230,2073,2074],{"class":258},"+",[230,2076,2077],{"class":384}," 1\n",[230,2079,2080,2083,2085],{"class":86,"line":1514},[230,2081,2082],{"class":262},"    pd_chart ",[230,2084,311],{"class":258},[230,2086,559],{"class":262},[230,2088,2089,2092,2094],{"class":86,"line":1525},[230,2090,2091],{"class":262},"    pd_chart.x_axis.delete ",[230,2093,311],{"class":258},[230,2095,583],{"class":384},[230,2097,2098,2101,2103],{"class":86,"line":1535},[230,2099,2100],{"class":262},"    pd_chart.y_axis.delete ",[230,2102,311],{"class":258},[230,2104,583],{"class":384},[230,2106,2107,2110,2112,2115,2117,2120,2122],{"class":86,"line":1545},[230,2108,2109],{"class":262},"    pd_chart.type, pd_chart.grouping, pd_chart.overlap ",[230,2111,311],{"class":258},[230,2113,2114],{"class":239}," \"col\"",[230,2116,348],{"class":262},[230,2118,2119],{"class":239},"\"stacked\"",[230,2121,348],{"class":262},[230,2123,2124],{"class":384},"100\n",[230,2126,2127,2130,2132,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2161,2163,2165,2167],{"class":86,"line":1559},[230,2128,2129],{"class":262},"    pd_chart.add_data(Reference(sheet, ",[230,2131,636],{"class":477},[230,2133,311],{"class":258},[230,2135,483],{"class":384},[230,2137,348],{"class":262},[230,2139,645],{"class":477},[230,2141,311],{"class":258},[230,2143,46],{"class":384},[230,2145,348],{"class":262},[230,2147,654],{"class":477},[230,2149,311],{"class":258},[230,2151,659],{"class":384},[230,2153,348],{"class":262},[230,2155,664],{"class":477},[230,2157,311],{"class":258},[230,2159,2160],{"class":262},"rows), ",[230,2162,680],{"class":477},[230,2164,311],{"class":258},[230,2166,685],{"class":384},[230,2168,672],{"class":262},[230,2170,2171,2174,2176,2178,2180,2182,2184,2186,2188,2190,2192,2194],{"class":86,"line":1589},[230,2172,2173],{"class":262},"    pd_chart.set_categories(Reference(sheet, ",[230,2175,636],{"class":477},[230,2177,311],{"class":258},[230,2179,659],{"class":384},[230,2181,348],{"class":262},[230,2183,654],{"class":477},[230,2185,311],{"class":258},[230,2187,483],{"class":384},[230,2189,348],{"class":262},[230,2191,664],{"class":477},[230,2193,311],{"class":258},[230,2195,2196],{"class":262},"rows))\n",[230,2198,2199,2202,2204],{"class":86,"line":1600},[230,2200,2201],{"class":262},"    sheet.add_chart(pd_chart, ",[230,2203,1293],{"class":239},[230,2205,672],{"class":262},[230,2207,2208,2210,2213,2216,2219,2221,2224],{"class":86,"line":1609},[230,2209,749],{"class":384},[230,2211,2212],{"class":262},"(wide.to_dict(",[230,2214,2215],{"class":239},"\"index\"",[230,2217,2218],{"class":262},")[",[230,2220,418],{"class":239},[230,2222,2223],{"class":262},"])      ",[230,2225,2226],{"class":572},"# {'North': 140, 'South': 90, 'East': 55, 'Total': 285}\n",[10,2228,2229,2230,2233,2234,976],{},"Selecting the columns explicitly after the pivot fixes the stacking order; a pivot would otherwise sort regions alphabetically and put East at the bottom. Writing the total as a value here is fine because the table is regenerated each run; in a workbook people edit, write it as a ",[14,2231,2232],{},"SUM"," formula as in the first example. Pivoting is covered in more depth in ",[19,2235,2237],{"href":2236},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcreate-pivot-table-from-excel-with-pandas\u002F","Create a Pivot Table from Excel with pandas",[216,2239,2241],{"id":2240},"common-pitfalls","Common pitfalls",[2243,2244,2245,2256,2262,2272],"ul",{},[2246,2247,2248,2252,2253,2255],"li",{},[2249,2250,2251],"strong",{},"Colours that change between charts."," If North is blue in one chart and green in the next, readers compare the wrong layers. Fix one colour per component across the whole report, as the ",[14,2254,788],{}," list does.",[2246,2257,2258,2261],{},[2249,2259,2260],{},"Too many layers."," Beyond four or five series a stack becomes unreadable. Group small components into \"Other\".",[2246,2263,2264,2267,2268,976],{},[2249,2265,2266],{},"Negative values."," Stacked columns with negative components stack below the axis separately from the positive ones, which is rarely what readers expect. Use a clustered chart or a waterfall for movements; see ",[19,2269,2271],{"href":2270},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python\u002F","Build a Waterfall Chart in Excel with Python",[2246,2273,2274,2277],{},[2249,2275,2276],{},"Totals from a different range."," The total series must cover the same rows as the stacked data, or the labels float at the wrong heights.",[216,2279,2281],{"id":2280},"conclusion","Conclusion",[10,2283,2284,2285,2287,2288,348,2290,2292,2293,2295,2296,2299,2300,2302],{},"A stacked column chart in openpyxl is a ",[14,2286,16],{}," with ",[14,2289,535],{},[14,2291,539],{}," and ",[14,2294,543],{},", one series per component in the order they should stack. Colour each series, label segments with centred values, add totals above each stack with an invisible line series that has top-positioned labels, and delete that series' legend entry. Switch to ",[14,2297,2298],{},"percentStacked"," for shares or ",[14,2301,1346],{}," for long category names.",[216,2304,2306],{"id":2305},"frequently-asked-questions","Frequently asked questions",[10,2308,2309,2312],{},[2249,2310,2311],{},"How do I make a stacked column chart with openpyxl?","\nCreate a BarChart, set type to \"col\", grouping to \"stacked\" and overlap to 100, then add the data columns as series. Each series becomes one layer of every stack.",[10,2314,2315,2318],{},[2249,2316,2317],{},"Why set overlap to 100 for a stacked chart?","\nExcel itself writes an overlap of 100 for stacked charts, meaning the series share one column position. Setting it explicitly matches what Excel expects; LibreOffice ignores the setting, so a chart can look right there and still be drawn differently in Excel without it.",[10,2320,2321,2324],{},[2249,2322,2323],{},"How do I show the total on top of each stack?","\nAdd a total column to the sheet, plot it as a line series on the same chart, hide the line and turn on its data labels positioned above the points. The labels then sit on top of each stack.",[10,2326,2327,2330],{},[2249,2328,2329],{},"What is the difference between stacked and percentStacked?","\nstacked shows absolute values, so taller stacks mean bigger totals. percentStacked scales every stack to 100 percent, showing each series' share while hiding the totals.",[216,2332,2334],{"id":2333},"related","Related",[2243,2336,2337,2343,2350,2357],{},[2246,2338,2339,2340,2342],{},"Up one level: ",[19,2341,22],{"href":21}," — charts written from Python.",[2246,2344,2345,2349],{},[19,2346,2348],{"href":2347},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002F","Create a Bar Chart in Excel with openpyxl"," — the unstacked starting point.",[2246,2351,2352,2356],{},[19,2353,2355],{"href":2354},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-combo-chart-with-a-secondary-axis-in-openpyxl\u002F","Add a Combo Chart with a Secondary Axis in openpyxl"," — combining chart types, as the total series does.",[2246,2358,2359,2363],{},[19,2360,2362],{"href":2361},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-trendline-to-an-excel-chart-with-python\u002F","Add a Trendline to an Excel Chart with Python"," — showing direction over time.",[2365,2366,2367],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":226,"searchDepth":272,"depth":272,"links":2369},[2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380],{"id":218,"depth":272,"text":219},{"id":525,"depth":272,"text":526},{"id":774,"depth":272,"text":775},{"id":979,"depth":272,"text":980},{"id":1221,"depth":272,"text":1222},{"id":1335,"depth":272,"text":1336},{"id":1761,"depth":272,"text":1762},{"id":2240,"depth":272,"text":2241},{"id":2280,"depth":272,"text":2281},{"id":2305,"depth":272,"text":2306},{"id":2333,"depth":272,"text":2334},"2026-09-23","Build stacked and 100% stacked column charts in Excel with openpyxl — grouping and overlap settings, series colours, value labels inside each segment, a total label above every stack using an invisible line series, hiding its legend entry, and horizontal stacked bars.","md",[2385,2387,2389,2391],{"q":2311,"a":2386},"Create a BarChart, set type to \"col\", grouping to \"stacked\" and overlap to 100, then add the data columns as series. Each series becomes one layer of every stack.",{"q":2317,"a":2388},"Excel itself writes an overlap of 100 for stacked charts, meaning the series share one column position. Setting it explicitly matches what Excel expects; LibreOffice ignores the setting, so a chart can look right there and still be drawn differently in Excel without it.",{"q":2323,"a":2390},"Add a total column to the sheet, plot it as a line series on the same chart, hide the line and turn on its data labels positioned above the points. The labels then sit on top of each stack.",{"q":2329,"a":2392},"stacked shows absolute values, so taller stacks mean bigger totals. percentStacked scales every stack to 100 percent, showing each series' share while hiding the totals.","create a stacked column chart with openpyxl",null,{"breadcrumb":2396},[2397,2400,2403],{"name":2398,"item":2399},"Home","\u002F",{"name":2401,"item":2402},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":22,"item":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl",{"title":2406,"description":2407},"Create a Stacked Column Chart with openpyxl (with totals)","Stacked column charts in openpyxl — BarChart grouping stacked and percentStacked, overlap 100, gapWidth, series fill colours, centred data labels, total labels on top of each stack with a hidden line series, legend entries and stacked bar charts.","create-a-stacked-column-chart-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl\u002Findex",[2411,2412],"charts-and-images","openpyxl","how-to","A6aWqOIN_B6lcp1XC5PX19qQ4KVbn7wtyeQl3cTLu-Y",[2416,2420],{"title":2417,"path":2418,"stem":2419,"children":-1},"Create a Slope Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-slope-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-slope-chart-in-excel-with-python\u002Findex",{"title":2421,"path":2422,"stem":2423,"children":-1},"Create a Step Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-step-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-step-chart-in-excel-with-python\u002Findex",[2425,2427],{"path":2426,"title":22},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2428,"title":2401},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2431,2434,2437,2440,2443,2446],{"path":2432,"title":2433,"kind":2413},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs","Build a Dashboard Sheet with Charts from Multiple Tabs",{"path":2435,"title":2436,"kind":2413},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-rolling-12-month-chart-on-an-excel-dashboard","Build a Rolling 12-Month Chart on an Excel Dashboard",{"path":2438,"title":2439,"kind":2413},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template","Keep Charts and Images When Filling an Excel Template",{"path":2441,"title":2442,"kind":2413},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter","Add a Chart to an Excel File with xlsxwriter",{"path":2444,"title":2445,"kind":2413},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-sparklines-to-an-excel-report-with-xlsxwriter","Add Sparklines to an Excel Report with xlsxwriter",{"path":2447,"title":2271,"kind":2413},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python",1790972111136]