Chart.js icon indicating copy to clipboard operation
Chart.js copied to clipboard

[FEATURE] Histograms

Open mrjv opened this issue 7 years ago • 25 comments

A nice feature would be to support histogram charts, or alternatively, update the bar chart so that a histogram can be manually created given the right data. At the moment it seems like a bar chart only can represent value-category pairs, while a histogram would require the bars to be drawn at the right positions on a numerical/linear axis.

mrjv avatar Feb 12 '17 23:02 mrjv

You're correct about the current limitations of the bar graph. If you pre-process your data into bins then you can easily create the histogram using the current bar chart.

Some thoughts on bar sizing in this case:

  1. Bars have a fixed width and gaps may appear between the bars
  2. Bars width is determined by the distance between bars and calculated so that bars touch

I think option 2 is better here since it produces a better result

In terms of data processing, I think this would be best as a new chart type that extends from the bar chart. The calculateBarX and calculateBarWidth methods would need to be overridden https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.bar.js#L196 https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.bar.js#L165

etimberg avatar Feb 13 '17 02:02 etimberg

Thanks for the tips! I will try that out.

mrjv avatar Feb 22 '17 21:02 mrjv

As long as one can get away with:

  1. bins of equal width, e.g., [0, 2), [2, 4), [4, 6), [6, 8], in contrast to bins of variable width,
  2. labels under bars that denote the bar's range, e.g., "[0, 2)", "[2, 4)", "[4, 6)", "[6, 8]", or "0-2", "2-4", "4-6", "6-8", in contrast to having a numerical axis (whose ticks may or may not align with the bars' vertical sides),
  3. manually calculating each bar's height, that is counting the value for each bin,

then the current bar chart may be used, just making sure that:

barPercentage : 1.0,
categoryPercentage : 1.0,

are used as axis options, so that the bars touch. I have used the bar chart this way myself where a histogram was needed and it was nice.

But if the histogram was to be fully supported, then points (1) and (2) should be dealt with, as shown in:

  • https://en.wikipedia.org/wiki/Histogram
  • https://bl.ocks.org/mbostock/3048450
  • http://bl.ocks.org/mbostock/1624660

I am not sure about point (3). I think that this cannot and should not be avoided. If thousands of values fell into one bin, should the chart "know" all those values? I guess not. I many cases the dev would not "know" all those values either.

So, one way or another, one should define something like the following as data for the histogram:

[
  [0, 2, 15],
  [2, 4, 29],
  [4, 6, 32],
  [6, 8, 15],
]

or (irregular histogram):

[
  [0, 1, 15],
  [1, 4, 29],
  [4, 7, 32],
  [7, 8, 15],
]

I am not familiar with the source code, so feedback is needed. My question is this: Is extending the bar chart the way to go? Because the numerical axis is definitely needed.

EDIT:

Scatter chart builds on line chart, but uses a numerical scale as well as a different data representation. Following this approach, maybe histogram could build on bar chart and a representation such as the following could be used (here width is added as a parameter, another option would be to have xLeft and xRight - this depends on library conventions):

data: [
  {
    x: 0,
    width: 1,
    y: 15,
  }, {
    x: 1,
    width: 3,
    y: 29,
  }, {
    x: 4,
    width: 3,
    y: 32,
  },
  {
    x: 7,
    width: 1,
    y: 15,
  },
]

xnakos avatar May 03 '17 11:05 xnakos

I feel like extending the bar graph is the way to go. Bins should be equal-sized, at least as the first cut. You can easily fit data into buckets by doing something along these lines with integer values, assuming a bucket size is 10.

In [20]: [(i/10)*10 for i in [1023, 1025, 1021, 1033, 1039, 1037, 1033]]
Out[20]: [1020, 1020, 1020, 1030, 1030, 1030, 1030]

This is simple enough to implement to group values into containers. The only tricky part is determining proper number of bins. Some people prefer fairly blocky histograms, some like the more hairy ones, where bins are smaller and you get more of them. Ideally one should be able to pick a number of bins to display, but you could probably make this value fixed for first, just to see how it works out with a bar graph. You could say stick with 30 to 50 graphs and then divide the range of data by that number to get a rough size of each bucket, perhaps.

I wish I had more free time to help with this, but have been slammed as of late. I am happy to test and offer constructive feedback. :)

szaydel avatar Jul 08 '17 03:07 szaydel

I'm trying to plot histograms but I have variable bins sizes (semi log) so I'd really like the option to set a [start, end[ for each bar - and overlay some cumulative % at the mid point

For now I'm trying to do this by hand using scatter but being completely new to chartjs I still haven't found how to draw lines between points (X,Y line graph instead of X,Y scatter point graph) - hint/pointer for that would be welcome

Edit: found that I can just use "line" as the mode and style have X,Y in the data - when also using type: 'linear' xAxes

ldemailly avatar Nov 08 '17 05:11 ldemailly

Here is a jsfiddle that I cobbled together over lunch using a bar chart to make a histogram. Just a rough draft to help people get started.

https://jsfiddle.net/s8qas3km/17/

spitzbubchen avatar Feb 07 '18 21:02 spitzbubchen

if you want to see you can see fortio's line based implementation on https://istio.fortio.org/

source code: https://github.com/istio/fortio/blob/master/ui/static/js/fortio_chart.js

ldemailly avatar Feb 07 '18 23:02 ldemailly

Any way to position the xAxis labels to align 'with' the gridlines instead of below the bar? So as to give more of that "bucket" feel...

rupsaijna avatar Apr 25 '18 05:04 rupsaijna

I'm using bar charts with fair results to plot histograms. I would like to see a feature to label edges rather than bars, and may look into implementing. I've implemented an NPM module to calculate the bin sizes here: https://www.npmjs.com/package/compute-histogram.

Here's an example of it in use: https://yield.io

baus avatar May 16 '18 18:05 baus

Testing this in v2.9,3, setting gridLines.offsetGridLines to false on the x axis gets a bit closer

v2.1.3 https://jsfiddle.net/z136gkL4/ v2.9.3 https://jsfiddle.net/3ehp4L58/1/ v3.0.0-alpha https://jsfiddle.net/8cothjzs/

@benmccann @kurkle one thought on how we could support this:

  • Keep offsetGridLines for the bar location in it's current form
  • Introduce a new option (name TBD) for controlling the tick label alignment. Values: 'start' and 'center'. We'd default to 'center' for bar charts and this would put mimic the current v2 behaviour. If the user overrides to 'start' we place it on the grid line.

etimberg avatar May 24 '20 01:05 etimberg

If I do a Google images search for histogram none of the results have vertical gridlines. I think I'd just turn off the gridlines in the fiddle and call it a day :smile:

benmccann avatar May 27 '20 12:05 benmccann

Any progress on this issue?

bnisevic avatar Mar 15 '21 00:03 bnisevic

@bnisevic no specific progress has been made, but if I use chart.js v3.0.0-beta.13 I can make a very nice histogram. I started with https://en.wikipedia.org/wiki/Histogram#/media/File:Histogram_of_arrivals_per_minute.svg from the wikipedia article on histograms.

What I made was https://jsfiddle.net/swg4y1at/ which i think is very close

etimberg avatar Mar 15 '21 00:03 etimberg

that wikipedia image is terrible - the colors literally mean nothing in that chart, not even correlated to the bar height. the only time that's appropriate is when the x axis represents the visible color spectrum and the bars are colored by the portion of the spectrum they represent, while the labels are in nanometers of wavelength :rainbow:

the key parts are (vs a bar chart):

  • continuous x range instead of category
  • tightly packed, since x dimension now has semantic meaning (no gaps between bars)
  • tick marks usually at the bar boundaries (usually leading edge)

most are pretty good: https://www.google.com/search?q=histogram&source=lnms&tbm=isch&biw=2560&bih=1299

https://www.mathsisfun.com/data/histograms.html

leeoniya avatar Mar 15 '21 01:03 leeoniya

Agreed @leeoniya that it's not the best example. Getting the tight packing and ticks well align required 4 key settings:

  • barPercentage: 1
  • categoryPercentage: 1
  • X scale offset: false
  • X scale gridLines.offsetGridLines: false

The first two direct the bars to take all available width, eliminating the spacing between. The last two put the ticks back on the grid lines.

I think the other keys were the linear X axis and computing the X locations to be in the center of the bucket range. I don't think Chart.js should be doing all of that itself, but writing a custom chart type plugin would be very easy. It'd need some defaults and maybe something to preprocess the data into buckets.

etimberg avatar Mar 15 '21 01:03 etimberg

Thanks guys for the explanation! I am starting to understand what is purpose of these 4 key settings when it comes to a histogram. But, how I can achieve different bar width based on given data as ranges? For example, if I have [1, 2, 3, 5, 8, 13, 21, ...] on the x axis and I want to plot one bar from 1 to 2, then next bar is from 2 to 3, the next one is from to 3 to 5, the next one 5 to 8 and so on. On the y axis I also have custom values which determine height of each bar.

thdadsa

bnisevic avatar Mar 15 '21 10:03 bnisevic

The variable width bars might be possible using the scriptable options added in v3.0.0-beta.12. You'd have to supply. a function for the barThickness prop and use that to compute the width based on the X scale values.

etimberg avatar Mar 15 '21 12:03 etimberg

I am using version 2.9.3. I cannot downgrade or upgrade this. Here is example using 2.9.3 https://codepen.io/vaasu070/pen/bGgKdML?editors=1111

is it not possible to align ticks with labels using plugins or something? Help me out on this, it is crucial for our project

vaasu070 avatar Apr 15 '21 13:04 vaasu070

I got some bad news for you @vaasu070. I was trying to do something similar using 2.9.3 as you can see in this SO post. I ended up having to upgrade to ChartJS 3 and I was able to accomplish my goal using a combination of the suggestions here.

Link to JSFiddle

The final key was setting the axis max of each non visible axis to the length of the data array minus 1.

mortalapeman avatar May 17 '21 18:05 mortalapeman

Thanks for the examples @etimberg and @mortalapeman!

I think a native histogram feature would be a great addition to Chart.js.

EwoutH avatar Dec 01 '21 11:12 EwoutH

Does a histogram feature have to be native (long term, it should be), or can it somehow simply wrap a bar chart implementation?

aentwist avatar Mar 22 '22 02:03 aentwist

The variable width bars might be possible using the scriptable options added in v3.0.0-beta.12. You'd have to supply. a function for the barThickness prop and use that to compute the width based on the X scale values.

Hello @etimberg , how do you make that ? barThickness is a setting of a dataset, so if i set a callback to this, for instance like that :

 barThickness: (e) => {
     return randomIntFromInterval(1, 100);
 }

=> the callback is just called once so all bars will take the same width, is not it ?

stephanebouget avatar Oct 18 '22 08:10 stephanebouget

Does a histogram feature have to be native (long term, it should be), or can it somehow simply wrap a bar chart implementation?

Yeah I might have worded that badly: I meant an user-exposed histogram feature, it could be both native, a wrap of the bar chart or even something else.

For an user, it's just way nicer than having to modify a bar plot to look like a histogram.

EwoutH avatar Oct 19 '22 10:10 EwoutH

For those who are interested, i made a quick fork based on 3.9 branch to manage dynamic width : https://github.com/stephanebouget/Chart.js/tree/3.9

For example :

image

Live demo

https://codepen.io/stephanebouget/pen/PoerxPP

stephanebouget avatar Oct 20 '22 16:10 stephanebouget

@stephanebouget & ChartJS contributors, would you accept a PR for this feature if someone put it together?

uglow avatar Nov 07 '22 03:11 uglow