[{"data":1,"prerenderedAt":2447},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python":2414,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python":2423,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python":2428,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python":2429},{"id":4,"title":5,"body":6,"dateModified":2381,"datePublished":2381,"description":2382,"extension":2383,"faq":2384,"keyword":2393,"kind":2394,"meta":2395,"navigation":252,"path":2403,"seo":2404,"slug":2407,"stem":2408,"topics":2409,"type":2412,"__hash__":2413},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python\u002Findex.md","Create a Histogram in Excel with Python",{"type":7,"value":8,"toc":2368},"minimark",[9,19,152,157,191,197,201,211,624,627,724,727,731,736,933,952,956,963,1350,1357,1604,1608,1615,1926,1934,1938,1945,2205,2215,2219,2269,2273,2281,2285,2303,2307,2313,2319,2325,2331,2335,2364],[10,11,12,13,18],"p",{},"Averages hide most of what matters about a set of values: whether they bunch together tightly or spread widely, whether there are two groups instead of one, whether a handful of huge orders drag the mean up. A histogram shows all of that at a glance — how many values fall in each range. Excel has a built-in histogram chart, but it belongs to a newer chart format that neither openpyxl nor xlsxwriter can write. The reliable approach is to count the values into bins in Python and chart the counts as touching columns. 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 histogram of order values that way.",[20,21,29,30,29,34,29,38,29,46,29,56,29,67,29,74,29,79,29,85,29,90,29,93,29,99,29,103,29,106,29,110,29,113,29,116,29,122,29,126,29,129,29,133,29,136,29,139,29,145,29,149],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 752 125","img",[25,26],"hist-flow-t","hist-flow-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},"From raw values to a histogram chart",[35,36,37],"desc",{"id":26},"Four steps. Choose bin edges with a round width. Count how many values fall in each bin. Write a table of bin labels and counts to the sheet. Chart the counts as a column chart with no gaps between the columns.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","752","125","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"20.0","16.0","153.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(84.5,29.0) scale(1.000)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Choose bins",[68,75,78],{"x":70,"y":76,"style":77},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","round width",[80,81],"line",{"x1":82,"y1":83,"x2":84,"y2":83,"stroke":54,"style":55},"178.0","62.5","200.0",[86,87],"polygon",{"points":88,"fill":89},"204.0,62.5 195.0,67.5 195.0,57.5","#5b5cf0",[39,91],{"x":92,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"209.0",[57,94,96],{"transform":95},"translate(273.5,29.0) scale(1.000)",[61,97],{"d":98,"fill":64,"stroke":65,"style":66},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[68,100,102],{"x":101,"y":71,"style":72},"285.5","Count",[68,104,105],{"x":101,"y":76,"style":77},"np.histogram",[80,107],{"x1":108,"y1":83,"x2":109,"y2":83,"stroke":54,"style":55},"367.0","389.0",[86,111],{"points":112,"fill":89},"393.0,62.5 384.0,67.5 384.0,57.5",[39,114],{"x":115,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"398.0",[57,117,119],{"transform":118},"translate(462.5,29.0) scale(1.000)",[61,120],{"d":121,"fill":64,"stroke":65,"style":66},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[68,123,125],{"x":124,"y":71,"style":72},"474.5","Write table",[68,127,128],{"x":124,"y":76,"style":77},"label and count",[80,130],{"x1":131,"y1":83,"x2":132,"y2":83,"stroke":54,"style":55},"556.0","578.0",[86,134],{"points":135,"fill":89},"582.0,62.5 573.0,67.5 573.0,57.5",[39,137],{"x":138,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"587.0",[57,140,142],{"transform":141},"translate(651.5,29.0) scale(1.000)",[61,143],{"d":144,"fill":64,"stroke":65,"style":66},"M4 20h16 M7 17v-5 M12 17V7 M17 17v-8",[68,146,148],{"x":147,"y":71,"style":72},"663.5","Chart",[68,150,151],{"x":147,"y":76,"style":77},"columns, gap 0",[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 numpy pandas openpyxl xlsxwriter\n","bash","",[165,166,167],"code",{"__ignoreMap":163},[168,169,171,175,179,182,185,188],"span",{"class":80,"line":170},1,[168,172,174],{"class":173},"sMTad","pip",[168,176,178],{"class":177},"srMev"," install",[168,180,181],{"class":177}," numpy",[168,183,184],{"class":177}," pandas",[168,186,187],{"class":177}," openpyxl",[168,189,190],{"class":177}," xlsxwriter\n",[10,192,193,196],{},[165,194,195],{},"orders.xlsx"," holds 480 order values. Most are between about 40 and 250; a handful are far larger.",[153,198,200],{"id":199},"step-1-choose-readable-bins","Step 1 — Choose readable bins",[10,202,203,206,207,210],{},[165,204,205],{},"numpy.histogram_bin_edges"," with ",[165,208,209],{},"bins=\"auto\""," picks a bin count suited to the amount of data. Its edges are rarely round numbers, though, and bin labels such as \"47.3–61.9\" are hard to read. Use the suggested width as a guide and round it to a friendly step:",[158,212,216],{"className":213,"code":214,"language":215,"meta":163,"style":163},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\norders = pd.read_excel(\"orders.xlsx\")\nvalues = orders[\"Value\"].to_numpy()\nauto = np.histogram_bin_edges(values, bins=\"auto\")\nprint(len(auto) - 1, \"auto bins of width\", round(auto[1] - auto[0], 1))\n\ndef nice_width(raw):\n    for step in [1, 2, 2.5, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000]:\n        if step >= raw:\n            return step\n    return raw\n\ncap = 400\nwidth = nice_width((np.percentile(values, 99) - values.min()) \u002F 14)\nedges = np.arange(0, cap + width, width)\nprint(\"width\", width, \"edges\", edges[:4], \"…\", edges[-2:], \"| values over\", cap, \":\", int((values >= cap).sum()))\n","python",[165,217,218,234,247,254,272,289,311,368,373,386,466,480,489,498,503,514,544,566],{"__ignoreMap":163},[168,219,220,224,228,231],{"class":80,"line":170},[168,221,223],{"class":222},"s-kum","import",[168,225,227],{"class":226},"skGVy"," numpy ",[168,229,230],{"class":222},"as",[168,232,233],{"class":226}," np\n",[168,235,237,239,242,244],{"class":80,"line":236},2,[168,238,223],{"class":222},[168,240,241],{"class":226}," pandas ",[168,243,230],{"class":222},[168,245,246],{"class":226}," pd\n",[168,248,250],{"class":80,"line":249},3,[168,251,253],{"emptyLinePlaceholder":252},true,"\n",[168,255,257,260,263,266,269],{"class":80,"line":256},4,[168,258,259],{"class":226},"orders ",[168,261,262],{"class":222},"=",[168,264,265],{"class":226}," pd.read_excel(",[168,267,268],{"class":177},"\"orders.xlsx\"",[168,270,271],{"class":226},")\n",[168,273,275,278,280,283,286],{"class":80,"line":274},5,[168,276,277],{"class":226},"values ",[168,279,262],{"class":222},[168,281,282],{"class":226}," orders[",[168,284,285],{"class":177},"\"Value\"",[168,287,288],{"class":226},"].to_numpy()\n",[168,290,292,295,297,300,304,306,309],{"class":80,"line":291},6,[168,293,294],{"class":226},"auto ",[168,296,262],{"class":222},[168,298,299],{"class":226}," np.histogram_bin_edges(values, ",[168,301,303],{"class":302},"sa561","bins",[168,305,262],{"class":222},[168,307,308],{"class":177},"\"auto\"",[168,310,271],{"class":226},[168,312,314,318,321,324,327,330,333,336,339,341,344,347,350,353,355,358,360,363,365],{"class":80,"line":313},7,[168,315,317],{"class":316},"sP0c6","print",[168,319,320],{"class":226},"(",[168,322,323],{"class":316},"len",[168,325,326],{"class":226},"(auto) ",[168,328,329],{"class":222},"-",[168,331,332],{"class":316}," 1",[168,334,335],{"class":226},", ",[168,337,338],{"class":177},"\"auto bins of width\"",[168,340,335],{"class":226},[168,342,343],{"class":316},"round",[168,345,346],{"class":226},"(auto[",[168,348,349],{"class":316},"1",[168,351,352],{"class":226},"] ",[168,354,329],{"class":222},[168,356,357],{"class":226}," auto[",[168,359,42],{"class":316},[168,361,362],{"class":226},"], ",[168,364,349],{"class":316},[168,366,367],{"class":226},"))\n",[168,369,371],{"class":80,"line":370},8,[168,372,253],{"emptyLinePlaceholder":252},[168,374,376,379,383],{"class":80,"line":375},9,[168,377,378],{"class":222},"def",[168,380,382],{"class":381},"s_Opv"," nice_width",[168,384,385],{"class":226},"(raw):\n",[168,387,389,392,395,398,401,403,405,408,410,413,415,418,420,423,425,428,430,433,435,438,440,443,445,448,450,453,455,458,460,463],{"class":80,"line":388},10,[168,390,391],{"class":222},"    for",[168,393,394],{"class":226}," step ",[168,396,397],{"class":222},"in",[168,399,400],{"class":226}," [",[168,402,349],{"class":316},[168,404,335],{"class":226},[168,406,407],{"class":316},"2",[168,409,335],{"class":226},[168,411,412],{"class":316},"2.5",[168,414,335],{"class":226},[168,416,417],{"class":316},"5",[168,419,335],{"class":226},[168,421,422],{"class":316},"10",[168,424,335],{"class":226},[168,426,427],{"class":316},"20",[168,429,335],{"class":226},[168,431,432],{"class":316},"25",[168,434,335],{"class":226},[168,436,437],{"class":316},"50",[168,439,335],{"class":226},[168,441,442],{"class":316},"100",[168,444,335],{"class":226},[168,446,447],{"class":316},"200",[168,449,335],{"class":226},[168,451,452],{"class":316},"250",[168,454,335],{"class":226},[168,456,457],{"class":316},"500",[168,459,335],{"class":226},[168,461,462],{"class":316},"1000",[168,464,465],{"class":226},"]:\n",[168,467,469,472,474,477],{"class":80,"line":468},11,[168,470,471],{"class":222},"        if",[168,473,394],{"class":226},[168,475,476],{"class":222},">=",[168,478,479],{"class":226}," raw:\n",[168,481,483,486],{"class":80,"line":482},12,[168,484,485],{"class":222},"            return",[168,487,488],{"class":226}," step\n",[168,490,492,495],{"class":80,"line":491},13,[168,493,494],{"class":222},"    return",[168,496,497],{"class":226}," raw\n",[168,499,501],{"class":80,"line":500},14,[168,502,253],{"emptyLinePlaceholder":252},[168,504,506,509,511],{"class":80,"line":505},15,[168,507,508],{"class":226},"cap ",[168,510,262],{"class":222},[168,512,513],{"class":316}," 400\n",[168,515,517,520,522,525,528,531,533,536,539,542],{"class":80,"line":516},16,[168,518,519],{"class":226},"width ",[168,521,262],{"class":222},[168,523,524],{"class":226}," nice_width((np.percentile(values, ",[168,526,527],{"class":316},"99",[168,529,530],{"class":226},") ",[168,532,329],{"class":222},[168,534,535],{"class":226}," values.min()) ",[168,537,538],{"class":222},"\u002F",[168,540,541],{"class":316}," 14",[168,543,271],{"class":226},[168,545,547,550,552,555,557,560,563],{"class":80,"line":546},17,[168,548,549],{"class":226},"edges ",[168,551,262],{"class":222},[168,553,554],{"class":226}," np.arange(",[168,556,42],{"class":316},[168,558,559],{"class":226},", cap ",[168,561,562],{"class":222},"+",[168,564,565],{"class":226}," width, width)\n",[168,567,569,571,573,576,579,582,585,587,589,592,595,597,599,602,605,608,611,613,616,619,621],{"class":80,"line":568},18,[168,570,317],{"class":316},[168,572,320],{"class":226},[168,574,575],{"class":177},"\"width\"",[168,577,578],{"class":226},", width, ",[168,580,581],{"class":177},"\"edges\"",[168,583,584],{"class":226},", edges[:",[168,586,41],{"class":316},[168,588,362],{"class":226},[168,590,591],{"class":177},"\"…\"",[168,593,594],{"class":226},", edges[",[168,596,329],{"class":222},[168,598,407],{"class":316},[168,600,601],{"class":226},":], ",[168,603,604],{"class":177},"\"| values over\"",[168,606,607],{"class":226},", cap, ",[168,609,610],{"class":177},"\":\"",[168,612,335],{"class":226},[168,614,615],{"class":316},"int",[168,617,618],{"class":226},"((values ",[168,620,476],{"class":222},[168,622,623],{"class":226}," cap).sum()))\n",[10,625,626],{},"The automatic choice gives 44 bins of about 28 each — far too many for a chart, and most of them empty stretches reaching out to the few very large orders. Basing the width on the 99th percentile instead, and aiming for about fourteen bins, gives a width of 25 — labels like \"75–100\" that anyone can read. Values above a cap of 400 will go in a single overflow bin rather than forcing a dozen empty bins between 400 and 1,250.",[20,628,29,633,29,636,29,639,29,642,29,648,29,653,29,659,29,662,29,666,29,671,29,677,29,679,29,683,29,685,29,688,29,691,29,695,29,697,29,701,29,703,29,706,29,709,29,713,29,715,29,719,29,721],{"viewBox":629,"role":23,"ariaLabelledBy":630,"xmlns":27,"style":28},"4 0 752 174",[631,632],"hist-width-t","hist-width-d",[31,634,635],{"id":631},"How the bin width changes the picture",[35,637,638],{"id":632},"A table of bin widths for the 480 orders up to 400. A width of 10 gives 40 bins, too noisy to read. A width of 25 gives 16 bins, which shows the shape clearly. A width of 50 gives 8 bins, which hides the peak between 75 and 100 inside a wider 50 to 100 bin.",[39,640],{"x":41,"y":42,"width":43,"height":641,"fill":45},"174",[39,643],{"x":48,"y":49,"width":644,"height":645,"rx":42,"fill":45,"stroke":646,"style":647},"86.6","40.0","var(--line,#cdd5e6)","stroke-width:1px",[39,649],{"x":650,"y":49,"width":651,"height":645,"rx":42,"fill":53,"stroke":54,"style":652},"106.6","316.7","stroke-width:1.2px",[68,654,658],{"x":655,"y":656,"style":657},"264.9","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Bins up to 400",[39,660],{"x":661,"y":49,"width":651,"height":645,"rx":42,"fill":53,"stroke":54,"style":652},"423.3",[68,663,665],{"x":664,"y":656,"style":657},"581.6","Reads as",[39,667],{"x":48,"y":668,"width":644,"height":669,"rx":42,"fill":670,"stroke":646,"style":647},"56.0","34.0","#e7ebef",[68,672,676],{"x":673,"y":674,"style":675},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Width 10",[39,678],{"x":650,"y":668,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,680,682],{"x":655,"y":681,"style":77},"77.0","40",[39,684],{"x":661,"y":668,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,686,687],{"x":664,"y":681,"style":77},"noisy — every bump looks important",[39,689],{"x":48,"y":690,"width":644,"height":669,"rx":42,"fill":670,"stroke":646,"style":647},"90.0",[68,692,694],{"x":673,"y":693,"style":675},"112.0","Width 25",[39,696],{"x":650,"y":690,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,698,700],{"x":655,"y":699,"style":77},"111.0","16",[39,702],{"x":661,"y":690,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,704,705],{"x":664,"y":699,"style":77},"clear peak and fall-off",[39,707],{"x":48,"y":708,"width":644,"height":669,"rx":42,"fill":670,"stroke":646,"style":647},"124.0",[68,710,712],{"x":673,"y":711,"style":675},"146.0","Width 50",[39,714],{"x":650,"y":708,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,716,718],{"x":655,"y":717,"style":77},"145.0","8",[39,720],{"x":661,"y":708,"width":651,"height":669,"rx":42,"fill":45,"stroke":646,"style":647},[68,722,723],{"x":664,"y":717,"style":77},"smooth but hides the 75–100 peak",[10,725,726],{},"There is no single correct width; try two or three and keep the one where the shape is clear without every small bump looking meaningful. Once chosen, keep the same edges from one report to the next, so readers compare like with like.",[153,728,730],{"id":729},"step-2-count-values-into-the-bins","Step 2 — Count values into the bins",[10,732,733,735],{},[165,734,105],{}," counts values per bin for a given set of edges. Add the overflow bin by counting separately, and build human-readable labels:",[158,737,739],{"className":213,"code":738,"language":215,"meta":163,"style":163},"counts, _ = np.histogram(values[values \u003C cap], bins=edges)\nlabels = [f\"{lo:.0f}–{hi:.0f}\" for lo, hi in zip(edges[:-1], edges[1:])]\nlabels.append(f\"{cap} and over\")\ncounts = np.append(counts, (values >= cap).sum())\n\nhist = pd.DataFrame({\"Order value\": labels, \"Orders\": counts})\nprint(hist.to_string(index=False))\nassert hist[\"Orders\"].sum() == len(values)\n",[165,740,741,764,832,853,868,872,894,911],{"__ignoreMap":163},[168,742,743,746,748,751,754,757,759,761],{"class":80,"line":170},[168,744,745],{"class":226},"counts, _ ",[168,747,262],{"class":222},[168,749,750],{"class":226}," np.histogram(values[values ",[168,752,753],{"class":222},"\u003C",[168,755,756],{"class":226}," cap], ",[168,758,303],{"class":302},[168,760,262],{"class":222},[168,762,763],{"class":226},"edges)\n",[168,765,766,769,771,773,776,779,783,786,789,792,795,797,800,802,804,806,809,812,814,817,820,822,824,827,829],{"class":80,"line":236},[168,767,768],{"class":226},"labels ",[168,770,262],{"class":222},[168,772,400],{"class":226},[168,774,775],{"class":222},"f",[168,777,778],{"class":177},"\"",[168,780,782],{"class":781},"sSjpA","{",[168,784,785],{"class":226},"lo",[168,787,788],{"class":222},":.0f",[168,790,791],{"class":781},"}",[168,793,794],{"class":177},"–",[168,796,782],{"class":781},[168,798,799],{"class":226},"hi",[168,801,788],{"class":222},[168,803,791],{"class":781},[168,805,778],{"class":177},[168,807,808],{"class":222}," for",[168,810,811],{"class":226}," lo, hi ",[168,813,397],{"class":222},[168,815,816],{"class":316}," zip",[168,818,819],{"class":226},"(edges[:",[168,821,329],{"class":222},[168,823,349],{"class":316},[168,825,826],{"class":226},"], edges[",[168,828,349],{"class":316},[168,830,831],{"class":226},":])]\n",[168,833,834,837,839,841,843,846,848,851],{"class":80,"line":249},[168,835,836],{"class":226},"labels.append(",[168,838,775],{"class":222},[168,840,778],{"class":177},[168,842,782],{"class":781},[168,844,845],{"class":226},"cap",[168,847,791],{"class":781},[168,849,850],{"class":177}," and over\"",[168,852,271],{"class":226},[168,854,855,858,860,863,865],{"class":80,"line":256},[168,856,857],{"class":226},"counts ",[168,859,262],{"class":222},[168,861,862],{"class":226}," np.append(counts, (values ",[168,864,476],{"class":222},[168,866,867],{"class":226}," cap).sum())\n",[168,869,870],{"class":80,"line":274},[168,871,253],{"emptyLinePlaceholder":252},[168,873,874,877,879,882,885,888,891],{"class":80,"line":291},[168,875,876],{"class":226},"hist ",[168,878,262],{"class":222},[168,880,881],{"class":226}," pd.DataFrame({",[168,883,884],{"class":177},"\"Order value\"",[168,886,887],{"class":226},": labels, ",[168,889,890],{"class":177},"\"Orders\"",[168,892,893],{"class":226},": counts})\n",[168,895,896,898,901,904,906,909],{"class":80,"line":313},[168,897,317],{"class":316},[168,899,900],{"class":226},"(hist.to_string(",[168,902,903],{"class":302},"index",[168,905,262],{"class":222},[168,907,908],{"class":316},"False",[168,910,367],{"class":226},[168,912,913,916,919,921,924,927,930],{"class":80,"line":370},[168,914,915],{"class":222},"assert",[168,917,918],{"class":226}," hist[",[168,920,890],{"class":177},[168,922,923],{"class":226},"].sum() ",[168,925,926],{"class":222},"==",[168,928,929],{"class":316}," len",[168,931,932],{"class":226},"(values)\n",[10,934,935,936,938,939,942,943,947,948,951],{},"Every bin includes its lower edge and excludes the upper one, except that ",[165,937,105],{}," includes the upper edge in the last bin — which is why the overflow values are separated first with ",[165,940,941],{},"values \u003C cap",". The assertion confirms every order is counted exactly once. ",[14,944,946],{"href":945},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut\u002F","Bin Values into Ranges with pandas cut"," does the same with ",[165,949,950],{},"pd.cut"," when each row needs its bin label.",[153,953,955],{"id":954},"step-3-chart-the-counts-with-openpyxl","Step 3 — Chart the counts with openpyxl",[10,957,958,959,962],{},"Write the table and add a column chart. Three settings make it a histogram rather than a bar chart: ",[165,960,961],{},"gapWidth = 0"," so the columns touch, no legend (there is one series), and visible axes:",[158,964,966],{"className":213,"code":965,"language":215,"meta":163,"style":163},"from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, Reference\n\nwb = Workbook(); ws = wb.active; ws.title = \"Distribution\"\nws.append([\"Order value\", \"Orders\"])\nfor row in hist.itertuples(index=False):\n    ws.append([row[0], int(row[1])])\nws.column_dimensions[\"A\"].width = 14\n\nchart = BarChart()\nchart.type = \"col\"\nchart.gapWidth = 0\nchart.title = f\"Order values, September 2026 (n = {len(values)})\"\nchart.add_data(Reference(ws, min_col=2, min_row=1, max_row=len(hist) + 1), titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=len(hist) + 1))\nchart.legend = None\nchart.x_axis.title = \"Order value\"\nchart.y_axis.title = \"Number of orders\"\nchart.x_axis.delete = False\nchart.y_axis.delete = False\nchart.series[0].graphicalProperties.line.solidFill = \"FFFFFF\"\nchart.width, chart.height = 20, 9\nws.add_chart(chart, \"D2\")\nwb.save(\"order_histogram.xlsx\")\nprint(hist.loc[hist[\"Orders\"].idxmax()].tolist())\n",[165,967,968,981,993,997,1017,1031,1053,1072,1088,1092,1102,1112,1122,1147,1197,1232,1242,1252,1262,1273,1283,1299,1315,1326,1337],{"__ignoreMap":163},[168,969,970,973,976,978],{"class":80,"line":170},[168,971,972],{"class":222},"from",[168,974,975],{"class":226}," openpyxl ",[168,977,223],{"class":222},[168,979,980],{"class":226}," Workbook\n",[168,982,983,985,988,990],{"class":80,"line":236},[168,984,972],{"class":222},[168,986,987],{"class":226}," openpyxl.chart ",[168,989,223],{"class":222},[168,991,992],{"class":226}," BarChart, Reference\n",[168,994,995],{"class":80,"line":249},[168,996,253],{"emptyLinePlaceholder":252},[168,998,999,1002,1004,1007,1009,1012,1014],{"class":80,"line":256},[168,1000,1001],{"class":226},"wb ",[168,1003,262],{"class":222},[168,1005,1006],{"class":226}," Workbook(); ws ",[168,1008,262],{"class":222},[168,1010,1011],{"class":226}," wb.active; ws.title ",[168,1013,262],{"class":222},[168,1015,1016],{"class":177}," \"Distribution\"\n",[168,1018,1019,1022,1024,1026,1028],{"class":80,"line":274},[168,1020,1021],{"class":226},"ws.append([",[168,1023,884],{"class":177},[168,1025,335],{"class":226},[168,1027,890],{"class":177},[168,1029,1030],{"class":226},"])\n",[168,1032,1033,1036,1039,1041,1044,1046,1048,1050],{"class":80,"line":291},[168,1034,1035],{"class":222},"for",[168,1037,1038],{"class":226}," row ",[168,1040,397],{"class":222},[168,1042,1043],{"class":226}," hist.itertuples(",[168,1045,903],{"class":302},[168,1047,262],{"class":222},[168,1049,908],{"class":316},[168,1051,1052],{"class":226},"):\n",[168,1054,1055,1058,1060,1062,1064,1067,1069],{"class":80,"line":313},[168,1056,1057],{"class":226},"    ws.append([row[",[168,1059,42],{"class":316},[168,1061,362],{"class":226},[168,1063,615],{"class":316},[168,1065,1066],{"class":226},"(row[",[168,1068,349],{"class":316},[168,1070,1071],{"class":226},"])])\n",[168,1073,1074,1077,1080,1083,1085],{"class":80,"line":370},[168,1075,1076],{"class":226},"ws.column_dimensions[",[168,1078,1079],{"class":177},"\"A\"",[168,1081,1082],{"class":226},"].width ",[168,1084,262],{"class":222},[168,1086,1087],{"class":316}," 14\n",[168,1089,1090],{"class":80,"line":375},[168,1091,253],{"emptyLinePlaceholder":252},[168,1093,1094,1097,1099],{"class":80,"line":388},[168,1095,1096],{"class":226},"chart ",[168,1098,262],{"class":222},[168,1100,1101],{"class":226}," BarChart()\n",[168,1103,1104,1107,1109],{"class":80,"line":468},[168,1105,1106],{"class":226},"chart.type ",[168,1108,262],{"class":222},[168,1110,1111],{"class":177}," \"col\"\n",[168,1113,1114,1117,1119],{"class":80,"line":482},[168,1115,1116],{"class":226},"chart.gapWidth ",[168,1118,262],{"class":222},[168,1120,1121],{"class":316}," 0\n",[168,1123,1124,1127,1129,1132,1135,1137,1139,1142,1144],{"class":80,"line":491},[168,1125,1126],{"class":226},"chart.title ",[168,1128,262],{"class":222},[168,1130,1131],{"class":222}," f",[168,1133,1134],{"class":177},"\"Order values, September 2026 (n = ",[168,1136,782],{"class":781},[168,1138,323],{"class":316},[168,1140,1141],{"class":226},"(values)",[168,1143,791],{"class":781},[168,1145,1146],{"class":177},")\"\n",[168,1148,1149,1152,1155,1157,1159,1161,1164,1166,1168,1170,1173,1175,1177,1180,1182,1184,1187,1190,1192,1195],{"class":80,"line":500},[168,1150,1151],{"class":226},"chart.add_data(Reference(ws, ",[168,1153,1154],{"class":302},"min_col",[168,1156,262],{"class":222},[168,1158,407],{"class":316},[168,1160,335],{"class":226},[168,1162,1163],{"class":302},"min_row",[168,1165,262],{"class":222},[168,1167,349],{"class":316},[168,1169,335],{"class":226},[168,1171,1172],{"class":302},"max_row",[168,1174,262],{"class":222},[168,1176,323],{"class":316},[168,1178,1179],{"class":226},"(hist) ",[168,1181,562],{"class":222},[168,1183,332],{"class":316},[168,1185,1186],{"class":226},"), ",[168,1188,1189],{"class":302},"titles_from_data",[168,1191,262],{"class":222},[168,1193,1194],{"class":316},"True",[168,1196,271],{"class":226},[168,1198,1199,1202,1204,1206,1208,1210,1212,1214,1216,1218,1220,1222,1224,1226,1228,1230],{"class":80,"line":505},[168,1200,1201],{"class":226},"chart.set_categories(Reference(ws, ",[168,1203,1154],{"class":302},[168,1205,262],{"class":222},[168,1207,349],{"class":316},[168,1209,335],{"class":226},[168,1211,1163],{"class":302},[168,1213,262],{"class":222},[168,1215,407],{"class":316},[168,1217,335],{"class":226},[168,1219,1172],{"class":302},[168,1221,262],{"class":222},[168,1223,323],{"class":316},[168,1225,1179],{"class":226},[168,1227,562],{"class":222},[168,1229,332],{"class":316},[168,1231,367],{"class":226},[168,1233,1234,1237,1239],{"class":80,"line":516},[168,1235,1236],{"class":226},"chart.legend ",[168,1238,262],{"class":222},[168,1240,1241],{"class":316}," None\n",[168,1243,1244,1247,1249],{"class":80,"line":546},[168,1245,1246],{"class":226},"chart.x_axis.title ",[168,1248,262],{"class":222},[168,1250,1251],{"class":177}," \"Order value\"\n",[168,1253,1254,1257,1259],{"class":80,"line":568},[168,1255,1256],{"class":226},"chart.y_axis.title ",[168,1258,262],{"class":222},[168,1260,1261],{"class":177}," \"Number of orders\"\n",[168,1263,1265,1268,1270],{"class":80,"line":1264},19,[168,1266,1267],{"class":226},"chart.x_axis.delete ",[168,1269,262],{"class":222},[168,1271,1272],{"class":316}," False\n",[168,1274,1276,1279,1281],{"class":80,"line":1275},20,[168,1277,1278],{"class":226},"chart.y_axis.delete ",[168,1280,262],{"class":222},[168,1282,1272],{"class":316},[168,1284,1286,1289,1291,1294,1296],{"class":80,"line":1285},21,[168,1287,1288],{"class":226},"chart.series[",[168,1290,42],{"class":316},[168,1292,1293],{"class":226},"].graphicalProperties.line.solidFill ",[168,1295,262],{"class":222},[168,1297,1298],{"class":177}," \"FFFFFF\"\n",[168,1300,1302,1305,1307,1310,1312],{"class":80,"line":1301},22,[168,1303,1304],{"class":226},"chart.width, chart.height ",[168,1306,262],{"class":222},[168,1308,1309],{"class":316}," 20",[168,1311,335],{"class":226},[168,1313,1314],{"class":316},"9\n",[168,1316,1318,1321,1324],{"class":80,"line":1317},23,[168,1319,1320],{"class":226},"ws.add_chart(chart, ",[168,1322,1323],{"class":177},"\"D2\"",[168,1325,271],{"class":226},[168,1327,1329,1332,1335],{"class":80,"line":1328},24,[168,1330,1331],{"class":226},"wb.save(",[168,1333,1334],{"class":177},"\"order_histogram.xlsx\"",[168,1336,271],{"class":226},[168,1338,1340,1342,1345,1347],{"class":80,"line":1339},25,[168,1341,317],{"class":316},[168,1343,1344],{"class":226},"(hist.loc[hist[",[168,1346,890],{"class":177},[168,1348,1349],{"class":226},"].idxmax()].tolist())\n",[10,1351,1352,1353,1356],{},"A thin white outline on each column keeps the bins distinguishable while they touch. The ",[165,1354,1355],{},"delete = False"," lines make Excel show the axes, which it otherwise hides for openpyxl 3.1 charts. The tallest bin is 75–100, with the gradual fall-off to the right typical of order values: many modest orders, a few large ones.",[20,1358,29,1364,29,1367,29,1370,29,1374,29,1380,29,1386,29,1392,29,1397,29,1401,29,1404,29,1408,29,1411,29,1415,29,1418,29,1422,29,1425,29,1429,29,1432,29,1436,29,1439,29,1443,29,1446,29,1450,29,1452,29,1454,29,1457,29,1461,29,1464,29,1468,29,1471,29,1475,29,1478,29,1482,29,1485,29,1489,29,1492,29,1495,29,1498,29,1502,29,1504,29,1507,29,1510,29,1514,29,1517,29,1520,29,1523,29,1527,29,1530,29,1533,29,1536,29,1540,29,1542,29,1544,29,1547,29,1551,29,1554,29,1557,29,1560,29,1564,29,1566,29,1568,29,1571,29,1575,29,1577,29,1579,29,1582,29,1586,29,1588,29,1590,29,1593,29,1597,29,1601],{"viewBox":1359,"role":23,"ariaLabelledBy":1360,"xmlns":27,"style":1363},"4 0 744 732",[1361,1362],"hist-counts-t","hist-counts-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1365,1366],{"id":1361},"Order values by 25-wide bin",[35,1368,1369],{"id":1362},"Histogram of 480 order values in bins 25 wide from 0 to 400, plus an overflow bin for 400 and over. Counts rise to a peak of 121 in the 75 to 100 bin and fall away gradually to the right, with 4 orders of 400 and over.",[39,1371],{"x":41,"y":42,"width":1372,"height":1373,"fill":45},"744","732",[68,1375,1379],{"x":1376,"y":1377,"style":1378},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Orders per bin",[39,1381],{"x":48,"y":1382,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"48.0","85.6","28.0","7",[68,1387,1391],{"x":1388,"y":1389,"style":1390},"62.8","66.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","0–25",[39,1393],{"x":1394,"y":1382,"width":1395,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"115.6","6.0","stroke-width:1.5px",[68,1398,349],{"x":1399,"y":1389,"style":1400},"129.6","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start",[39,1402],{"x":48,"y":1403,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"88.0",[68,1405,1407],{"x":1388,"y":1406,"style":1390},"106.5","25–50",[39,1409],{"x":1394,"y":1403,"width":1410,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"151.0",[68,1412,1414],{"x":1413,"y":1406,"style":1400},"274.7","31",[39,1416],{"x":48,"y":1417,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"128.0",[68,1419,1421],{"x":1388,"y":1420,"style":1390},"146.5","50–75",[39,1423],{"x":1394,"y":1417,"width":1424,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"433.6",[68,1426,1428],{"x":1427,"y":1420,"style":1400},"557.3","89",[39,1430],{"x":48,"y":1431,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"168.0",[68,1433,1435],{"x":1388,"y":1434,"style":1390},"186.5","75–100",[39,1437],{"x":1394,"y":1431,"width":1438,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"589.6",[68,1440,1442],{"x":1441,"y":1434,"style":1400},"713.2","121",[39,1444],{"x":48,"y":1445,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"208.0",[68,1447,1449],{"x":1388,"y":1448,"style":1390},"226.5","100–125",[39,1451],{"x":1394,"y":1445,"width":1424,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1453,1428],{"x":1427,"y":1448,"style":1400},[39,1455],{"x":48,"y":1456,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"248.0",[68,1458,1460],{"x":1388,"y":1459,"style":1390},"266.5","125–150",[39,1462],{"x":1394,"y":1456,"width":1463,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"258.2",[68,1465,1467],{"x":1466,"y":1459,"style":1400},"381.9","53",[39,1469],{"x":48,"y":1470,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"288.0",[68,1472,1474],{"x":1388,"y":1473,"style":1390},"306.5","150–175",[39,1476],{"x":1394,"y":1470,"width":1477,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"160.8",[68,1479,1481],{"x":1480,"y":1473,"style":1400},"284.4","33",[39,1483],{"x":48,"y":1484,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"328.0",[68,1486,1488],{"x":1388,"y":1487,"style":1390},"346.5","175–200",[39,1490],{"x":1394,"y":1484,"width":1491,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"121.8",[68,1493,432],{"x":1494,"y":1487,"style":1400},"245.5",[39,1496],{"x":48,"y":1497,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"368.0",[68,1499,1501],{"x":1388,"y":1500,"style":1390},"386.5","200–225",[39,1503],{"x":1394,"y":1497,"width":674,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1505,700],{"x":1506,"y":1500,"style":1400},"201.6",[39,1508],{"x":48,"y":1509,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"408.0",[68,1511,1513],{"x":1388,"y":1512,"style":1390},"426.5","225–250",[39,1515],{"x":1394,"y":1509,"width":1516,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"39.0",[68,1518,718],{"x":1519,"y":1512,"style":1400},"162.6",[39,1521],{"x":48,"y":1522,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"448.0",[68,1524,1526],{"x":1388,"y":1525,"style":1390},"466.5","250–275",[39,1528],{"x":1394,"y":1522,"width":1529,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"19.5",[68,1531,41],{"x":1532,"y":1525,"style":1400},"143.1",[39,1534],{"x":48,"y":1535,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"488.0",[68,1537,1539],{"x":1388,"y":1538,"style":1390},"506.5","275–300",[39,1541],{"x":1394,"y":1535,"width":1529,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1543,41],{"x":1532,"y":1538,"style":1400},[39,1545],{"x":48,"y":1546,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"528.0",[68,1548,1550],{"x":1388,"y":1549,"style":1390},"546.5","300–325",[39,1552],{"x":1394,"y":1546,"width":1553,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},"9.7",[68,1555,407],{"x":1556,"y":1549,"style":1400},"133.4",[39,1558],{"x":48,"y":1559,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"568.0",[68,1561,1563],{"x":1388,"y":1562,"style":1390},"586.5","325–350",[39,1565],{"x":1394,"y":1559,"width":1395,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1567,42],{"x":1399,"y":1562,"style":1400},[39,1569],{"x":48,"y":1570,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"608.0",[68,1572,1574],{"x":1388,"y":1573,"style":1390},"626.5","350–375",[39,1576],{"x":1394,"y":1570,"width":1395,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1578,42],{"x":1399,"y":1573,"style":1400},[39,1580],{"x":48,"y":1581,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"648.0",[68,1583,1585],{"x":1388,"y":1584,"style":1390},"666.5","375–400",[39,1587],{"x":1394,"y":1581,"width":1395,"height":1384,"rx":1385,"fill":53,"stroke":54,"style":1396},[68,1589,42],{"x":1399,"y":1584,"style":1400},[39,1591],{"x":48,"y":1592,"width":1383,"height":1384,"rx":1385,"fill":670,"stroke":646,"style":647},"688.0",[68,1594,1596],{"x":1388,"y":1595,"style":1390},"706.5","400+",[39,1598],{"x":1394,"y":1592,"width":1529,"height":1384,"rx":1385,"fill":1599,"stroke":1600,"style":1396},"#fdefd8","var(--gold,#b4740a)",[68,1602,41],{"x":1532,"y":1595,"style":1603},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start",[153,1605,1607],{"id":1606},"step-4-the-same-chart-with-xlsxwriter","Step 4 — The same chart with xlsxwriter",[10,1609,1610,1611,1614],{},"In xlsxwriter the gap is a series option, and pandas can write the table in the same ",[165,1612,1613],{},"ExcelWriter",":",[158,1616,1618],{"className":213,"code":1617,"language":215,"meta":163,"style":163},"with pd.ExcelWriter(\"order_histogram_xw.xlsx\", engine=\"xlsxwriter\") as writer:\n    hist.to_excel(writer, sheet_name=\"Distribution\", index=False)\n    book, sheet = writer.book, writer.sheets[\"Distribution\"]\n    col = book.add_chart({\"type\": \"column\"})\n    n = len(hist)\n    col.add_series({\"name\": \"Orders\", \"categories\": [\"Distribution\", 1, 0, n, 0], \"values\": [\"Distribution\", 1, 1, n, 1],\n                    \"gap\": 0, \"border\": {\"color\": \"white\"}})\n    col.set_title({\"name\": \"Order values, September 2026\"})\n    col.set_x_axis({\"name\": \"Order value\"})\n    col.set_y_axis({\"name\": \"Number of orders\", \"major_gridlines\": {\"visible\": True}})\n    col.set_legend({\"none\": True})\n    col.set_size({\"width\": 760, \"height\": 340})\n    sheet.insert_chart(\"D2\", col)\nprint(\"written\", n, \"bins\")\n",[165,1619,1620,1648,1671,1686,1708,1720,1779,1807,1821,1834,1862,1876,1900,1910],{"__ignoreMap":163},[168,1621,1622,1625,1628,1631,1633,1636,1638,1641,1643,1645],{"class":80,"line":170},[168,1623,1624],{"class":222},"with",[168,1626,1627],{"class":226}," pd.ExcelWriter(",[168,1629,1630],{"class":177},"\"order_histogram_xw.xlsx\"",[168,1632,335],{"class":226},[168,1634,1635],{"class":302},"engine",[168,1637,262],{"class":222},[168,1639,1640],{"class":177},"\"xlsxwriter\"",[168,1642,530],{"class":226},[168,1644,230],{"class":222},[168,1646,1647],{"class":226}," writer:\n",[168,1649,1650,1653,1656,1658,1661,1663,1665,1667,1669],{"class":80,"line":236},[168,1651,1652],{"class":226},"    hist.to_excel(writer, ",[168,1654,1655],{"class":302},"sheet_name",[168,1657,262],{"class":222},[168,1659,1660],{"class":177},"\"Distribution\"",[168,1662,335],{"class":226},[168,1664,903],{"class":302},[168,1666,262],{"class":222},[168,1668,908],{"class":316},[168,1670,271],{"class":226},[168,1672,1673,1676,1678,1681,1683],{"class":80,"line":249},[168,1674,1675],{"class":226},"    book, sheet ",[168,1677,262],{"class":222},[168,1679,1680],{"class":226}," writer.book, writer.sheets[",[168,1682,1660],{"class":177},[168,1684,1685],{"class":226},"]\n",[168,1687,1688,1691,1693,1696,1699,1702,1705],{"class":80,"line":256},[168,1689,1690],{"class":226},"    col ",[168,1692,262],{"class":222},[168,1694,1695],{"class":226}," book.add_chart({",[168,1697,1698],{"class":177},"\"type\"",[168,1700,1701],{"class":226},": ",[168,1703,1704],{"class":177},"\"column\"",[168,1706,1707],{"class":226},"})\n",[168,1709,1710,1713,1715,1717],{"class":80,"line":274},[168,1711,1712],{"class":226},"    n ",[168,1714,262],{"class":222},[168,1716,929],{"class":316},[168,1718,1719],{"class":226},"(hist)\n",[168,1721,1722,1725,1728,1730,1732,1734,1737,1740,1742,1744,1746,1748,1750,1753,1755,1757,1760,1762,1764,1766,1768,1770,1772,1774,1776],{"class":80,"line":291},[168,1723,1724],{"class":226},"    col.add_series({",[168,1726,1727],{"class":177},"\"name\"",[168,1729,1701],{"class":226},[168,1731,890],{"class":177},[168,1733,335],{"class":226},[168,1735,1736],{"class":177},"\"categories\"",[168,1738,1739],{"class":226},": [",[168,1741,1660],{"class":177},[168,1743,335],{"class":226},[168,1745,349],{"class":316},[168,1747,335],{"class":226},[168,1749,42],{"class":316},[168,1751,1752],{"class":226},", n, ",[168,1754,42],{"class":316},[168,1756,362],{"class":226},[168,1758,1759],{"class":177},"\"values\"",[168,1761,1739],{"class":226},[168,1763,1660],{"class":177},[168,1765,335],{"class":226},[168,1767,349],{"class":316},[168,1769,335],{"class":226},[168,1771,349],{"class":316},[168,1773,1752],{"class":226},[168,1775,349],{"class":316},[168,1777,1778],{"class":226},"],\n",[168,1780,1781,1784,1786,1788,1790,1793,1796,1799,1801,1804],{"class":80,"line":313},[168,1782,1783],{"class":177},"                    \"gap\"",[168,1785,1701],{"class":226},[168,1787,42],{"class":316},[168,1789,335],{"class":226},[168,1791,1792],{"class":177},"\"border\"",[168,1794,1795],{"class":226},": {",[168,1797,1798],{"class":177},"\"color\"",[168,1800,1701],{"class":226},[168,1802,1803],{"class":177},"\"white\"",[168,1805,1806],{"class":226},"}})\n",[168,1808,1809,1812,1814,1816,1819],{"class":80,"line":370},[168,1810,1811],{"class":226},"    col.set_title({",[168,1813,1727],{"class":177},[168,1815,1701],{"class":226},[168,1817,1818],{"class":177},"\"Order values, September 2026\"",[168,1820,1707],{"class":226},[168,1822,1823,1826,1828,1830,1832],{"class":80,"line":375},[168,1824,1825],{"class":226},"    col.set_x_axis({",[168,1827,1727],{"class":177},[168,1829,1701],{"class":226},[168,1831,884],{"class":177},[168,1833,1707],{"class":226},[168,1835,1836,1839,1841,1843,1846,1848,1851,1853,1856,1858,1860],{"class":80,"line":388},[168,1837,1838],{"class":226},"    col.set_y_axis({",[168,1840,1727],{"class":177},[168,1842,1701],{"class":226},[168,1844,1845],{"class":177},"\"Number of orders\"",[168,1847,335],{"class":226},[168,1849,1850],{"class":177},"\"major_gridlines\"",[168,1852,1795],{"class":226},[168,1854,1855],{"class":177},"\"visible\"",[168,1857,1701],{"class":226},[168,1859,1194],{"class":316},[168,1861,1806],{"class":226},[168,1863,1864,1867,1870,1872,1874],{"class":80,"line":468},[168,1865,1866],{"class":226},"    col.set_legend({",[168,1868,1869],{"class":177},"\"none\"",[168,1871,1701],{"class":226},[168,1873,1194],{"class":316},[168,1875,1707],{"class":226},[168,1877,1878,1881,1883,1885,1888,1890,1893,1895,1898],{"class":80,"line":482},[168,1879,1880],{"class":226},"    col.set_size({",[168,1882,575],{"class":177},[168,1884,1701],{"class":226},[168,1886,1887],{"class":316},"760",[168,1889,335],{"class":226},[168,1891,1892],{"class":177},"\"height\"",[168,1894,1701],{"class":226},[168,1896,1897],{"class":316},"340",[168,1899,1707],{"class":226},[168,1901,1902,1905,1907],{"class":80,"line":491},[168,1903,1904],{"class":226},"    sheet.insert_chart(",[168,1906,1323],{"class":177},[168,1908,1909],{"class":226},", col)\n",[168,1911,1912,1914,1916,1919,1921,1924],{"class":80,"line":500},[168,1913,317],{"class":316},[168,1915,320],{"class":226},[168,1917,1918],{"class":177},"\"written\"",[168,1920,1752],{"class":226},[168,1922,1923],{"class":177},"\"bins\"",[168,1925,271],{"class":226},[10,1927,1928,1929,1933],{},"xlsxwriter always writes its axes as visible, so there is no equivalent of the openpyxl axis setting. ",[14,1930,1932],{"href":1931},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter\u002F","Add a Chart to an Excel File with xlsxwriter"," covers the other chart options.",[153,1935,1937],{"id":1936},"step-5-a-live-histogram-with-frequency","Step 5 — A live histogram with FREQUENCY",[10,1939,1940,1941,1944],{},"When readers will paste new values into the workbook, the counts should update with them. Excel's ",[165,1942,1943],{},"FREQUENCY"," function counts values into bins given their upper edges, and in current Excel it returns all the counts at once as a dynamic array. Write the upper edges and the formula, and point the same chart at the formula results:",[158,1946,1948],{"className":213,"code":1947,"language":215,"meta":163,"style":163},"wb = Workbook(); ws = wb.active; ws.title = \"Live\"\nws.append([\"Value\", \"\", \"Up to\", \"Orders\"])\nfor v in values:\n    ws.append([float(v)])\nlast = len(values) + 1\nfor i, hi in enumerate(edges[1:], start=2):\n    ws.cell(row=i, column=3, value=float(hi))\nws.cell(row=len(edges) + 1, column=3, value=\"over\")\nws[\"D2\"] = f\"=FREQUENCY(A2:A{last},C2:C{len(edges)})\"\nwb.save(\"live_histogram.xlsx\")\nprint(\"bins:\", len(edges) - 1, \"+ overflow; formula:\", ws[\"D2\"].value)\n",[165,1949,1950,1967,1989,2001,2012,2029,2057,2090,2127,2164,2173],{"__ignoreMap":163},[168,1951,1952,1954,1956,1958,1960,1962,1964],{"class":80,"line":170},[168,1953,1001],{"class":226},[168,1955,262],{"class":222},[168,1957,1006],{"class":226},[168,1959,262],{"class":222},[168,1961,1011],{"class":226},[168,1963,262],{"class":222},[168,1965,1966],{"class":177}," \"Live\"\n",[168,1968,1969,1971,1973,1975,1978,1980,1983,1985,1987],{"class":80,"line":236},[168,1970,1021],{"class":226},[168,1972,285],{"class":177},[168,1974,335],{"class":226},[168,1976,1977],{"class":177},"\"\"",[168,1979,335],{"class":226},[168,1981,1982],{"class":177},"\"Up to\"",[168,1984,335],{"class":226},[168,1986,890],{"class":177},[168,1988,1030],{"class":226},[168,1990,1991,1993,1996,1998],{"class":80,"line":249},[168,1992,1035],{"class":222},[168,1994,1995],{"class":226}," v ",[168,1997,397],{"class":222},[168,1999,2000],{"class":226}," values:\n",[168,2002,2003,2006,2009],{"class":80,"line":256},[168,2004,2005],{"class":226},"    ws.append([",[168,2007,2008],{"class":316},"float",[168,2010,2011],{"class":226},"(v)])\n",[168,2013,2014,2017,2019,2021,2024,2026],{"class":80,"line":274},[168,2015,2016],{"class":226},"last ",[168,2018,262],{"class":222},[168,2020,929],{"class":316},[168,2022,2023],{"class":226},"(values) ",[168,2025,562],{"class":222},[168,2027,2028],{"class":316}," 1\n",[168,2030,2031,2033,2036,2038,2041,2044,2046,2048,2051,2053,2055],{"class":80,"line":291},[168,2032,1035],{"class":222},[168,2034,2035],{"class":226}," i, hi ",[168,2037,397],{"class":222},[168,2039,2040],{"class":316}," enumerate",[168,2042,2043],{"class":226},"(edges[",[168,2045,349],{"class":316},[168,2047,601],{"class":226},[168,2049,2050],{"class":302},"start",[168,2052,262],{"class":222},[168,2054,407],{"class":316},[168,2056,1052],{"class":226},[168,2058,2059,2062,2065,2067,2070,2073,2075,2078,2080,2083,2085,2087],{"class":80,"line":313},[168,2060,2061],{"class":226},"    ws.cell(",[168,2063,2064],{"class":302},"row",[168,2066,262],{"class":222},[168,2068,2069],{"class":226},"i, ",[168,2071,2072],{"class":302},"column",[168,2074,262],{"class":222},[168,2076,2077],{"class":316},"3",[168,2079,335],{"class":226},[168,2081,2082],{"class":302},"value",[168,2084,262],{"class":222},[168,2086,2008],{"class":316},[168,2088,2089],{"class":226},"(hi))\n",[168,2091,2092,2095,2097,2099,2101,2104,2106,2108,2110,2112,2114,2116,2118,2120,2122,2125],{"class":80,"line":370},[168,2093,2094],{"class":226},"ws.cell(",[168,2096,2064],{"class":302},[168,2098,262],{"class":222},[168,2100,323],{"class":316},[168,2102,2103],{"class":226},"(edges) ",[168,2105,562],{"class":222},[168,2107,332],{"class":316},[168,2109,335],{"class":226},[168,2111,2072],{"class":302},[168,2113,262],{"class":222},[168,2115,2077],{"class":316},[168,2117,335],{"class":226},[168,2119,2082],{"class":302},[168,2121,262],{"class":222},[168,2123,2124],{"class":177},"\"over\"",[168,2126,271],{"class":226},[168,2128,2129,2132,2134,2136,2138,2140,2143,2145,2148,2150,2153,2155,2157,2160,2162],{"class":80,"line":375},[168,2130,2131],{"class":226},"ws[",[168,2133,1323],{"class":177},[168,2135,352],{"class":226},[168,2137,262],{"class":222},[168,2139,1131],{"class":222},[168,2141,2142],{"class":177},"\"=FREQUENCY(A2:A",[168,2144,782],{"class":781},[168,2146,2147],{"class":226},"last",[168,2149,791],{"class":781},[168,2151,2152],{"class":177},",C2:C",[168,2154,782],{"class":781},[168,2156,323],{"class":316},[168,2158,2159],{"class":226},"(edges)",[168,2161,791],{"class":781},[168,2163,1146],{"class":177},[168,2165,2166,2168,2171],{"class":80,"line":388},[168,2167,1331],{"class":226},[168,2169,2170],{"class":177},"\"live_histogram.xlsx\"",[168,2172,271],{"class":226},[168,2174,2175,2177,2179,2182,2184,2186,2188,2190,2192,2194,2197,2200,2202],{"class":80,"line":468},[168,2176,317],{"class":316},[168,2178,320],{"class":226},[168,2180,2181],{"class":177},"\"bins:\"",[168,2183,335],{"class":226},[168,2185,323],{"class":316},[168,2187,2103],{"class":226},[168,2189,329],{"class":222},[168,2191,332],{"class":316},[168,2193,335],{"class":226},[168,2195,2196],{"class":177},"\"+ overflow; formula:\"",[168,2198,2199],{"class":226},", ws[",[168,2201,1323],{"class":177},[168,2203,2204],{"class":226},"].value)\n",[10,2206,2207,2209,2210,2214],{},[165,2208,1943],{}," returns one more count than there are edges — the values above the last edge — which lands next to the \"over\" label automatically. Written by openpyxl the formula has no cached result, so the counts appear only when Excel recalculates the file; for spilling dynamic-array formulas written from Python, see ",[14,2211,2213],{"href":2212},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-dynamic-array-formulas-with-python\u002F","Write Dynamic Array Formulas with Python",". For reports that are regenerated anyway, the static counts from Step 2 are simpler and show correctly in every program.",[153,2216,2218],{"id":2217},"common-pitfalls","Common pitfalls",[2220,2221,2222,2230,2243,2249,2255,2261],"ul",{},[2223,2224,2225,2229],"li",{},[2226,2227,2228],"strong",{},"Expecting the native histogram chart."," Neither library writes it; chart pre-counted bins.",[2223,2231,2232,2235,2236,2238,2239,2242],{},[2226,2233,2234],{},"Gaps between columns."," Set ",[165,2237,961],{}," (openpyxl) or ",[165,2240,2241],{},"\"gap\": 0"," (xlsxwriter).",[2223,2244,2245,2248],{},[2226,2246,2247],{},"Unreadable bin labels."," Round the width to a friendly step and label ranges clearly.",[2223,2250,2251,2254],{},[2226,2252,2253],{},"A few outliers stretching the axis."," Use an overflow bin and say how many it holds.",[2223,2256,2257,2260],{},[2226,2258,2259],{},"Unequal bin widths."," Column heights mislead when bins differ in width; keep them equal.",[2223,2262,2263,2235,2266,2268],{},[2226,2264,2265],{},"Missing axes in Excel.",[165,2267,1355],{}," on both axes of openpyxl charts.",[153,2270,2272],{"id":2271},"performance-notes","Performance notes",[10,2274,2275,2277,2278,2280],{},[165,2276,105],{}," counts millions of values in milliseconds; only the table of counts goes into the workbook, so the chart stays small however large the data. The live ",[165,2279,1943],{}," version stores every value in the sheet and recalculates on every change, which is fine for thousands of rows but slow for millions.",[153,2282,2284],{"id":2283},"conclusion","Conclusion",[10,2286,2287,2288,2290,2291,2293,2294,2296,2297,2299,2300,2302],{},"Pick bin edges with a round width, guided by ",[165,2289,205],{}," and a percentile rather than the extreme values, and send outliers to an overflow bin. Count with ",[165,2292,105],{},", check the counts add up, label each bin as a range, and chart the table as a column chart with no gaps — ",[165,2295,961],{}," and visible axes in openpyxl, ",[165,2298,2241],{}," in xlsxwriter. Use ",[165,2301,1943],{}," with upper edges when the workbook must recount as its data changes.",[153,2304,2306],{"id":2305},"frequently-asked-questions","Frequently asked questions",[10,2308,2309,2312],{},[2226,2310,2311],{},"Can openpyxl or xlsxwriter create Excel's built-in histogram chart?","\nNo. Excel's histogram, box-and-whisker and waterfall charts use a newer chart format that neither library writes. Count the values into bins in Python and draw the counts as a column chart with no gap between the columns — it looks and reads the same.",[10,2314,2315,2318],{},[2226,2316,2317],{},"How do I choose histogram bins?","\nStart from numpy.histogram_bin_edges(values, bins=\"auto\") to get a sensible number of bins, then round the width to a readable step such as 10, 25 or 50 so the bin labels are round numbers. Keep all bins the same width.",[10,2320,2321,2324],{},[2226,2322,2323],{},"How do I remove the gaps between columns in an openpyxl chart?","\nSet chart.gapWidth = 0 on the BarChart. In xlsxwriter, pass {\"gap\": 0} in the series options. Touching columns tell the reader the bins are continuous ranges rather than separate categories.",[10,2326,2327,2330],{},[2226,2328,2329],{},"How do I handle a few very large values?","\nPut them in a final overflow bin labelled, for example, \"500 and over\", instead of stretching the axis to include them. Say in the chart or a note how many values the overflow bin contains.",[153,2332,2334],{"id":2333},"related","Related",[2220,2336,2337,2343,2350,2357],{},[2223,2338,2339,2340,2342],{},"Up one level: ",[14,2341,17],{"href":16}," — native Excel charts from Python.",[2223,2344,2345,2349],{},[14,2346,2348],{"href":2347},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002F","Create a Bar Chart in Excel with openpyxl"," — the chart type underneath.",[2223,2351,2352,2356],{},[14,2353,2355],{"href":2354},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fdetect-outliers-in-excel-data-with-pandas\u002F","Detect Outliers in Excel Data with pandas"," — the values in the overflow bin.",[2223,2358,2359,2363],{},[14,2360,2362],{"href":2361},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fembed-a-matplotlib-chart-in-an-excel-report\u002F","Embed a Matplotlib Chart in an Excel Report"," — when you need a fitted curve too.",[2365,2366,2367],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":163,"searchDepth":236,"depth":236,"links":2369},[2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380],{"id":155,"depth":236,"text":156},{"id":199,"depth":236,"text":200},{"id":729,"depth":236,"text":730},{"id":954,"depth":236,"text":955},{"id":1606,"depth":236,"text":1607},{"id":1936,"depth":236,"text":1937},{"id":2217,"depth":236,"text":2218},{"id":2271,"depth":236,"text":2272},{"id":2283,"depth":236,"text":2284},{"id":2305,"depth":236,"text":2306},{"id":2333,"depth":236,"text":2334},"2026-09-25","Show how values are distributed — order sizes, delivery times, invoice amounts — as a histogram in an Excel report. Choose bin edges with numpy, count values per bin, label bins the way readers expect, draw them as touching columns with openpyxl or xlsxwriter, and keep a live FREQUENCY version for data that changes.","md",[2385,2387,2389,2391],{"q":2311,"a":2386},"No. Excel's histogram, box-and-whisker and waterfall charts use a newer chart format that neither library writes. Count the values into bins in Python and draw the counts as a column chart with no gap between the columns — it looks and reads the same.",{"q":2317,"a":2388},"Start from numpy.histogram_bin_edges(values, bins=\"auto\") to get a sensible number of bins, then round the width to a readable step such as 10, 25 or 50 so the bin labels are round numbers. Keep all bins the same width.",{"q":2323,"a":2390},"Set chart.gapWidth = 0 on the BarChart. In xlsxwriter, pass {\"gap\": 0} in the series options. Touching columns tell the reader the bins are continuous ranges rather than separate categories.",{"q":2329,"a":2392},"Put them in a final overflow bin labelled, for example, \"500 and over\", instead of stretching the axis to include them. Say in the chart or a note how many values the overflow bin contains.","create a histogram in excel with python",null,{"breadcrumb":2396},[2397,2399,2402],{"name":2398,"item":538},"Home",{"name":2400,"item":2401},"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-histogram-in-excel-with-python",{"title":2405,"description":2406},"Make a Histogram Chart in Excel from Python (openpyxl, xlsxwriter)","Build an Excel histogram from Python: numpy histogram_bin_edges and round bin widths, counting with np.histogram or pd.cut, bin labels, a column chart with gapWidth 0 in openpyxl or gap 0 in xlsxwriter, visible axes, outliers in an overflow bin and FREQUENCY formulas.","create-a-histogram-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-histogram-in-excel-with-python\u002Findex",[2410,2411],"charts-and-images","pivots-and-summaries","how-to","5tp-aggtEEyuXaYYQjkCcxzSkYX5v9mE5QoldaPdLe4",[2415,2419],{"title":2416,"path":2417,"stem":2418,"children":-1},"Create a Gauge Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gauge-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gauge-chart-in-excel-with-python\u002Findex",{"title":2420,"path":2421,"stem":2422,"children":-1},"Create a Pie Chart in Excel with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-pie-chart-in-excel-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-pie-chart-in-excel-with-openpyxl\u002Findex",[2424,2426],{"path":2425,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2427,"title":2400},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2430,2433,2436,2439,2441,2444],{"path":2431,"title":2432,"kind":2412},"\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":2434,"title":2435,"kind":2412},"\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":2437,"title":2438,"kind":2412},"\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":2440,"title":1932,"kind":2412},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter",{"path":2442,"title":2443,"kind":2412},"\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":2445,"title":2446,"kind":2412},"\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",1790972111004]