【问题标题】:How to calculate percentages in a stacked barplot bar-wise?如何按条形计算堆积条形图中的百分比?
【发布时间】:2017-08-29 22:32:30
【问题描述】:

问题

条中的当前百分比是根据数据总量计算的。我希望每个堆栈都有完全 100%。 (已解决)

百分比也应该四舍五入到最接近的整数。 (已解决)

编辑:删除所有低于或等于 1 的百分比。(已解决)

Edit2:确保没有标签重叠。

我已经在谷歌上搜索了一段时间。似乎没有适当的方法来防止标签重叠。

我发现的可能解决方案:

  • 反转剧情
  • 添加 angle() 以旋转标签
  • “手动”计算每个位置
  • 利用 check_overlap = TRUE

当前状态

到目前为止我的代码

# Load libraries & packages =================================
library("ggplot2")
library("scales")
library("dplyr")
library("foreign")
library("tidyverse")
library("forcats")


# Data setup =================================
spss_file_path <- "D:\\Programming\\Testing\\2017-03-15_data_import&ggplot2\\Beispieldatensatz(fiktiv).sav"
exampledata <- read.spss(spss_file_path, use.value.labels = TRUE,
                         to.data.frame = TRUE, reencode = TRUE)


exampledata$V43   <- factor(exampledata$V43,
                            levels = c(1,2,3,4,5),
                            labels = c("1 Sehr zufrieden","2","3","4", "5 Sehr unzufrieden"))

exampledata$V43   <- factor(exampledata$V43, levels = rev(unique(levels(exampledata$V43))))
exampledata$A_REF <- factor(exampledata$A_REF, levels = rev(unique(levels(exampledata$A_REF))))
exampledata$V101  <- factor(exampledata$V101, levels = rev(unique(levels(exampledata$V101))))

labels <- exampledata %>% 
  filter(!is.na(V101), !is.na(V43)) %>% 
  count(A_REF) %>% 
  mutate(labels = paste(A_REF,"(n=", n, ")")) %>% 
  select(A_REF, labels)

plot_data <-  exampledata %>% 
  filter(!is.na(V101), !is.na(V43)) %>% 
  left_join(labels, by = "A_REF")

plot_data <- plot_data %>% 
  group_by(labels) %>% 
  summarize(`5 Sehr unzufrieden` = sum(ifelse(V43 == "5 Sehr unzufrieden", 1, 0)) / n(),
            `4` = sum(ifelse(V43 == "4", 1, 0)) / n(),
            `3` = sum(ifelse(V43 == "3", 1, 0)) / n(),
            `2` = sum(ifelse(V43 == "2", 1, 0)) / n(),
            `1 Sehr zufrieden` = sum(ifelse(V43 == "1 Sehr zufrieden", 1, 0)) / n()) %>%
  gather(key = Rating, value = prop, -labels)

plot_data$labels <- factor(plot_data$labels)
plot_data$Rating <- factor(plot_data$Rating) %>% fct_rev()

# Plot =================================
ggplot(plot_data, aes(x = labels, y = prop, fill = Rating)) +
  geom_col() + 
  scale_y_continuous(labels = scales::percent, breaks = c(0, 0.2, 0.4, 0.6, 0.8, 1)) +
  labs(y=NULL, x=NULL, fill=NULL) + 
  ggtitle(paste(attr(exampledata, "variable.labels")[77])) + 
  theme_classic() + 
  geom_text(aes(label = if_else(prop > 0.02, scales::percent(round(prop, 2)), NULL)), position = position_fill(vjust=0.5)) +
  coord_flip()

数据

structure(list(exampledata.V101 = structure(c(2L, NA, 2L, 2L, 
2L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, NA, 2L, 2L, 2L, 1L, 2L, NA, 
NA, NA, 1L, 1L, 2L, NA, 2L, 2L, 2L, NA, 2L, 2L, NA, NA, 1L, NA, 
2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, NA, NA, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, NA, 1L, NA, 1L, NA, 
1L, 2L, NA, NA, 2L, NA, 1L, 2L, 2L, NA, 2L, NA, 2L, 2L, 1L, 2L, 
1L, 2L, 1L, 1L, 2L, 1L, NA, 2L, 2L, 2L, 2L, NA, 2L, 1L, 2L, 2L
), .Label = c("Weiblich", "Männlich"), class = "factor"), exampledata.A_REF = structure(c(18L, 
18L, 18L, 18L, 18L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 16L, 18L, 
18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 16L, 18L, 18L, 16L, 18L, 
16L, 18L, 18L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 
16L, 18L, 18L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 17L, 16L, 18L, 
18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 17L, 18L, 18L, 
16L, 18L, 16L, 18L, 18L, 16L, 16L, 18L, 18L, 18L, 18L, 18L, 18L, 
18L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 16L, 18L, 
16L, 16L, 18L, 18L, 18L, 17L, 16L, 18L), .Label = c("Zertifikat eines Aufbau- oder Ergänzungsstudiums", 
"LA Berufliche Schulen", "LA Sonderschule", "LA Gymnasium", "LA Haupt- und Realschule", 
"LA Grundschule", "Künstlerischer/musischer Abschluss", "Kirchlicher Abschluss", 
"Staatsexamen (ohne Lehramt)", "Diplom Fachhochschule, Diplom I an Gesamthochschulen", 
"Diplom Universität, Diplom II an Gesamthochschulen", "Sonstiges", 
"Promotion", "Staatsexamen", "Magister", "Diplom", "Master", 
"Bachelor"), class = "factor"), exampledata.V43 = structure(c(3L, 
5L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 4L, 3L, 3L, 2L, NA, 4L, 5L, 5L, 
4L, 4L, 4L, 4L, NA, 2L, 4L, 3L, 5L, 4L, 4L, 4L, NA, 4L, 4L, NA, 
NA, 3L, 5L, 2L, 4L, 5L, 4L, 4L, 5L, 5L, 4L, NA, NA, 4L, NA, 3L, 
4L, 5L, 5L, 2L, 4L, 4L, 3L, 4L, 4L, 4L, 3L, 5L, 4L, 5L, NA, 4L, 
NA, 4L, NA, 4L, 5L, 4L, NA, 5L, NA, 4L, 4L, 4L, NA, 4L, NA, 5L, 
4L, 4L, 4L, 4L, 4L, 3L, 3L, 4L, 2L, 4L, 4L, 4L, 3L, 4L, NA, 4L, 
5L, 5L, 4L), .Label = c("5 Sehr unzufrieden", "4", "3", "2", 
"1 Sehr zufrieden"), class = "factor")), .Names = c("exampledata.V101", 
"exampledata.A_REF", "exampledata.V43"), row.names = c(NA, 100L
), class = "data.frame")

【问题讨论】:

  • 您可以缩短百分比计算如下:plot_data &lt;- plot_data %&gt;% group_by(labels, V43) %&gt;% tally %&gt;% mutate(Percent = n/sum(n)).

标签: r ggplot2 dplyr tidyverse


【解决方案1】:

通常最好在绘制图表之前将数据处理成汇总数据。我发现尝试让ggplot2 为你做总结要么是有限的,要么是很难让它以你想要的方式显示。

library(tidyverse)
library(forcats)

因为最好在将数据绘制到ggplot2 之前对其进行汇总,所以下面的代码会计算每组label 在比例上选择特定答案的比例。在最后一步中,我将数据从宽变为长,以便所有要绘制的比例都在同一个变量中(我称之为prop)。

plot_data <- plot_data %>% group_by(labels) %>% 
            summarize(`5 Sehr unzufrieden` = sum(ifelse(V43 == "5 Sehr unzufrieden", 1, 0)) / n(),
                      `4` = sum(ifelse(V43 == "4", 1, 0)) / n(),
                      `3` = sum(ifelse(V43 == "3", 1, 0)) / n(),
                      `2` = sum(ifelse(V43 == "2", 1, 0)) / n(),
                      `1 Sehr zufrieden` = sum(ifelse(V43 == "1 Sehr zufrieden", 1, 0)) / n()) %>%
            gather(key = Rating, value = prop, -labels)

最好将分类变量设置为用于操作的因素,例如顺序和颜色,所以下面就是这样做的。最初,我的代码有比例标签(我在上面的gather 函数中调用Rating)的顺序与你的相反,所以我使用forcats 包中的fct_rev 来反转它返回。

plot_data$labels <- factor(plot_data$labels)
plot_data$Rating <- factor(plot_data$Rating) %>% fct_rev()

对于下面的图表,我只是做了一些更改。最值得注意的是我使用的是geom_col 而不是geom_bar。在后台,geom_colgeom_bar(stat = "identity") 相同 - 只是打字更快。我们实际上是在告诉ggplot2 按原样绘制数据图表,而不是将其视为原始数据。但是,我确实需要指定 y 美学来指示我想要绘制哪些数据,因此我指定在初始 ggplot 调用中使用 prop 变量。

# Plot =================================
ggplot(plot_data, aes(x = labels, y = prop, fill = Rating)) +
geom_col() + 
scale_y_continuous(labels = scales::percent, breaks = c(0, 0.2, 0.4, 0.6, 0.8, 1)) +
labs(y=NULL, x=NULL, fill=NULL) + 
ggtitle(paste(attr(exampledata, "variable.labels")[77])) + 
theme_classic() + 
geom_text(aes(label = if_else(prop > 0.01, scales::percent(round(prop, 2)), NULL)), position = position_fill(vjust=0.5)) +
coord_flip()

我唯一更改的另一行是上面的geom_text 调用。我添加了一个if_else 函数,以便它显示标签(如果它高于 1%)或不显示标签(1% 或更少)。此外,我对百分比进行了四舍五入,这样您就没有使用 round 函数的任何小数。请记住,您需要四舍五入到小数点后两位。

【讨论】:

  • 非常感谢您的努力!它工作正常,但我想删除低于 1% 的所有百分比。因为我不知道你做了什么,如果你能给我一些解释/帮助会很好。你无法达到样本量,因为我只给了你数据的前 100 行。
  • @MarcBrinkmann 我添加了一些额外的解释,并修改了 geom_text 代码以删除 1% 或更少的标签。
  • 不幸的是,我收到以下消息:警告:忽略未知的美学:位置不知道如何为 PositionFill/PositionStack/Position/ggproto 类型的对象自动选择比例。默认为连续。错误:美学必须是长度 1 或与数据 (15) 相同:标签、位置、x、y、填充
  • 嗨@MarcBrinkmann 用上面修改过的ggplot 代码再试一次?括号在geom_text 层内的错误位置,导致错误。道歉。
【解决方案2】:

不确定这是否会让您到达您想去的地方,但这里有一个基于我稍早前编写的一些代码的简单版本。没有包括所有 ggplot2 位,因为我同意@Phil 的总结应该在绘图之前完成。

devtools::install_github("ekstroem/MESS")
x <- c(35, 34.6, 12, 5, .1, .99, 1.2, 11.11)  # Input percentages

round_percent(x)

给了

[1] 35 35 12  5  0  1  1 11

或者你可以有

round_percent(x[x>1])

给了

[1] 36 35 12  5  1 11

您需要确保颜色与其余组相匹配,所以还有一些工作要做。

【讨论】:

  • 感谢您分享您的解决方案。我总是喜欢有多种选择来解决给定的问题。
猜你喜欢
  • 1970-01-01
  • 2021-05-31
  • 2018-12-15
  • 1970-01-01
  • 1970-01-01
  • 2019-09-27
  • 2017-09-15
  • 2019-12-19
  • 1970-01-01
相关资源
最近更新 更多