ggplot - stacked geom_bar - reorder each x by y [duplicate] - r

This question already has answers here:
Stacked barchart, independent fill order for each stack
(3 answers)
Closed 3 years ago.
Im trying to reorder a stacked geom_bar for each x without success. What I would like to achieve is a plot where the y-values are ordered from smallest to largest for each value of x. Something like this:
The problem however seems to be that ggplot threats y as discrete values instead of continuous and therefore I am not able to change breaks and labels of my y-axis. I have tried using scale_x_discrete without success.
library(tidyverse)
df <- data.frame(q= c(rep("2011", 3), rep("2012", 3)),
typ = rep(c("A", "B", "C"), 2),
val = c(7,2,1,2,3,4), stringsAsFactors = F) %>% as_tibble()
ggplot(df) + geom_col(mapping = aes(x = q, y = reorder(val, val), fill = typ))
ggplot(df) + geom_col(mapping = aes(x = q, y = reorder(val, val), fill = typ)) + scale_y_continuous()
Error: Discrete value supplied to continuous scale
The following code does not change my breaks at all.
ggplot(df) + geom_col(mapping = aes(x = q, y = reorder(val, val), fill = typ)) + scale_y_discrete(breaks = 1:10)

With help from #kath I managed to solve it
library(tidyverse)
df <- data.frame(q= c(rep("2011", 3), rep("2012", 3)),
typ = rep(c("A", "B", "C"), 2),
val = c(7,2,1,2,3,4), stringsAsFactors = F) %>% as_tibble()
bars <- map(unique(df$q)
, ~geom_bar(stat = "identity", position = "stack"
, data = df %>% filter(q == .x)))
df %>%
ggplot(aes(x = q, y = val, fill = reorder(typ,val))) +
bars +
guides(fill=guide_legend("ordering")) +
scale_y_continuous(breaks = 1:10, limits = c(0, 10))

Related

ggplot missing plot with x-axis factor

The following works fine:
my_df <- data.frame(x_val = 1:10, y_val = sample(1:20,10),
labels = sample(c("a", "b"), 10, replace = T))
ggplot(data = my_df, aes(x = x_val, y = y_val)) + geom_line()
but if I chance x_val to factor, I am getting blank plot and message:
my_df <- data.frame(x_val = 1:10, y_val = sample(1:20,10),
labels = sample(c("a", "b"), 10, replace = T))
my_df$x_val <- as.factor(my_df$x_val)
ggplot(data = my_df, aes(x = x_val, y = y_val)) + geom_line()
message:
geom_path: Each group consists of only one observation. Do you
need to adjust the group aesthetic?
I can obviously drop factor conversion, but I need it in order to replace labels of x axis with scale_x_discrete(breaks = 1:10,labels= my_df$labels). Here is where I borrowed it link
Any thoughts?
Can you just leave x_val as numeric and use scale_x_continuous(breaks = 1:10,labels= my_df$labels) instead?

Move chart labels of variables in opposite directions

I couldn't find out how to do this anywhere so I thought I would post the solution now that I've figured it out.
I created a simple chart with labels based on a data set in long format (see below for dat). There are two lines and the labels overlap. I would like to move the labels for the upper chart up and for the lower chart down.
library(dplyr)
library(ggplot2)
library(tidyr)
# sample data
dat <- data.frame(
x = seq(1, 10, length.out = 10),
y1 = seq(1, 5, length.out = 10),
y2 = seq(1, 6, length.out = 10))
# convert to long format
dat <- dat %>%
gather(var, value, -x)
# plot it
ggplot(data = dat, aes(x = x, y = value, color = var)) +
geom_line() +
geom_label(aes(label = value))
To move the labels in opposite directions, one can create a step function in nudge_y to multiply the upper line's labels by +1 times a nudge factor and the multiply the lower line's labels by -1 times the nudge factor:
# move labels in opposite directions
ggplot(data = dat, aes(x = x, y = value, color = var)) +
geom_line() +
geom_label(aes(label = value),
nudge_y = ifelse(dat$var == "y2", 1, -1) * 1)
This produces the following chart with adjusted labels.

Mix color and fill aesthetics in ggplot

I wonder if there is the possibility to change the fill main colour according to a categorical variable
Here is a reproducible example
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = c(rep('a', times = 10),
rep('b', times = 10)),
val = rep(1:10, times = 2))
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(color = grp,
fill = val,
size = val))
Of course it is easy to change the circle colour/shape, according to the variable grp, but I'd like to have the a group in shades of red and the b group in shades of blue.
I also thought about using facets, but don't know if the fill gradient can be changed for the two panels.
Anyone knows if that can be done, without gridExtra?
Thanks!
I think there are two ways to do this. The first is using the alpha aesthetic for your val column. This is a quick and easy way to accomplish your goal but may not be exactly what you want:
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(alpha=val,
fill = grp,
size = val)) + theme_minimal()
The second way would be to do something similar to this post: Vary the color gradient on a scatter plot created with ggplot2. I edited the code slightly so its not a range from white to your color of interest but from a lighter color to a darker color. This requires a little bit of work and using the scale_fill_identity function which basically takes a variable that has the colors you want and maps them directly to each point (so it doesn't do any scaling).
This code is:
#Rescale val to [0,1]
df$scaled_val <- rescale(df$val)
low_cols <- c("firebrick1","deepskyblue")
high_cols <- c("darkred","deepskyblue4")
df$col <- ddply(df, .(grp), function(x)
data.frame(col=apply(colorRamp(c(low_cols[as.numeric(x$grp)[1]], high_cols[as.numeric(x$grp)[1]]))(x$scaled_val),
1,function(x)rgb(x[1],x[2],x[3], max=255)))
)$col
df
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(
fill = col,
size = val)) + theme_minimal() +scale_fill_identity()
Thanks to this other post I found a way to visualize the fill bar in the legend, even though that wasn't what I meant to do.
Here's the ouptup
And the code
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = factor(c(rep('a', times = 10),
rep('b', times = 10)),
levels = c('a', 'b')),
val = rep(1:10, times = 2)) %>%
group_by(grp) %>%
mutate(scaledVal = rescale(val)) %>%
ungroup %>%
mutate(scaledValOffSet = scaledVal + 100*(as.integer(grp) - 1))
scalerange <- range(df$scaledVal)
gradientends <- scalerange + rep(c(0,100,200), each=2)
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(fill = scaledValOffSet,
size = val)) +
scale_fill_gradientn(colours = c('white',
'darkred',
'white',
'deepskyblue4'),
values = rescale(gradientends))
Basically one should rescale fill values (e.g. between 0 and 1) and separate them using another order of magnitude, provided by the categorical variable grp.
This is not what I wanted though: the snippet can be improved, of course, to make the whole thing less manual, but still lacks the simple usual discrete fill legend.

control horizontal spacing between geom_bar in ggplot2

I have the following code:
library("ggplot2")
set.seed(12351234)
names <- factor(rep(paste("C", 1:10, sep = "_"), each = 10))
time <- rep(1:10, 10)
outcome <- rnorm(mean = 1e7, sd = 1e7, n = length(time))
outcome <-ifelse(outcome < 0, 0, outcome)
data.toy <- data.frame(names, time, outcome)
ggplot(data = data.toy, aes(y = outcome, x = time)) + geom_bar(stat = "identity", aes(fill = names)) + scale_x_continuous(breaks = unique(data.toy$time))
and it produces the following image: http://picpaste.com/data_toy-OR0jVHj5.png
I am wondering if there is a way to remove the horizontal "gray" space between the bars on the x-axis (the space that the arrows are pointing at). I suspect I am using this geom incorrectly as time is not categorical and there is a more appropriate geom for this.

insert labels in proportional bar chart with ggplot2 and geom_text

I am trying to insert labels into a proportional barchart: one label per segment, with as text the percentage of each segment. With the help of thothal I managed to do this:
var1 <- factor(as.character(c(1,1,2,3,1,4,3,2,3,2,1,4,2,3,2,1,4,3,1,2)))
var2 <- factor(as.character(c(1,4,2,3,4,2,1,2,3,4,2,1,1,3,2,1,2,4,3,2)))
data <- data.frame(var1, var2)
dat <- ddply(data, .(var1), function(.) {
res <- cumsum(prop.table(table(factor(.$var2))))
data.frame(lab = names(res), y = c(res))
})
ggplot(data, aes(x = var1)) + geom_bar(aes(fill = var2), position = 'fill') +
geom_text(aes(label = lab, x = var1, y = y), data = dat)
I would like to have for labels the percentage of each level, and not the level name.
Any help appreciated!
You are telling geom_text to use var2 as your y variable. That is in fact as.numeric(data$var2), which translates to a range of 1-4. However, your barplot uses the cumulative percentages.
Hence you have to calculate these positions before:
library(ggplot2)
library(plyr) # just for convenience
var1 <- factor(as.character(c(1,1,2,3,1,4,3,2,3,2,1,4,2,3,2,1,4,3,1,2)))
var2 <- factor(as.character(c(1,4,2,3,4,2,1,2,3,4,2,1,1,3,2,1,2,4,3,2)))
data <- data.frame(var1, var2)
dat <- ddply(data, .(var1), function(.) {
res <- cumsum(prop.table(table(factor(.$var2)))) # re-factor to use only used levels
res2 <- prop.table(table(factor(.$var2))) # re-factor to use only used levels
data.frame(lab = names(res), y = c(res), lab2 = c(res2))
})
ggplot(data, aes(x = var1)) + geom_bar(aes(fill = var2), position = 'fill') +
geom_text(aes(label = round(lab2, 2), x = var1, y = y), data = dat)
This places the labs at the end of each bar. If you want to have them slightly offset, you should play arround in the creation of dat.
Another way to get non-cumulative percentage plus centering the labels, for future reference:
dat <- ddply(data, .(var1), function(.) {
good <- prop.table(table(factor(.$var2)))
res <- cumsum(prop.table(table(factor(.$var2))))
data.frame(lab = names(res), y = c(res), good = good, pos = cumsum(good) - 0.5*good)
})
ggplot(data, aes(x = var1)) + geom_bar(aes(fill = var2), position = 'fill') +
geom_text(aes(label = round(good.Freq, 2), x = var1, y = pos.Freq), data = dat)
I used the following code and work well for me, give it a try.
geom_text(aes(label = paste(round(dat2$value,0), "%"),
vjust = ifelse(value >= 0, -0.05, 1.15)
),
size = 4, position = position_stack(vjust=0.5)
)
Basically, you need label = paste(y value, "%"). In my code, dat2 is the data file name; value is the Y value in the figure. In this case, I rounded up the number with 0 decimal.Good luck.

Resources