[{"data":1,"prerenderedAt":2087},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl":2053,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl":2062,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl":2067,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl":2068},{"id":4,"title":5,"body":6,"dateModified":2019,"datePublished":2019,"description":2020,"extension":2021,"faq":2022,"keyword":2031,"kind":2032,"meta":2033,"navigation":234,"path":2042,"seo":2043,"slug":2046,"stem":2047,"topics":2048,"type":2051,"__hash__":2052},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl\u002Findex.md","Create a Bubble Chart in Excel with openpyxl",{"type":7,"value":8,"toc":2006},"minimark",[9,19,152,157,183,186,190,193,797,812,816,836,986,1002,1121,1125,1132,1559,1566,1839,1843,1846,1850,1853,1857,1913,1917,1920,1924,1941,1945,1951,1957,1963,1969,1973,2002],[10,11,12,13,18],"p",{},"A scatter chart shows how two measures relate; a bubble chart adds a third as the size of each point. That makes it the classic portfolio view: products placed by margin and growth, sized by revenue, so the large, profitable, growing products stand out from the small and declining ones in one glance. The same shape works for customers (order frequency, average order value, lifetime revenue) or projects (cost, benefit, risk). This page, part of ",[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",", builds a native Excel bubble chart with openpyxl, with bubbles grouped by colour and labelled by name.",[20,21,29,30,29,34,29,38,29,46,29,54,29,61,29,68,29,71,29,75,29,80,29,86,29,88,29,93,29,95,29,98,29,101,29,105,29,107,29,111,29,113,29,116,29,119,29,123,29,125,29,129,29,131,29,134,29,137,29,141,29,143,29,147,29,149],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 752 208","img",[25,26],"bub-roles-t","bub-roles-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Three measures, three visual channels",[35,36,37],"desc",{"id":26},"A table showing how each measure maps to the chart. Margin percent is the horizontal position, passed as xvalues. Growth percent is the vertical position, passed as values. Revenue is the bubble size, passed as zvalues. Region is the colour, one series per region.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","752","208","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":42,"fill":45,"stroke":52,"style":53},"20.0","16.0","91.8","40.0","var(--line,#cdd5e6)","stroke-width:1px",[39,55],{"x":56,"y":49,"width":57,"height":51,"rx":42,"fill":58,"stroke":59,"style":60},"111.8","314.1","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[62,63,67],"text",{"x":64,"y":65,"style":66},"268.9","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Channel",[39,69],{"x":70,"y":49,"width":57,"height":51,"rx":42,"fill":58,"stroke":59,"style":60},"425.9",[62,72,74],{"x":73,"y":65,"style":66},"583.0","Series argument",[39,76],{"x":48,"y":77,"width":50,"height":78,"rx":42,"fill":79,"stroke":52,"style":53},"56.0","34.0","#e7ebef",[62,81,85],{"x":82,"y":83,"style":84},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Margin %",[39,87],{"x":56,"y":77,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,89,92],{"x":64,"y":90,"style":91},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","across (x)",[39,94],{"x":70,"y":77,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,96,97],{"x":73,"y":90,"style":91},"xvalues",[39,99],{"x":48,"y":100,"width":50,"height":78,"rx":42,"fill":79,"stroke":52,"style":53},"90.0",[62,102,104],{"x":82,"y":103,"style":84},"112.0","Growth %",[39,106],{"x":56,"y":100,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,108,110],{"x":64,"y":109,"style":91},"111.0","up (y)",[39,112],{"x":70,"y":100,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,114,115],{"x":73,"y":109,"style":91},"values",[39,117],{"x":48,"y":118,"width":50,"height":78,"rx":42,"fill":79,"stroke":52,"style":53},"124.0",[62,120,122],{"x":82,"y":121,"style":84},"146.0","Revenue",[39,124],{"x":56,"y":118,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,126,128],{"x":64,"y":127,"style":91},"145.0","bubble size",[39,130],{"x":70,"y":118,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,132,133],{"x":73,"y":127,"style":91},"zvalues",[39,135],{"x":48,"y":136,"width":50,"height":78,"rx":42,"fill":79,"stroke":52,"style":53},"158.0",[62,138,140],{"x":82,"y":139,"style":84},"180.0","Region",[39,142],{"x":56,"y":136,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,144,146],{"x":64,"y":145,"style":91},"179.0","colour",[39,148],{"x":70,"y":136,"width":57,"height":78,"rx":42,"fill":45,"stroke":52,"style":53},[62,150,151],{"x":73,"y":145,"style":91},"one series each",[153,154,156],"h2",{"id":155},"prerequisites","Prerequisites",[158,159,164],"pre",{"className":160,"code":161,"language":162,"meta":163,"style":163},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[165,166,167],"code",{"__ignoreMap":163},[168,169,172,176,180],"span",{"class":170,"line":171},"line",1,[168,173,175],{"class":174},"sMTad","pip",[168,177,179],{"class":178},"srMev"," install",[168,181,182],{"class":178}," openpyxl\n",[10,184,185],{},"The example portfolio has six products in two regions, each with a margin, a year-on-year growth rate and a revenue figure.",[153,187,189],{"id":188},"step-1-lay-out-the-data-and-add-one-series-per-group","Step 1 — Lay out the data and add one series per group",[10,191,192],{},"A bubble series needs three columns: x positions, y positions and sizes. Keep each group's rows together so each series can reference one contiguous block:",[158,194,198],{"className":195,"code":196,"language":197,"meta":163,"style":163},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.chart import BubbleChart, Reference, Series\n\nrows = [(\"Brackets\", \"North\", 32, 8, 480), (\"Hinges\", \"North\", 24, 15, 220), (\"Panels\", \"North\", 18, -4, 610),\n        (\"Frames\", \"South\", 28, 22, 150), (\"Latches\", \"South\", 35, 3, 90), (\"Rails\", \"South\", 12, 11, 330)]\nwb = Workbook(); ws = wb.active; ws.title = \"Portfolio\"\nws.append([\"Product\", \"Region\", \"Margin %\", \"Growth %\", \"Revenue (k)\"])\nfor r in sorted(rows, key=lambda r: r[1]):\n    ws.append(r)\n\ndef block(region):\n    found = [i for i in range(2, ws.max_row + 1) if ws.cell(row=i, column=2).value == region]\n    return found[0], found[-1]\n\nchart = BubbleChart()\nchart.title = \"Product portfolio — margin, growth and revenue\"\nfor region in [\"North\", \"South\"]:\n    first, last = block(region)\n    chart.series.append(Series(values=Reference(ws, min_col=4, min_row=first, max_row=last),\n                               xvalues=Reference(ws, min_col=3, min_row=first, max_row=last),\n                               zvalues=Reference(ws, min_col=5, min_row=first, max_row=last),\n                               title=region))\nprint([(s.tx.v, s.xVal.numRef.f, s.bubbleSize.numRef.f) for s in chart.series])\n","python",[165,199,200,216,229,236,327,407,428,460,494,500,505,518,587,608,613,624,635,657,668,706,736,767,778],{"__ignoreMap":163},[168,201,202,206,210,213],{"class":170,"line":171},[168,203,205],{"class":204},"s-kum","from",[168,207,209],{"class":208},"skGVy"," openpyxl ",[168,211,212],{"class":204},"import",[168,214,215],{"class":208}," Workbook\n",[168,217,219,221,224,226],{"class":170,"line":218},2,[168,220,205],{"class":204},[168,222,223],{"class":208}," openpyxl.chart ",[168,225,212],{"class":204},[168,227,228],{"class":208}," BubbleChart, Reference, Series\n",[168,230,232],{"class":170,"line":231},3,[168,233,235],{"emptyLinePlaceholder":234},true,"\n",[168,237,239,242,245,248,251,254,257,259,263,265,268,270,273,276,279,281,283,285,288,290,293,295,298,300,303,305,307,309,312,314,317,319,321,324],{"class":170,"line":238},4,[168,240,241],{"class":208},"rows ",[168,243,244],{"class":204},"=",[168,246,247],{"class":208}," [(",[168,249,250],{"class":178},"\"Brackets\"",[168,252,253],{"class":208},", ",[168,255,256],{"class":178},"\"North\"",[168,258,253],{"class":208},[168,260,262],{"class":261},"sP0c6","32",[168,264,253],{"class":208},[168,266,267],{"class":261},"8",[168,269,253],{"class":208},[168,271,272],{"class":261},"480",[168,274,275],{"class":208},"), (",[168,277,278],{"class":178},"\"Hinges\"",[168,280,253],{"class":208},[168,282,256],{"class":178},[168,284,253],{"class":208},[168,286,287],{"class":261},"24",[168,289,253],{"class":208},[168,291,292],{"class":261},"15",[168,294,253],{"class":208},[168,296,297],{"class":261},"220",[168,299,275],{"class":208},[168,301,302],{"class":178},"\"Panels\"",[168,304,253],{"class":208},[168,306,256],{"class":178},[168,308,253],{"class":208},[168,310,311],{"class":261},"18",[168,313,253],{"class":208},[168,315,316],{"class":204},"-",[168,318,41],{"class":261},[168,320,253],{"class":208},[168,322,323],{"class":261},"610",[168,325,326],{"class":208},"),\n",[168,328,330,333,336,338,341,343,346,348,351,353,356,358,361,363,365,367,370,372,375,377,380,382,385,387,389,391,394,396,399,401,404],{"class":170,"line":329},5,[168,331,332],{"class":208},"        (",[168,334,335],{"class":178},"\"Frames\"",[168,337,253],{"class":208},[168,339,340],{"class":178},"\"South\"",[168,342,253],{"class":208},[168,344,345],{"class":261},"28",[168,347,253],{"class":208},[168,349,350],{"class":261},"22",[168,352,253],{"class":208},[168,354,355],{"class":261},"150",[168,357,275],{"class":208},[168,359,360],{"class":178},"\"Latches\"",[168,362,253],{"class":208},[168,364,340],{"class":178},[168,366,253],{"class":208},[168,368,369],{"class":261},"35",[168,371,253],{"class":208},[168,373,374],{"class":261},"3",[168,376,253],{"class":208},[168,378,379],{"class":261},"90",[168,381,275],{"class":208},[168,383,384],{"class":178},"\"Rails\"",[168,386,253],{"class":208},[168,388,340],{"class":178},[168,390,253],{"class":208},[168,392,393],{"class":261},"12",[168,395,253],{"class":208},[168,397,398],{"class":261},"11",[168,400,253],{"class":208},[168,402,403],{"class":261},"330",[168,405,406],{"class":208},")]\n",[168,408,410,413,415,418,420,423,425],{"class":170,"line":409},6,[168,411,412],{"class":208},"wb ",[168,414,244],{"class":204},[168,416,417],{"class":208}," Workbook(); ws ",[168,419,244],{"class":204},[168,421,422],{"class":208}," wb.active; ws.title ",[168,424,244],{"class":204},[168,426,427],{"class":178}," \"Portfolio\"\n",[168,429,431,434,437,439,442,444,447,449,452,454,457],{"class":170,"line":430},7,[168,432,433],{"class":208},"ws.append([",[168,435,436],{"class":178},"\"Product\"",[168,438,253],{"class":208},[168,440,441],{"class":178},"\"Region\"",[168,443,253],{"class":208},[168,445,446],{"class":178},"\"Margin %\"",[168,448,253],{"class":208},[168,450,451],{"class":178},"\"Growth %\"",[168,453,253],{"class":208},[168,455,456],{"class":178},"\"Revenue (k)\"",[168,458,459],{"class":208},"])\n",[168,461,463,466,469,472,475,478,482,485,488,491],{"class":170,"line":462},8,[168,464,465],{"class":204},"for",[168,467,468],{"class":208}," r ",[168,470,471],{"class":204},"in",[168,473,474],{"class":261}," sorted",[168,476,477],{"class":208},"(rows, ",[168,479,481],{"class":480},"sa561","key",[168,483,484],{"class":204},"=lambda",[168,486,487],{"class":208}," r: r[",[168,489,490],{"class":261},"1",[168,492,493],{"class":208},"]):\n",[168,495,497],{"class":170,"line":496},9,[168,498,499],{"class":208},"    ws.append(r)\n",[168,501,503],{"class":170,"line":502},10,[168,504,235],{"emptyLinePlaceholder":234},[168,506,508,511,515],{"class":170,"line":507},11,[168,509,510],{"class":204},"def",[168,512,514],{"class":513},"s_Opv"," block",[168,516,517],{"class":208},"(region):\n",[168,519,521,524,526,529,531,534,536,539,542,545,548,551,554,557,560,563,566,568,571,574,576,578,581,584],{"class":170,"line":520},12,[168,522,523],{"class":208},"    found ",[168,525,244],{"class":204},[168,527,528],{"class":208}," [i ",[168,530,465],{"class":204},[168,532,533],{"class":208}," i ",[168,535,471],{"class":204},[168,537,538],{"class":261}," range",[168,540,541],{"class":208},"(",[168,543,544],{"class":261},"2",[168,546,547],{"class":208},", ws.max_row ",[168,549,550],{"class":204},"+",[168,552,553],{"class":261}," 1",[168,555,556],{"class":208},") ",[168,558,559],{"class":204},"if",[168,561,562],{"class":208}," ws.cell(",[168,564,565],{"class":480},"row",[168,567,244],{"class":204},[168,569,570],{"class":208},"i, ",[168,572,573],{"class":480},"column",[168,575,244],{"class":204},[168,577,544],{"class":261},[168,579,580],{"class":208},").value ",[168,582,583],{"class":204},"==",[168,585,586],{"class":208}," region]\n",[168,588,590,593,596,598,601,603,605],{"class":170,"line":589},13,[168,591,592],{"class":204},"    return",[168,594,595],{"class":208}," found[",[168,597,42],{"class":261},[168,599,600],{"class":208},"], found[",[168,602,316],{"class":204},[168,604,490],{"class":261},[168,606,607],{"class":208},"]\n",[168,609,611],{"class":170,"line":610},14,[168,612,235],{"emptyLinePlaceholder":234},[168,614,616,619,621],{"class":170,"line":615},15,[168,617,618],{"class":208},"chart ",[168,620,244],{"class":204},[168,622,623],{"class":208}," BubbleChart()\n",[168,625,627,630,632],{"class":170,"line":626},16,[168,628,629],{"class":208},"chart.title ",[168,631,244],{"class":204},[168,633,634],{"class":178}," \"Product portfolio — margin, growth and revenue\"\n",[168,636,638,640,643,645,648,650,652,654],{"class":170,"line":637},17,[168,639,465],{"class":204},[168,641,642],{"class":208}," region ",[168,644,471],{"class":204},[168,646,647],{"class":208}," [",[168,649,256],{"class":178},[168,651,253],{"class":208},[168,653,340],{"class":178},[168,655,656],{"class":208},"]:\n",[168,658,660,663,665],{"class":170,"line":659},18,[168,661,662],{"class":208},"    first, last ",[168,664,244],{"class":204},[168,666,667],{"class":208}," block(region)\n",[168,669,671,674,676,678,681,684,686,688,690,693,695,698,701,703],{"class":170,"line":670},19,[168,672,673],{"class":208},"    chart.series.append(Series(",[168,675,115],{"class":480},[168,677,244],{"class":204},[168,679,680],{"class":208},"Reference(ws, ",[168,682,683],{"class":480},"min_col",[168,685,244],{"class":204},[168,687,41],{"class":261},[168,689,253],{"class":208},[168,691,692],{"class":480},"min_row",[168,694,244],{"class":204},[168,696,697],{"class":208},"first, ",[168,699,700],{"class":480},"max_row",[168,702,244],{"class":204},[168,704,705],{"class":208},"last),\n",[168,707,709,712,714,716,718,720,722,724,726,728,730,732,734],{"class":170,"line":708},20,[168,710,711],{"class":480},"                               xvalues",[168,713,244],{"class":204},[168,715,680],{"class":208},[168,717,683],{"class":480},[168,719,244],{"class":204},[168,721,374],{"class":261},[168,723,253],{"class":208},[168,725,692],{"class":480},[168,727,244],{"class":204},[168,729,697],{"class":208},[168,731,700],{"class":480},[168,733,244],{"class":204},[168,735,705],{"class":208},[168,737,739,742,744,746,748,750,753,755,757,759,761,763,765],{"class":170,"line":738},21,[168,740,741],{"class":480},"                               zvalues",[168,743,244],{"class":204},[168,745,680],{"class":208},[168,747,683],{"class":480},[168,749,244],{"class":204},[168,751,752],{"class":261},"5",[168,754,253],{"class":208},[168,756,692],{"class":480},[168,758,244],{"class":204},[168,760,697],{"class":208},[168,762,700],{"class":480},[168,764,244],{"class":204},[168,766,705],{"class":208},[168,768,770,773,775],{"class":170,"line":769},22,[168,771,772],{"class":480},"                               title",[168,774,244],{"class":204},[168,776,777],{"class":208},"region))\n",[168,779,781,784,787,789,792,794],{"class":170,"line":780},23,[168,782,783],{"class":261},"print",[168,785,786],{"class":208},"([(s.tx.v, s.xVal.numRef.f, s.bubbleSize.numRef.f) ",[168,788,465],{"class":204},[168,790,791],{"class":208}," s ",[168,793,471],{"class":204},[168,795,796],{"class":208}," chart.series])\n",[10,798,799,801,802,804,805,807,808,811],{},[165,800,115],{}," is the vertical position, ",[165,803,97],{}," the horizontal and ",[165,806,133],{}," the size — the order is easy to mix up, and a chart with growth across and margin up still renders without complaint. Sorting the rows by region before writing keeps each group contiguous; with pandas, ",[165,809,810],{},"sort_values(\"Region\")"," does the same.",[153,813,815],{"id":814},"step-2-size-bubbles-by-area-and-set-the-scale","Step 2 — Size bubbles by area and set the scale",[10,817,818,819,822,823,827,828,831,832,835],{},"Two settings control the bubbles. ",[165,820,821],{},"sizeRepresents = \"area\""," makes a bubble's ",[824,825,826],"em",{},"area"," proportional to its value, which matches how people judge size; the alternative, ",[165,829,830],{},"\"w\"",", sizes by width, so a value twice as large looks four times as big. ",[165,833,834],{},"bubbleScale"," shrinks or enlarges every bubble as a percentage of the default:",[158,837,839],{"className":195,"code":838,"language":197,"meta":163,"style":163},"chart.sizeRepresents = \"area\"\nchart.bubbleScale = 60\nchart.x_axis.title = \"Margin %\"\nchart.y_axis.title = \"Growth % (year on year)\"\nchart.x_axis.scaling.min, chart.x_axis.scaling.max = 0, 40\nchart.y_axis.scaling.min, chart.y_axis.scaling.max = -10, 30\nchart.x_axis.delete = False\nchart.y_axis.delete = False\nchart.x_axis.tickLblPos = \"low\"\nchart.width, chart.height = 20, 12\nws.add_chart(chart, \"G2\")\nwb.save(\"portfolio_bubbles.xlsx\")\nprint(chart.sizeRepresents, chart.bubbleScale)\n",[165,840,841,851,861,871,881,896,914,924,933,943,958,969,979],{"__ignoreMap":163},[168,842,843,846,848],{"class":170,"line":171},[168,844,845],{"class":208},"chart.sizeRepresents ",[168,847,244],{"class":204},[168,849,850],{"class":178}," \"area\"\n",[168,852,853,856,858],{"class":170,"line":218},[168,854,855],{"class":208},"chart.bubbleScale ",[168,857,244],{"class":204},[168,859,860],{"class":261}," 60\n",[168,862,863,866,868],{"class":170,"line":231},[168,864,865],{"class":208},"chart.x_axis.title ",[168,867,244],{"class":204},[168,869,870],{"class":178}," \"Margin %\"\n",[168,872,873,876,878],{"class":170,"line":238},[168,874,875],{"class":208},"chart.y_axis.title ",[168,877,244],{"class":204},[168,879,880],{"class":178}," \"Growth % (year on year)\"\n",[168,882,883,886,888,891,893],{"class":170,"line":329},[168,884,885],{"class":208},"chart.x_axis.scaling.min, chart.x_axis.scaling.max ",[168,887,244],{"class":204},[168,889,890],{"class":261}," 0",[168,892,253],{"class":208},[168,894,895],{"class":261},"40\n",[168,897,898,901,903,906,909,911],{"class":170,"line":409},[168,899,900],{"class":208},"chart.y_axis.scaling.min, chart.y_axis.scaling.max ",[168,902,244],{"class":204},[168,904,905],{"class":204}," -",[168,907,908],{"class":261},"10",[168,910,253],{"class":208},[168,912,913],{"class":261},"30\n",[168,915,916,919,921],{"class":170,"line":430},[168,917,918],{"class":208},"chart.x_axis.delete ",[168,920,244],{"class":204},[168,922,923],{"class":261}," False\n",[168,925,926,929,931],{"class":170,"line":462},[168,927,928],{"class":208},"chart.y_axis.delete ",[168,930,244],{"class":204},[168,932,923],{"class":261},[168,934,935,938,940],{"class":170,"line":496},[168,936,937],{"class":208},"chart.x_axis.tickLblPos ",[168,939,244],{"class":204},[168,941,942],{"class":178}," \"low\"\n",[168,944,945,948,950,953,955],{"class":170,"line":502},[168,946,947],{"class":208},"chart.width, chart.height ",[168,949,244],{"class":204},[168,951,952],{"class":261}," 20",[168,954,253],{"class":208},[168,956,957],{"class":261},"12\n",[168,959,960,963,966],{"class":170,"line":507},[168,961,962],{"class":208},"ws.add_chart(chart, ",[168,964,965],{"class":178},"\"G2\"",[168,967,968],{"class":208},")\n",[168,970,971,974,977],{"class":170,"line":520},[168,972,973],{"class":208},"wb.save(",[168,975,976],{"class":178},"\"portfolio_bubbles.xlsx\"",[168,978,968],{"class":208},[168,980,981,983],{"class":170,"line":589},[168,982,783],{"class":261},[168,984,985],{"class":208},"(chart.sizeRepresents, chart.bubbleScale)\n",[10,987,988,989,992,993,996,997,1001],{},"Fixed axis ranges keep bubbles near the edges from being clipped and make the chart comparable from one report to the next. Because Panels has negative growth, the horizontal axis would cross the plot at zero with its labels in the middle; ",[165,990,991],{},"tickLblPos = \"low\""," moves the labels to the bottom edge. The ",[165,994,995],{},"delete = False"," lines keep both axes visible in Excel — see ",[14,998,1000],{"href":999},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fformat-chart-axes-with-openpyxl\u002F","Format Chart Axes with openpyxl",".",[20,1003,29,1008,29,1011,29,1014,29,1017,29,1025,29,1036,29,1040,29,1045,29,1050,29,1056,29,1060,29,1065,29,1069,29,1074,29,1079,29,1086,29,1091,29,1095,29,1099,29,1105,29,1108,29,1113,29,1116],{"viewBox":1004,"role":23,"ariaLabelledBy":1005,"xmlns":27,"style":28},"4 0 752 207",[1006,1007],"bub-size-t","bub-size-d",[31,1009,1010],{"id":1006},"Sizing bubbles by area or by width",[35,1012,1013],{"id":1007},"Two panels comparing Panels, with revenue 610, and Latches, with revenue 90, a ratio of about 6.8. Sized by area, the Panels bubble has 6.8 times the area of Latches, which looks right. Sized by width, its width is 6.8 times as large and its area about 46 times as large, which exaggerates the difference.",[39,1015],{"x":41,"y":42,"width":43,"height":1016,"fill":45},"207",[39,1018],{"x":48,"y":49,"width":1019,"height":1020,"rx":1021,"fill":1022,"stroke":1023,"style":1024},"351.0","175.0","14","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[1026,1027,1029],"g",{"transform":1028},"translate(182.5,29.0) scale(1.083)",[1030,1031],"path",{"d":1032,"fill":1033,"stroke":1034,"style":1035},"M5 12.5l4.5 4.5L19 7","none","var(--teal-ink,#0b6157)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[62,1037,821],{"x":1038,"y":83,"style":1039},"195.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[170,1041],{"x1":1042,"y1":1043,"x2":1044,"y2":1043,"stroke":1023,"style":53},"36.0","91.0","355.0",[62,1046,1049],{"x":1047,"y":109,"style":1048},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","610 vs 90 → 6.8× the area",[1026,1051,1053],{"transform":1052},"translate(38.5,99.5) scale(0.625)",[1030,1054],{"d":1032,"fill":1033,"stroke":1023,"style":1055},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[62,1057,1059],{"x":1047,"y":1058,"style":1048},"137.0","matches how size is judged",[1026,1061,1063],{"transform":1062},"translate(38.5,125.5) scale(0.625)",[1030,1064],{"d":1032,"fill":1033,"stroke":1023,"style":1055},[62,1066,1068],{"x":1047,"y":1067,"style":1048},"163.0","Excel's default",[1026,1070,1072],{"transform":1071},"translate(38.5,151.5) scale(0.625)",[1030,1073],{"d":1032,"fill":1033,"stroke":1023,"style":1055},[39,1075],{"x":1076,"y":49,"width":1019,"height":1020,"rx":1021,"fill":1077,"stroke":1078,"style":1024},"389.0","#fee8f2","var(--accent,#f43f8f)",[1026,1080,1082],{"transform":1081},"translate(551.5,29.0) scale(1.083)",[1030,1083],{"d":1084,"fill":1033,"stroke":1085,"style":1035},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)",[62,1087,1090],{"x":1088,"y":83,"style":1089},"564.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","sizeRepresents = \"w\"",[170,1092],{"x1":1093,"y1":1043,"x2":1094,"y2":1043,"stroke":1078,"style":53},"405.0","724.0",[62,1096,1098],{"x":1097,"y":109,"style":1048},"429.0","610 vs 90 → 6.8× the width",[1026,1100,1102],{"transform":1101},"translate(407.5,99.5) scale(0.625)",[1030,1103],{"d":1104,"fill":1033,"stroke":1078,"style":1055},"M6 6l12 12 M18 6L6 18",[62,1106,1107],{"x":1097,"y":1058,"style":1048},"about 46× the area",[1026,1109,1111],{"transform":1110},"translate(407.5,125.5) scale(0.625)",[1030,1112],{"d":1104,"fill":1033,"stroke":1078,"style":1055},[62,1114,1115],{"x":1097,"y":1067,"style":1048},"big values exaggerated",[1026,1117,1119],{"transform":1118},"translate(407.5,151.5) scale(0.625)",[1030,1120],{"d":1104,"fill":1033,"stroke":1078,"style":1055},[153,1122,1124],{"id":1123},"step-3-label-every-bubble-with-its-name","Step 3 — Label every bubble with its name",[10,1126,1127,1128,1131],{},"A bubble chart without names makes readers match colours and sizes back to a table. openpyxl cannot label points with arbitrary cell text, but it can show a series' ",[824,1129,1130],{},"name"," as its label — so give each product its own series, coloured by region, and turn on series-name labels:",[158,1133,1135],{"className":195,"code":1134,"language":197,"meta":163,"style":163},"from openpyxl.chart.label import DataLabelList\n\nREGION_COLOURS = {\"North\": \"0B6157\", \"South\": \"B4740A\"}\nlabelled = BubbleChart()\nlabelled.title = \"Product portfolio — labelled\"\nfor i in range(2, ws.max_row + 1):\n    name, region = ws.cell(row=i, column=1).value, ws.cell(row=i, column=2).value\n    series = Series(values=Reference(ws, min_col=4, min_row=i), xvalues=Reference(ws, min_col=3, min_row=i),\n                    zvalues=Reference(ws, min_col=5, min_row=i), title=name)\n    series.graphicalProperties.solidFill = REGION_COLOURS[region]\n    series.graphicalProperties.line.solidFill = \"FFFFFF\"\n    series.dLbls = DataLabelList(showSerName=True, showVal=False)\n    labelled.series.append(series)\nlabelled.sizeRepresents = \"area\"\nlabelled.bubbleScale = 60\nlabelled.legend = None\nlabelled.x_axis.title, labelled.y_axis.title = \"Margin %\", \"Growth % (year on year)\"\nlabelled.x_axis.scaling.min, labelled.x_axis.scaling.max = 0, 40\nlabelled.y_axis.scaling.min, labelled.y_axis.scaling.max = -10, 30\nlabelled.x_axis.delete = labelled.y_axis.delete = False\nlabelled.x_axis.tickLblPos = \"low\"\nlabelled.width, labelled.height = 20, 12\nws.add_chart(labelled, \"G27\")\nwb.save(\"portfolio_bubbles.xlsx\")\nprint(len(labelled.series), \"series, one per product:\", [s.tx.v for s in labelled.series])\n",[165,1136,1137,1149,1153,1184,1193,1203,1226,1265,1317,1347,1360,1370,1400,1405,1414,1423,1433,1448,1461,1476,1490,1499,1512,1522,1531],{"__ignoreMap":163},[168,1138,1139,1141,1144,1146],{"class":170,"line":171},[168,1140,205],{"class":204},[168,1142,1143],{"class":208}," openpyxl.chart.label ",[168,1145,212],{"class":204},[168,1147,1148],{"class":208}," DataLabelList\n",[168,1150,1151],{"class":170,"line":218},[168,1152,235],{"emptyLinePlaceholder":234},[168,1154,1155,1158,1161,1164,1166,1169,1172,1174,1176,1178,1181],{"class":170,"line":231},[168,1156,1157],{"class":261},"REGION_COLOURS",[168,1159,1160],{"class":204}," =",[168,1162,1163],{"class":208}," {",[168,1165,256],{"class":178},[168,1167,1168],{"class":208},": ",[168,1170,1171],{"class":178},"\"0B6157\"",[168,1173,253],{"class":208},[168,1175,340],{"class":178},[168,1177,1168],{"class":208},[168,1179,1180],{"class":178},"\"B4740A\"",[168,1182,1183],{"class":208},"}\n",[168,1185,1186,1189,1191],{"class":170,"line":238},[168,1187,1188],{"class":208},"labelled ",[168,1190,244],{"class":204},[168,1192,623],{"class":208},[168,1194,1195,1198,1200],{"class":170,"line":329},[168,1196,1197],{"class":208},"labelled.title ",[168,1199,244],{"class":204},[168,1201,1202],{"class":178}," \"Product portfolio — labelled\"\n",[168,1204,1205,1207,1209,1211,1213,1215,1217,1219,1221,1223],{"class":170,"line":409},[168,1206,465],{"class":204},[168,1208,533],{"class":208},[168,1210,471],{"class":204},[168,1212,538],{"class":261},[168,1214,541],{"class":208},[168,1216,544],{"class":261},[168,1218,547],{"class":208},[168,1220,550],{"class":204},[168,1222,553],{"class":261},[168,1224,1225],{"class":208},"):\n",[168,1227,1228,1231,1233,1235,1237,1239,1241,1243,1245,1247,1250,1252,1254,1256,1258,1260,1262],{"class":170,"line":430},[168,1229,1230],{"class":208},"    name, region ",[168,1232,244],{"class":204},[168,1234,562],{"class":208},[168,1236,565],{"class":480},[168,1238,244],{"class":204},[168,1240,570],{"class":208},[168,1242,573],{"class":480},[168,1244,244],{"class":204},[168,1246,490],{"class":261},[168,1248,1249],{"class":208},").value, ws.cell(",[168,1251,565],{"class":480},[168,1253,244],{"class":204},[168,1255,570],{"class":208},[168,1257,573],{"class":480},[168,1259,244],{"class":204},[168,1261,544],{"class":261},[168,1263,1264],{"class":208},").value\n",[168,1266,1267,1270,1272,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1296,1298,1300,1302,1304,1306,1308,1310,1312,1314],{"class":170,"line":462},[168,1268,1269],{"class":208},"    series ",[168,1271,244],{"class":204},[168,1273,1274],{"class":208}," Series(",[168,1276,115],{"class":480},[168,1278,244],{"class":204},[168,1280,680],{"class":208},[168,1282,683],{"class":480},[168,1284,244],{"class":204},[168,1286,41],{"class":261},[168,1288,253],{"class":208},[168,1290,692],{"class":480},[168,1292,244],{"class":204},[168,1294,1295],{"class":208},"i), ",[168,1297,97],{"class":480},[168,1299,244],{"class":204},[168,1301,680],{"class":208},[168,1303,683],{"class":480},[168,1305,244],{"class":204},[168,1307,374],{"class":261},[168,1309,253],{"class":208},[168,1311,692],{"class":480},[168,1313,244],{"class":204},[168,1315,1316],{"class":208},"i),\n",[168,1318,1319,1322,1324,1326,1328,1330,1332,1334,1336,1338,1340,1342,1344],{"class":170,"line":496},[168,1320,1321],{"class":480},"                    zvalues",[168,1323,244],{"class":204},[168,1325,680],{"class":208},[168,1327,683],{"class":480},[168,1329,244],{"class":204},[168,1331,752],{"class":261},[168,1333,253],{"class":208},[168,1335,692],{"class":480},[168,1337,244],{"class":204},[168,1339,1295],{"class":208},[168,1341,31],{"class":480},[168,1343,244],{"class":204},[168,1345,1346],{"class":208},"name)\n",[168,1348,1349,1352,1354,1357],{"class":170,"line":502},[168,1350,1351],{"class":208},"    series.graphicalProperties.solidFill ",[168,1353,244],{"class":204},[168,1355,1356],{"class":261}," REGION_COLOURS",[168,1358,1359],{"class":208},"[region]\n",[168,1361,1362,1365,1367],{"class":170,"line":507},[168,1363,1364],{"class":208},"    series.graphicalProperties.line.solidFill ",[168,1366,244],{"class":204},[168,1368,1369],{"class":178}," \"FFFFFF\"\n",[168,1371,1372,1375,1377,1380,1383,1385,1388,1390,1393,1395,1398],{"class":170,"line":520},[168,1373,1374],{"class":208},"    series.dLbls ",[168,1376,244],{"class":204},[168,1378,1379],{"class":208}," DataLabelList(",[168,1381,1382],{"class":480},"showSerName",[168,1384,244],{"class":204},[168,1386,1387],{"class":261},"True",[168,1389,253],{"class":208},[168,1391,1392],{"class":480},"showVal",[168,1394,244],{"class":204},[168,1396,1397],{"class":261},"False",[168,1399,968],{"class":208},[168,1401,1402],{"class":170,"line":589},[168,1403,1404],{"class":208},"    labelled.series.append(series)\n",[168,1406,1407,1410,1412],{"class":170,"line":610},[168,1408,1409],{"class":208},"labelled.sizeRepresents ",[168,1411,244],{"class":204},[168,1413,850],{"class":178},[168,1415,1416,1419,1421],{"class":170,"line":615},[168,1417,1418],{"class":208},"labelled.bubbleScale ",[168,1420,244],{"class":204},[168,1422,860],{"class":261},[168,1424,1425,1428,1430],{"class":170,"line":626},[168,1426,1427],{"class":208},"labelled.legend ",[168,1429,244],{"class":204},[168,1431,1432],{"class":261}," None\n",[168,1434,1435,1438,1440,1443,1445],{"class":170,"line":637},[168,1436,1437],{"class":208},"labelled.x_axis.title, labelled.y_axis.title ",[168,1439,244],{"class":204},[168,1441,1442],{"class":178}," \"Margin %\"",[168,1444,253],{"class":208},[168,1446,1447],{"class":178},"\"Growth % (year on year)\"\n",[168,1449,1450,1453,1455,1457,1459],{"class":170,"line":659},[168,1451,1452],{"class":208},"labelled.x_axis.scaling.min, labelled.x_axis.scaling.max ",[168,1454,244],{"class":204},[168,1456,890],{"class":261},[168,1458,253],{"class":208},[168,1460,895],{"class":261},[168,1462,1463,1466,1468,1470,1472,1474],{"class":170,"line":670},[168,1464,1465],{"class":208},"labelled.y_axis.scaling.min, labelled.y_axis.scaling.max ",[168,1467,244],{"class":204},[168,1469,905],{"class":204},[168,1471,908],{"class":261},[168,1473,253],{"class":208},[168,1475,913],{"class":261},[168,1477,1478,1481,1483,1486,1488],{"class":170,"line":708},[168,1479,1480],{"class":208},"labelled.x_axis.delete ",[168,1482,244],{"class":204},[168,1484,1485],{"class":208}," labelled.y_axis.delete ",[168,1487,244],{"class":204},[168,1489,923],{"class":261},[168,1491,1492,1495,1497],{"class":170,"line":738},[168,1493,1494],{"class":208},"labelled.x_axis.tickLblPos ",[168,1496,244],{"class":204},[168,1498,942],{"class":178},[168,1500,1501,1504,1506,1508,1510],{"class":170,"line":769},[168,1502,1503],{"class":208},"labelled.width, labelled.height ",[168,1505,244],{"class":204},[168,1507,952],{"class":261},[168,1509,253],{"class":208},[168,1511,957],{"class":261},[168,1513,1514,1517,1520],{"class":170,"line":780},[168,1515,1516],{"class":208},"ws.add_chart(labelled, ",[168,1518,1519],{"class":178},"\"G27\"",[168,1521,968],{"class":208},[168,1523,1525,1527,1529],{"class":170,"line":1524},24,[168,1526,973],{"class":208},[168,1528,976],{"class":178},[168,1530,968],{"class":208},[168,1532,1534,1536,1538,1541,1544,1547,1550,1552,1554,1556],{"class":170,"line":1533},25,[168,1535,783],{"class":261},[168,1537,541],{"class":208},[168,1539,1540],{"class":261},"len",[168,1542,1543],{"class":208},"(labelled.series), ",[168,1545,1546],{"class":178},"\"series, one per product:\"",[168,1548,1549],{"class":208},", [s.tx.v ",[168,1551,465],{"class":204},[168,1553,791],{"class":208},[168,1555,471],{"class":204},[168,1557,1558],{"class":208}," labelled.series])\n",[10,1560,1561,1562,1565],{},"Each bubble now carries its product name, and colours still show the region; the legend is dropped because it would list six products rather than two regions — say what the colours mean in the title or a note beside the chart. ",[165,1563,1564],{},"showVal=False"," stops the label also printing the y value. Beyond a dozen or so bubbles, labels start to collide, and labelling only the largest few is clearer.",[20,1567,29,1573,29,1576,29,1579,29,1584,29,1590,29,1595,29,1599,29,1605,29,1609,29,1613,29,1617,29,1621,29,1625,29,1629,29,1633,29,1637,29,1641,29,1644,29,1646,29,1650,29,1652,29,1654,29,1656,29,1658,29,1660,29,1662,29,1664,29,1667,29,1670,29,1673,29,1675,29,1679,29,1681,29,1684,29,1686,29,1688,29,1690,29,1692,29,1694,29,1696,29,1699,29,1702,29,1704,29,1707,29,1709,29,1711,29,1713,29,1715,29,1717,29,1719,29,1721,29,1723,29,1726,29,1728,29,1730,29,1733,29,1735,29,1737,29,1739,29,1741,29,1744,29,1748,29,1752,29,1755,29,1758,29,1761,29,1763,29,1766,29,1768,29,1771,29,1773,29,1775,29,1777,29,1779,29,1781,29,1783,29,1786,29,1790,29,1792,29,1795,29,1797,29,1799,29,1801,29,1803,29,1805,29,1807,29,1809,29,1811,29,1814,29,1818,29,1820,29,1823,29,1825,29,1827,29,1829,29,1831,29,1833,29,1835,29,1837],{"viewBox":1568,"role":23,"ariaLabelledBy":1569,"xmlns":27,"style":1572},"142 0 476 284",[1570,1571],"bub-data-t","bub-data-d","width:100%;max-width:476px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1574,1575],{"id":1570},"The portfolio data behind the chart",[35,1577,1578],{"id":1571},"The Portfolio sheet sorted by region. North: Brackets margin 32, growth 8, revenue 480; Hinges 24, 15, 220; Panels 18, minus 4, 610. South: Frames 28, 22, 150; Latches 35, 3, 90; Rails 12, 11, 330. Panels is the largest bubble but the only product shrinking.",[39,1580],{"x":1581,"y":42,"width":1582,"height":1583,"fill":45},"142","476","284",[62,1585,1589],{"x":1586,"y":1587,"style":1588},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Portfolio",[39,1591],{"x":1592,"y":1593,"width":82,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"158.1","48.0","24.0",[39,1596],{"x":1597,"y":1593,"width":1598,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"190.1","78.7",[62,1600,1604],{"x":1601,"y":1602,"style":1603},"229.5","65.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,1606],{"x":1607,"y":1593,"width":1608,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"268.8","66.7",[62,1610,1612],{"x":1611,"y":1602,"style":1603},"302.2","B",[39,1614],{"x":1615,"y":1593,"width":1616,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"335.5","83.2",[62,1618,1620],{"x":1619,"y":1602,"style":1603},"377.2","C",[39,1622],{"x":1623,"y":1593,"width":1624,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"418.8","85.3",[62,1626,1628],{"x":1627,"y":1602,"style":1603},"461.4","D",[39,1630],{"x":1631,"y":1593,"width":1632,"height":1594,"rx":42,"fill":79,"stroke":52,"style":53},"504.0","97.9",[62,1634,1636],{"x":1635,"y":1602,"style":1603},"552.9","E",[39,1638],{"x":1592,"y":1639,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"72.0","28.0",[62,1642,490],{"x":1643,"y":1043,"style":1603},"174.1",[39,1645],{"x":1597,"y":1639,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1647,1649],{"x":1601,"y":1043,"style":1648},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Product",[39,1651],{"x":1607,"y":1639,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1653,140],{"x":1611,"y":1043,"style":1648},[39,1655],{"x":1615,"y":1639,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1657,85],{"x":1619,"y":1043,"style":1648},[39,1659],{"x":1623,"y":1639,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1661,104],{"x":1627,"y":1043,"style":1648},[39,1663],{"x":1631,"y":1639,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1665,1666],{"x":1635,"y":1043,"style":1648},"Revenue (k)",[39,1668],{"x":1592,"y":1669,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"100.0",[62,1671,544],{"x":1643,"y":1672,"style":1603},"119.0",[39,1674],{"x":1597,"y":1669,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1676,1678],{"x":1601,"y":1672,"style":1677},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Brackets",[39,1680],{"x":1607,"y":1669,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1682,1683],{"x":1611,"y":1672,"style":1677},"North",[39,1685],{"x":1615,"y":1669,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1687,262],{"x":1619,"y":1672,"style":1677},[39,1689],{"x":1623,"y":1669,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1691,267],{"x":1627,"y":1672,"style":1677},[39,1693],{"x":1631,"y":1669,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1695,272],{"x":1635,"y":1672,"style":1677},[39,1697],{"x":1592,"y":1698,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"128.0",[62,1700,374],{"x":1643,"y":1701,"style":1603},"147.0",[39,1703],{"x":1597,"y":1698,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1705,1706],{"x":1601,"y":1701,"style":1677},"Hinges",[39,1708],{"x":1607,"y":1698,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1710,1683],{"x":1611,"y":1701,"style":1677},[39,1712],{"x":1615,"y":1698,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1714,287],{"x":1619,"y":1701,"style":1677},[39,1716],{"x":1623,"y":1698,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1718,292],{"x":1627,"y":1701,"style":1677},[39,1720],{"x":1631,"y":1698,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1722,297],{"x":1635,"y":1701,"style":1677},[39,1724],{"x":1592,"y":1725,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"156.0",[62,1727,41],{"x":1643,"y":1020,"style":1603},[39,1729],{"x":1597,"y":1725,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1731,1732],{"x":1601,"y":1020,"style":1677},"Panels",[39,1734],{"x":1607,"y":1725,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1736,1683],{"x":1611,"y":1020,"style":1677},[39,1738],{"x":1615,"y":1725,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1740,311],{"x":1619,"y":1020,"style":1677},[39,1742],{"x":1623,"y":1725,"width":1624,"height":1640,"rx":42,"fill":1077,"stroke":1078,"style":1743},"stroke-width:1.5px",[62,1745,1747],{"x":1627,"y":1020,"style":1746},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","-4",[39,1749],{"x":1631,"y":1725,"width":1632,"height":1640,"rx":42,"fill":1750,"stroke":1751,"style":1743},"#fdefd8","var(--gold,#b4740a)",[62,1753,323],{"x":1635,"y":1020,"style":1754},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[39,1756],{"x":1592,"y":1757,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"184.0",[62,1759,752],{"x":1643,"y":1760,"style":1603},"203.0",[39,1762],{"x":1597,"y":1757,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1764,1765],{"x":1601,"y":1760,"style":1677},"Frames",[39,1767],{"x":1607,"y":1757,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1769,1770],{"x":1611,"y":1760,"style":1677},"South",[39,1772],{"x":1615,"y":1757,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1774,345],{"x":1619,"y":1760,"style":1677},[39,1776],{"x":1623,"y":1757,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1778,350],{"x":1627,"y":1760,"style":1677},[39,1780],{"x":1631,"y":1757,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1782,355],{"x":1635,"y":1760,"style":1677},[39,1784],{"x":1592,"y":1785,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"212.0",[62,1787,1789],{"x":1643,"y":1788,"style":1603},"231.0","6",[39,1791],{"x":1597,"y":1785,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1793,1794],{"x":1601,"y":1788,"style":1677},"Latches",[39,1796],{"x":1607,"y":1785,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1798,1770],{"x":1611,"y":1788,"style":1677},[39,1800],{"x":1615,"y":1785,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1802,369],{"x":1619,"y":1788,"style":1677},[39,1804],{"x":1623,"y":1785,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1806,374],{"x":1627,"y":1788,"style":1677},[39,1808],{"x":1631,"y":1785,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1810,379],{"x":1635,"y":1788,"style":1677},[39,1812],{"x":1592,"y":1813,"width":82,"height":1640,"rx":42,"fill":79,"stroke":52,"style":53},"240.0",[62,1815,1817],{"x":1643,"y":1816,"style":1603},"259.0","7",[39,1819],{"x":1597,"y":1813,"width":1598,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1821,1822],{"x":1601,"y":1816,"style":1677},"Rails",[39,1824],{"x":1607,"y":1813,"width":1608,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1826,1770],{"x":1611,"y":1816,"style":1677},[39,1828],{"x":1615,"y":1813,"width":1616,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1830,393],{"x":1619,"y":1816,"style":1677},[39,1832],{"x":1623,"y":1813,"width":1624,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1834,398],{"x":1627,"y":1816,"style":1677},[39,1836],{"x":1631,"y":1813,"width":1632,"height":1640,"rx":42,"fill":45,"stroke":52,"style":53},[62,1838,403],{"x":1635,"y":1816,"style":1677},[153,1840,1842],{"id":1841},"reading-the-chart","Reading the chart",[10,1844,1845],{},"The layout invites a four-quadrant reading. Top right — high margin and growing — are the products to invest in; here Frames stands out, small but growing fastest at a good margin. Bottom left would be low margin and shrinking. The biggest bubble, Panels, sits low and to the left: the largest revenue line has the thinnest margin and is shrinking, which is the kind of finding the chart exists to surface. Add reference lines at the average margin and growth by charting two extra series with two points each, or simply mention the averages in the title.",[153,1847,1849],{"id":1848},"keep-bubble-sizes-comparable-between-reports","Keep bubble sizes comparable between reports",[10,1851,1852],{},"Excel sizes bubbles relative to the largest value in the chart: the biggest bubble always gets the same diameter, and the rest are scaled against it. That is fine within one chart, but it means sizes cannot be compared between two charts — last quarter's 610 and this quarter's 900 would both be drawn as the same largest bubble. When a report is produced every month and readers compare the charts side by side, add a reference point with a fixed size larger than any real value, such as 1,000, placed outside the visible axis range or in a corner with a label like \"1,000 k\". Every chart then scales against the same reference, and a bubble of the same revenue has the same size in every edition. The same idea helps when one chart shows several groups whose largest members differ greatly: the reference keeps the small group from being scaled up to look as important as the large one. Mention the reference in the chart title or a note, so that nobody mistakes it for a real product.",[153,1854,1856],{"id":1855},"common-pitfalls","Common pitfalls",[1858,1859,1860,1877,1886,1892,1898,1907],"ul",{},[1861,1862,1863,1867,1868,1870,1871,1873,1874,1876],"li",{},[1864,1865,1866],"strong",{},"Swapped arguments."," ",[165,1869,115],{}," is vertical, ",[165,1872,97],{}," horizontal, ",[165,1875,133],{}," size.",[1861,1878,1879,1882,1883,1885],{},[1864,1880,1881],{},"Sizing by width."," Use ",[165,1884,821],{}," so large values are not exaggerated.",[1861,1887,1888,1891],{},[1864,1889,1890],{},"Non-contiguous groups."," Sort rows by group so each series references one block.",[1861,1893,1894,1897],{},[1864,1895,1896],{},"Negative bubble sizes."," Sizes must be positive; Excel hides or misdraws bubbles with negative size values.",[1861,1899,1900,1903,1904,1906],{},[1864,1901,1902],{},"Missing axes in Excel."," Set ",[165,1905,995],{}," on both axes.",[1861,1908,1909,1912],{},[1864,1910,1911],{},"Too many labelled bubbles."," Label only the few that matter when there are many points.",[153,1914,1916],{"id":1915},"performance-notes","Performance notes",[10,1918,1919],{},"Each series is a small XML element; a chart with a series per product is fine for dozens of products. Hundreds of bubbles are unreadable before they are slow — aggregate to groups, or use a scatter chart with small markers.",[153,1921,1923],{"id":1922},"conclusion","Conclusion",[10,1925,1926,1927,1930,1931,1934,1935,1937,1938,1940],{},"Lay out one column each for the x position, the y position and the size, sorted so each group is contiguous, and add a ",[165,1928,1929],{},"Series(values=..., xvalues=..., zvalues=..., title=...)"," per group to a ",[165,1932,1933],{},"BubbleChart",". Size bubbles by area, adjust ",[165,1936,834],{},", fix the axis ranges, keep axes visible with ",[165,1939,995],{},", and move labels to the bottom when values go negative. To name every bubble, give each item its own series, colour it by group and show series-name data labels.",[153,1942,1944],{"id":1943},"frequently-asked-questions","Frequently asked questions",[10,1946,1947,1950],{},[1864,1948,1949],{},"How do I make a bubble chart with openpyxl?","\nCreate a BubbleChart and append Series objects built with Series(values=y_ref, xvalues=x_ref, zvalues=size_ref, title=...), where each argument is a Reference to a column of cells. Place the chart with ws.add_chart and set both axes' delete to False.",[10,1952,1953,1956],{},[1864,1954,1955],{},"Should bubble size represent area or width?","\nArea. With sizeRepresents = \"area\", a value twice as large gets a bubble with twice the area, which is how people perceive size. Sizing by width makes large values look far bigger than they are, because the area grows with the square.",[10,1958,1959,1962],{},[1864,1960,1961],{},"How do I label each bubble with its name?","\nopenpyxl cannot label points with text from cells. Put each item in its own series titled with its name, and turn on data labels that show the series name. With a handful of bubbles this reads well; with many, labels overlap.",[10,1964,1965,1968],{},[1864,1966,1967],{},"How do I colour bubbles by group?","\nAdd one series per group — region, category, segment. Each series gets its own colour and legend entry.",[153,1970,1972],{"id":1971},"related","Related",[1858,1974,1975,1981,1988,1995],{},[1861,1976,1977,1978,1980],{},"Up one level: ",[14,1979,17],{"href":16}," — every chart type.",[1861,1982,1983,1987],{},[14,1984,1986],{"href":1985},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-scatter-chart-in-excel-with-openpyxl\u002F","Create a Scatter Chart in Excel with openpyxl"," — two measures without size.",[1861,1989,1990,1994],{},[14,1991,1993],{"href":1992},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl\u002F","Style Chart Colors and Data Labels with openpyxl"," — more on labels and colours.",[1861,1996,1997,2001],{},[14,1998,2000],{"href":1999},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-a-pareto-analysis-on-excel-data-with-pandas\u002F","Run a Pareto Analysis on Excel Data with pandas"," — which products carry the revenue.",[2003,2004,2005],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":163,"searchDepth":218,"depth":218,"links":2007},[2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018],{"id":155,"depth":218,"text":156},{"id":188,"depth":218,"text":189},{"id":814,"depth":218,"text":815},{"id":1123,"depth":218,"text":1124},{"id":1841,"depth":218,"text":1842},{"id":1848,"depth":218,"text":1849},{"id":1855,"depth":218,"text":1856},{"id":1915,"depth":218,"text":1916},{"id":1922,"depth":218,"text":1923},{"id":1943,"depth":218,"text":1944},{"id":1971,"depth":218,"text":1972},"2026-09-25","Plot three measures at once — margin across, growth up, revenue as bubble size — to see a product portfolio or customer list at a glance. Build a native Excel bubble chart with openpyxl, add one series per group, size bubbles by area, scale them, label each bubble with its name, and keep axis labels clear of negative values.","md",[2023,2025,2027,2029],{"q":1949,"a":2024},"Create a BubbleChart and append Series objects built with Series(values=y_ref, xvalues=x_ref, zvalues=size_ref, title=...), where each argument is a Reference to a column of cells. Place the chart with ws.add_chart and set both axes' delete to False.",{"q":1955,"a":2026},"Area. With sizeRepresents = \"area\", a value twice as large gets a bubble with twice the area, which is how people perceive size. Sizing by width makes large values look far bigger than they are, because the area grows with the square.",{"q":1961,"a":2028},"openpyxl cannot label points with text from cells. Put each item in its own series titled with its name, and turn on data labels that show the series name. With a handful of bubbles this reads well; with many, labels overlap.",{"q":1967,"a":2030},"Add one series per group — region, category, segment. Each series gets its own colour and legend entry.","create a bubble chart with openpyxl",null,{"breadcrumb":2034},[2035,2038,2041],{"name":2036,"item":2037},"Home","\u002F",{"name":2039,"item":2040},"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-bubble-chart-with-openpyxl",{"title":2044,"description":2045},"Excel Bubble Chart with Python openpyxl (Three Variables)","Create a bubble chart with openpyxl BubbleChart: Series with xvalues, values and zvalues, one series per group, sizeRepresents area versus width, bubbleScale, labelling bubbles with series names, axis titles and ranges, and visible axes in Excel.","create-a-bubble-chart-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-bubble-chart-with-openpyxl\u002Findex",[2049,2050],"charts-and-images","openpyxl","how-to","Zxkxgft4mEbkKOv0V-DGSn5y9CZ8-q6uleNiRkJxNW4",[2054,2058],{"title":2055,"path":2056,"stem":2057,"children":-1},"Create a Box and Whisker Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-box-and-whisker-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-box-and-whisker-chart-in-excel-with-python\u002Findex",{"title":2059,"path":2060,"stem":2061,"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",[2063,2065],{"path":2064,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2066,"title":2039},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2069,2072,2075,2078,2081,2084],{"path":2070,"title":2071,"kind":2051},"\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":2073,"title":2074,"kind":2051},"\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":2076,"title":2077,"kind":2051},"\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":2079,"title":2080,"kind":2051},"\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":2082,"title":2083,"kind":2051},"\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":2085,"title":2086,"kind":2051},"\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",1790972108955]