[{"data":1,"prerenderedAt":2181},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python":2147,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python":2156,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python":2161,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python":2162},{"id":4,"title":5,"body":6,"dateModified":2113,"datePublished":2113,"description":2114,"extension":2115,"faq":2116,"keyword":2125,"kind":2126,"meta":2127,"navigation":96,"path":2136,"seo":2137,"slug":2140,"stem":2141,"topics":2142,"type":2145,"__hash__":2146},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python\u002Findex.md","Create a Butterfly Chart in Excel with Python",{"type":7,"value":8,"toc":2099},"minimark",[9,19,24,53,64,68,407,410,687,691,902,924,928,931,974,985,1142,1146,1149,1174,1184,1188,1381,1388,1392,1793,1799,1935,1939,1958,1962,2016,2020,2034,2038,2044,2050,2056,2062,2066,2095],[10,11,12,13,18],"p",{},"Comparing two groups across the same categories — women and men by age band, this year and last year by product, survey agreement and disagreement per question — is clearest when the bars grow outward from a shared centre line: one group to the left, the other to the right. That shape is called a butterfly, tornado or population-pyramid chart. Excel has no dedicated type, but a horizontal bar chart becomes one with three small tricks: plot one series as negative numbers, make both series share the same row, and use a number format that hides the minus signs. openpyxl can set all of it, so the chart is generated as a native, editable Excel chart. This page builds one step by step and renders it to check the details. It belongs to ",[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",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pillow\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," openpyxl",[35,51,52],{"class":45}," pillow\n",[10,54,55,56,59,60,63],{},"LibreOffice (",[32,57,58],{},"soffice",") and ",[32,61,62],{},"pdftoppm"," render the chart in Step 6; they are optional. The example shows survey respondents by age band and gender.",[20,65,67],{"id":66},"step-1-lay-out-the-data-with-a-negative-helper-column","Step 1 — Lay out the data with a negative helper column",[25,69,73],{"className":70,"code":71,"language":72,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Survey\"\nws.append([\"Age band\", \"Women\", \"Men\", \"Men (plotted)\"])\nrows = [(\"18–24\", 112, 98), (\"25–34\", 240, 251), (\"35–44\", 205, 230),\n        (\"45–54\", 176, 190), (\"55–64\", 120, 108), (\"65+\", 64, 41)]\nfor r, (band, women, men) in enumerate(rows, start=2):\n    ws.append([band, women, men])\n    ws.cell(r, 4, f\"=-C{r}\")\nws.column_dimensions[\"A\"].width = 11\nprint([[c.value for c in row] for row in ws.iter_rows(min_row=1, max_row=3)])\n","python",[32,74,75,91,98,110,121,132,160,219,271,301,307,340,357],{"__ignoreMap":30},[35,76,77,81,85,88],{"class":37,"line":38},[35,78,80],{"class":79},"s-kum","from",[35,82,84],{"class":83},"skGVy"," openpyxl ",[35,86,87],{"class":79},"import",[35,89,90],{"class":83}," Workbook\n",[35,92,94],{"class":37,"line":93},2,[35,95,97],{"emptyLinePlaceholder":96},true,"\n",[35,99,101,104,107],{"class":37,"line":100},3,[35,102,103],{"class":83},"wb ",[35,105,106],{"class":79},"=",[35,108,109],{"class":83}," Workbook()\n",[35,111,113,116,118],{"class":37,"line":112},4,[35,114,115],{"class":83},"ws ",[35,117,106],{"class":79},[35,119,120],{"class":83}," wb.active\n",[35,122,124,127,129],{"class":37,"line":123},5,[35,125,126],{"class":83},"ws.title ",[35,128,106],{"class":79},[35,130,131],{"class":45}," \"Survey\"\n",[35,133,135,138,141,144,147,149,152,154,157],{"class":37,"line":134},6,[35,136,137],{"class":83},"ws.append([",[35,139,140],{"class":45},"\"Age band\"",[35,142,143],{"class":83},", ",[35,145,146],{"class":45},"\"Women\"",[35,148,143],{"class":83},[35,150,151],{"class":45},"\"Men\"",[35,153,143],{"class":83},[35,155,156],{"class":45},"\"Men (plotted)\"",[35,158,159],{"class":83},"])\n",[35,161,163,166,168,171,174,176,180,182,185,188,191,193,196,198,201,203,206,208,211,213,216],{"class":37,"line":162},7,[35,164,165],{"class":83},"rows ",[35,167,106],{"class":79},[35,169,170],{"class":83}," [(",[35,172,173],{"class":45},"\"18–24\"",[35,175,143],{"class":83},[35,177,179],{"class":178},"sP0c6","112",[35,181,143],{"class":83},[35,183,184],{"class":178},"98",[35,186,187],{"class":83},"), (",[35,189,190],{"class":45},"\"25–34\"",[35,192,143],{"class":83},[35,194,195],{"class":178},"240",[35,197,143],{"class":83},[35,199,200],{"class":178},"251",[35,202,187],{"class":83},[35,204,205],{"class":45},"\"35–44\"",[35,207,143],{"class":83},[35,209,210],{"class":178},"205",[35,212,143],{"class":83},[35,214,215],{"class":178},"230",[35,217,218],{"class":83},"),\n",[35,220,222,225,228,230,233,235,238,240,243,245,248,250,253,255,258,260,263,265,268],{"class":37,"line":221},8,[35,223,224],{"class":83},"        (",[35,226,227],{"class":45},"\"45–54\"",[35,229,143],{"class":83},[35,231,232],{"class":178},"176",[35,234,143],{"class":83},[35,236,237],{"class":178},"190",[35,239,187],{"class":83},[35,241,242],{"class":45},"\"55–64\"",[35,244,143],{"class":83},[35,246,247],{"class":178},"120",[35,249,143],{"class":83},[35,251,252],{"class":178},"108",[35,254,187],{"class":83},[35,256,257],{"class":45},"\"65+\"",[35,259,143],{"class":83},[35,261,262],{"class":178},"64",[35,264,143],{"class":83},[35,266,267],{"class":178},"41",[35,269,270],{"class":83},")]\n",[35,272,274,277,280,283,286,289,293,295,298],{"class":37,"line":273},9,[35,275,276],{"class":79},"for",[35,278,279],{"class":83}," r, (band, women, men) ",[35,281,282],{"class":79},"in",[35,284,285],{"class":178}," enumerate",[35,287,288],{"class":83},"(rows, ",[35,290,292],{"class":291},"sa561","start",[35,294,106],{"class":79},[35,296,297],{"class":178},"2",[35,299,300],{"class":83},"):\n",[35,302,304],{"class":37,"line":303},10,[35,305,306],{"class":83},"    ws.append([band, women, men])\n",[35,308,310,313,316,318,321,324,328,331,334,337],{"class":37,"line":309},11,[35,311,312],{"class":83},"    ws.cell(r, ",[35,314,315],{"class":178},"4",[35,317,143],{"class":83},[35,319,320],{"class":79},"f",[35,322,323],{"class":45},"\"=-C",[35,325,327],{"class":326},"sSjpA","{",[35,329,330],{"class":83},"r",[35,332,333],{"class":326},"}",[35,335,336],{"class":45},"\"",[35,338,339],{"class":83},")\n",[35,341,343,346,349,352,354],{"class":37,"line":342},12,[35,344,345],{"class":83},"ws.column_dimensions[",[35,347,348],{"class":45},"\"A\"",[35,350,351],{"class":83},"].width ",[35,353,106],{"class":79},[35,355,356],{"class":178}," 11\n",[35,358,360,363,366,368,371,373,376,378,381,383,386,389,391,394,396,399,401,404],{"class":37,"line":359},13,[35,361,362],{"class":178},"print",[35,364,365],{"class":83},"([[c.value ",[35,367,276],{"class":79},[35,369,370],{"class":83}," c ",[35,372,282],{"class":79},[35,374,375],{"class":83}," row] ",[35,377,276],{"class":79},[35,379,380],{"class":83}," row ",[35,382,282],{"class":79},[35,384,385],{"class":83}," ws.iter_rows(",[35,387,388],{"class":291},"min_row",[35,390,106],{"class":79},[35,392,393],{"class":178},"1",[35,395,143],{"class":83},[35,397,398],{"class":291},"max_row",[35,400,106],{"class":79},[35,402,403],{"class":178},"3",[35,405,406],{"class":83},")])\n",[10,408,409],{},"The real counts stay in columns B and C, readable and summable. Column D holds the men's values negated with a formula, purely for plotting; keeping it as a formula means a corrected count in column C flows into the chart automatically. The negative values are what push the men's bars to the left of the axis. If no one will edit the workbook, writing the negated numbers as values works equally well and lets other tools read them without recalculation.",[411,412,420,421,420,425,420,429,420,437,420,446,420,450,420,457,420,461,420,465,420,469,420,473,420,477,420,481,420,486,420,490,420,492,420,495,420,497,420,500,420,503,420,506,420,508,420,512,420,514,420,516,420,518,420,520,420,523,420,526,420,528,420,531,420,533,420,535,420,537,420,539,420,542,420,545,420,547,420,550,420,552,420,554,420,556,420,558,420,561,420,564,420,567,420,570,420,573,420,577,420,580,420,582,420,585,420,587,420,589,420,591,420,593,420,595,420,598,420,600,420,602,420,604,420,606,420,608,420,610,420,614,420,618,420,620,420,622,420,624,420,626,420,628,420,630,420,632,420,635,420,637,420,639,420,641,420,643,420,645,420,647,420,649,420,652,420,658,420,662,420,668,420,673,420,681],"svg",{"viewBox":413,"role":414,"ariaLabelledBy":415,"xmlns":418,"style":419},"63 0 634 192","img",[416,417],"bf-negate-t","bf-negate-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:634px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[422,423,424],"title",{"id":416},"One side of the chart is plotted as negative values",[426,427,428],"desc",{"id":417},"On the left the survey table with women and men per age band. On the right the plotted values, women positive and men negated, so that the men's bars extend to the left of the centre line.",[430,431],"rect",{"x":432,"y":433,"width":434,"height":435,"fill":436},"63","0","634","192","#ffffff",[430,438],{"x":439,"y":440,"width":441,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"79.4","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[430,447],{"x":448,"y":440,"width":449,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"111.4","75.6",[451,452,456],"text",{"x":453,"y":454,"style":455},"149.2","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[430,458],{"x":459,"y":440,"width":460,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"187.0","65.7",[451,462,464],{"x":463,"y":454,"style":455},"219.8","B",[430,466],{"x":467,"y":440,"width":468,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"252.7","47.7",[451,470,472],{"x":471,"y":454,"style":455},"276.5","C",[430,474],{"x":439,"y":475,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},"64.0","28.0",[451,478,393],{"x":479,"y":480,"style":455},"95.4","83.0",[430,482],{"x":448,"y":475,"width":449,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[451,487,489],{"x":453,"y":480,"style":488},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Age band",[430,491],{"x":459,"y":475,"width":460,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},[451,493,494],{"x":463,"y":480,"style":488},"Women",[430,496],{"x":467,"y":475,"width":468,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},[451,498,499],{"x":471,"y":480,"style":488},"Men",[430,501],{"x":439,"y":502,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},"92.0",[451,504,297],{"x":479,"y":505,"style":455},"111.0",[430,507],{"x":448,"y":502,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,509,511],{"x":453,"y":505,"style":510},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","18–24",[430,513],{"x":459,"y":502,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,515,179],{"x":463,"y":505,"style":510},[430,517],{"x":467,"y":502,"width":468,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,519,184],{"x":471,"y":505,"style":510},[430,521],{"x":439,"y":522,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},"120.0",[451,524,403],{"x":479,"y":525,"style":455},"139.0",[430,527],{"x":448,"y":522,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,529,530],{"x":453,"y":525,"style":510},"25–34",[430,532],{"x":459,"y":522,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,534,195],{"x":463,"y":525,"style":510},[430,536],{"x":467,"y":522,"width":468,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,538,200],{"x":471,"y":525,"style":510},[430,540],{"x":439,"y":541,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},"148.0",[451,543,315],{"x":479,"y":544,"style":455},"167.0",[430,546],{"x":448,"y":541,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,548,549],{"x":453,"y":544,"style":510},"35–44",[430,551],{"x":459,"y":541,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,553,210],{"x":463,"y":544,"style":510},[430,555],{"x":467,"y":541,"width":468,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,557,215],{"x":471,"y":544,"style":510},[430,559],{"x":560,"y":440,"width":441,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"410.4",[430,562],{"x":563,"y":440,"width":449,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"442.4",[451,565,456],{"x":566,"y":454,"style":455},"480.2",[430,568],{"x":569,"y":440,"width":460,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"518.0",[451,571,464],{"x":572,"y":454,"style":455},"550.8",[430,574],{"x":575,"y":440,"width":576,"height":442,"rx":433,"fill":443,"stroke":444,"style":445},"583.7","96.9",[451,578,472],{"x":579,"y":454,"style":455},"632.1",[430,581],{"x":560,"y":475,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},[451,583,393],{"x":584,"y":480,"style":455},"426.4",[430,586],{"x":563,"y":475,"width":449,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},[451,588,489],{"x":566,"y":480,"style":488},[430,590],{"x":569,"y":475,"width":460,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},[451,592,494],{"x":572,"y":480,"style":488},[430,594],{"x":575,"y":475,"width":576,"height":476,"rx":433,"fill":483,"stroke":484,"style":485},[451,596,597],{"x":579,"y":480,"style":488},"Men (plotted)",[430,599],{"x":560,"y":502,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},[451,601,297],{"x":584,"y":505,"style":455},[430,603],{"x":563,"y":502,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,605,511],{"x":566,"y":505,"style":510},[430,607],{"x":569,"y":502,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,609,179],{"x":572,"y":505,"style":510},[430,611],{"x":575,"y":502,"width":576,"height":476,"rx":433,"fill":612,"stroke":613,"style":485},"#fdefd8","var(--gold,#b4740a)",[451,615,617],{"x":579,"y":505,"style":616},"font-size:10.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","−98",[430,619],{"x":560,"y":522,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},[451,621,403],{"x":584,"y":525,"style":455},[430,623],{"x":563,"y":522,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,625,530],{"x":566,"y":525,"style":510},[430,627],{"x":569,"y":522,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,629,195],{"x":572,"y":525,"style":510},[430,631],{"x":575,"y":522,"width":576,"height":476,"rx":433,"fill":612,"stroke":613,"style":485},[451,633,634],{"x":579,"y":525,"style":616},"−251",[430,636],{"x":560,"y":541,"width":441,"height":476,"rx":433,"fill":443,"stroke":444,"style":445},[451,638,315],{"x":584,"y":544,"style":455},[430,640],{"x":563,"y":541,"width":449,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,642,549],{"x":566,"y":544,"style":510},[430,644],{"x":569,"y":541,"width":460,"height":476,"rx":433,"fill":436,"stroke":444,"style":445},[451,646,210],{"x":572,"y":544,"style":510},[430,648],{"x":575,"y":541,"width":576,"height":476,"rx":433,"fill":612,"stroke":613,"style":485},[451,650,651],{"x":579,"y":544,"style":616},"−230",[451,653,657],{"x":654,"y":655,"style":656},"189.9","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","counts",[451,659,661],{"x":660,"y":655,"style":656},"545.5","plotted values",[37,663],{"x1":664,"y1":665,"x2":666,"y2":665,"stroke":484,"style":667},"310.4","108.0","396.4","stroke-width:2px",[669,670],"polygon",{"points":671,"fill":672},"400.4,108.0 391.4,113.0 391.4,103.0","#5b5cf0",[430,674],{"x":675,"y":676,"width":677,"height":678,"rx":679,"fill":436,"stroke":444,"style":680},"330.9","75.0","49.1","22.0","11","stroke-width:1.2px",[451,682,686],{"x":683,"y":684,"style":685},"355.4","90.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=-C2",[20,688,690],{"id":689},"step-2-a-horizontal-bar-chart-with-overlapping-series","Step 2 — A horizontal bar chart with overlapping series",[25,692,694],{"className":70,"code":693,"language":72,"meta":30,"style":30},"from openpyxl.chart import BarChart, Reference\n\nchart = BarChart()\nchart.type = \"bar\"                 # horizontal bars\nchart.grouping = \"clustered\"\nchart.overlap = 100                # both series on the same row\nchart.gapWidth = 30                # thick bars, small gaps between rows\nchart.add_data(Reference(ws, min_col=2, min_row=1, max_row=7), titles_from_data=True)\nchart.add_data(Reference(ws, min_col=4, min_row=1, max_row=7), titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=7))\nfor series in chart.series:\n    series.invertIfNegative = False\n",[32,695,696,708,712,722,736,746,759,772,814,850,880,892],{"__ignoreMap":30},[35,697,698,700,703,705],{"class":37,"line":38},[35,699,80],{"class":79},[35,701,702],{"class":83}," openpyxl.chart ",[35,704,87],{"class":79},[35,706,707],{"class":83}," BarChart, Reference\n",[35,709,710],{"class":37,"line":93},[35,711,97],{"emptyLinePlaceholder":96},[35,713,714,717,719],{"class":37,"line":100},[35,715,716],{"class":83},"chart ",[35,718,106],{"class":79},[35,720,721],{"class":83}," BarChart()\n",[35,723,724,727,729,732],{"class":37,"line":112},[35,725,726],{"class":83},"chart.type ",[35,728,106],{"class":79},[35,730,731],{"class":45}," \"bar\"",[35,733,735],{"class":734},"s-wDw","                 # horizontal bars\n",[35,737,738,741,743],{"class":37,"line":123},[35,739,740],{"class":83},"chart.grouping ",[35,742,106],{"class":79},[35,744,745],{"class":45}," \"clustered\"\n",[35,747,748,751,753,756],{"class":37,"line":134},[35,749,750],{"class":83},"chart.overlap ",[35,752,106],{"class":79},[35,754,755],{"class":178}," 100",[35,757,758],{"class":734},"                # both series on the same row\n",[35,760,761,764,766,769],{"class":37,"line":162},[35,762,763],{"class":83},"chart.gapWidth ",[35,765,106],{"class":79},[35,767,768],{"class":178}," 30",[35,770,771],{"class":734},"                # thick bars, small gaps between rows\n",[35,773,774,777,780,782,784,786,788,790,792,794,796,798,801,804,807,809,812],{"class":37,"line":221},[35,775,776],{"class":83},"chart.add_data(Reference(ws, ",[35,778,779],{"class":291},"min_col",[35,781,106],{"class":79},[35,783,297],{"class":178},[35,785,143],{"class":83},[35,787,388],{"class":291},[35,789,106],{"class":79},[35,791,393],{"class":178},[35,793,143],{"class":83},[35,795,398],{"class":291},[35,797,106],{"class":79},[35,799,800],{"class":178},"7",[35,802,803],{"class":83},"), ",[35,805,806],{"class":291},"titles_from_data",[35,808,106],{"class":79},[35,810,811],{"class":178},"True",[35,813,339],{"class":83},[35,815,816,818,820,822,824,826,828,830,832,834,836,838,840,842,844,846,848],{"class":37,"line":273},[35,817,776],{"class":83},[35,819,779],{"class":291},[35,821,106],{"class":79},[35,823,315],{"class":178},[35,825,143],{"class":83},[35,827,388],{"class":291},[35,829,106],{"class":79},[35,831,393],{"class":178},[35,833,143],{"class":83},[35,835,398],{"class":291},[35,837,106],{"class":79},[35,839,800],{"class":178},[35,841,803],{"class":83},[35,843,806],{"class":291},[35,845,106],{"class":79},[35,847,811],{"class":178},[35,849,339],{"class":83},[35,851,852,855,857,859,861,863,865,867,869,871,873,875,877],{"class":37,"line":303},[35,853,854],{"class":83},"chart.set_categories(Reference(ws, ",[35,856,779],{"class":291},[35,858,106],{"class":79},[35,860,393],{"class":178},[35,862,143],{"class":83},[35,864,388],{"class":291},[35,866,106],{"class":79},[35,868,297],{"class":178},[35,870,143],{"class":83},[35,872,398],{"class":291},[35,874,106],{"class":79},[35,876,800],{"class":178},[35,878,879],{"class":83},"))\n",[35,881,882,884,887,889],{"class":37,"line":309},[35,883,276],{"class":79},[35,885,886],{"class":83}," series ",[35,888,282],{"class":79},[35,890,891],{"class":83}," chart.series:\n",[35,893,894,897,899],{"class":37,"line":342},[35,895,896],{"class":83},"    series.invertIfNegative ",[35,898,106],{"class":79},[35,900,901],{"class":178}," False\n",[10,903,904,907,908,911,912,915,916,919,920,923],{},[32,905,906],{},"type = \"bar\""," turns the column chart sideways. With the default ",[32,909,910],{},"overlap"," of 0, the two series would sit on separate thin rows; ",[32,913,914],{},"overlap = 100"," puts both on the same row, and because one is positive and the other negative, they extend in opposite directions from zero. ",[32,917,918],{},"invertIfNegative = False"," matters: without it, some applications draw negative bars in an inverted (white) fill. A small ",[32,921,922],{},"gapWidth"," makes the bars substantial, which suits a chart with only six rows.",[20,925,927],{"id":926},"step-3-hide-the-minus-signs","Step 3 — Hide the minus signs",[10,929,930],{},"The men's axis labels and values are negative numbers; readers should see positive counts on both sides. A two-part number format shows negatives without the sign:",[25,932,934],{"className":70,"code":933,"language":72,"meta":30,"style":30},"chart.y_axis.number_format = \"#,##0;#,##0\"\nchart.y_axis.title = \"Respondents\"\nchart.x_axis.delete = False\nchart.y_axis.delete = False\n",[32,935,936,946,956,965],{"__ignoreMap":30},[35,937,938,941,943],{"class":37,"line":38},[35,939,940],{"class":83},"chart.y_axis.number_format ",[35,942,106],{"class":79},[35,944,945],{"class":45}," \"#,##0;#,##0\"\n",[35,947,948,951,953],{"class":37,"line":93},[35,949,950],{"class":83},"chart.y_axis.title ",[35,952,106],{"class":79},[35,954,955],{"class":45}," \"Respondents\"\n",[35,957,958,961,963],{"class":37,"line":100},[35,959,960],{"class":83},"chart.x_axis.delete ",[35,962,106],{"class":79},[35,964,901],{"class":178},[35,966,967,970,972],{"class":37,"line":112},[35,968,969],{"class":83},"chart.y_axis.delete ",[35,971,106],{"class":79},[35,973,901],{"class":178},[10,975,976,977,980,981,18],{},"A number format's sections are separated by semicolons — positive; negative — and the second section here has no minus sign, so −200 displays as 200. The axis then reads 300, 200, 100, 0, 100, 200, 300 from left to right. The two ",[32,978,979],{},"delete = False"," lines make sure Excel shows both axes, which openpyxl otherwise leaves to the application's defaults. Custom number formats are covered in ",[14,982,984],{"href":983},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python\u002F","Write Custom Number Format Codes in Excel with Python",[411,986,420,992,420,995,420,998,420,1002,420,1008,420,1013,420,1016,420,1019,420,1024,420,1029,420,1033,420,1037,420,1040,420,1044,420,1047,420,1051,420,1054,420,1059,420,1062,420,1065,420,1069,420,1073,420,1077,420,1083,420,1087,420,1092,420,1097,420,1102,420,1107,420,1112,420,1116,420,1121,420,1126,420,1131,420,1136],{"viewBox":987,"role":414,"ariaLabelledBy":988,"xmlns":418,"style":991},"4 0 748 306",[989,990],"bf-shape-t","bf-shape-d","width:100%;max-width:748px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[422,993,994],{"id":989},"Women and men by age band",[426,996,997],{"id":990},"Respondents by age band. Women 112, 240, 205, 176, 120 and 64; men 98, 251, 230, 190, 108 and 41 for the bands 18–24 to 65+. In the butterfly chart the men's bars extend left and the women's right.",[430,999],{"x":315,"y":433,"width":1000,"height":1001,"fill":436},"748","306",[430,1003],{"x":475,"y":1004,"width":1005,"height":1005,"rx":403,"fill":1006,"stroke":1007,"style":485},"18.0","14.0","#d9f4f1","var(--teal,#0f9488)",[451,1009,494],{"x":1010,"y":1011,"style":1012},"84.0","29.5","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start",[430,1014],{"x":1015,"y":1004,"width":1005,"height":1005,"rx":403,"fill":483,"stroke":484,"style":485},"159.7",[451,1017,499],{"x":1018,"y":1011,"style":1012},"179.7",[37,1020],{"x1":1021,"y1":1022,"x2":1023,"y2":1022,"stroke":444,"style":445},"53.7","242.0","736.0",[451,1025,433],{"x":1026,"y":1027,"style":1028},"30.9","246.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[37,1030],{"x1":1021,"y1":1031,"x2":1023,"y2":1031,"stroke":444,"style":1032},"178.7","stroke-width:1px;stroke-dasharray:5 4",[451,1034,1036],{"x":1026,"y":1035,"style":1028},"182.7","100",[37,1038],{"x1":1021,"y1":1039,"x2":1023,"y2":1039,"stroke":444,"style":1032},"115.3",[451,1041,1043],{"x":1026,"y":1042,"style":1028},"119.3","200",[37,1045],{"x1":1021,"y1":1046,"x2":1023,"y2":1046,"stroke":444,"style":1032},"52.0",[451,1048,1050],{"x":1026,"y":1049,"style":1028},"56.0","300",[37,1052],{"x1":1021,"y1":1022,"x2":1023,"y2":1022,"stroke":1053,"style":485},"var(--muted,#5b6780)",[451,1055,511],{"x":1056,"y":1057,"style":1058},"110.6","260.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[451,1060,530],{"x":1061,"y":1057,"style":1058},"224.3",[451,1063,549],{"x":1064,"y":1057,"style":1058},"338.0",[451,1066,1068],{"x":1067,"y":1057,"style":1058},"451.7","45–54",[451,1070,1072],{"x":1071,"y":1057,"style":1058},"565.4","55–64",[451,1074,1076],{"x":1075,"y":1057,"style":1058},"679.1","65+",[430,1078],{"x":1079,"y":1080,"width":1081,"height":1082,"rx":403,"fill":1006,"stroke":1007,"style":485},"73.6","171.1","36.0","70.9",[430,1084],{"x":1085,"y":684,"width":1081,"height":1086,"rx":403,"fill":1006,"stroke":1007,"style":485},"187.3","152.0",[430,1088],{"x":1089,"y":1090,"width":1081,"height":1091,"rx":403,"fill":1006,"stroke":1007,"style":485},"301.0","112.2","129.8",[430,1093],{"x":1094,"y":1095,"width":1081,"height":1096,"rx":403,"fill":1006,"stroke":1007,"style":485},"414.7","130.5","111.5",[430,1098],{"x":1099,"y":1100,"width":1081,"height":1101,"rx":403,"fill":1006,"stroke":1007,"style":485},"528.4","166.0","76.0",[430,1103],{"x":1104,"y":1105,"width":1081,"height":1106,"rx":403,"fill":1006,"stroke":1007,"style":485},"642.1","201.5","40.5",[430,1108],{"x":1109,"y":1110,"width":1081,"height":1111,"rx":403,"fill":483,"stroke":484,"style":485},"111.6","179.9","62.1",[430,1113],{"x":1114,"y":480,"width":1081,"height":1115,"rx":403,"fill":483,"stroke":484,"style":485},"225.3","159.0",[430,1117],{"x":1118,"y":1119,"width":1081,"height":1120,"rx":403,"fill":483,"stroke":484,"style":485},"339.0","96.3","145.7",[430,1122],{"x":1123,"y":1124,"width":1081,"height":1125,"rx":403,"fill":483,"stroke":484,"style":485},"452.7","121.7","120.3",[430,1127],{"x":1128,"y":1129,"width":1081,"height":1130,"rx":403,"fill":483,"stroke":484,"style":485},"566.4","173.6","68.4",[430,1132],{"x":1133,"y":1134,"width":1081,"height":1135,"rx":403,"fill":483,"stroke":484,"style":485},"680.1","216.0","26.0",[451,1137,1141],{"x":1138,"y":1139,"style":1140},"380.0","286.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","The same counts as ordinary columns; the butterfly layout places the two series back to back.",[20,1143,1145],{"id":1144},"step-4-move-the-category-labels-to-the-edge","Step 4 — Move the category labels to the edge",[10,1147,1148],{},"By default, the age-band labels sit next to the zero line — in the middle of the chart, on top of the men's bars. Moving them to the low end of the axis puts them along the left edge:",[25,1150,1152],{"className":70,"code":1151,"language":72,"meta":30,"style":30},"chart.x_axis.tickLblPos = \"low\"\nchart.x_axis.scaling.orientation = \"minMax\"\n",[32,1153,1154,1164],{"__ignoreMap":30},[35,1155,1156,1159,1161],{"class":37,"line":38},[35,1157,1158],{"class":83},"chart.x_axis.tickLblPos ",[35,1160,106],{"class":79},[35,1162,1163],{"class":45}," \"low\"\n",[35,1165,1166,1169,1171],{"class":37,"line":93},[35,1167,1168],{"class":83},"chart.x_axis.scaling.orientation ",[35,1170,106],{"class":79},[35,1172,1173],{"class":45}," \"minMax\"\n",[10,1175,1176,1179,1180,1183],{},[32,1177,1178],{},"tickLblPos = \"low\""," places the category labels at the minimum end of the value axis, which in a butterfly chart is the far left. Bar charts list categories from the bottom up, so the first row (18–24) appears at the bottom — the conventional orientation for a population pyramid, with the oldest at the top. For a ranked tornado chart where the first item should be at the top, set ",[32,1181,1182],{},"orientation = \"maxMin\""," instead.",[20,1185,1187],{"id":1186},"step-5-labels-colours-and-title","Step 5 — Labels, colours and title",[25,1189,1191],{"className":70,"code":1190,"language":72,"meta":30,"style":30},"from openpyxl.chart.label import DataLabelList\n\ncolours = {0: \"0F9488\", 1: \"4338CA\"}\nfor i, series in enumerate(chart.series):\n    series.graphicalProperties.solidFill = colours[i]\n    series.graphicalProperties.line.solidFill = colours[i]\n    series.dLbls = DataLabelList()\n    series.dLbls.showVal = True\n    series.dLbls.numFmt = \"#,##0;#,##0\"\nchart.title = \"Respondents by age band and gender\"\nchart.legend.position = \"b\"\nchart.width, chart.height = 17, 9\nws.add_chart(chart, \"A9\")\nwb.save(\"survey_butterfly.xlsx\")\nprint(\"chart saved with\", len(chart.series), \"series\")\n",[32,1192,1193,1205,1209,1239,1253,1263,1272,1282,1292,1301,1311,1321,1336,1346,1357],{"__ignoreMap":30},[35,1194,1195,1197,1200,1202],{"class":37,"line":38},[35,1196,80],{"class":79},[35,1198,1199],{"class":83}," openpyxl.chart.label ",[35,1201,87],{"class":79},[35,1203,1204],{"class":83}," DataLabelList\n",[35,1206,1207],{"class":37,"line":93},[35,1208,97],{"emptyLinePlaceholder":96},[35,1210,1211,1214,1216,1219,1221,1224,1227,1229,1231,1233,1236],{"class":37,"line":100},[35,1212,1213],{"class":83},"colours ",[35,1215,106],{"class":79},[35,1217,1218],{"class":83}," {",[35,1220,433],{"class":178},[35,1222,1223],{"class":83},": ",[35,1225,1226],{"class":45},"\"0F9488\"",[35,1228,143],{"class":83},[35,1230,393],{"class":178},[35,1232,1223],{"class":83},[35,1234,1235],{"class":45},"\"4338CA\"",[35,1237,1238],{"class":83},"}\n",[35,1240,1241,1243,1246,1248,1250],{"class":37,"line":112},[35,1242,276],{"class":79},[35,1244,1245],{"class":83}," i, series ",[35,1247,282],{"class":79},[35,1249,285],{"class":178},[35,1251,1252],{"class":83},"(chart.series):\n",[35,1254,1255,1258,1260],{"class":37,"line":123},[35,1256,1257],{"class":83},"    series.graphicalProperties.solidFill ",[35,1259,106],{"class":79},[35,1261,1262],{"class":83}," colours[i]\n",[35,1264,1265,1268,1270],{"class":37,"line":134},[35,1266,1267],{"class":83},"    series.graphicalProperties.line.solidFill ",[35,1269,106],{"class":79},[35,1271,1262],{"class":83},[35,1273,1274,1277,1279],{"class":37,"line":162},[35,1275,1276],{"class":83},"    series.dLbls ",[35,1278,106],{"class":79},[35,1280,1281],{"class":83}," DataLabelList()\n",[35,1283,1284,1287,1289],{"class":37,"line":221},[35,1285,1286],{"class":83},"    series.dLbls.showVal ",[35,1288,106],{"class":79},[35,1290,1291],{"class":178}," True\n",[35,1293,1294,1297,1299],{"class":37,"line":273},[35,1295,1296],{"class":83},"    series.dLbls.numFmt ",[35,1298,106],{"class":79},[35,1300,945],{"class":45},[35,1302,1303,1306,1308],{"class":37,"line":303},[35,1304,1305],{"class":83},"chart.title ",[35,1307,106],{"class":79},[35,1309,1310],{"class":45}," \"Respondents by age band and gender\"\n",[35,1312,1313,1316,1318],{"class":37,"line":309},[35,1314,1315],{"class":83},"chart.legend.position ",[35,1317,106],{"class":79},[35,1319,1320],{"class":45}," \"b\"\n",[35,1322,1323,1326,1328,1331,1333],{"class":37,"line":342},[35,1324,1325],{"class":83},"chart.width, chart.height ",[35,1327,106],{"class":79},[35,1329,1330],{"class":178}," 17",[35,1332,143],{"class":83},[35,1334,1335],{"class":178},"9\n",[35,1337,1338,1341,1344],{"class":37,"line":359},[35,1339,1340],{"class":83},"ws.add_chart(chart, ",[35,1342,1343],{"class":45},"\"A9\"",[35,1345,339],{"class":83},[35,1347,1349,1352,1355],{"class":37,"line":1348},14,[35,1350,1351],{"class":83},"wb.save(",[35,1353,1354],{"class":45},"\"survey_butterfly.xlsx\"",[35,1356,339],{"class":83},[35,1358,1360,1362,1365,1368,1370,1373,1376,1379],{"class":37,"line":1359},15,[35,1361,362],{"class":178},[35,1363,1364],{"class":83},"(",[35,1366,1367],{"class":45},"\"chart saved with\"",[35,1369,143],{"class":83},[35,1371,1372],{"class":178},"len",[35,1374,1375],{"class":83},"(chart.series), ",[35,1377,1378],{"class":45},"\"series\"",[35,1380,339],{"class":83},[10,1382,1383,1384,18],{},"The same sign-hiding format on the data labels shows 98 instead of −98 at the end of each men's bar. Distinct colours for the two sides, with the legend at the bottom, make the reading obvious. Placing the chart at A9, below the table, keeps it on the same printed page. Styling series in general is covered in ",[14,1385,1387],{"href":1386},"\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",[20,1389,1391],{"id":1390},"step-6-check-the-rendered-chart","Step 6 — Check the rendered chart",[25,1393,1395],{"className":70,"code":1394,"language":72,"meta":30,"style":30},"import subprocess\nfrom PIL import Image\n\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"survey_butterfly.xlsx\"], check=True, capture_output=True)\nsubprocess.run([\"pdftoppm\", \"-png\", \"-r\", \"60\", \"-f\", \"1\", \"-l\", \"1\", \"survey_butterfly.pdf\", \"butterfly\"], check=True)\nimg = Image.open(\"butterfly-1.png\").convert(\"RGB\")\nteal, indigo = (0x0F, 0x94, 0x88), (0x43, 0x38, 0xCA)\nxs = {name: [x for x in range(img.width) for y in range(0, img.height, 4) if img.getpixel((x, y)) == rgb]\n      for name, rgb in ((\"women\", teal), (\"men\", indigo))}\nprint(\"women bars span x =\", min(xs[\"women\"]), \"to\", max(xs[\"women\"]))\nprint(\"men bars span   x =\", min(xs[\"men\"]), \"to\", max(xs[\"men\"]))\nprint(\"men drawn to the left of women:\", max(xs[\"men\"]) \u003C= min(xs[\"women\"]) + 2)\n",[32,1396,1397,1404,1417,1421,1469,1530,1551,1604,1660,1685,1722,1753],{"__ignoreMap":30},[35,1398,1399,1401],{"class":37,"line":38},[35,1400,87],{"class":79},[35,1402,1403],{"class":83}," subprocess\n",[35,1405,1406,1408,1411,1414],{"class":37,"line":93},[35,1407,80],{"class":79},[35,1409,1410],{"class":178}," PIL",[35,1412,1413],{"class":79}," import",[35,1415,1416],{"class":83}," Image\n",[35,1418,1419],{"class":37,"line":100},[35,1420,97],{"emptyLinePlaceholder":96},[35,1422,1423,1426,1429,1431,1434,1436,1439,1441,1444,1446,1448,1451,1454,1456,1458,1460,1463,1465,1467],{"class":37,"line":112},[35,1424,1425],{"class":83},"subprocess.run([",[35,1427,1428],{"class":45},"\"soffice\"",[35,1430,143],{"class":83},[35,1432,1433],{"class":45},"\"--headless\"",[35,1435,143],{"class":83},[35,1437,1438],{"class":45},"\"--convert-to\"",[35,1440,143],{"class":83},[35,1442,1443],{"class":45},"\"pdf\"",[35,1445,143],{"class":83},[35,1447,1354],{"class":45},[35,1449,1450],{"class":83},"], ",[35,1452,1453],{"class":291},"check",[35,1455,106],{"class":79},[35,1457,811],{"class":178},[35,1459,143],{"class":83},[35,1461,1462],{"class":291},"capture_output",[35,1464,106],{"class":79},[35,1466,811],{"class":178},[35,1468,339],{"class":83},[35,1470,1471,1473,1476,1478,1481,1483,1486,1488,1491,1493,1496,1498,1501,1503,1506,1508,1510,1512,1515,1517,1520,1522,1524,1526,1528],{"class":37,"line":123},[35,1472,1425],{"class":83},[35,1474,1475],{"class":45},"\"pdftoppm\"",[35,1477,143],{"class":83},[35,1479,1480],{"class":45},"\"-png\"",[35,1482,143],{"class":83},[35,1484,1485],{"class":45},"\"-r\"",[35,1487,143],{"class":83},[35,1489,1490],{"class":45},"\"60\"",[35,1492,143],{"class":83},[35,1494,1495],{"class":45},"\"-f\"",[35,1497,143],{"class":83},[35,1499,1500],{"class":45},"\"1\"",[35,1502,143],{"class":83},[35,1504,1505],{"class":45},"\"-l\"",[35,1507,143],{"class":83},[35,1509,1500],{"class":45},[35,1511,143],{"class":83},[35,1513,1514],{"class":45},"\"survey_butterfly.pdf\"",[35,1516,143],{"class":83},[35,1518,1519],{"class":45},"\"butterfly\"",[35,1521,1450],{"class":83},[35,1523,1453],{"class":291},[35,1525,106],{"class":79},[35,1527,811],{"class":178},[35,1529,339],{"class":83},[35,1531,1532,1535,1537,1540,1543,1546,1549],{"class":37,"line":134},[35,1533,1534],{"class":83},"img ",[35,1536,106],{"class":79},[35,1538,1539],{"class":83}," Image.open(",[35,1541,1542],{"class":45},"\"butterfly-1.png\"",[35,1544,1545],{"class":83},").convert(",[35,1547,1548],{"class":45},"\"RGB\"",[35,1550,339],{"class":83},[35,1552,1553,1556,1558,1561,1564,1567,1569,1571,1574,1576,1578,1581,1583,1585,1588,1590,1592,1595,1597,1599,1602],{"class":37,"line":162},[35,1554,1555],{"class":83},"teal, indigo ",[35,1557,106],{"class":79},[35,1559,1560],{"class":83}," (",[35,1562,1563],{"class":79},"0x",[35,1565,1566],{"class":178},"0F",[35,1568,143],{"class":83},[35,1570,1563],{"class":79},[35,1572,1573],{"class":178},"94",[35,1575,143],{"class":83},[35,1577,1563],{"class":79},[35,1579,1580],{"class":178},"88",[35,1582,187],{"class":83},[35,1584,1563],{"class":79},[35,1586,1587],{"class":178},"43",[35,1589,143],{"class":83},[35,1591,1563],{"class":79},[35,1593,1594],{"class":178},"38",[35,1596,143],{"class":83},[35,1598,1563],{"class":79},[35,1600,1601],{"class":178},"CA",[35,1603,339],{"class":83},[35,1605,1606,1609,1611,1614,1616,1619,1621,1624,1627,1629,1632,1634,1636,1638,1640,1643,1645,1648,1651,1654,1657],{"class":37,"line":221},[35,1607,1608],{"class":83},"xs ",[35,1610,106],{"class":79},[35,1612,1613],{"class":83}," {name: [x ",[35,1615,276],{"class":79},[35,1617,1618],{"class":83}," x ",[35,1620,282],{"class":79},[35,1622,1623],{"class":178}," range",[35,1625,1626],{"class":83},"(img.width) ",[35,1628,276],{"class":79},[35,1630,1631],{"class":83}," y ",[35,1633,282],{"class":79},[35,1635,1623],{"class":178},[35,1637,1364],{"class":83},[35,1639,433],{"class":178},[35,1641,1642],{"class":83},", img.height, ",[35,1644,315],{"class":178},[35,1646,1647],{"class":83},") ",[35,1649,1650],{"class":79},"if",[35,1652,1653],{"class":83}," img.getpixel((x, y)) ",[35,1655,1656],{"class":79},"==",[35,1658,1659],{"class":83}," rgb]\n",[35,1661,1662,1665,1668,1670,1673,1676,1679,1682],{"class":37,"line":273},[35,1663,1664],{"class":79},"      for",[35,1666,1667],{"class":83}," name, rgb ",[35,1669,282],{"class":79},[35,1671,1672],{"class":83}," ((",[35,1674,1675],{"class":45},"\"women\"",[35,1677,1678],{"class":83},", teal), (",[35,1680,1681],{"class":45},"\"men\"",[35,1683,1684],{"class":83},", indigo))}\n",[35,1686,1687,1689,1691,1694,1696,1699,1702,1704,1707,1710,1712,1715,1717,1719],{"class":37,"line":303},[35,1688,362],{"class":178},[35,1690,1364],{"class":83},[35,1692,1693],{"class":45},"\"women bars span x =\"",[35,1695,143],{"class":83},[35,1697,1698],{"class":178},"min",[35,1700,1701],{"class":83},"(xs[",[35,1703,1675],{"class":45},[35,1705,1706],{"class":83},"]), ",[35,1708,1709],{"class":45},"\"to\"",[35,1711,143],{"class":83},[35,1713,1714],{"class":178},"max",[35,1716,1701],{"class":83},[35,1718,1675],{"class":45},[35,1720,1721],{"class":83},"]))\n",[35,1723,1724,1726,1728,1731,1733,1735,1737,1739,1741,1743,1745,1747,1749,1751],{"class":37,"line":309},[35,1725,362],{"class":178},[35,1727,1364],{"class":83},[35,1729,1730],{"class":45},"\"men bars span   x =\"",[35,1732,143],{"class":83},[35,1734,1698],{"class":178},[35,1736,1701],{"class":83},[35,1738,1681],{"class":45},[35,1740,1706],{"class":83},[35,1742,1709],{"class":45},[35,1744,143],{"class":83},[35,1746,1714],{"class":178},[35,1748,1701],{"class":83},[35,1750,1681],{"class":45},[35,1752,1721],{"class":83},[35,1754,1755,1757,1759,1762,1764,1766,1768,1770,1773,1776,1779,1781,1783,1785,1788,1791],{"class":37,"line":342},[35,1756,362],{"class":178},[35,1758,1364],{"class":83},[35,1760,1761],{"class":45},"\"men drawn to the left of women:\"",[35,1763,143],{"class":83},[35,1765,1714],{"class":178},[35,1767,1701],{"class":83},[35,1769,1681],{"class":45},[35,1771,1772],{"class":83},"]) ",[35,1774,1775],{"class":79},"\u003C=",[35,1777,1778],{"class":178}," min",[35,1780,1701],{"class":83},[35,1782,1675],{"class":45},[35,1784,1772],{"class":83},[35,1786,1787],{"class":79},"+",[35,1789,1790],{"class":178}," 2",[35,1792,339],{"class":83},[10,1794,1795,1796,1798],{},"Finding where each colour appears on the rendered page confirms the defining property of the chart: the men's bars lie entirely to the left of the women's, meeting at the centre line. A quick check like this catches the most common mistake — forgetting ",[32,1797,914],{},", which renders the two series as thin interleaved bars instead of a butterfly.",[411,1800,420,1806,420,1809,420,1812,420,1816,420,1825,420,1836,420,1842,420,1846,420,1851,420,1858,420,1862,420,1867,420,1871,420,1876,420,1880,420,1885,420,1888,420,1895,420,1900,420,1904,420,1908,420,1913,420,1916,420,1921,420,1923,420,1928,420,1930],{"viewBox":1801,"role":414,"ariaLabelledBy":1802,"xmlns":418,"style":1805},"4 0 752 233",[1803,1804],"bf-settings-t","bf-settings-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[422,1807,1808],{"id":1803},"The settings that make a bar chart a butterfly",[426,1810,1811],{"id":1804},"Without the settings, the chart shows thin bars side by side with negative numbers and labels in the middle. With overlap 100, a two-part number format, labels at the low position and invertIfNegative off, it becomes a back-to-back chart with positive labels along the edge.",[430,1813],{"x":315,"y":433,"width":1814,"height":1815,"fill":436},"752","233",[430,1817],{"x":1818,"y":1819,"width":1820,"height":1821,"rx":1822,"fill":1823,"stroke":1824,"style":667},"20.0","16.0","351.0","201.0","14","#fce9e9","var(--danger,#dc2626)",[1826,1827,1829],"g",{"transform":1828},"translate(182.5,29.0) scale(1.083)",[1830,1831],"path",{"d":1832,"fill":1833,"stroke":1834,"style":1835},"M6 6l12 12 M18 6L6 18","none","var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[451,1837,1841],{"x":1838,"y":1839,"style":1840},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Plain bar chart",[37,1843],{"x1":1081,"y1":1844,"x2":1845,"y2":1844,"stroke":1824,"style":445},"91.0","355.0",[451,1847,1850],{"x":1848,"y":505,"style":1849},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","two thin bars per row",[1826,1852,1854],{"transform":1853},"translate(38.5,99.5) scale(0.625)",[1830,1855],{"d":1832,"fill":1833,"stroke":1856,"style":1857},"var(--accent,#f43f8f)","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[451,1859,1861],{"x":1848,"y":1860,"style":1849},"137.0","axis shows −300 … 300",[1826,1863,1865],{"transform":1864},"translate(38.5,125.5) scale(0.625)",[1830,1866],{"d":1832,"fill":1833,"stroke":1856,"style":1857},[451,1868,1870],{"x":1848,"y":1869,"style":1849},"163.0","labels sit on the centre line",[1826,1872,1874],{"transform":1873},"translate(38.5,151.5) scale(0.625)",[1830,1875],{"d":1832,"fill":1833,"stroke":1856,"style":1857},[451,1877,1879],{"x":1848,"y":1878,"style":1849},"189.0","negative bars may be hollow",[1826,1881,1883],{"transform":1882},"translate(38.5,177.5) scale(0.625)",[1830,1884],{"d":1832,"fill":1833,"stroke":1856,"style":1857},[430,1886],{"x":1887,"y":1819,"width":1820,"height":1821,"rx":1822,"fill":1006,"stroke":1007,"style":667},"389.0",[1826,1889,1891],{"transform":1890},"translate(551.5,29.0) scale(1.083)",[1830,1892],{"d":1893,"fill":1833,"stroke":1894,"style":1835},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[451,1896,1899],{"x":1897,"y":1839,"style":1898},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Butterfly settings",[37,1901],{"x1":1902,"y1":1844,"x2":1903,"y2":1844,"stroke":1007,"style":445},"405.0","724.0",[451,1905,1907],{"x":1906,"y":505,"style":1849},"429.0","overlap = 100, gapWidth = 30",[1826,1909,1911],{"transform":1910},"translate(407.5,99.5) scale(0.625)",[1830,1912],{"d":1893,"fill":1833,"stroke":1007,"style":1857},[451,1914,1915],{"x":1906,"y":1860,"style":1849},"format #,##0;#,##0",[1826,1917,1919],{"transform":1918},"translate(407.5,125.5) scale(0.625)",[1830,1920],{"d":1893,"fill":1833,"stroke":1007,"style":1857},[451,1922,1178],{"x":1906,"y":1869,"style":1849},[1826,1924,1926],{"transform":1925},"translate(407.5,151.5) scale(0.625)",[1830,1927],{"d":1893,"fill":1833,"stroke":1007,"style":1857},[451,1929,918],{"x":1906,"y":1878,"style":1849},[1826,1931,1933],{"transform":1932},"translate(407.5,177.5) scale(0.625)",[1830,1934],{"d":1893,"fill":1833,"stroke":1007,"style":1857},[20,1936,1938],{"id":1937},"the-same-chart-as-a-sensitivity-tornado","The same chart as a sensitivity tornado",[10,1940,1941,1942,1946,1947,1949,1950,1953,1954,18],{},"The butterfly layout has a second life in finance: the ",[1943,1944,1945],"em",{},"tornado"," chart of a sensitivity analysis, where each row is an assumption and the bars show how far profit falls (left) or rises (right) when that assumption moves to its low or high case. The construction is identical — the downside as negative values, the upside as positive, ",[32,1948,914],{}," and the sign-hiding format — with two changes: sort the rows by total swing so the most influential assumption is widest, and set ",[32,1951,1952],{},"chart.x_axis.scaling.orientation = \"maxMin\""," so that it appears at the top, giving the chart its funnel shape. The low and high cases themselves can come from the scenario runs described in ",[14,1955,1957],{"href":1956},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frun-what-if-scenarios-on-an-excel-model-with-python\u002F","Run What-If Scenarios on an Excel Model with Python",[20,1959,1961],{"id":1960},"common-pitfalls","Common pitfalls",[1963,1964,1965,1976,1982,1995,2004],"ul",{},[1966,1967,1968,1972,1973,1975],"li",{},[1969,1970,1971],"strong",{},"Forgetting overlap."," Without ",[32,1974,914],{}," the series sit side by side instead of back to back.",[1966,1977,1978,1981],{},[1969,1979,1980],{},"Minus signs on the axis and labels."," Use a two-part number format on the axis and on the data labels.",[1966,1983,1984,1987,1988,1991,1992,18],{},[1969,1985,1986],{},"Labels in the middle."," Set the category axis ",[32,1989,1990],{},"tickLblPos"," to ",[32,1993,1994],{},"\"low\"",[1966,1996,1997,2000,2001,2003],{},[1969,1998,1999],{},"Hollow negative bars."," Set ",[32,2002,918],{}," on each series.",[1966,2005,2006,2009,2010,2012,2013,2015],{},[1969,2007,2008],{},"Unbalanced axes."," If one side is much larger, fix the axis ",[32,2011,1698],{}," and ",[32,2014,1714],{}," symmetrically so the centre line stays in the middle.",[20,2017,2019],{"id":2018},"conclusion","Conclusion",[10,2021,2022,2023,2026,2027,2029,2030,2033],{},"A butterfly chart is a horizontal bar chart with one series plotted as negative values. Keep the real numbers in their own columns and negate a helper column for plotting, overlap the series fully with a small gap, hide minus signs with ",[32,2024,2025],{},"#,##0;#,##0"," on the axis and the data labels, move category labels to the edge with ",[32,2028,1178],{},", and turn off ",[32,2031,2032],{},"invertIfNegative",". Render it once to confirm the two sides meet at the centre.",[20,2035,2037],{"id":2036},"frequently-asked-questions","Frequently asked questions",[10,2039,2040,2043],{},[1969,2041,2042],{},"How do I make a butterfly or tornado chart in Excel with Python?","\nCreate a horizontal bar chart with two series, one of them negated, set overlap to 100 so they share each row, and use the number format #,##0;#,##0 so negative values display without a minus sign. openpyxl supports all of these settings.",[10,2045,2046,2049],{},[1969,2047,2048],{},"How do I hide the minus sign on negative values in an Excel chart?","\nUse a number format with two sections whose negative section has no minus sign, such as #,##0;#,##0, on the value axis and on the data labels.",[10,2051,2052,2055],{},[1969,2053,2054],{},"Why are my category labels in the middle of the chart?","\nThey are drawn next to the zero line by default. Set the category axis tick label position to low (in openpyxl, chart.x_axis.tickLblPos = \"low\") to move them to the edge.",[10,2057,2058,2061],{},[1969,2059,2060],{},"Why are the negative bars white or hollow?","\nSome applications invert the fill of negative bars unless invertIfNegative is turned off. Set series.invertIfNegative = False for each series.",[20,2063,2065],{"id":2064},"related","Related",[1963,2067,2068,2074,2081,2088],{},[1966,2069,2070,2071,2073],{},"Up one level: ",[14,2072,17],{"href":16}," — native charts from Python.",[1966,2075,2076,2080],{},[14,2077,2079],{"href":2078},"\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 underlying chart type.",[1966,2082,2083,2087],{},[14,2084,2086],{"href":2085},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fformat-chart-axes-with-openpyxl\u002F","Format Chart Axes with openpyxl"," — scaling and orientation options.",[1966,2089,2090,2094],{},[14,2091,2093],{"href":2092},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fshow-negative-numbers-in-red-with-parentheses\u002F","Show Negative Numbers in Red with Parentheses"," — more about negative-number formats.",[2096,2097,2098],"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}",{"title":30,"searchDepth":93,"depth":93,"links":2100},[2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111,2112],{"id":22,"depth":93,"text":23},{"id":66,"depth":93,"text":67},{"id":689,"depth":93,"text":690},{"id":926,"depth":93,"text":927},{"id":1144,"depth":93,"text":1145},{"id":1186,"depth":93,"text":1187},{"id":1390,"depth":93,"text":1391},{"id":1937,"depth":93,"text":1938},{"id":1960,"depth":93,"text":1961},{"id":2018,"depth":93,"text":2019},{"id":2036,"depth":93,"text":2037},{"id":2064,"depth":93,"text":2065},"2026-10-02","Build a butterfly (tornado, population-pyramid) chart as a native Excel bar chart with openpyxl: plot one side as negative values, overlap the two series on the same rows, hide the minus signs with a two-part number format, move the category labels to the edge, show positive data labels, colour each side, and check the rendered result.","md",[2117,2119,2121,2123],{"q":2042,"a":2118},"Create a horizontal bar chart with two series, one of them negated, set overlap to 100 so they share each row, and use the number format #,##0;#,##0 so negative values display without a minus sign. openpyxl supports all of these settings.",{"q":2048,"a":2120},"Use a number format with two sections whose negative section has no minus sign, such as #,##0;#,##0, on the value axis and on the data labels.",{"q":2054,"a":2122},"They are drawn next to the zero line by default. Set the category axis tick label position to low (in openpyxl, chart.x_axis.tickLblPos = \"low\") to move them to the edge.",{"q":2060,"a":2124},"Some applications invert the fill of negative bars unless invertIfNegative is turned off. Set series.invertIfNegative = False for each series.","create a butterfly chart in excel with python",null,{"breadcrumb":2128},[2129,2132,2135],{"name":2130,"item":2131},"Home","\u002F",{"name":2133,"item":2134},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":17,"item":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python",{"title":2138,"description":2139},"Create a Butterfly (Tornado) Chart in Excel with openpyxl","A back-to-back bar chart from Python: a negated helper column, overlap=100 and a small gap, number format #,##0;#,##0 to show negatives as positive, tickLblPos=\"low\" for edge labels, invertIfNegative=False, data labels and colours.","create-a-butterfly-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-butterfly-chart-in-excel-with-python\u002Findex",[2143,2144],"charts-and-images","openpyxl","how-to","jBAEc5UuhfrGrTVXv7-YUhDsdSnDDzh9QlOr_7YH4o4",[2148,2152],{"title":2149,"path":2150,"stem":2151,"children":-1},"Create a Bullet Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bullet-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bullet-chart-in-excel-with-python\u002Findex",{"title":2153,"path":2154,"stem":2155,"children":-1},"Create a Doughnut Chart with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-doughnut-chart-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-doughnut-chart-with-openpyxl\u002Findex",[2157,2159],{"path":2158,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2160,"title":2133},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2163,2166,2169,2172,2175,2178],{"path":2164,"title":2165,"kind":2145},"\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":2167,"title":2168,"kind":2145},"\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":2170,"title":2171,"kind":2145},"\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":2173,"title":2174,"kind":2145},"\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":2176,"title":2177,"kind":2145},"\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":2179,"title":2180,"kind":2145},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python","Build a Waterfall Chart in Excel with Python",1790972109012]