Gadfly.jl
Gadfly.jl copied to clipboard
Is there an easy way to pass aesthetics?
Hi! I have code like this:
using Gadfly
using RDatasets
iris = dataset("datasets", "iris")
plot(iris, x = :SepalLength, y = :PetalLength, Geom.point, Geom.smooth)
I'd like to color points with blue and line with red. I know that this code does what I want:
plot(iris,
layer(x = :SepalLength, y = :PetalLength, Geom.point, Theme(default_color = colorant"blue")),
layer(x = :SepalLength, y = :PetalLength, Geom.smooth, Theme(default_color = colorant"red"))
)
But here is a list of things I tried intuitively before coming to this. 1.
plot(iris, x = :SepalLength, y = :PetalLength, Geom.point, color = colorant"blue",
Geom.smooth, color = colorant"red")
plot(iris, x = :SepalLength, y = :PetalLength, Geom.point(aes(color = colorant"blue")),
Geom.smooth(aes(color = colorant"red")))
plot(iris, x = :SepalLength, y = :PetalLength,
layer(Geom.point, color = colorant"blue"),
layer(Geom.smooth, color = colorant"red")
)
I think that the second one feels like the most reasonable approach. It strongly resembles ggplot2 way of doing things. Does Gadfly have DataType for Aesthetics?
I agree that the Theme(default_color=colorant"blue")
isn't very intuitive, personally I think the 3rd option makes the most sense and feels the most "Gadfly"esque.
I'm interested in implementing this since this makes Gadfly more intuitive and easier to pick up.
I agree that layer(x = :SepalLength, y = :PetalLength, Geom.point, Theme(default_color = colorant"blue"))
is verbose for each different thing you want to plot. The awkwardness seems to come from the fact that you have to specify x
and y
again.
It looks like there is a pattern we can build around option 3. However, I don't know how to nicely get rid of Theme
. But we can reduce its unfriendliness by 1) creating an alias called Style
which seems to be a more well-known name for this sort of thing, while "Theme" usually means a ready-made set of styles 2) choosing a different color for each layer automatically. That said, here's the pattern I see coming from option 3:
plot(iris, x = :SepalWidth, y = :PetalLength, layer(Geom.point), layer(Geom.smooth))
would pick distinct colors for the layers automatically. One can imagine a short-hand to this as
plot(iris, x = :SepalWidth, y = :PetalLength, Geom.point, Geom.smooth)
Although the automatically changed colors will be a breaking change.
plot(iris, x = :SepalWidth, y = :PetalLength, layer(Geom.point, Style(color=colorant"black")), layer(Geom.smooth))
would set black as the color for layer 1, overriding the automatically chosen one.
plot(iris, x = :SepalWidth, y = :PetalLength, layer(Style(color=colorant"black")), layer(y=:SomeOtherField), Geom.line)
The second layer plots different data, both layers use Geom.line. In general, options set outside the layer(...)
definitions are taken as common for each layer, so x
remains the same in both, but you can change it if you want to in one or both of them.
One of the things lacking right now is a way to create a legend manually. It seems the (only?) way to create it is now using a DataFrames input and setting which column the color comes from. While this is useful, I've wanted to set my own legend many times. How about introducing Guide.legend
which sets the legend label for the layer?
plot(iris, x = :SepalWidth, y = :PetalLength, layer(Style(color=colorant"black"), Guide.legend("Petal length")), layer(y=:SomeOtherField, Guide.legend("Second line")), Geom.line)
I'd love for
y=rand(10,2)
plot(x = 1:10, y=y, Geom.line)
to become a short hand for
plot(x = 1:10, layer(y=y[:,1], Geom.legend("col 1")), layer(y=y[:,2], Geom.legend("col 2")), Geom.line)
and just draw the lines.
One way to get rid of the Theme
/Style
thing, which I don't know if is safe is by assuming all kwargs to layer
other than x
and y
are arguments to Theme
. It seems awkward to reserve all keyword arguments from being used in the future though, that's what I meant when I said Theme
is not easy to get rid of.
Here is the current fast way of doing the initial example, just for the record.
iris = dataset("datasets", "iris")
theme(x) = Theme(default_color=parse(Colors.Colorant,x))
colv = ["deepskyblue","red"]
p = plot(iris, x=:SepalLength, y=:PetalLength,
layer( Geom.point, theme(colv[1]) ),
layer( Geom.smooth, theme(colv[2]), order=2 ),
Guide.manual_color_key("Key", ["Points", "Smooth"], colv)
)
And here is the same in R (I don't know if this can be further condensed):
colv = c("deepskyblue","red")
p = ggplot(iris, aes(x=Sepal.Length, y=Petal.Length))+
geom_point(aes(color="Points"))+
geom_smooth(aes(color="Smooth"))+
scale_color_manual(name="Key",values=colv)
Do either of you know why manual_color_key
doesn't change the plotting colors too? Only the legend colors? That doesn't make sense to me. That's the primary difference between the ggplot2 and Gadfly code you have there @Mattriks
Is there a reason this hasn't moved forward?
A simple way to explicitly set the color like is possible in ggplot2 seems like a pretty important feature..
Currently, there are several ways in Gadfly to specify color:
using Colors
plot(iris, x=:SepalLength, y=:PetalLength, Geom.point, color=[RGB(0,0,1)])
plot(iris, x=:SepalLength, y=:PetalLength, Geom.point, color=[colorant"red"])
plot(iris, x=:SepalLength, y=:PetalLength, Geom.point, Theme(default_color="red"))
plot(iris, x=:SepalLength, y=:PetalLength, Geom.point, color=:Species)
In your post, are you referring to the manual_color_key
issue mentioned above, or some change to the color syntax? If the latter, what changes would you like to see?
That's still a bit verbose.. color='red'
compared to color=[colorant"red"]
but better than I had thought. However, it doesn't seem to work with Geom.line
:
julia> plot(DataFrame(x=1:100, y=rand(100)), x=:x, y=:y, Geom.line, color=[colorant"red"])
Error showing value of type Plot:
ERROR: The following aesthetics are required by Geom.line to be of equal length: x, y, color
Following #1463, the initial example can be done like this now:
plot(iris, x=:SepalLength, y=:PetalLength,
layer(Geom.smooth, color=["Smooth"]),
layer(Geom.point, color=["Points"]),
Scale.color_discrete_manual("red","deepskyblue"))