I'm very new to R and I hope this question is still interesting enough. I have the following dataframe:
> dput(df)
structure(list(Proportion = c(0.491475825983558, 0.624947117938639,
0.284285973983444, 0.459936074937072, 0.438167575182789, 0.5923527,
0.269347638359089, 0.444195335296524, 0.472343382529259, 0.6119936,
0.280545311041942, 0.45582336843016), Lower = c(0.373501802431026,
0.506815311121949, 0.196793171052086, 0.344394223066228, 0.342020291619279,
0.4962054, 0.197239652248339, 0.347543569904938, 0.362690139261045,
0.5158463, 0.198654362934906, 0.347479674558168), Upper = c(0.610508712286318,
0.729864865043791, 0.39179224043653, 0.580031198686217, 0.539194328764963,
0.6885, 0.356122647401151, 0.545263076314964, 0.5847316572176,
0.7081409, 0.380178492952045, 0.56851602179505), Area = c("SNP",
"SNP", "LGCA", "LGCA", "SNP", "SNP", "LGCA", "LGCA", "SNP", "SNP",
"LGCA", "LGCA"), Time = c("Day", "Night", "Day", "Night", "Day",
"Night", "Day", "Night", "Day", "Night", "Day", "Night"), Collar = c(41361,
41361, 41361, 41361, 41365, 41365, 41365, 41365, 41366, 41366,
41366, 41366)), row.names = c(NA, -12L), class = c("tbl_df",
"tbl", "data.frame"))
For which I have created the following plot:
Using the script below:
dfnew <- df %>%
mutate(ymin = Proportion - Lower,
ymax = Proportion + Upper)
p <- ggplot(data = dfnew, aes(x = Time, y = Proportion, color=Area, group=Area)) +
geom_point(size = 6, stroke = 0, shape = 16,
position = position_dodge(width = 0.1))+
geom_errorbar(aes(ymin = Lower, ymax = Upper), width=0.1, size=1,
position = position_dodge(width = 0.1)) +
theme(axis.text=element_text(size=15),
axis.title=element_text(size=20)) +
scale_color_manual(values = c("SNP" = "coral",
"LGCA" = "darkgoldenrod2")) +
geom_line(size=1,linetype="dotted")
p
I would like plot different symbols (e.g. ∆, O, ◊) accounting for the different collars in df. Also, I would like these to be moved slightly (position_dodge) so that not all points are on top of each other.
How can I access a symbol library and implement it into my script?
Any help would be very appreciated!
If you map the shape within an aes() call you can vary the shapes and if you want specific shapes you can use scale_shape_manual() for example just like with the colors. The dodging within one group can be achieved by either using geom_jitter() or replacing position_dodge() with position_jitterdodge().
Unfortunately this messes with the errorbars.
EDIT: There is a fix for the error bars in this answer by Marcelo. I also included a way to connect the same symbols with the dotted line. This is easiest done by adding another grouping column to your data.
dfnew <- df %>%
mutate(ymin = Proportion - Lower,
ymax = Proportion + Upper,
linegroup = paste(Area, Collar))
set.seed(2)
myjit <- ggproto("fixJitter", PositionDodge,
width = 0.6,
dodge.width = 0,
jit = NULL,
compute_panel = function (self, data, params, scales)
{
#Generate Jitter if not yet
if(is.null(self$jit) ) {
self$jit <-jitter(rep(0, nrow(data)), amount=self$dodge.width)
}
data <- ggproto_parent(PositionDodge, self)$compute_panel(data, params, scales)
data$x <- data$x + self$jit
#For proper error extensions
if("xmin" %in% colnames(data)) data$xmin <- data$xmin + self$jit
if("xmax" %in% colnames(data)) data$xmax <- data$xmax + self$jit
data
} )
ggplot(data = dfnew, aes(x = Time, y = Proportion, color=Area, group=linegroup)) +
geom_point(aes(shape = as.character(Collar)), size = 6, stroke = 0,
position = myjit)+
geom_line(aes(group = linegroup),linetype = "dotted",size=1, position = myjit) +
theme(axis.text=element_text(size=15),
axis.title=element_text(size=20)) +
geom_errorbar(aes(ymin = Lower, ymax = Upper), width=0.3, size=1,
position = myjit) +
scale_color_manual(values = c("SNP" = "coral",
"LGCA" = "darkgoldenrod2"))
Related
I struggling on how I can plot my real values, present in the real_values vector, next to the estimates values. My problem here is that the estimates values have a range (via the geom_errorbar), and for the real values I would like to plot just the point, in black, on the left side of each of the 10 estimates.
Here's an example of what I tried:
est_values = rnorm(20)
real_values = rnorm(10)
dat_ex = data.frame(
xvalues = 1:10,
values = est_values,
method = c(rep("A",10),rep("B",10)),
ic_0.025 = c(est_values - rnorm(20,1,0.1)),
ic_0.975 = c(est_values + rnorm(20,1,0.1)))
ggplot(dat_ex) +
#geom_point(aes(x = 1:10, y= real_values), size = 2) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2)
ggplot generally works best with data in data frames. So we put your real_values in a data frame and plot them in a separate layer, and "nudge" them to the left, as requested:
ggplot(dat_ex) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2) +
geom_point(
data = data.frame(values = real_values, xvalues = dat_ex$xvalues),
aes(x = xvalues, y = values),
position = position_nudge(x = -.4),
color = "black")
A nicer method might be to put them all in the same data frame. This can simplify the code and will automatically put them in the legend.
library(dplyr)
dat_ex = data.frame(
xvalues = 1:10,
values = real_values,
method = "real"
) %>%
bind_rows(dat_ex) %>%
mutate(method = factor(method, levels = c("real", "A", "B")))
ggplot(dat_ex, aes(x = xvalues, y = values, color = method)) +
geom_point(position=position_dodge(.9), size = 3) +
geom_errorbar(aes(ymin = ic_0.025, ymax = ic_0.975, group = method),
size = 1.3, position=position_dodge(.9), width = .2) +
scale_color_manual(values = c("real" = "black", "A" = "orange", "B" = "blue"))
I would add real_values to your data as another level of method, so they will be dodged along with "A" and "B" (and included in the legend):
library(ggplot2)
dat_ex <- rbind(
dat_ex,
data.frame(
xvalues = 1:10,
values = real_values,
method = "Real",
ic_0.025 = NA_real_,
ic_0.975 = NA_real_
)
)
# arrange so "Real" is on the left
dat_ex$method <- factor(dat_ex$method, levels = c("Real", "A", "B"))
ggplot(dat_ex) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2) +
scale_colour_manual(values = c("black", "forestgreen", "royalblue"))
I have a problem with errorbars in bar chart in ggplot. I have an interaction between categorical (condition) and continuous (moderator) variable. I want to show error bars, but they are the same color as bars, which makes them impossible to interpret.
I tried adding color = "black" etc. for error bars, but it won't change anything.
Here is a code:
moderator = runif(n = 100, min = 1, max = 7)
condition <- rep(letters[1:2], length.out = 100)
y = runif(n = 100, min = 1, max = 100)
df <- data.frame(moderator, condition, y)
lm21 <- lm(y~ condition* moderator, data = df)
summary(lm21)
library(ggeffects)
library(ggplot2)
library(magrittr)
pd <- position_dodge()
ggeffect(lm21, terms = c("condition", "moderator")) %>%
plot(show.title = FALSE) +
stat_summary(fun.y = mean, geom = "bar", position = pd, width = 0.25) +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar",
position = pd, size = 8.5, alpha=13.2) +
scale_y_continuous("Voting", limits = c(0, 100)) +
scale_color_discrete(name = "Control", labels = c("Low", "Medium", "High")) +
scale_x_continuous(name = "Condition",
breaks = 0:1,
labels = c("Low","High"))
The graph looks like this:
How can I change the color of error bars so that they are fully visible?
Thank you in advance!
I tried to convert the ggeffect value to a data.frame and ended like this, hope it's what you wanted.
The width control is made by hand sorry, I played with it to put it in the middle. Maybe someone better than me knows how to do it.
ggplot(as.data.frame(ggeffect(lm21, terms = c("condition", "moderator"))), aes(x = factor(x))) +
geom_col(aes(y = predicted, fill = factor(group)), position = position_dodge2(width = .5, preserve = "single", padding = 0)) +
geom_errorbar(aes(ymin = conf.low, ymax = conf.high, group = factor(group)), position = position_dodge(width = .9), width = .15) +
geom_point(aes(y = predicted, group = factor(group)), position = position_dodge2(width = .9)) +
scale_fill_discrete(name = "Control", labels = c("Low", "Medium", "High")) +
scale_y_continuous("Voting", limits = c(0, 100)) +
scale_x_discrete(name = "Condition", labels = c("Low","High")) +
theme_light()
Ok it's not the easiest way but that's what I'd done:
p = ggeffect(lm21, terms = c("condition", "moderator")) %>%
plot(show.title = FALSE) +
stat_summary(fun.y = mean, geom = "bar", position = pd, width = 0.25) +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar",
position = pd, size = 8.5, alpha=13.2) +
scale_y_continuous("Voting", limits = c(0, 100)) +
scale_color_discrete(name = "Control", labels = c("Low", "Medium", "High")) +
scale_x_continuous(name = "Condition",
breaks = 0:1,
labels = c("Low","High"))+
scale_colour_manual(values = rep('black',3))+
theme(legend.position = 'none')
The output is:
The only thing is that the legend is missing because scale_colour_manual changes it. But you can use this post to extract the legend How to plot just the legends in ggplot2? and the combine it to your plot.
I hope this is what you wanted
Here is another solution based on grobs manipulation.
p <- ggeffect(lm21, terms = c("condition", "moderator")) %>%
plot(show.title = FALSE) +
stat_summary(fun.y = mean, geom = "bar", position = pd, width = 0.25) +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar",
position = pd, size = 8.5, alpha=13.2) +
scale_y_continuous("Voting", limits = c(0, 100)) +
scale_color_discrete(name = "Control", labels = c("Low", "Medium", "High")) +
scale_x_continuous(name = "Condition",
breaks = 0:1,
labels = c("Low","High"))
# Change the order of ggplot layers (error bars are printed after mean bars)
p$layers <- p$layers[c(3,1,2,4)]
# Set colors of polyline grob (error bars)
q <- ggplotGrob(p)
q$grobs[[6]]$children[[5]]$gp$col <- rep("black",6)
grid::grid.draw(q)
This is what is the output.I have a data set which contains unit, weight of each unit and compliance score for each unit in year 2016.
I was not able to add the table but here is the screenshot for the data in csv
I have named the columns in the data as unit, weight and year(which is compliance score) .
I want to create a sunburst chart where the first ring will be the unit divided based on weight and the second ring will be the same but will have labels compliance score.
The colour for each ring will be different.
I was able to do some code with the help from an online blog and the output I have gotten is similar to what I want but I am facing difficulty in positioning of the labels and also the colour coding for each ring
#using ggplot
library(ggplot2) # Visualisation
library(dplyr) # data wrangling
library(scales) # formatting
#read file
weight.eg = read.csv("Dummy Data.csv", header = FALSE, sep =
";",encoding = "UTF-8")
#change column names
colnames(weight.eg) <- c ("unit","weight","year")
#as weight column is factor change into integer
weight.eg$weight = as.numeric(levels(weight.eg$weight))
[as.integer(weight.eg$weight)]
weight.eg$year = as.numeric(levels(weight.eg$year))
[as.integer(weight.eg$year)]
#Nas are introduced, remove
weight.eg <- na.omit(weight.eg)
#Sum of the total weight
sum_total_weight = sum(weight.eg$weight)
#First layer
firstLevel = weight.eg %>% summarize(total_weight=sum(weight))
sunburst_0 = ggplot(firstLevel) # Just a foundation
#this will generate a bar chart
sunburst_1 =
sunburst_0 +
geom_bar(data=firstLevel, aes(x=1, y=total_weight),
fill='darkgrey', stat='identity') +
geom_text(aes(x=1, y=sum_total_weight/2, label=paste("Total
Weight", comma(total_weight))), color='black')
#View
sunburst_1
#this argument is used to rotate the plot around the y-axis which
the total weight
sunburst_1 + coord_polar(theta = "y")
sunburst_2=
sunburst_1 +
geom_bar(data=weight.eg,
aes(x=2, y=weight.eg$weight, fill=weight.eg$weight),
color='white', position='stack', stat='identity', size=0.6)
+
geom_text(data=weight.eg, aes(label=paste(weight.eg$unit,
weight.eg$weight), x=2, y=weight.eg$weight), position='stack')
sunburst_2 + coord_polar(theta = "y")
sunburst_3 =
sunburst_2 +
geom_bar(data=weight.eg,
aes(x=3, y=weight.eg$weight,fill=weight.eg$weight),
color='white', position='stack', stat='identity',
size=0.6)+
geom_text(data = weight.eg,
aes(label=paste(weight.eg$year),x=3,y=weight.eg$weight),position =
'stack')
sunburst_3 + coord_polar(theta = "y")
sunburst_3 + scale_y_continuous(labels=comma) +
scale_fill_continuous(low='white', high='darkred') +
coord_polar('y') + theme_minimal()
Output for dput(weight.eg)
structure(list(unit = structure(2:7, .Label = c("", "A", "B",
"C", "D", "E", "F", "Unit"), class = "factor"), weight = c(30,
25, 10, 17, 5, 13), year = c(70, 80, 50, 30, 60, 40)), .Names =
c("unit",
"weight", "year"), row.names = 2:7, class = "data.frame", na.action
= structure(c(1L,
8L), .Names = c("1", "8"), class = "omit"))
output for dput(firstLevel)
structure(list(total_weight = 100), .Names = "total_weight", row.names
= c(NA,
-1L), na.action = structure(c(1L, 8L), .Names = c("1", "8"), class =
"omit"), class = "data.frame")
So I think I might have some sort of solution for you. I wasn't sure what you wanted to color-code on the outer ring; from your code it seems you wanted it to be the weight again, but it was not obvious to me. For different colour scales per ring, you could use the ggnewscale package:
library(ggnewscale)
For the centering of the labels you could write a function:
cs_fun <- function(x){(cumsum(x) + c(0, cumsum(head(x , -1))))/ 2}
Now the plotting code could look something like this:
ggplot(weight.eg) +
# Note: geom_col is equivalent to geom_bar(stat = "identity")
geom_col(data = firstLevel,
aes(x = 1, y = total_weight)) +
geom_text(data = firstLevel,
aes(x = 1, y = total_weight / 2,
label = paste("Total Weight:", total_weight)),
colour = "black") +
geom_col(aes(x = 2,
y = weight, fill = weight),
colour = "white", size = 0.6) +
scale_fill_gradient(name = "Weight",
low = "white", high = "darkred") +
# Open up new fill scale for next ring
new_scale_fill() +
geom_text(aes(x = 2, y = cs_fun(weight),
label = paste(unit, weight))) +
geom_col(aes(x = 3, y = weight, fill = weight),
size = 0.6, colour = "white") +
scale_fill_gradient(name = "Another Weight?",
low = "forestgreen", high = "white") +
geom_text(aes(label = paste0(year), x = 3,
y = cs_fun(weight))) +
coord_polar(theta = "y")
Which looks like this:
I'm trying to add a bivariate legend to my ggplot2 chart but I don't know whether (a) this is possible through some guides options and (b) how to achieve it.
The only way I've managed to produce something close to the desired outcome was by specifically creating a new chart which resembles a legend (named p.legend below) and inserting it, via the cowplot package, somewhere in the original chart (named p.chart below). But surely there must be a better way than this, given that this approach requires creating the legend in the first place and fiddling with its size/location to fit it in the original chart.
Here's code for a dummy example of my approach:
library(tidyverse)
# Create Dummy Data #
set.seed(876)
n <- 2
df <- expand.grid(Area = LETTERS[1:n],
Period = c("Summer", "Winter"),
stringsAsFactors = FALSE) %>%
mutate(Objective = runif(2 * n, min = 0, max = 2),
Performance = runif(2 * n) * Objective) %>%
gather(Type, Value, Objective:Performance)
# Original chart without legend #
p.chart <- df %>%
ggplot(., aes(x = Area)) +
geom_col(data = . %>% filter(Type == "Objective"),
aes(y = Value, fill = Period),
position = "dodge", width = 0.7, alpha = 0.6) +
geom_col(data = . %>% filter(Type == "Performance"),
aes(y = Value, fill = Period),
position = "dodge", width = 0.7) +
scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) +
theme_minimal() +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank())
# Create a chart resembling a legend #
p.legend <- expand.grid(Period = c("Summer", "Winter"),
Type = c("Objective", "Performance"),
stringsAsFactors = FALSE) %>%
ggplot(., aes(x = Period, y = factor(Type, levels = c("Performance", "Objective")),
fill = Period, alpha = Type)) +
geom_tile() +
scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) +
scale_alpha_manual(values = c("Objective" = 0.7, "Performance" = 1), guide = FALSE) +
ggtitle("Legend") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5),
rect = element_rect(fill = "transparent"),
axis.title = element_blank(),
panel.grid.major = element_blank())
# Add legend to original chart #
p.final <- cowplot::ggdraw() +
cowplot::draw_plot(plot = p.chart) +
cowplot::draw_plot(plot = p.legend, x = 0.5, y = 0.65, width = 0.4, height = 0.28, scale = 0.7)
# Save chart #
cowplot::ggsave("Bivariate Legend.png", p.final, width = 8, height = 6, dpi = 500)
... and the resulting chart:
Is there an easier way of doing this?
This might work at some point, but right now the colorbox seems to ignore all breaks, names and labels (#ClausWilke?). Probably because the multiscales package is in really early stages.
Posting since it might work when future readers are here.
library(multiscales)
df %>%
mutate(
period = as.numeric(factor(Period)),
type = as.numeric(factor(Type))
) %>%
ggplot(., aes(x = Area, y = Value, fill = zip(period, type), group = interaction(Area, Period))) +
geom_col(width = 0.7, position = 'dodge') +
bivariate_scale(
"fill",
pal_hue_sat(c(0.07, 0.6), c(0.4, 0.8)),
guide = guide_colorbox(
nbin = 2,
name = c("Period", "Type"), #ignored
breaks = list(1:2, 1:2), #ignored
labels = list(levels(.$Period), levels(.$Type)) #ignored
)
I read in this stack overflow question a clever way to simulate setting an aesthetic to panel background using geom_rect.
Conditionally change panel background with facet_grid?
Unfortunately, it doesn't work if you want to put other colors in the plot. The colors mix and the legend gets polluted. Instead, I would prefer that the color only applies to the background and doesn't get mixed. My other question is: is there an approach that would work in polar coordinates?
For a reproducible example, see the code below:
pies <- data_frame(pie = c(rep("hawaiian", 3), rep("pepperoni", 2)),
fraction = c(c(0.3, 0.2, 0.5), c(0.4, 0.6)),
ingredient = c("cheese", "pineapple", "ham",
"peperroni", "cheese"),
deepdish = c(rep(TRUE, 3), rep(FALSE, 2)))
p <- pies %>%
ggplot() +
geom_bar(aes(x = factor(1),
y = fraction,
fill = ingredient),
width = 0.6,
stat = "identity",
position = "fill") +
facet_wrap(~ pie) +
geom_rect(mapping = aes(fill = deepdish),
alpha = 0.1,
xmin = -Inf, xmax = Inf,
ymin=-Inf, ymax=Inf,
show.legend = FALSE)
p
p + coord_polar(theta = "y")
pies <- data_frame(pie = c(rep("hawaiian", 3), rep("pepperoni", 2)),
fraction = c(c(0.3, 0.2, 0.5), c(0.4, 0.6)),
ingredient = c("cheese", "pineapple", "ham",
"peperroni", "cheese"),
deepdish = c(rep(TRUE, 3), rep(FALSE, 2)))
library(ggplot2)
library(dplyr)
p <- pies %>%
ggplot() +
geom_bar(aes(x = factor(1), y = fraction, fill = ingredient),
width = 0.6, stat = "identity", position = "fill") +
facet_wrap(~ pie) + coord_polar(theta = "y")
g <- ggplotGrob(p)
# Set manually the background color for each panel
g$grobs[[2]]$children[[1]]$children[[1]]$gp$fill <- "#88334466"
g$grobs[[3]]$children[[1]]$children[[1]]$gp$fill <- "#44338866"
library(grid)
grid.draw(g)
library(egg)
library(grid)
pies <- data.frame(pie = c(rep("hawaiian", 3), rep("pepperoni", 2)),
fraction = c(c(0.3, 0.2, 0.5), c(0.4, 0.6)),
ingredient = c("cheese", "pineapple", "ham",
"peperroni", "cheese"))
dummy <- data.frame(x = 0, y = 0,
pie = c("hawaiian","pepperoni"),
deepdish = c("green","yellow"), stringsAsFactors = FALSE)
p <- ggplot(pies) +
facet_wrap(~ pie) +
geom_custom(data= dummy, mapping = aes(x = factor(0),
y = y,
data = deepdish),
grob_fun = function(x) rectGrob(gp=gpar(fill=x,col=NA)), inherit.aes = TRUE) +
geom_bar(aes(x = factor(1),
y = fraction,
fill = ingredient),
width = 0.6,
stat = "identity",
position = "fill")
p + coord_polar(theta = "y")