leeoniya / uPlot

📈 A small, fast chart for time series, lines, areas, ohlc & bars
MIT License
8.51k stars 371 forks source link

bar charts with category x-axis #9

Closed leeoniya closed 4 years ago

leeoniya commented 4 years ago

Architecturally, some additional chart types can be supported as long as they conform to the following:

const data = [
  [1566453600, 1566457260, 1566460860, 1566464460],   // x values
  [0.54,       0.15,       3.27,       7.51      ],   // series 1
  [12.85,      null,      13.65,      14.01      ],   // series 2
  [0.52,       1.25,       0.75,       null      ],   // series 3
];

this means we can support:

while the x-values have to be numeric, x labels can be text, so for something like this:

json-parser

you could simply keep a map in app-space

let xCats = ["Censys", "Gharchive", "NYTimes"];

let data = [
  [   0,     1,     2],
  [3.09,   2.7,  2.92],
  [1.93,   1.6,  2.02],
  [1.06,  0.93,  1.22],
];

let opts = {
  scales: {
    x: {
      type: 'n'
    }
  },
  series: {
    y: [
      {label: "PyPyBaseline"},
      {label: "PyPyKeySringCaching"},
      {label: "PyPyMapNoCache"},
    ],
  },
  axes: {
    x: {
      values: (vals, space) => vals.map(v => xCats[v]),
    }
  }
}
danyalejandro commented 4 years ago

You could follow Plotly's modular architecture and make these additional includes. However, since the selling point of uPlot is performance and the strongest use case is big data, people looking for plots would probably prefer the feature-rich alternatives.

leeoniya commented 4 years ago

i think this type of chart is a great complement to trend lines because it fundamentally represents a type of data that cannot be represented as a trendline; it literally doubles the possible use-cases while other types of charts mostly just show the same data in a different UX (stacked, ohcl/candlestick, area, pie). i already added numeric X-axis basically for free which now opens the possibility to use uPlot as a generic function plotter.

adding this would not impact perf since this will be able to use the existing data structure and not add new mem allocations. also, it will definitely use a different drawing loop compared to the lines. the main question here is one of additional code size. my gut feeling is that it can be done for an additional 1-3k with no perf impact, and that's a worthwhile trade-off to me.

danyalejandro commented 4 years ago

Yeah, I get your point. My comment was in the best interest of your lib being used, since Plotly and other heavyweights would be the go-to option for everyone that wanted a bar chart right now: 2MB of features, free, open source, bindings with Python, R and Matlab, it has everything.

I work with data scientists so I'm familiar with their preferences, and I believe uplot can explode in popularity if it becomes the main choice when performance is a concern. Plotly offers a webgl interface but you can only have a dozen of them per page (I have dashboards with 1100 plots). Dygraphs works but is abandoned. Apexcharts is slow. JsCharts is old. Other libs either lack interactivity or other features.

Are you set on limiting your lib to the bare minimal? If so, allow me to fork your project, it's a gold mine...

On Mon, Oct 28, 2019, 11:53 AM Leon Sorokin notifications@github.com wrote:

i think this type of chart is a great complement to trend lines because it fundamentally represents a type of data that cannot be represented as a trendline; it literally doubles the possible use-cases while other types of charts mostly just show the same data in a different UX (stacked, ohcl/candlestick, area, pie). i already added numeric X-axis basically for free which now opens the possibility to use uPlot as a generic function plotter.

adding this would not impact perf since this will be able to use the existing data structure and not add new mem allocations. also, it will definitely use a different drawing loop compared to the lines. the main question here is one of additional code size. my gut feeling is that it can be done for an additional 1-3k with no perf impact, and that's a worthwhile trade-off to me.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/leeoniya/uPlot/issues/9?email_source=notifications&email_token=AAJJQMB7BYFI6CVM7OCEOLTQQ4YL7A5CNFSM4I7QADHKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECOACKA#issuecomment-547094824, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJJQMHT33ILQ65TYSPDFUTQQ4YL7ANCNFSM4I7QADHA .

leeoniya commented 4 years ago

Are you set on limiting your lib to the bare minimal? If so, allow me to fork your project, it's a gold mine...

you don't need my permission. you can fork it if you'd like if/when you feel it is no longer minimal enough for you.

my goal is to extract as much value out of the current core as possible while maintaining the current perf, keeping a uniform API without hacks and staying within ~20K (which is still at least 6x smaller than anything else i've been able to find). i have already rejected features like scatter plots, which do not fit neatly into the current design. if you're asking me whether i'm committed to limiting uPlot to only ever being a line plotting lib, then the answer is no. but the goal is for it to continue to be the fastest and smallest choice (by far) of any js plotting lib.

since Plotly and other heavyweights would be the go-to option for everyone that wanted a bar chart right now: 2MB of features, free, open source, bindings with Python, R and Matlab, it has everything.

uPlot has never been and will never be for those who want everything; it's for those who want the basics (and a bit more) for the web - where bindings don't matter but 2MB certainly does. there's enormous value and opportunity in adding bar charting functionality unless you can show me another js charting lib that can make me some bar charts for a < 15KB lib weight and is pleasant to use.

(I have dashboards with 1100 plots)

this seems unhealthy, no matter how fast a lib you use.

leeoniya commented 4 years ago

@ldstein

if we can do candlesticks as a plugin, i'm pretty sure we can pull this off as a plugin, too ;)

EDIT: might want to wait till custom path points (#10) is sorted out. i'll probably convert the candlesticks in that demo to be drawn as path points.

ldstein commented 4 years ago

I had a go over the weekend. Demo here.

barchart-demo

The approach to get the X axis labels to align horizontally with each group of bars is... creative :) I'm adding an extra stop to the min/max range, then positioning each group of bars 50% left. Interested to know if there is a better approach.

As a bonus - it should also work with negative values.

leeoniya commented 4 years ago

nice!

i'll review this later today.

leeoniya commented 4 years ago

i had an independent go at it, then looked at yours. turned out nearly the same. works for negatives as well. :)

https://jsfiddle.net/y7bder3j/

mine is not in plugin form yet, and has a hard-coded bar width but not a configurable gap - i think the bar width config is the better route here. also i don't stroke the bars since this tends to overlap them (without additional math). i'll probably add some pixel alignment logic to the path builder. i disabled zooming, since i don't think there is a great use-case for it in charts like this and it adds to the requirement to handle it properly - i don't think the demo needs this added cognitive burden. i'm not sure if the cursor points should be active or not, but they would also require the proper bar offsets applied to them and there's currently no elegant way to do that besides hacking at the DOM directly. i would probably prefer a hover interaction with a bg div like in the candlesticks demo - it works well when the chart has few discrete points with mostly gaps everywhere else.

once i get series.points published, we can use that to draw text labels on top of each bar, too :D

if we really want to get crazy, we can draw the 1.0 baseline marker in a draw hook, turn off the vertical grid and style the hz grid & bg color to match the original chart:

https://jsfiddle.net/y7bder3j/4/

leeoniya commented 4 years ago

i've pushed an initial impl of series.points & series.point.

i use it in the new [wip] bars demo to draw a text label over each bar:

https://leeoniya.github.io/uPlot/demos/multi-bars.html

ldstein commented 4 years ago

Nice. That looks intuitive enough.

leeoniya commented 4 years ago

That looks intuitive enough.

it wasn't to me!

i've switched it to be more uniform. among other refinements.

there is no more axis.tick and series.point for styling options; they have been rolled into single respective structures: series.points: {show: () => bool, width, size, stroke, fill} and i've renamed what used to be axis.ticks() to axis.split() since it applies to both ticks and grid, regardless of the visibility of either. the new axis.ticks: {show: bool, width, size, stroke} is used for styling and visibility.

next up is doing the same for cursor.points