Gadfly.jl icon indicating copy to clipboard operation
Gadfly.jl copied to clipboard

Is there an easy way to pass aesthetics?

Open idmitrievsky opened this issue 8 years ago • 12 comments

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?

idmitrievsky avatar Mar 31 '16 22:03 idmitrievsky

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.

tlnagy avatar Apr 02 '16 01:04 tlnagy

I'm interested in implementing this since this makes Gadfly more intuitive and easier to pick up.

tlnagy avatar Sep 01 '16 05:09 tlnagy

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)

shashi avatar Sep 01 '16 09:09 shashi

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.

shashi avatar Sep 01 '16 09:09 shashi

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.

shashi avatar Sep 01 '16 09:09 shashi

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)
)

iris1

Mattriks avatar Sep 01 '16 22:09 Mattriks

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)

Mattriks avatar Sep 02 '16 00:09 Mattriks

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

tlnagy avatar Sep 02 '16 20:09 tlnagy

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..

robsmith11 avatar Apr 05 '19 20:04 robsmith11

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?

Mattriks avatar Apr 05 '19 22:04 Mattriks

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

robsmith11 avatar Apr 06 '19 02:04 robsmith11

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"))

iris_points_smooth

Mattriks avatar Jul 08 '20 12:07 Mattriks