You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow precomputed statistics input for box traces #1059
used during jitter computation (as a fallback to the IQR)
the mean
O(n)
optional, used the draw "mean line" and on hover
the standard deviation
an addition O(n) once the mean is computed
optional, used the draw additional std lines and on hover
median, q1, q3
not very costly once the sample is sorted (mostly just indexing in the sample array)
required for all box traces
lower/upper fences
needs an addition binary search (with a sorted sample)
required for all box traces
lower/upper outliers
compute the IQR then O(n) to filter out outliers pts from the sample
lower/upper notch values
not very costly (a function of the median, the IQR and sample size)
A few thoughts:
The most costly operation here is sorting the sample
We could skip computing the mean and std when they don't appear on the graph, saving two O(n) loops
even if we allow users to set "their own" median, q1 and q3, we would still be left with two required binary searches to compute the boxes' upper and lower fences.
A somewhat deeper thought:
What's the main goal here?
Allow users to provide their own q1, median and q3 (and potentially upper/lower fences)?
Speed up box/calc.js for very large datasets?
If the latter, then making users sort their samples before calling Plotly.newPlot (with an addition flag turned on in their box traces) may be enough of a performance boost for most users.
@etpinard The goal, in my eyes, is i. I would like to use plotly.js to make graphs. I don't want to be forced into crunching data using plotly.js.
Recently I wanted to use plotly.js to graph a box and whiskers plot where my raw data was billions of rows of data that live in a database server side. This kind of data can never be sent to the front end. Any calculations on it will always happen in SQL. I want to be able to summarize it in sql, once a day, and use the resulting parameters in constructing visualizations. Plotly.js will never be able to loop over that data, no matter how fast you make it.
Reacted by Nick Pope, Netanel Draiman, Carson Sievert, Cyril Cecchinel, Moa Raji, Ravi Kalia, Adrian Hauser, David, Tom Scherr and Oli
lf or lowerfence / uf or upperfence OR fences as a 2-item array
Separate attrs feels cleaner to me.
ln or lowernotch / un or uppernotch OR notches as a 2-item array (would make notched true by default if set)
Allowing the notch to be asymmetric? We could go with notchwidth or confidenceinterval to keep it symmetric.
outliers as a data array OR as #1059 (comment) suggest maybe we can reuse the y (x for horizontal boxes) for that
The only problem I see with reusing x or y is the partial definition case below. Which do you think would be less confusing for users to work with:
If you want to provide outliers and not the whole sample set with precomputed stats, you need to provide all the stats (which seems like it should be obvious, unless there's a case where you omit one stat because it matches another one - upperfence omitted or matching q3 for example - which can happen if there are no outliers within what would be the fenced range, even if there are outliers beyond it)
When using precomputed stats we "recommend" using outliers even though 99% of the time using x or y would work exactly the same. Then we have to answer what we'd do if you provide only outliers, or outliers with partial stats...
{type: 'box', q1: 1}
be allowed? I personally think so
Seems reasonable at first glance - values above what you provide match the last one below it, and vice versa. But it's a bit confusing to think about what happens if you're missing some in the middle. You provide q1 and q3 but not median - do you use the average of the two? Seems sketchy. What if you provide only lowerfence and upperfence? Seems even sketchier. So I might say you need to at least provide q1, q3, and median
{type: 'box', q1: 1, y: [1, 2, 3, 4]}
Personally, I think the third option could be interesting as one could want to override Q1 and Q3 (using some other quartile-finding algorithm) and have plotly compute all the other statistics - including those that depend on Q1 and Q3 (i.e. the fences, the outlier bounds and the notches).
Sounds good to me. To be clear, for the values that depend on q1 and q3 we would insert the user-provided values, so the derived values would change as well, correct?
Separate attrs [for the lower & upper fences] feels cleaner to me.
So I might say you need to at least provide q1, q3, and median
Yeah good call. Thanks!! In fact, I'm starting think perhaps all these new precomputed attributes should be valType: 'data_array' (or at least arrayOk: true) so that we can draw multiple boxes with a single trace e.g.:
{type: 'box',x: ['apple','banana','orange'],q1: [1,2,3],median: [2,3,4],q3: [3,4,5]}// would draw three boxes
Things get trickier for defining outliers though, but using a 2d array should be good enough e.g:
{type: 'box',x: ['apple','banana','orange'],q1: [1,2,3],median: [2,3,4],q3: [3,4,5],outliers: [[-10,10],// outliers for 'apple'[12],// outliers for 'banana'[-20,-10,15]// outliers for 'orange']}
By allowing array q1, median, q3 and friends, it might be best to split the box API into two signatures so that the trace length (i.e. trace._length) is always well defined.
In the current version, where we use y (x, for horizontal boxes) to set the sample and x or x0 (y or y0) to position the boxes. Here _length corresponds to the sum of the sample sizes from each box to be drawn
In the new one, where we use q1/median/q3 and optionally lowerfence/upperfence/outliers/mean/sd/notchspan to describe how to draw the boxes and x (or y, for horizontal boxes) to position them. Here _length corresponds to the number of boxes to be drawn.
it might be best to split the box API into two signatures
That makes a lot of sense to me - could even extend this to the original full-sample case: x is just one value per box, and y is a 2D array, with an outer length that matches x and the inner arrays - which need not all have the same length - are the samples for each x.
For precomputed statistics I wouldn’t want to prohibit single values (or force those to be wrapped in an array) when your goal is a single box; so I’d go with arrayOk
Does this introduce any ambiguities? I don't see any... there would be nonsensical inputs (q1 is an array but median is a scalar, etc) but that's fine, wouldn't need to impose a meaning, we'd just need to not break.
@nicolaskruchten any concerns about this from the standpoint of RCE?
For precomputed statistics I wouldn’t want to prohibit single values (or force those to be wrapped in an array) when your goal is a single box; so I’d go with arrayOk
At the moment, I think enforcing (data) arrays for q1, median, q3 and friends is a better idea - mostly become allowing something like:
{type: 'box',q1: 0,median: 1,q3: 2}
would mean having a visible trace with no set valType: 'data_array' attributes. I guess that wouldn't be a first (indicator comes to mind), but that looks a little odd to me.
Does "splitting into two signatures" mean a new trace-type boxmanual or something? I'm not sure of the implication there but I'm fine with a new trace-type if that's what's needed.
I have no a priori concerns from the POV of RCE, no, but thanks for asking!
Also, I would favour adding attrs for upper and lower outlier thresholds, and then accepting y with a mode that says "show all points" vs "show outlier points". That way if the user specifies only outliers, they can show that, but if they want to show all points they can do that also.
Given all the variants we've been discussing, it's not entirely clear to me where we would draw the line between the two trace types. Anyway we have plenty of precedent for variable dimensionality and varying degrees of manual control - I'd keep a single trace type.
it's not entirely clear to me where we would draw the line between the two trace types
the new one would accept the q1/q2 etc options and the old one wouldn't
Anyway we have plenty of precedent for variable dimensionality
what's an example of a trace where the expected dimensionality of attrA is the same as attrB unless attrC is set, in which case it's expected to be something else?
the new one would accept the q1/q2 etc options and the old one wouldn't
So the new one would also accept y (for all points) if you just want to override the quartile calculations but leave everything else calculated? Or we won't allow that option? Moreover the idea of extending the 2D option to the original full-sample case (x is just one value per box, and y is a 2D array) seems to further blur the lines.
what's an example of a trace where the expected dimensionality of attrA is the same as attrB unless attrC is set, in which case it's expected to be something else?
Not sure if we have exactly that situation... we certainly have attributes where the allowed values depend on other attributes. And we have one that I'd argue should have dimensionality interdependencies though it doesn't right now: surface currently accepts 1D or 2D x/y arrays, and a 2D z array. But I'd argue it should also allow all 3 arrays to be 1D so that you could always take a heatmap or contour and convert it to surface. Currently you can pass all 1D arrays to mesh3d, but it behaves rather differently than surface (and different from heatmap and contour) and anyway it breaks the very simple idea of "show me this heatmap as 3D" by just converting to surface. And if we were to support that, then you could only tell whether 2D x & y are valid by looking at z.
Regardless, I don't think the criterion of coupled dimensionality requirements is enough to warrant calling this a new trace type. Most of the attributes are the same, the result is the same, it's just a few additional attributes and a few with a different dimensionality.
At the moment, I think enforcing (data) arrays for q1, median, q3 and friends is a better idea
...
a visible trace with no set valType: 'data_array' attributes. I guess that wouldn't be a first (indicator comes to mind), but that looks a little odd to me.
I see your point, and I guess it's nice from the standpoint of making it obvious you can include multiple boxes in the same trace - if you first learn the scalar form you might never realize the array form exists. Sure, we can do that.
So the new one would also accept y (for all points) if you just want to override the quartile calculations but leave everything else calculated? Or we won't allow that option?
We just wouldn't allow this
Moreover the idea of extending the 2D option to the original full-sample case (x is just one value per box, and y is a 2D array) seems to further blur the lines.
I can see nice uses of both of those variants (though they could both certainly be later additions), and to me this kind of polymorphism doesn't feel particularly out of character. So if the choices are (a) add a new trace type that's a little easier to document or (b) put all this in the existing trace and allow for future expansion along these lines, I'd choose (b).
Note the first one (full y alongside explicit stats) could also be useful with all stats provided, and would allow things like including your own jitter in the y direction - perhaps y has low precision so just x jitter obscures the density - without affecting the stats. Or perhaps you have a requirement to use a certain rounding for the stats but you still want the raw data displayed.
Activity
rsandler00 commentedon Oct 20, 2017
2nd cut violin calc/plot attributes + improve violin curve paths
pditommaso commentedon Dec 7, 2017
nicheath commentedon Feb 15, 2018
fabiomolinar commentedon Dec 28, 2018
sylfel commentedon Jan 11, 2019
ekalosak commentedon Feb 8, 2019
alexcjohnson commentedon Sep 26, 2019
Note: should still accept a data array alongside the precomputed stats, that you can use to pass in either all points or just outliers.
etpinard commentedon Oct 4, 2019
During
box/calc.jswe compute:A few thoughts:
A somewhat deeper thought:
box/calc.jsfor very large datasets?If the latter, then making users sort their samples before calling
Plotly.newPlot(with an addition flag turned on in their box traces) may be enough of a performance boost for most users.ellisonch commentedon Oct 4, 2019
@etpinard The goal, in my eyes, is i. I would like to use plotly.js to make graphs. I don't want to be forced into crunching data using plotly.js.
Recently I wanted to use plotly.js to graph a box and whiskers plot where my raw data was billions of rows of data that live in a database server side. This kind of data can never be sent to the front end. Any calculations on it will always happen in SQL. I want to be able to summarize it in sql, once a day, and use the resulting parameters in constructing visualizations. Plotly.js will never be able to loop over that data, no matter how fast you make it.
11 remaining items
alexcjohnson commentedon Nov 26, 2019
Separate attrs feels cleaner to me.
Allowing the notch to be asymmetric? We could go with
notchwidthorconfidenceintervalto keep it symmetric.The only problem I see with reusing
xoryis the partial definition case below. Which do you think would be less confusing for users to work with:upperfenceomitted or matchingq3for example - which can happen if there are no outliers within what would be the fenced range, even if there are outliers beyond it)outlierseven though 99% of the time usingxorywould work exactly the same. Then we have to answer what we'd do if you provide onlyoutliers, oroutlierswith partial stats...Seems reasonable at first glance - values above what you provide match the last one below it, and vice versa. But it's a bit confusing to think about what happens if you're missing some in the middle. You provide
q1andq3but notmedian- do you use the average of the two? Seems sketchy. What if you provide onlylowerfenceandupperfence? Seems even sketchier. So I might say you need to at least provideq1,q3, andmedianSounds good to me. To be clear, for the values that depend on
q1andq3we would insert the user-provided values, so the derived values would change as well, correct?etpinard commentedon Nov 26, 2019
Yeah good call. Thanks!! In fact, I'm starting think perhaps all these new precomputed attributes should be
valType: 'data_array'(or at leastarrayOk: true) so that we can draw multiple boxes with a single trace e.g.:Things get trickier for defining outliers though, but using a 2d array should be good enough e.g:
By allowing array
q1,median,q3and friends, it might be best to split theboxAPI into two signatures so that the trace length (i.e.trace._length) is always well defined.y(x, for horizontal boxes) to set the sample andxorx0(yory0) to position the boxes. Here_lengthcorresponds to the sum of the sample sizes from each box to be drawnq1/median/q3and optionallylowerfence/upperfence/outliers/mean/sd/notchspanto describe how to draw the boxes andx(ory, for horizontal boxes) to position them. Here_lengthcorresponds to the number of boxes to be drawn.alexcjohnson commentedon Nov 27, 2019
That makes a lot of sense to me - could even extend this to the original full-sample case:
xis just one value per box, andyis a 2D array, with an outer length that matchesxand the inner arrays - which need not all have the same length - are the samples for eachx.For precomputed statistics I wouldn’t want to prohibit single values (or force those to be wrapped in an array) when your goal is a single box; so I’d go with
arrayOkDoes this introduce any ambiguities? I don't see any... there would be nonsensical inputs (
q1is an array butmedianis a scalar, etc) but that's fine, wouldn't need to impose a meaning, we'd just need to not break.@nicolaskruchten any concerns about this from the standpoint of RCE?
etpinard commentedon Nov 27, 2019
At the moment, I think enforcing (data) arrays for
q1,median,q3and friends is a better idea - mostly become allowing something like:would mean having a visible trace with no set
valType: 'data_array'attributes. I guess that wouldn't be a first (indicatorcomes to mind), but that looks a little odd to me.nicolaskruchten commentedon Nov 28, 2019
Does "splitting into two signatures" mean a new trace-type
boxmanualor something? I'm not sure of the implication there but I'm fine with a new trace-type if that's what's needed.I have no a priori concerns from the POV of RCE, no, but thanks for asking!
etpinard commentedon Nov 28, 2019
No, more like if
q1/median/q3are set,xandymeans something different than if they're not set.That said, I wouldn't be opposed to making this a new trace type. Documenting the two APIs inside the same trace might be difficult.
nicolaskruchten commentedon Nov 28, 2019
new trace type works for me :)
nicolaskruchten commentedon Nov 28, 2019
Also, I would favour adding attrs for upper and lower outlier thresholds, and then accepting
ywith a mode that says "show all points" vs "show outlier points". That way if the user specifies only outliers, they can show that, but if they want to show all points they can do that also.alexcjohnson commentedon Nov 29, 2019
Given all the variants we've been discussing, it's not entirely clear to me where we would draw the line between the two trace types. Anyway we have plenty of precedent for variable dimensionality and varying degrees of manual control - I'd keep a single trace type.
nicolaskruchten commentedon Nov 29, 2019
the new one would accept the q1/q2 etc options and the old one wouldn't
what's an example of a trace where the expected dimensionality of attrA is the same as attrB unless attrC is set, in which case it's expected to be something else?
alexcjohnson commentedon Nov 30, 2019
So the new one would also accept
y(for all points) if you just want to override the quartile calculations but leave everything else calculated? Or we won't allow that option? Moreover the idea of extending the 2D option to the original full-sample case (xis just one value per box, andyis a 2D array) seems to further blur the lines.Not sure if we have exactly that situation... we certainly have attributes where the allowed values depend on other attributes. And we have one that I'd argue should have dimensionality interdependencies though it doesn't right now:
surfacecurrently accepts 1D or 2Dx/yarrays, and a 2Dzarray. But I'd argue it should also allow all 3 arrays to be 1D so that you could always take aheatmaporcontourand convert it tosurface. Currently you can pass all 1D arrays tomesh3d, but it behaves rather differently thansurface(and different fromheatmapandcontour) and anyway it breaks the very simple idea of "show me this heatmap as 3D" by just converting tosurface. And if we were to support that, then you could only tell whether 2Dx&yare valid by looking atz.Regardless, I don't think the criterion of coupled dimensionality requirements is enough to warrant calling this a new trace type. Most of the attributes are the same, the result is the same, it's just a few additional attributes and a few with a different dimensionality.
I see your point, and I guess it's nice from the standpoint of making it obvious you can include multiple boxes in the same trace - if you first learn the scalar form you might never realize the array form exists. Sure, we can do that.
nicolaskruchten commentedon Dec 2, 2019
We just wouldn't allow this
I also wouldn't do this.
alexcjohnson commentedon Dec 2, 2019
I can see nice uses of both of those variants (though they could both certainly be later additions), and to me this kind of polymorphism doesn't feel particularly out of character. So if the choices are (a) add a new trace type that's a little easier to document or (b) put all this in the existing trace and allow for future expansion along these lines, I'd choose (b).
Note the first one (full
yalongside explicit stats) could also be useful with all stats provided, and would allow things like including your own jitter in theydirection - perhapsyhas low precision so justxjitter obscures the density - without affecting the stats. Or perhaps you have a requirement to use a certain rounding for the stats but you still want the raw data displayed.