Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add colorbar #84

Closed
wants to merge 5 commits into from
Closed

[WIP] Add colorbar #84

wants to merge 5 commits into from

Conversation

greimel
Copy link
Collaborator

@greimel greimel commented Aug 18, 2020

using RDatasets: dataset
using AlgebraOfGraphics, AbstractPlotting, CairoMakie
mpg = dataset("ggplot2", "mpg")
cols = style(:Displ, :Hwy)
grp = style(color = :Cyl => categorical)
mpg[!,:grp_cyl] = mpg.Cyl .> 6
mpg[!,:col] = 3 .* rand.() .+ 2 .* mpg.grp_cyl 

scn = data(mpg) * style(:Displ, :Hwy, color = :col, layout_x = :grp_cyl => categorical) * spec(Scatter) |> draw

colormap

The function in colorbar.jl should probably go to AbstractPlotting.MakieLayout when it's done. (Should probably work with observables)

EDIT: Thinking more about it, a function linkcolorranges! (similar to linkxaxes!) would probably be the better way to do it. What do you think, @jkrumbiegel?

@greimel
Copy link
Collaborator Author

greimel commented Aug 18, 2020

fixes #82

@greimel greimel marked this pull request as draft August 18, 2020 14:35
@greimel
Copy link
Collaborator Author

greimel commented Aug 18, 2020

now with label

colorbar

@jkrumbiegel
Copy link
Member

jkrumbiegel commented Aug 18, 2020

a function linkcolorranges! (similar to linkxaxes!) but probably be the better way to do it.

I would lift one colorrange from each plot, lift one max range out of those, then pass that to the plots and the colorbar.
It could get messy if you link them after the fact, you can look at the code for linked axes. It's a bit more complex than just lifting all axis limits and send them to all the others (that would give you endless cycles)

@greimel
Copy link
Collaborator Author

greimel commented Aug 18, 2020

I thought that this might be a problem, because one "overwrites" the colorranges in the plots. so that they don't update anymore when the inputs change?

would one have to use the z vector directly, use all of the z vectors to get the master-colorrange and feed that into the plot and colorbar?

@jkrumbiegel
Copy link
Member

It depends a bit how much "updatability" you need / want in AlgebraOfGraphics. Updating data always complicates things, especially if you get into layouts, legends, etc. which have to change given different data.

one "overwrites" the colorranges in the plots. so that they don't update anymore when the inputs change?

That just depends on which way you choose to implement it. For me, it's usually easier to construct the Node up front, and then pass it to the object at creation. So in your case, a master colorrange node.

@greimel
Copy link
Collaborator Author

greimel commented Aug 18, 2020

if that means that you are opposed to a function linkcolorranges! in AbstractPlotting, I can hack something together here.

If you think that such a function would make sense, I'll think a bit more about it (with the goal of eventually contributing it there)

@jkrumbiegel
Copy link
Member

I think that would need to be implemented as a low-level feature in AbstractPlotting, so that every plot object in its color sampler can store a list of linked plot objects that also have color sampler. Otherwise you get really messy observables quite soon if you all link them up

@piever
Copy link
Collaborator

piever commented Aug 18, 2020

Is there a specific consensus whether the colorrange should be the same across plots? As far as I understand from ggplot2, it must be the same across layers, but can be allowed to vary (not by default though) across facets.

Note that there is some scale machinery to compute extrema globally here, which needs a revamp for better continuous scale support.

@jkrumbiegel
Copy link
Member

I think usually, if there's just one variable that is translated into a color, you get only one colorbar and therefore one color range, even if another variable splits across facets. But I guess there could be a need for facets where each facet has its own color bar, so I guess a switch might be helpful to distribute one colorbar per facet. But the single colorbar thing is definitely the most common scenario I'd say

@greimel
Copy link
Collaborator Author

greimel commented Aug 31, 2020

@piever, you are getting at a bigger issue here. Should scales be linked by default?

So far, axes are linked, whenever it allows to save space (drop x-labels for stacked plots and and y-labels for dodged plots). So, linking colorbars is consistent with this design.

Similar to #85, we need to find a way to pass kwargs like link_x, link_y, link_color, etc

I think, all should be linked by default, with the option to unlink.

@jkrumbiegel
Copy link
Member

jkrumbiegel commented Aug 31, 2020

wouldn't it be a good solution to go back to types for AoG specific options like these?

Instead of

style(:width, :length, layout_x = :Species, link_x = true, label_x = :shared, ...)

we could do

style(:width, :length, LayoutX(:species, link = true, label = :shared))

@greimel
Copy link
Collaborator Author

greimel commented Aug 31, 2020

yes, that would be nice.

Where would you put link_color? Is it a problem that it doesn't have meaning in Color() unless LayoutX() or LayoutY() are used?

EDIT: What if we want separate colorbars for each row? So we need link_x and link_y keyword arguments in Color()?

@greimel greimel mentioned this pull request Aug 31, 2020
2 tasks
@jkrumbiegel
Copy link
Member

jkrumbiegel commented Aug 31, 2020

Where would you put link_color?

Hm good point, maybe it's better not to separate these three things then. What about one of these:

style(:width, :length, Facets(:species, link_x = true, link_color = true)

style(:width, :length, Facets(:species, link = [:x, :color, :markersize, ...])

Actually all things with ranges appearing in a legend can be linked I guess, so I put markersize in there, too. Possibly others as well, so how could that be supported for arbitrary attributes?

I wonder if it's better to have only Facets with multiple dispatch for x, y or x & y (how to separate x or y with one argument though?) or FacetsX, FacetsY, FacetsXY and FacetWrap

@greimel
Copy link
Collaborator Author

greimel commented Sep 25, 2020

Improved the alignment in the presence of colorbar and other legends

using RDatasets: dataset
using AlgebraOfGraphics, AbstractPlotting, GLMakie
using AlgebraOfGraphics.MakieLayout

mpg = dataset("ggplot2", "mpg")
cols = style(:Displ, :Hwy)
grp = style(color = :Cyl => categorical)
mpg[!,:Group] = mpg.Cyl .> 6
mpg[!,:Color] = 3 .* rand.() .+ 2 .* mpg.grp_cyl 

aog2 = data(mpg) * style(:Displ, :Hwy, layout_x = :Drv, marker = :Group => categorical, color = :Color) * spec(Scatter)

scene, layout = layoutscene()
 AlgebraOfGraphics.layoutplot!(scene, layout, aog2)

Screenshot 2020-09-25 at 16 24 05

@greimel
Copy link
Collaborator Author

greimel commented Sep 25, 2020

and here is some code to place the legend at the top

Screenshot 2020-09-25 at 16 44 43

scene, layout = layoutscene()
 AlgebraOfGraphics.layoutplot!(scene, layout, aog2)

 position = :top
 leg_lay = contents(layout[1,2])[1]
 leg = contents(leg_lay[1,1])[1]
 bar = contents(leg_lay[2,1])[1]
 bar_title = contents(leg_lay[2,1, Top()])[1]

 leg.orientation[]   = position == :right ? :vertical : :horizontal
 leg.titleposition[] = position == :right ? :top : :left 
 leg.tellheight[] = true
 leg.tellwidth[] = true
 
 bar.vertical[] = position == :right
 if position == :right
   bar.height[] = 120
   bar.width[] = 20 #Relative(0.5)
   bar.ticksize[] = 10
   bar.ticklabelpad[] = 5
   bar.ticklabelsize[] = 20
 else
   bar.width[] = 120
   bar.height[] = 20
   bar.ticklabelalign = (:center, :bottom)
   bar.ticksize[] = 5
   bar.ticklabelpad[] = 1.5
   bar.ticklabelsize[] = 15
 end

 if position == :right
    leg_lay.tellheight[] = false
    leg_lay.tellwidth[] = true
    leg_lay[2,1] = bar
    leg_lay[2,1,Top()] = bar_title
    layout[1,2] = leg_lay
 elseif position == :top
   leg_lay.tellwidth[] = false
   leg_lay.tellheight[] = true
    leg_lay[1,2] = bar
    leg_lay[1,2,Left()] = bar_title
    layout[0,1] = leg_lay
 end
 trim!(leg_lay)
 trim!(layout)

 scene

@greimel
Copy link
Collaborator Author

greimel commented Sep 25, 2020

In case of a vertical colorbar, it should be moved to the left, so that it is approximately aligned with the markers. This should be achievable by wrapping it in a GridLayout.

@greimel
Copy link
Collaborator Author

greimel commented May 13, 2021

implemented as part of https://github.com/piever/SplitApplyPlot.jl/ refactor.

@greimel greimel closed this May 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants