Adjust size of pie charts - r

I've made these pie charts:
df <- expand.grid(log.var = c(TRUE, FALSE), zone = 1:4)
df$proportion <- c(0.3, 0.7, 0.4, 0.6, 0.2, 0.8, 0.5, 0.5)
df$size = sample(1:20, 8)
library(ggplot2)
ggplot(df, aes(factor(1), proportion, fill = log.var)) +
geom_bar(stat = "identity") + coord_polar(theta = "y") + facet_grid(.~zone)
Is there any way of adjusting the size of each pie chart according to the sum of size in each zone?

#lukeA's suggestion is sensible but doesn't quite work:
library("ggplot2"); theme_set(theme_bw())
library("dplyr") ## for mutate()
set.seed(101)
df <- expand.grid(log.var = c(TRUE, FALSE), zone = 1:4)
df <- mutate(df,
proportion=c(0.3, 0.7, 0.4, 0.6, 0.2, 0.8, 0.5, 0.5),
size = sample(1:20, 8),
totsize=ave(size, zone,FUN=sum))
g0 <- ggplot(df, aes(x=factor(1), y=proportion, fill = log.var))
g0 + geom_bar(stat="identity",aes(width=totsize))+facet_grid(.~zone)+
coord_polar(theta = "y")
The problem here is that the bars are drawn (in rectangular coordinates) in the middle of the x-axis; we'd like them to be drawn with their x-coordinates running from 0 to the full width, but I'm not sure how to do that. The alternative would be to do a bunch of the cumulative proportion/stacking computations by hand (or at least outside ggplot2), then use geom_rect() ...
Here's how:
df <- df %>% group_by(zone) %>%
mutate(cp1=c(0,head(cumsum(proportion),-1)),
cp2=cumsum(proportion))
ggplot(df) + geom_rect(aes(xmin=0,xmax=totsize,ymin=cp1,ymax=cp2,
fill=log.var)) + facet_grid(.~zone)+
coord_polar(theta = "y")

Related

Individual axes labels in facet_wrap without scales="free"

My data looks like this:
df <- data.frame(Year = as.factor(c(rep(2015, 3), rep(2016, 3), rep(2017,3))),
Tax = as.factor(c(rep(c("A", "B", "C"), 3))),
Depth = as.factor(c(10, 30, 50, 20,30,50,10,30,40)),
values= c(0.5, 0.25, 0.25, 0.1, 0.4, 0.5, 0.2, 0.6, 0.2))
I want to plot it with gaps for missing data and individual axis labels.
library(ggplot2)
The scale argument of facet_wrap gives individual axes, but is not performing as desired, as missing data is not reflected.:
ggplot(df, aes(Depth, values, fill=Tax)) + geom_bar(stat="identity")+
facet_wrap(~Year, scale="free") +
coord_flip()
Without scales:
ggplot(df, aes(Depth, values, fill=Tax)) + geom_bar(stat="identity")+
facet_wrap(~Year) +
coord_flip()
The missing data is represented (which i want!), but it lacks axis labels (which i need).
is there anything i can do?
It looks like this can be done using the lemon package:
library(tidyverse)
library(lemon)
df <- data.frame(Year = as.factor(c(rep(2015, 3), rep(2016, 3), rep(2017,3))),
Tax = as.factor(c(rep(c("A", "B", "C"), 3))),
Depth = as.factor(c(10, 30, 50, 20,30,50,10,30,40)),
values= c(0.5, 0.25, 0.25, 0.1, 0.4, 0.5, 0.2, 0.6, 0.2))
ggplot(df, aes(Depth, values, fill=Tax)) + geom_bar(stat="identity")+
facet_rep_wrap(~Year,repeat.tick.labels = T) +
coord_flip()

How to simulate passing an aesthetic to panel background in ggplot2?

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

R stacked % frequency histogram with percentage of aggregated data based on

I believe my question is very similar to this post. Only difference is my aes fill is a factor with multiple levels. This what I am after
and this is how far I have gotten
set.seed(123)
n = 100
LoanStatus = sample(c('Chargedoff', 'Completed', 'Current', 'Defaulted', 'PastDue'), n, replace = T, prob = NULL)
ProsperScore = sample(1:11, n, replace = T, prob = NULL)
df = data.frame(ProsperScore,factor(LoanStatus))
df = data.frame(ProsperScore,LoanStatus)
probs = data.frame(prop.table(table(df),1))
Code for the stacked bar plot could look something like this:
library(ggplot2)
brks <- c(0, 0.25, 0.5, 0.75, 1)
ggplot(data=probs,aes(x=ProsperScore,y=Freq,fill=LoanStatus)) +
geom_bar(stat="identity") +
scale_y_continuous(breaks = brks, labels = scales::percent(brks)) +
scale_x_discrete(breaks = c(3,6,9))
More complete code, demonstrating how you would go about adding percentages to the plot, is here:
library(ggplot2)
library(plyr)
brks <- c(0, 0.25, 0.5, 0.75, 1)
probs <- probs %>% dplyr::group_by(ProsperScore) %>%
dplyr::mutate(pos=cumsum(Freq)-(Freq*0.5)) %>%
dplyr::mutate(pos=ifelse(Freq==0,NA,pos))
probs$LoanStatus <- factor(probs$LoanStatus, levels = rev(levels(probs$LoanStatus)))
ggplot(data=probs,aes(x=ProsperScore,y=Freq,fill=LoanStatus)) +
geom_bar(stat="identity") +
scale_y_continuous(breaks = brks, labels = scales::percent(brks)) +
scale_x_discrete(breaks = c(3,6,9)) +
geom_text(data=probs, aes(x = ProsperScore, y = pos,
label = paste0(round(100*Freq),"%")), size=2)
To only show the percentages in the first column of the graph, add %>%
dplyr::mutate(pos=ifelse(ProsperScore==1,pos,NA)) to the dplyr calls.

Create ggplots with the same scale in R

I'd like to do the following in R: I have 2 datasets (one consisting of 4, the other of 3 values) and I'd like to plot them with ggplot2 as bar charts (separately). However, I'd like to use the same scale for the both, i.e.: if the minimum value of dataset #1 is 0.2 and 0.4 of dataset #2, then I want to use 0.2 for both. Same applies for the maximum values (choosing the greater there).
So, basically, I want to make the 2 plots comparable. Of course, would be great to apply the common scale for coloring the bars, as well. Now, I'm using colorRampPalette and applying it in the scale_fill_gradient2 property.
A MWE provided below:
library("ggplot2")
val <- c(0.2, 0.35, 0.5, 0.65)
labels <- c('A', 'B', 'C', 'D')
LtoM <-colorRampPalette(c('green', 'yellow'))
df <- data.frame(val)
bar <- ggplot(data = df,
aes(x = factor(labels),
y = val,
fill = val)) +
geom_bar(stat = 'identity') +
scale_fill_gradient2(low=LtoM(100), mid='snow3',
high=LtoM(100), space='Lab') +
geom_text(aes(label = val), vjust = -1, fontface = "bold") +
labs(title = "Title", y = "Value", x = "Methods") +
theme(legend.position = "none")
print(bar)
Given the code above, and another dataset like c(0.4, 0.8, 1.2) with labels c('E', 'F', 'G'), how to adjust the code to create 2 different and separated plots (saved into PNGs finally, i.e.) but use the common (0.2 to 1.2) scale for both the heights of bars and their colors (so moving the images exactly next to each other indicates that the bars with the same height but belonging to different images appear in the same way and their colors are the same)?
We can use a mix of the breaks argument in scale_y_continuous to ensure that we have consistent axis ticks, then use coord_cartesian to ensure that we force both plots to have the same y-axis range.
df1 <- data.frame(val = c(0.2, 0.35, 0.5, 0.65), labels = c('A', 'B', 'C', 'D'))
df2 <- data.frame(val = c(0.4, 0.8, 1.2), labels = c('E', 'F', 'G'))
g_plot <- function(df) {
ggplot(data = df,
aes(x = factor(labels),
y = val,
fill = val)) +
geom_bar(stat = 'identity') +
scale_fill_gradient2(low=LtoM(100), mid='snow3',
high=LtoM(100), space='Lab') +
geom_text(aes(label = val), vjust = -1, fontface = "bold") +
scale_y_continuous(breaks = seq(0, 1.2, 0.2)) +
coord_cartesian(ylim = c(0, 1.2)) +
labs(title = "Title", y = "Value", x = "Methods") +
theme(legend.position = "none")
}
bar1 <- g_plot(df1);
bar2 <- g_plot(df2);
gridExtra::grid.arrange(bar1, bar2, ncol = 2);
You actually dont need to use coord_cartesian. You can just use the limits argument in scale_y_continuous, like this:
scale_y_continuous(limits = c(0,1.2), breaks = seq(0, 1.2, 0.2))

Make a rectangular legend, with rows and columns labeled, in grid

I've got a ggplot where I'm mapping factors to both fill and alpha, like this:
set.seed(47)
the_data <- data.frame(value = rpois(6, lambda=20),
cat1 = rep(c("A", "B"), each = 3),
cat2 = rep(c("X", "Y", "Z"), 2))
ggplot(the_data, aes(y = value, x = cat2, alpha = cat1, fill = cat2)) +
geom_bar(stat = "identity", position = "dodge") +
scale_alpha_discrete(range = c(0.5, 1)) +
theme_bw()
The people I'm producing it for don't find the legend for alpha very clear. I think a good alternative would be something like this (which I hacked together in base graphics):
I know I can't generate a legend like that with high-level ggplot commands, but can I do it in grid and put it on top of my plot?
Here is one possible starting point. I create two different plots which have the appropriate legends - a 'bright' and a 'pale'. Extract the legends from the plot objects. Then use grid viewports, one for the plot, and one for each legend, to put the pieces together.
library(grid)
library(gtable)
# create plot with legend with alpha = 1
g1 <- ggplot(the_data, aes(y = value, x = cat2, alpha = cat1, fill = cat2)) +
geom_bar(stat = "identity", position = "dodge") +
scale_alpha_discrete(range = c(0.5, 1)) +
theme_bw() +
guides(fill = guide_legend(title = "A",
title.hjust = 0.4),
alpha = FALSE) +
theme_bw() +
theme(legend.text = element_blank())
g1
# grab legend
legend_g1 <- gtable_filter(ggplot_gtable(ggplot_build(g1)), "guide-box")
# create plot with 'pale' legend
g2 <- ggplot(the_data, aes(y = value, x = cat2, alpha = cat1, fill = cat2)) +
geom_bar(stat = "identity", position = "dodge") +
scale_alpha_discrete(range = c(0.5, 1)) +
guides(fill = guide_legend(override.aes = list(alpha = 0.5),
title = "B",
title.hjust = 0.3),
alpha = FALSE) +
theme_bw()
g2
# grab legend
legend_g2 <- gtable_filter(ggplot_gtable(ggplot_build(g2)), "guide-box")
# arrange plot and legends
# legends to the right
# define plotting regions (viewports)
vp_plot <- viewport(x = 0.4, y = 0.5,
width = 0.8, height = 1)
vp_legend_g1 <- viewport(x = 0.85, y = 0.5,
width = 0.4, height = 0.4)
vp_legend_g2 <- viewport(x = 0.90, y = 0.5,
width = 0.4, height = 0.4)
# clear current device
grid.newpage()
# add objects to the viewports
# plot without legend
print(g1 + theme(legend.position = "none"), vp = vp_plot)
upViewport(0)
pushViewport(vp_legend_g1)
grid.draw(legend_g1)
upViewport(0)
pushViewport(vp_legend_g2)
grid.draw(legend_g2)
# legends on top
vp_plot <- viewport(x = 0.5, y = 0.4,
width = 1, height = 0.85)
vp_legend_g1 <- viewport(x = 0.5, y = 0.9,
width = 0.4, height = 0.4)
vp_legend_g2 <- viewport(x = 0.55, y = 0.9,
width = 0.4, height = 0.4)
grid.newpage()
print(g1 + theme(legend.position = "none"), vp = vp_plot)
upViewport(0)
pushViewport(vp_legend_g1)
grid.draw(legend_g1)
upViewport(0)
pushViewport(vp_legend_g2)
grid.draw(legend_g2)
#Henrik
This might be a little easier,
g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(p2)
leg1 <- gtable_filter(g1, "guide-box")
leg2 <- gtable_filter(g2, "guide-box")
leg <- gtable:::cbind_gtable(leg1[["grobs"]][[1]], leg2[["grobs"]][[1]], "first")
g1$grobs[g1$layout$name == "guide-box"][[1]] <- leg
g1$widths[max(subset(g1$layout, name == "guide-box")[["r"]])] <- list(leg1$width + leg2$width)
grid.newpage()
grid.draw(g1)

Resources