Circular stacked barplot in R - Aesthetics must be either length 1 or the same as the data (26)

主宰稳场 提交于 2021-02-11 15:45:51

问题


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.

  1. The hjust argument: geom_text(data=base_data, aes(...), hjust=c(1,1,0,0), ...
  2. The y-axis limits: ylim(-150, max(label_data$tot, na.rm=T))
  3. The data are highly skewed.
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!