ggplot missing plot with x-axis factor - r

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?

Related

Labelling points with ggrepel using a second dataframe

I have two lines that I wish to label with ggrepel.
I require a second, separate dataframe to have the label information.
The error I get is:
Error in `geom_label_repel()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 2nd layer.
Caused by error in `FUN()`:
! object 'comp' not found
Run `rlang::last_error()` to see where the error occurred.
I thought when I assign new data to geom_label_repel(data = comp_label_df the old dataframe, df, would not be referenced. That is why I am puzzled by the error that refers to comp which is a column header in df.
How do I label the two lines at x = "2023-01-08"?
library("tidyverse")
library("ggrepel")
d1 <- as.Date("2023-01-07")
d2 <- as.Date("2023-01-08")
d3 <- as.Date("2023-01-09")
comp_label_df <- tibble(
x = c(d2, d2),
one = c(1.3, 2.5),
label = c("Line 1", "Line 2")
)
df <- tibble(
comp = c("A", "B", "A", "B", "A", "B"),
one = c(1, 1.1, 1.3, 2.5, 5, 8),
date = c(d1, d1, d2, d2, d3, d3)
)
# OK
ggplot(data = df, aes(x = date, y = one, group = comp)) +
geom_line()
# ERROR
ggplot(data = df, aes(x = date, y = one, group = comp)) +
geom_line() +
geom_label_repel(data = comp_label_df, aes(x = x, y = one, label = label))
aesthetics are passed on to lower layers if defined at the parent level. Since comp is not present in comp_label_df it returns an error. Specify aesthetics at geom level.
library(ggplot2)
library(ggrepel)
ggplot(data = df) +
geom_line(aes(x = date, y = one, group = comp)) +
geom_label_repel(data = comp_label_df, aes(x = x, y = one, label = label))

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

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

position_stack() changes data when used with geom_line() in ggplot2

I would like to stack several geom_line() plots one above the other. However they appeared with changed data.
Here is an example:
# make 3 data.frame with some random data
x <- seq(5, 15, length = 1000)
data1 <- data.frame(x = x, y = dnorm(x, mean = 10, sd = 3), sample = "1")
data2 <- data.frame(x = x, y = dnorm(x, mean = 7.5, sd = 3), sample = "2")
data3 <- data.frame(x = x, y = dnorm(x, mean = 12.5, sd = 1), sample = "3")
# bind data
data <- bind_rows(data1, data2, data3)
# plot data without stacking
plot.data <- data %>% ggplot(mapping = aes(x = x, y = y, color = sample)) + geom_line()
# plot data with stacking
plot.data <- data %>% ggplot(mapping = aes(x = x, y = y, color = sample)) + geom_line(position = position_stack(vjust = 1, reverse = T))
The plot without stacking looks like this:
The plot with stacking looks like this:
So it seems that position_stack sums the data, not shifts them to some constnant value, which is not expected behaviour for geom_line in my opinion. Could you suggest how to make the plots to be just shifted one above the other?

How to control legend with many groups

I have a plot like this:
Which was created with this code:
# Make data:
set.seed(42)
n <- 1000
df <- data.frame(values = sample(0:5, size = n, replace = T, prob = c(9/10, rep(0.0167,5))),
group = rep(1:100, each = 10),
fill2 = rep(rnorm(10), each = 100),
year = rep(2001:2010, times = 100)
)
df$values <- ifelse(df$year %in% 2001:2007 == T, 0, df$values)
# Plot
require(ggplot2)
p <- ggplot(data = df, aes(x = year, y = values, colour = as.factor(group))) + geom_line()
p
Since there are so many groups, the legend is really not helpfull.
Ideally I would like just two elements in the legend, one for group = 1 and for all the other groups (they should all have the same color). Is there a way to force this?
you can define a new variable that has only two values, but still plot lines according to their original group,
ggplot(data = df, aes(x = year, y = values, group = group,
colour = ifelse(group == 1, "1", "!1"))) +
geom_line() +
scale_colour_brewer("groups", palette="Set1")

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.

Resources