vega / vega

A visualization grammar.
https://vega.github.io/vega
BSD 3-Clause "New" or "Revised" License
11.17k stars 1.5k forks source link

D3 bandwidth/step vs Vega bandSize #649

Closed kanitw closed 7 years ago

kanitw commented 7 years ago

In D3, bandWidth represents the true width of a band, ignoring padding in each step. D3's bandWidth() and step() are both readonly.
bandWidth() is only available for band scale (it always returns 0 for point scale).

In Vega, we have bandSize for setting scale size from bottom up for both point and band scale. With bandSize, the total width in the range of the scale = cardinality * bandSize.

However, I just notice yesterday that there is one fundamental difference between Vega bandSize and D3's bandWidth. Basically, value D3's bandWidth doesn't include the padding. Meanwhile, value of Vega's bandSize include the padding part too. In other words, Vega's bandSize is equivalent to step in D3.

Here is an example - I basically modify xscale in the bar example in Vega 3 to have bandSize: 16, padding: 0.5 instead of range: "width".

vega_editor

I wonder if bandSize should be named step instead for two reasons: 1) it makes sense for both band and point scale to have "step" too (while bandSize/Width sounds specific to band scale). 2) consistency with D3.

Fortunately, bandSize is not documented in Vega wiki, which means that its adoption should be fairly minimal for Vega users if we want to change this.
That said, this will be quite a breaking change at the Vega-Lite level though. (But we are breaking scale with point/band/ordinal anyway.)

Any thoughts?

jheer commented 7 years ago

Regarding sizing, I don't think your analysis is totally correct: Vega bandSize and D3 step are not identical. For Vega, range = bandSize * cardinality. For D3, range = step * (cardinality - paddingInner + 2 * paddingOuter). Of course, if we want to we could modify the Vega definition. (Though perhaps at the cost of requiring Vega-Lite to have more complex layout calculations? Let me know!)

In any case, I could be convinced to improve the name of the parameter. I think "step" may be too ambiguous. Perhaps "rangeStep" is more interpretable, as we use it to determine the overall range?

kanitw commented 7 years ago

Regarding sizing, I don't think your analysis is totally correct:

Oops. Sorry!

Of course, if we want to we could modify the Vega definition. (Though perhaps at the cost of requiring Vega-Lite to have more complex layout calculations? Let me know!)

I think it makes sense to just use the same formula as D3. It would be confusing if users try to modify inner and outer paddings and do not get the right result because we use different formula.

I can adjust the formula in Vega-Lite accordingly for both band and point scales. :)

To reduce problem with rounding, I guess we can force step to be integer in the JSON schema?

In any case, I could be convinced to improve the name of the parameter. I think "step" may be too ambiguous. Perhaps "rangeStep" is more interpretable, as we use it to determine the overall range?

While I like that step is consistent with D3, which might help a bit with learnability, I agree that it's pretty confusing. Since this name will be integral to how we size bar chart in Vega-Lite, I think rangeStep is a better choice. :+1:

Note: @domoritz say stepSize is another alternative to consider. (I like rangeStep more though.)

domoritz commented 7 years ago

To reduce problem with rounding, I guess we can force step to be integer in the JSON schema?

Even if step is an integer, padding is a float.

kanitw commented 7 years ago

@domoritz Re: rounding, yeah that's why I say "reduce" (not eliminate).

If I understand correctly, there might be a case where: we calculate range = step * (cardinality - paddingInner + 2 * paddingOuter) and pass to D3.

Then D3 calculates step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); but get step = original_step - epsilon and then flooring will mess it up to be original_step - 1

It should be rare though. That said, I don't understand enough how floating point works and if/how do they prevent this case these days.

kanitw commented 7 years ago

Looking closely, the way D3 does rounding only make (range)step smaller. So using normal formula will ensure that we have enough width/height for things, which should be sufficient. So maybe we don't have to impose constraint for rangeStep.

jheer commented 7 years ago

bandSize was renamed rangeStep in vega/vega-encode@94653e919ce112f9daab84fa0c4472c3bd831119 and released in v3.0.0-beta.6. We now incorporate padding information to match D3.

However, for nested ordinal plots in Vega-Lite and elsewhere, I recommend not using padding, as it leads to complicated layout calculations. Instead, I recommend keeping "padding": 0 so that the extent of a layout is a simple multiple of the rangeStep. To do this and still have "padded" plots, see the updated nested-plot example spec.