Reputation: 197
I like to have different shapes depending on the values of some variables in R. Actually, a combination of variables. I like to give an exact shape number ( stored as s) to the combination.
I already tried to generate a new variable that sums the shape I want.
#gen some example code
c <- c('a', 'a', 'b', 'b')
d <- c('firstsecond', 'firstfirst', 'lowerupper', 'lowerlower')
e <- c(0.2, 0.3, 0.4, 0.5)
f <- c('w', 'v','w', 'v')
df <- cbind(c,d,e,f)
df<- as.data.frame(df)
df$e <- as.numeric(df$e)
orderd <- rev(c( 'firstfirst', 'firstsecond', 'lowerupper', 'lowerlower' ))
df<- within(df, d <- factor(d, levels=orderd))
My current solution approach is that I try to set shape in a variable s:
library(data.table)
df <- setDT(df)
df$c <- as.character(df$c)
df$f <- as.character(df$f)
df[c %chin% c('a') & f %chin% c('w') , s := 16 ]
df[c %chin% c('a') & f %chin% c('v') , s := 1 ]
df[c %chin% c('b') & f %chin% c('w') , s := 17 ]
df[c %chin% c('b') & f %chin% c('v'), s := 2 ]
However all shapes are the same :( they do not differ by group, as I would like them to do.
#plotting it:
library(ggplot2)
p<- ggplot(df, aes(x = d, y = e, color = f)) +
geom_pointrange(aes(min = e - 1.95 * sqrt(e), max = e + 1.95 * sqrt(e)), shape = s) +
theme_bw() +
facet_wrap(c ~ ., scales = "free", nrow = 5, strip.position = "left") +
coord_flip() +
scale_colour_viridis_d(begin = 0.75 , end = 0) +
geom_text(aes(label = f), colour = "black", size = 2.5, hjust=1.05, vjust=1.2)
p
Upvotes: 2
Views: 9670
Reputation: 93861
The code below uses the pre-existing c
and f
columns to generate the shape mapping, without creating a separate s
column.
library(ggplot2)
#gen some example code
c <- c('a', 'a', 'b', 'b')
d <- c('firstsecond', 'firstfirst', 'lowerupper', 'lowerlower')
e <- c(0.2, 0.3, 0.4, 0.5)
f <- c('w', 'v','w', 'v')
df <- data.frame(c,d,e,f)
orderd <- rev(c( 'firstfirst', 'firstsecond', 'lowerupper', 'lowerlower' ))
df$d <- factor(df$d, levels=orderd)
ggplot(df, aes(x = d, y = e, color = f, shape=interaction(c,f))) +
geom_linerange(aes(min = e - 1.95 * sqrt(e), max = e + 1.95 * sqrt(e)), size=1) +
geom_point(aes(size=interaction(c,f))) +
geom_text(aes(label = f), colour="white", size = 5, vjust=0.4) +
facet_wrap(c ~ ., scales = "free", nrow = 5, strip.position = "left") +
coord_flip() +
scale_colour_viridis_d(begin = 0.75 , end = 0) +
scale_shape_manual(values=15:18) +
scale_size_manual(values=c(5,6,5,7)) +
theme_bw() +
labs(shape="Shape Title", colour="Colour Title") +
guides(shape=guide_legend(override.aes=list(size=3)),
colour=guide_legend(override.aes=list(size=3, linetype=0)),
size=FALSE)
Note: In the code above, the size
aesthetic is solely to make each shape marker roughly the same physical size. At any given size
specification, the square and triangle markers look bigger than the circle and diamond markers. I used the size
aesthetic to specify custom sizes (in scale_size_manual
) for each shape. If you don't want to do this, or want to use different shapes, then you can remove the size aesthetic from the code.
If you decide you want the labels placed below, rather than within, the point markers, I suggest using the nudge
arguments, rather than messing with vjust
and hjust
, which can give unpredictable results. For example:
geom_text(aes(label = f), colour = "black", size = 3, nudge_x = -0.2) +
UPDATE: Regarding your comment, to have a separate color scale for the text labels, we can use the ggnewscale
package. Note in the code below that we now place the color aesthetics in the geoms to which they apply, rather than into the main call to ggplot
:
library(ggnewscale)
ggplot(df, aes(x = d, y = e, shape=interaction(c,f))) +
geom_linerange(aes(min = e - 1.95 * sqrt(e), max = e + 1.95 * sqrt(e), color = f),
size=1) +
geom_point(aes(size=interaction(c,f), color = f)) +
facet_wrap(c ~ ., scales = "free", nrow = 5, strip.position = "left") +
coord_flip() +
scale_colour_viridis_d(name="Colour Title", begin = 0.75 , end = 0) +
scale_shape_manual(values=c(0, 1, 17, 18)) +
scale_size_manual(values=c(5,6,5,7)) +
new_scale_colour() +
geom_text(aes(label = f, colour=f), size = 5, vjust=0.4, show.legend=FALSE) +
scale_colour_manual(values=c("black", "white")) +
theme_bw() +
labs(shape="Shape Title") +
guides(shape=guide_legend(override.aes=list(size=3)),
size=FALSE)
Another option for having more than one color scale is the relayer
package. Note that ggplot is not inherently designed to have more than one scale for a given aesthetic and both of these packages are experimental.
Upvotes: 3