绘制一个类别变量的一个类别相对于第二个变量的所有类别的份额
Plot the Share of one Category of a Categorical Variable with Respect to all Categories of a Second Variable
我有这样一个数据框:
df <- data.frame(Reason = sample(rep(c("R1", "R2", "R3", "R4"), each = 100)),
Answer = sample(rep(c("yes", "no", "no", "no"), 100)))
head(df)
我希望 ggplot 绘制一个条形图,显示每个原因(x 轴)的 "yes" 个答案(y 轴)的份额。
我试过这个:
ggplot(data = df, aes(x = interaction(Reason, Answer))) +
geom_bar(aes(y = ..count../sum(..count..)))
这会导致以下结果:
how it looks like
问题是条形总和为 1(总计)。我希望他们在每个原因类别中总结为一个。 (R1.no 和 R1.yes 应该总和为 1,R2.no 和 R2.yes 应该总和为 1,依此类推)。
完成后,我想丢弃所有带有 "no" 答案信息的条形图。所以基本上,我只想要 "yes"-answers 在每个 Reason-category 中的份额。这应该看起来像这样:
how it should look like
这样做我得到了想要的结果:
a <- prop.table(table(df$Reason, df$Answer),1)
df2 <- data.frame(Reason = rownames(as.matrix(a)),
share = as.matrix(a)[,2])
ggplot(data = df2, aes(x = reorder(Reason, share), y = share)) +
geom_bar(stat = "identity") +
ylab("share of yes-answers")
我能否避免这种变通方法并直接从 ggplot 获得所需的结果?这对我来说有一些主要优势。
非常感谢,
安迪
ggplot(df[df$Answer == "yes", ]) +
geom_bar(aes(x = Reason, y = sort(..prop..), group = 1))
Yuriy 的解决方案只有在总和为 100 时才有效。我认为你必须以某种方式计算比例,否则你无法预先排序。因此,在第一部分中,我通过添加列 p 来操作数据,如果是,则为 1;如果否,则为 0。
library(dplyr)
library(ggplot2)
set.seed(99)
df <- data.frame(
Reason = sample(rep(c("R1", "R2", "R3", "R4"), each = 100)),
Answer = sample(rep(c("yes", "no", "no", "no"), 100)))
head(df %>% mutate(p=as.numeric(Answer=="yes")),3)
Reason Answer p
1 R3 no 0
2 R3 yes 1
3 R1 no 0
然后我们用这个数据框作图,y轴就是x轴上每组的平均值,我们可以用stat_summary
和fun.y=mean
。现在 reorder
在这种情况下效果很好,因为它计算每个类别的平均值并根据该值重新排序:
ggplot(df %>% mutate(p=as.numeric(Answer=="yes")),
aes(x=reorder(Reason,p),y=p)) +
stat_summary(fun.y="mean",geom="bar",fill="orchid4")
这适用于不同类别的观察数量不同的情况:
set.seed(100)
df <- data.frame(
Reason = rep(c("R1", "R2", "R3", "R4"),times=seq(50,200,length.out=4)),
Answer = sample(c("yes","no"),500,prob=c(0.5,0.5),replace=TRUE)
)
# we expect
sort(tapply(df$Answer=="yes",df$Reason,mean))
R2 R4 R3 R1
0.460 0.505 0.520 0.540
ggplot(df %>% mutate(p=as.numeric(Answer=="yes")),
aes(x=reorder(Reason,p),y=p)) +
stat_summary(fun.y="mean",geom="bar",fill="orange")
我有这样一个数据框:
df <- data.frame(Reason = sample(rep(c("R1", "R2", "R3", "R4"), each = 100)),
Answer = sample(rep(c("yes", "no", "no", "no"), 100)))
head(df)
我希望 ggplot 绘制一个条形图,显示每个原因(x 轴)的 "yes" 个答案(y 轴)的份额。
我试过这个:
ggplot(data = df, aes(x = interaction(Reason, Answer))) +
geom_bar(aes(y = ..count../sum(..count..)))
这会导致以下结果:
how it looks like
问题是条形总和为 1(总计)。我希望他们在每个原因类别中总结为一个。 (R1.no 和 R1.yes 应该总和为 1,R2.no 和 R2.yes 应该总和为 1,依此类推)。
完成后,我想丢弃所有带有 "no" 答案信息的条形图。所以基本上,我只想要 "yes"-answers 在每个 Reason-category 中的份额。这应该看起来像这样:
how it should look like
这样做我得到了想要的结果:
a <- prop.table(table(df$Reason, df$Answer),1)
df2 <- data.frame(Reason = rownames(as.matrix(a)),
share = as.matrix(a)[,2])
ggplot(data = df2, aes(x = reorder(Reason, share), y = share)) +
geom_bar(stat = "identity") +
ylab("share of yes-answers")
我能否避免这种变通方法并直接从 ggplot 获得所需的结果?这对我来说有一些主要优势。
非常感谢, 安迪
ggplot(df[df$Answer == "yes", ]) +
geom_bar(aes(x = Reason, y = sort(..prop..), group = 1))
Yuriy 的解决方案只有在总和为 100 时才有效。我认为你必须以某种方式计算比例,否则你无法预先排序。因此,在第一部分中,我通过添加列 p 来操作数据,如果是,则为 1;如果否,则为 0。
library(dplyr)
library(ggplot2)
set.seed(99)
df <- data.frame(
Reason = sample(rep(c("R1", "R2", "R3", "R4"), each = 100)),
Answer = sample(rep(c("yes", "no", "no", "no"), 100)))
head(df %>% mutate(p=as.numeric(Answer=="yes")),3)
Reason Answer p
1 R3 no 0
2 R3 yes 1
3 R1 no 0
然后我们用这个数据框作图,y轴就是x轴上每组的平均值,我们可以用stat_summary
和fun.y=mean
。现在 reorder
在这种情况下效果很好,因为它计算每个类别的平均值并根据该值重新排序:
ggplot(df %>% mutate(p=as.numeric(Answer=="yes")),
aes(x=reorder(Reason,p),y=p)) +
stat_summary(fun.y="mean",geom="bar",fill="orchid4")
这适用于不同类别的观察数量不同的情况:
set.seed(100)
df <- data.frame(
Reason = rep(c("R1", "R2", "R3", "R4"),times=seq(50,200,length.out=4)),
Answer = sample(c("yes","no"),500,prob=c(0.5,0.5),replace=TRUE)
)
# we expect
sort(tapply(df$Answer=="yes",df$Reason,mean))
R2 R4 R3 R1
0.460 0.505 0.520 0.540
ggplot(df %>% mutate(p=as.numeric(Answer=="yes")),
aes(x=reorder(Reason,p),y=p)) +
stat_summary(fun.y="mean",geom="bar",fill="orange")