问题
I used to make interaction plot with ggplot2
and code is given below. Now I want to reproduce the same plot with ggvis
as shown below which not the same as ggplto2
output. How can I get the same plot with ggvis
?
library(ggplot2)
p <- qplot(as.factor(dose), len, data=ToothGrowth, geom = "boxplot", color = supp) + theme_bw()
p <- p + labs(x="Dose", y="Response")
p <- p + stat_summary(fun.y = mean, geom = "point", color = "blue", aes(group=supp))
p <- p + stat_summary(fun.y = mean, geom = "line", aes(group = supp))
p <- p + theme(axis.title.x = element_text(size = 12, hjust = 0.54, vjust = 0))
p <- p + theme(axis.title.y = element_text(size = 12, angle = 90, vjust = 0.25))
print(p)
library(ggvis)
ggvis(data=ToothGrowth, x= ~as.factor(dose), y= ~len, fill= ~supp, stroke = ~supp) %>%
layer_points(shape=~supp) %>%
layer_lines(fillOpacity=0)
回答1:
The basic problem, when trying to implement this in ggvis
, is that there is no position = dodge
option like in ggplot2
, and therefore the boxplots for different supp
values cannot be plotted at the same x
coordinate. So indexing the x
axis by as.factor(dose)
doesn't appear to be an option. However, what we can do is use an integer index of length equal to the number of unique dose values, and then manually offset the x
position of the data to the left or right, according to the supp
value:
library(ggvis)
library(dplyr)
d <- ToothGrowth
d$xpos <- as.integer(factor(d$dose)) + ifelse(d$supp == "OJ", -.2, .2)
So we can now use x = ~xpos
to plot the boxplots at the right positions. The next step is to define the data holding the means used to plot the points that are connected by lines.
means <- d %>% group_by(dose, supp) %>% summarise(len = mean(len))
means$xpos <- as.integer(factor(means$dose))
means <- group_by(means, supp) # The grouping is needed for layer_paths()
The graph can now be obtained as
ggvis(d, x = ~xpos, y = ~len, stroke = ~supp) %>%
layer_boxplots() %>%
layer_points(data = means, fill := "blue") %>%
layer_paths(data = means)
Now we have the problem that the x
position of the plots will be at 1, 2, 3 rather than the actual dose values. This is not very straightforward to overcome because add_axis()
gives no way to re-label the axis ticks (also, we couldn't have used the actual dose values instead of 1, 2, 3 in the first place because that would have placed the boxplots at dose values 0.5 and 1 closer to each other than the ones at dose values 1 and 2). This can be overcome by a not so elegant hack, which is to add an axis for each single dose value. The function add_axis()
gives a way to modify the axis properties (which includes the labels) but it will use the same label for the whole axis, since the properties apply to the whole axis. So by adding an axis for each dose value, we can manipulate the labels one by one. This looks like
ggvis(d, x = ~xpos, y = ~len, stroke = ~supp) %>%
layer_boxplots() %>%
layer_points(data = means, fill := "blue") %>%
layer_paths(data = means) %>%
add_axis("x", title = "Dose",
values = c(1, 1), # For some reason values of length 1 don't work...
properties = axis_props(labels = list(text = "0.5"))) %>%
add_axis("x", title = "",
values = c(2, 2),
properties = axis_props(labels = list(text = "1"))) %>%
add_axis("x", title = "",
values = c(3, 3),
properties = axis_props(labels = list(text = "2"))) %>%
add_axis("y", title = "Response")
Alternatively, you can use a loop for these so you don't have to type the same thing over and over
labs <- data.frame(dose = unique(d$dose))
labs$xpos <- as.integer(factor(labs$dose))
v <- ggvis(d, x = ~xpos, y = ~len, stroke = ~supp) %>%
layer_boxplots() %>%
layer_points(data = means, fill := "blue") %>%
layer_paths(data = means) %>%
add_axis("x", title = "Dose", ticks = 0) %>%
add_axis("y", title = "Response")
for (i in 1:nrow(labs)) {
v <- add_axis(v, "x", title = "", values = rep(labs[i, "xpos"], 2),
properties = axis_props(labels = list(text = as.character(labs[i, "dose"]))))
}
The final outcome looks like this
来源:https://stackoverflow.com/questions/27150056/ggvis-interaction-plot