JesperLekland / react-native-svg-charts

📈 One library to rule all charts for React Native 📊
MIT License
2.36k stars 410 forks source link

StackedAreaChart does not support decorator children the same way as AreaChart #122

Open Amnesthesia opened 6 years ago

Amnesthesia commented 6 years ago

What is the problem?

Props received by children of StackedAreaChart are different than those received by the AreaChart (which all in all is ok), but they dont seem to contain enough data to create decorators from (e.g can't calculate X / Y because the data array may contain objects)

When does it happen?

When you try to follow any of the examples to render decorators as children, but using StackedAreaChart

What platform?

React Native version: 0.55.2

I'm liking the new 5.0, but StackedAreaChart seems to be a special child that needs a little bit more love :(

JesperLekland commented 6 years ago

The "stacked" charts are indeed in need of some more love. Their complex data structure make things a lot harder to calculate. I'll see what I can do 👍

JesperLekland commented 6 years ago

Out of curiosity, what types of decorators would you like to render?

Amnesthesia commented 6 years ago

Well, I've had a couple of different issues I'm trying to work around — one is that making "timeline" charts, e.g data following a time axis, can be challenging since the points are spaced evenly, but I've worked around this by providing values with everything 0 for e.g missing minutes; but what I'm doing that requires decorators is making it possible to click the graph and drag a finger to show the values in a little pop-up.

This is especially difficult when you have two charts on top of one another, and I've solved this (in a not-so-nice way) by having a decorator child that receives all datapoints and finds their X/Y position, so that a parent pan-responder can pass down what coordinates the finger is currently on, and the decorator can check what datapoint is closest to those coordinates, and render a line and a dot with a box showing the values at that specific point.

Currently I'm doing this with AreaChart and LineChart, because StackedAreaChart gets different props and I couldn't get this working with StackedAreaChart.

I feel like having a timeline chart where the position is decided by timestamp (rather than index in the list) and its relative position to the first / last timestamp would be an amazing addition to this library; and maybe a better way to dealing with PanResponders to show decorators only when the user is clicking anywhere near them.

For now, this works, but it doesn't work at all with StackedAreaChart

JesperLekland commented 6 years ago

Thank you for the elaborate explanation.

First of all, the spacing of your data points in different scale than linear is supported by passing in a different xScale, check this example.

Regarding the PanResponder I think you've landed in an "ok" way of doing it. I myself did something similar but haven't since had the need for a PanResponder and as such haven't worked on it's support either.

I think I might be able to extend the variable x scale to the stacked charts as well, I'll give it a go as soon as I can. Not sure it will solve your problem though, but might bring us a step closer

vjsingh commented 6 years ago

I'm actually also interested in decorators for stacked charts I believe, unless there is an easier way of doing what I'd like to do.

I want to display a small tooltip with the value of a section of the graph when a user clicks that section. You could also imagine being able to overlay the number value of each section of the stacked bar graph over the sections.

Amnesthesia commented 6 years ago

This seems to also be a problem for AreaChart if you use AreaChart with an array of objects and a yAccessor / xAccessor (e.g for using scaleTime). When the array isn't just numbers, the x() function provided to Decorator children doesn't work very well anymore

JesperLekland commented 6 years ago

The x function is the same as the one used by the chart, i.e it's supposed to be fed the value returned by xAccessor, not the complex object

Amnesthesia commented 6 years ago

Yeah, sorry about that. After some time I realised the x function in the decorator needed a Date object when using scaleTime

JesperLekland commented 6 years ago

Well there you go 👍 So is this issue ready to be closed?

Amnesthesia commented 6 years ago

@JesperLekland That's up to you :-) Do StackedAreaCharts support decorators now?

I'd like to discuss usage of decorators with PanResponders and what the best practice for this is, especially if you have multiple overlaying charts (which turns out to be really messy) - can I open a new issue for a general discussion on best-practice for decorator use cases, or does that belong somewhere else?

vjsingh commented 6 years ago

@JesperLekland I still have my question from above, I'd like to display a tooltip above the different sections of a StackedBarGraph. It appears from the documentation that this isn't currently supported?

JesperLekland commented 6 years ago

Well they do support decorators but they're perhaps not as intuitive as for the other charts. You have to stack the values yourself in order to get the right y value but the x function should work exactly the same as for other graphs. I might be able to create some examples for the examples repository that incorporate decorators for a stacked chart, it won't be highly prioritised though.

Regarding the pan-responder I think that's better suited for a separate issue.

@vjsingh What you're trying to do is supported, albeit tricky. What in the documentation makes you think it isn't? Look through the examples (specifically the one with a tooltip), check the source code and if you're still at a loss, open a new issue detailing your problem.

vjsingh commented 6 years ago

@JesperLekland I thought so because the "data" prop isn't passed to the children of the stacked charts, i.e.:

This chart does not call a child with the data argument. This is due to the fact that a lot of calculations go into creating the stacked chart, meaning that the original data prop doesn't provide especially valuable information when trying to layout decorators. It does however call with the rest of the common arguments

Without access to the data array, do I write decorators with just the x and y functions, and maybe my own data array passed in from elsewhere?

JesperLekland commented 6 years ago

You render the child yourself, just pass in the data prop to it.


<Chart data={data} >
  <Child data={data} />
</Chart>
JesperLekland commented 6 years ago

But I hear you, it's not the same as for the other charts. I could of course add it but not sure it would do any difference?

Amnesthesia commented 6 years ago

One of the difficulties with decorators right now — pan responders aside — is when you have charts stacked on top of each other, meaning you have two very different datasets.

In one of my charts, I'm plotting a change over time in value, and on top of that chart, I have a LineChart to plot a change over time in another value, to let you see how two values correlate on a timeline.

This means the charts are siblings, and have to be positioned absolutely or offset to be on top of each-other, but it also means that the Decorator needs to be a child of one of the charts, and will only receive the x() and y() function of that chart. Retrieving a dataset from that chart works fine, but when you then want to find out what y value is plotted on that x coordinate in the sibling chart, it gets way more complicated.

I've solved this but the solution is very messy — I think StackedAreaChart uses a static function that you can use manually to calculate the value positions if I'm not mistaken, and I'm wondering if it would be a possibility for a similar solution for other charts as well

JesperLekland commented 6 years ago

But if you have two charts on top of each other then they should share the same domain and extent (min/max for both axes), otherwise they won't make any sense, meaning that their x() and y() should be exactly the same.

Could you provide a specific use case when this isn't true? (it should be for the one above).

JesperLekland commented 6 years ago

As I said in the comment earlier you can easily pass in extra props to your decorator e.g

....
<Decorator data2={data2}/>
...

Would give you access to both datasets. Allowing you to easily retrieve the value of the opposite dataset, e.g data2[indexfoo]

Amnesthesia commented 6 years ago

They share the same X-axis, e.g displaying data for the same month, but they have completely different X-axis, e.g if one shows percentage 0-100 and the other one shows arbitrary values 0-15000, so the X-function will be the same, but not Y

vjsingh commented 6 years ago

I made a pull request for my example here: https://github.com/JesperLekland/react-native-svg-charts/pull/175

(It's a stacked bar chart with labels)

@JesperLekland The code wasn't too bad, but maybe some of this functionality could move into StackedBarChart to make a little easier / more intuitive?

JesperLekland commented 6 years ago

@Amnesthesia hmm.. I see. How would you expect a chart like that to behave? I can't visualise it, could you possibly make a snack That better explains showcases your problem?

Amnesthesia commented 6 years ago

@JesperLekland Sorry, spent some time trying to get a snack working but my laptop just kept crashing :(

But a typical example would be trying to implement any of these charts for example: http://www.tylervigen.com/spurious-correlations

Ideally, I would like if charts would accept the same props but as an array, imagine e.g:

<AreaChart
    series={[{
       name: 'Films Nicholas cage appeared in',
       data: [ // current data prop],
       xAccessor: ({ item }) => item.value,
       yAccessor: ({ item }) => item.timestamp,
   },{
      name: 'Number of people who died by falling into pools',
       data: [ // current data prop],
       xAccessor: ({ item }) => item.count,
       yAccessor: ({ item }) => item.timestamp,
   }]}
>
  { /* Decorator could receive data prop as an array and identify it by e.g name, order, etc and each item would have its own x/y function */}
  <Decorator /> 
</AreaChart>

In an even more ideal world, that could just be Chart and the items could accept a type setting for line, area, or bar, but that's unrelated to this I suppose.

Bottomline is, as it is now, it's very very difficult to do correlation charts, and it does feel a little bit hacky to have to overlay two charts and position them absolutely or with negative margins to show them together — it would be super super nice if the charts would natively support showing different YAxis on the same XAxis (which is especially useful for charting time scale data)

JesperLekland commented 6 years ago

Sounds like a great proposal, I'll see what I can do. Thanks for helping out in making this library become better and better :)

Amnesthesia commented 6 years ago

Thank you so much!

I'm really sorry I haven't made any PRs yet — when time allows, this is the first library I'll contribute to :) Thank you for all your great work on this!