问题
I am trying to create a circular stacked barplot as mentioned here (https://www.r-graph-gallery.com/299-circular-stacked-barplot.html). And I get the following error when when I get to the step of making the plot (in bold below):
Error: Aesthetics must be either length 1 or the same as the data (26): hjust
Run rlang::last_error()
to see where the error occurred.
In addition: Warning message:
Removed 208 rows containing missing values (position_stack).
This is what my data looks like (with 5 columns and 70 rows):
Individual; group; value1; value2; value3; value4
Here are my codes:
Transform data in a tidy format (long format)
data <- data %>% gather(key = "observation", value="value", -c(1,2))
Make the plot
p <- ggplot(data) +
geom_bar(aes(x=as.factor(id), y=value, fill=observation), stat="identity", alpha=0.5) +
scale_fill_viridis(discrete=TRUE) +
geom_segment(data=grid_data, aes(x = end, y = 0, xend = start, yend = 0), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 2, xend = start, yend = 2), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 4, xend = start, yend = 4), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 6, xend = start, yend = 6), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 8, xend = start, yend = 8), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
ggplot2::annotate("text", x = rep(max(data$id),5), y = c(0, 2, 4, 6, 8), label = c("0", "2", "4", "6", "8") , color="grey", size=6 , angle=0, fontface="bold", hjust=1) +
ylim(-150,max(label_data$tot, na.rm=T)) +
theme_minimal() +
theme(
legend.position = "none",
axis.text = element_blank(),
axis.title = element_blank(),
panel.grid = element_blank(),
plot.margin = unit(rep(-1,4), "cm")
) +
coord_polar() +
geom_text(data=label_data, aes(x=id, y=tot+10, label=individual, hjust=hjust), color="black", fontface="bold",alpha=0.6, size=5, angle= label_data$angle, inherit.aes = FALSE ) +
geom_segment(data=base_data, aes(x = start, y = -5, xend = end, yend = -5), colour = "black", alpha=0.8, size=0.6 , inherit.aes = FALSE ) +
geom_text(data=base_data, aes(x = title, y = -18, label=group), hjust=c(1,1,0,0), colour = "black", alpha=0.8, size=4, fontface="bold", inherit.aes = FALSE)
**ggsave(p, file="output1.png", width=10, height=10)**
I would appreciate any help with this.
Thanks!!
individual group value1 value2 value3 value4
Biomarker1 Group1 0 1 2 2
Biomarker2 Group2 0 1 0 2
Biomarker3 Group2 0 1 0 1
Biomarker4 Group3 1 2 1 0
Biomarker5 Group4 0 2 4 1
Biomarker6 Group4 0 1 0 1
Biomarker7 Group4 0 1 0 1
Biomarker8 Group5 0 1 0 1
Biomarker9 Group6 0 1 1 1
Biomarker10 Group6 0 2 1 1
回答1:
This is the modified code from the linked post to produce a circular barplot with angled group labels in the centre.
library(ggplot2)
library(viridis)
library(tidyverse)
# Create dataset
data <- data.frame(
individual=paste( "Mister ", seq(1,60), sep=""),
group=c( rep('Group A', 10), rep('Group B', 30), rep('Group C', 14), rep('Group D', 6)) ,
value=sample( seq(10,100), 60, replace=T), stringsAsFactors = TRUE)
# Set a number of 'empty bar' to add at the end of each group
empty_bar <- 3
to_add <- data.frame( matrix(NA, empty_bar*nlevels(data$group), ncol(data)) )
colnames(to_add) <- colnames(data)
to_add$group <- rep(levels(data$group), each=empty_bar)
data <- rbind(data, to_add)
data <- data %>% arrange(group)
data$id <- seq(1, nrow(data))
# Get the name and the y position of each label
label_data <- data
number_of_bar <- nrow(label_data)
angle <- 90 - 360 * (label_data$id-0.5) /number_of_bar
label_data$hjust <- ifelse( angle < -90, 1, 0)
label_data$angle <- ifelse(angle < -90, angle+180, angle)
# prepare a data frame for base lines
base_data <- data %>%
group_by(group) %>%
summarize(start=min(id), end=max(id) - empty_bar) %>%
rowwise() %>%
mutate(title=mean(c(start, end)))
This is the additional code to create the angles for the group labels
angle <- 90 - 360 * (base_data$title-0.5)/number_of_bar
base_data$angle <- ifelse(angle < -90, angle+180, angle)
# prepare a data frame for grid (scales)
grid_data <- base_data
grid_data$end <- grid_data$end[ c( nrow(grid_data), 1:nrow(grid_data)-1)] + 1
grid_data$start <- grid_data$start - 1
grid_data <- grid_data[-1,]
# Make the plot
ggplot(data, aes(x=as.factor(id), y=value, fill=group)) + # Note that id is a factor. If x is numeric, there is some space between the first bar
geom_bar(aes(x=as.factor(id), y=value, fill=group), stat="identity", alpha=0.5) +
# Add a val=100/75/50/25 lines. I do it at the beginning to make sur barplots are OVER it.
geom_segment(data=grid_data, aes(x = end, y = 80, xend = start, yend = 80), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 60, xend = start, yend = 60), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 40, xend = start, yend = 40), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
geom_segment(data=grid_data, aes(x = end, y = 20, xend = start, yend = 20), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
# Add text showing the value of each 100/75/50/25 lines
annotate("text", x = rep(max(data$id),4), y = c(20, 40, 60, 80), label = c("20", "40", "60", "80") , color="grey", size=3 , angle=0, fontface="bold", hjust=1) +
geom_bar(aes(x=as.factor(id), y=value, fill=group), stat="identity", alpha=0.5) +
ylim(-50,120) +
theme_minimal() +
theme(
legend.position = "none",
axis.text = element_blank(),
axis.title = element_blank(),
panel.grid = element_blank(),
# plot.margin = unit(rep(-1,4), "cm")
) +
coord_polar() +
geom_text(data=label_data, aes(x=id, y=value+10, label=individual, hjust=hjust), color="black", fontface="bold",alpha=0.6, size=2.5, angle= label_data$angle, inherit.aes = FALSE ) +
# Add base line information
geom_segment(data=base_data, aes(x = start, y = -1, xend = end, yend = -1), colour = "black", alpha=0.6, size=0.8, inherit.aes = F ) +
And here we utilize the angle:
geom_text(data=base_data, aes(x = title, y = -6, label=group, angle=angle),
hjust=c(1,1,0,0), colour = "black", alpha=0.7, size=2, fontface="bold", inherit.aes = FALSE)
回答2:
There were many issues here. The first two are serious and concern use of "hard_coding". That is, the code does not depend on the data. The second two are minor problems with your data, not the code.
- The
hjust
argument:geom_text(data=base_data, aes(...), hjust=c(1,1,0,0), ...
- The y-axis limits:
ylim(-150, max(label_data$tot, na.rm=T))
- The data are highly skewed.
- You have too many bars and too many groups.
Number 1 causes the error:
# Error: Aesthetics must be either length 1 or the same as the data (24): hjust
# Run `rlang::last_error()` to see where the error occurred.
In the linked post, the base_data
looks like this:
# A tibble: 4 x 4
group start end title
<chr> <int> <dbl> <dbl>
1 A 1 8 4.5
2 B 11 38 24.5
3 C 41 52 46.5
4 D 55 58 56.5
But for your data it looks like this:
# A tibble: 24 x 4
group start end title
<chr> <int> <dbl> <dbl>
1 Group1 1 -1 0
2 Group10 2 1 1.5
3 Group11 4 5 4.5
4 Group12 8 8 8
5 Group13 11 9 10
6 Group14 12 42 27
7 Group15 45 43 44
8 Group16 46 47 46.5
9 Group17 50 49 49.5
10 Group18 52 52 52
# ... with 14 more rows
Therefore the hjust
argument needs to be changed to something that depends on the data, and not hard-coded with values. Perhaps just omit it and see what the graph looks like and change it if required.
Number 2 is not serious but causes your graph to have a large hole in the centre. The value of -150 was based on the data, so this (hard-coding) is bad practice. It looks like the minimum y-axis value should be changed to -max(label_data$tot, na.rm=T)
, which is now more general. But this may need some tinkering to get the best result. The ideal value may also depend on the number of bars and the skewness of the data.
There are other parts where hard-coding was also used.
Number 3 is not serious but causes some of the labels to appear off the plotting region. You can try to transform the y-axis, but I'll leave that to you to decide.
Number 4 is not serious either but causes the labels within the inner circle to overlap.
After fixing these and other minor issues, you should get the following:
来源:https://stackoverflow.com/questions/61721664/circular-stacked-barplot-in-r-aesthetics-must-be-either-length-1-or-the-same-a