-
Notifications
You must be signed in to change notification settings - Fork 65
Add stacking and dodging to bar plot #580
Conversation
|
Can I somehow access attributes of previously plotted plots of the same |
|
Could you give me some feedback if (this is how) you want this? If so, I'll try to remove the dependency on DataFrames.jl again and add some tests. |
Sorry, I was super busy in my freetime to actually release JSServe & WGLMakie.
Hm, I guess we need to figure out how to do that 😅 |
|
Would be nice to make this independent of Dataframes. |
|
Alright, I probably won't have time for this in the next two weeks, but who knows. Note to self: I need to make this work for negative values and for a mixture of positive and negative values. |
ExamplesPlain barDodged and stackedDodgedStackedData(copied from test) begin
x1 = [1, 1, 1, 1]
y1 = [2, 3, -3, -2]
grp_dodge1 = [2, 2, 1, 1]
grp_stack1 = [1, 2, 1, 2]
x2 = [2, 2, 2, 2]
y2 = [2, 3, -3, -2]
grp_dodge2 = [1, 2, 1, 2]
grp_stack2 = [1, 1, 2, 2]
from, to = stack_from_to(grp_stack1, y1, (; x1, grp_dodge1))
from1 = [0.0, 2.0, 0.0, -3.0]
to1 = [2.0, 5.0, -3.0, -5.0]
@test from == from1
@test to == to1
from, to = stack_from_to(grp_stack2, y2, (; x2, grp_dodge2))
from2 = [0.0, 0.0, 0.0, 0.0]
to2 = [2.0, 3.0, -3.0, -2.0]
@test from == from2
@test to == to2
perm = [1, 4, 2, 7, 5, 3, 8, 6]
x = [x1; x2][perm]
y = [y1; y2][perm]
grp_dodge = [grp_dodge1; grp_dodge2][perm]
grp_stack = [grp_stack1; grp_stack2][perm]
from_test = [from1; from2][perm]
to_test = [to1; to2][perm]
from, to = stack_from_to(grp_stack, y, (; x, grp_dodge))
@test from == from_test
@test to == to_test
bar_df = (; x, grp_dodge, grp_stack, y)
endIf desired I can add these to the documentation. Also, where should the tests go? (Which file/folder?) |
|
For tests, you can make your own file under |
|
done, is a test dependency on CategoricalArrays ok? if you want #667, then I could test the functionality from there in this test here as well. |
|
Great!
Sounds good :) |
|
Done, this PR now relies on #667 |
|
here's the plot I added to the tests: using CategoricalArrays: categorical
x1 = ["a_right", "a_right", "a_right", "a_right"]
y1 = [2, 3, -3, -2]
grp_dodge1 = ["b", "b", "a", "a"]
grp_stack1 = [1, 2, 1, 2]
x2 = ["z_left", "z_left", "z_left", "z_left"]
y2 = [2, 3, -3, -2]
grp_dodge2 = ["a", "b", "a", "b"]
grp_stack2 = [1, 1, 2, 2]
perm = [1, 4, 2, 7, 5, 3, 8, 6]
x = [x1; x2][perm]
x = categorical(x, levels = ["z_left", "a_right"])
y = [y1; y2][perm]
grp_dodge = [grp_dodge1; grp_dodge2][perm]
grp_stack = [grp_stack1; grp_stack2][perm]
tbl = (; x, grp_dodge, grp_stack, y)
fig = Figure()
ax = Axis(fig[1,1])
barplot!(ax, tbl.x, tbl.y, dodge = tbl.grp_dodge, stack = tbl.grp_stack, color = tbl.grp_stack)
categorical_ticks(cat) = AbstractPlotting.categorical_range(cat),
AbstractPlotting.categorical_labels(cat)
ax.xticks = categorical_ticks(tbl.x)
fig |
|
Tests now pass locally |
qualify names from DataFrames remove @show edge cases 1 5
use `categoric_positions` fix fix
|
I think the deps need to clean up to pass tests (remove Dataframes, add StructArrays or so?). |
|
right, I've overlooked this. I had done this, but I've rebased this PR way too often now :-\ |
|
tests failed only on 1.4 due to NamedTuple syntax - this is now fixed. CI still fails because docs previews fail for non-JuliaPlots people |
|
friendly bump 🙂 |
|
@SimonDanisch I added examples to the docs |
|
The svgs are interacting with each other, thats why we use the master branch of documenter |
|
I see. I rebuilt with This is ready from my side. |
|
Sorry for only looking carefully at this now! Concerning "incremental build-ups", the key idea is that, in AlgebraOfGraphics, once the user add series, those are accumulated in an abstract object, which is then plotted, so there may not be a need for a "build-up mode" (I may need to think harder about this). What it would need though is a way to pass dodging and stacking information where the set of "dodgeable values" is not taken from I confess I would prefer the default approach to be something like The issue with supporting both approaches is what it would mean to do |
src/basic_recipes/barplot.jl
Outdated
| if dodge === automatic | ||
| n_dodge = 1 | ||
| else | ||
| n_dodge = length(unique(dodge)) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe i_dodge could be computed before this code block. Then this could simply be n_dodge = maximum(i_dodge)? That way if we change the logic to compute i_dodge, we only have to change it once.
src/basic_recipes/barplot.jl
Outdated
| if dodge === automatic | ||
| i_dodge = 1 | ||
| else | ||
| i_dodge = categoric_position.(dodge, Ref(categoric_labels(dodge))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the "incriminated line" for me. I think somehow it would be nice for the user to be able to pass i_dodge directly.
Right, I've worked around this in TabularMakie.jl.
That's a good point. For dodging, we can support I'd be happy to only allowing integers for dodging and stacking for now. Categorical variables can be supported once it has become clear how Makie deals with them in general. Before I make these changes I'd like to hear what Julius and Simon think, though. |
There may be smart ways to go about this using Observables, but let's worry about those in a future iteration.
Yes, I also think that's best, this should be done in a unified way (but yes, let's also see what Simon and Julius think). |
|
I'm ready to merge this once the docs build: #697, if there are no other concerns by @greimel, @jkrumbiegel or @piever :) |
|
Docs build, any final concerns? |
|
@piever suggested to disallow non-integer inputs for stacking and dodging and add support for categorical inputs when Makie-wide support for categorical variables has been improved. |
|
I pushed one more commit that restricts inputs of Please take a look, @piever. tbl = (x = [1, 1, 1, 2, 2, 2, 3, 3, 3],
height = 0.1:0.1:0.9,
grp = [1, 2, 3, 1, 2, 3, 1, 2, 3],
grp1 = [3, 2, 2, 1, 1, 2, 1, 1, 3],
grp2 = [1, 1, 3, 1, 2, 1, 1, 3, 1]
)
barplot(tbl.x, tbl.height,
dodge = tbl.grp1,
stack = tbl.grp2,
color = tbl.grp,
axis = (xticks = (1:3, ["left", "middle", "right"]),
title = "Dodged and stacked bars"),
figure = (resolution = (800, 600), )
) |
|
Thanks a lot for the change! Looks great to me, this should already be usable from my WIP AoG rewrite! |
|
Haven't had time to check this out, but looks good so far! One thing I'd like to see is a stacked / dodged barplot where
|
|
I don't touch the fillto argument except for stacked barplots. (In my opinion scaled axes don't make sense for stacked bar plots because then the bar lengths depend on the stack index.) Can you give me a MWE for a standard barplot with log-y-axis, then I can adapt it for dodging and show that it works. |
|
Thanks, this is really great :) |
| x_unique = unique(filter(isfinite, x)) | ||
|
|
||
| if length(x_unique) == 1 | ||
| width = 1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit late for this comment, but I've just realized that this approach (setting default width to take the full space, and reducing it later) could be wrong. Now the width passed by the user is no longer respected, so for example in barplot(1:3, rand(3), width=1), bars no longer touch (this is problematic for histograms, where there shouldn't be space between the bars).
The "proper way" IMO is to stick with the previous approach of defaulting to width = 0.8 * default_gap, and do not add extra "outside spacing" later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@greimel Is fixing this just a matter of changing defaults, where default width goes 0.8 * default gap and default x_gap goes to 0?
EDIT: even setting x_gap = 0 does not seem to completely remove the gap between bars
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to set both x_gap and dodge_gap to 0.
begin
f = Figure(resolution = (800, 600))
Axis(f[1, 1], xticks = 0.5:0.1:2.5)
xs = 1:2
ys = 0.5 .* sin.(xs)
barplot!(xs, ys, color = :red, strokecolor = :black, width = 0.8, strokewidth = 0.1, x_gap = 0, dodge_gap = 0)
f
endWe could set these two to automatic as well. dodge_gap would be zero if dodge === automatic. And x_gap would be zero if width !== automatic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation! I think the cleanest solution is to only use x_gap to determine the width if it is automatic. This allows us to simplify those annoying dodge formulas as well, because there is no longer x_gap to worry about, see #702














Example
Generate data
RFC
In order to be really useful for AlgebraOfGraphics, this should also support incremental build-up of plots. That is, one needs to have a struct like
and whenever you add to the plot you
yandfilltogiven the current plotI would somehow change the behaviour of how
barplot!works now, wouldn't it?Do you have any other ideas how to support incremental plotting of grouped bar plots?
Do you want to support this in AbstractPlotting?