man*_*459 2 r bar-chart ggplot2 stacked-chart likert
假设我有一个如下所示的数据框:
P Q1 Q2 ...
1 1 4 1
2 2 3 4
3 1 1 4
Run Code Online (Sandbox Code Playgroud)
其中的列告诉我哪个人相应地回答了问题 q1、q2、... 中的哪一个。这些问题需要按照 4 分李克特量表进行回答(例如,“批准”意味着 1,“稍微批准”意味着 2,等等)。我如何绘制两个问题结果的堆积条形图(以%为单位)?
它应该看起来有点像这样。
我在网上找到的都是非常复杂的代码,我无法处理或无法理解......不是只有一个简单的函数可以完成我想要的事情吗?
谢谢你!
我确信我不是唯一一个对你问题的这一部分提出异议的人:
我在网上找到的都是非常复杂的代码,我无法处理或无法理解......不是只有一个简单的函数可以完成我想要的事情吗?
“非常复杂的代码”是相当主观的。然而,我可以理解,学习代码并试图弄清楚如何做你想做的事情(乍一看可能很简单)可能会令人畏惧和沮丧。我将尝试向您展示如何以非常合乎逻辑且清晰的方式处理此问题,以便您可以理解此处显示的代码实际上并不太复杂。
OP 没有提供数据集,但我将在这里演示一个随机数据集。这也是展示如何通过代码生成此类数据(并使其可扩展)的好机会。假设有 20 个人回答 20 个问题。我将通过首先仅提供一列人员,然后向其中添加 20 列问题来创建数据框架结构中的数据。问题答案的每个单元格将从 1 到 5 中随机选择一个答案。
library(dplyr)
library(tidyr)
library(ggplot2)
# make the dataset
set.seed(8675309)
questions <- data.frame(Person = 1:20)
for (i in 1:20) {
questions[[paste0('Q',i)]] <- sample(1:5, 20, replace=TRUE)
}
Run Code Online (Sandbox Code Playgroud)
这为我们提供了 20 行和 21 列的数据框(1 列用于人员 + 20 列用于问题)。
当准备生成绘图时,您几乎总是需要以某种方式准备数据。在我们绘制之前,我只想先在这里做两件事。第一步是将我们的数据转换为一种称为Tidy Data的格式。按照我们现在的格式......可以在 Excel 中绘制,但如果我们想要有一种高质量的方式来组织和汇总这些数据,我们希望将其组织为“更长”的表格格式。我们需要的是以如下方式组织列:
Person | Question_num | Answer
Run Code Online (Sandbox Code Playgroud)
您可以通过几种方式做到这一点。这里我使用dplyr
andtidyr
包和gather()
函数,但还存在其他方法(即 using pivot_longer()
):
questions <- questions %>% gather(key='Question_num', value='Answer', -Person)
Run Code Online (Sandbox Code Playgroud)
我想做的最后一件事是将我们的列转换questions$Answer
为分类变量,而不是连续数字。为什么?那么,参与者只能回答 1、2、3、4 或 5。答案“3.4”没有意义,所以我们的数据应该是离散的,而不是连续的。我们将通过转换questions$Answer
成一个因子来做到这一点。这也允许我们同时做两件事,这在这里非常有用:
levels
- 这表明您想要因子水平的顺序。labels
- 这允许您重新映射1
为"Approve"
和2
将是"Slightly Approve"
等等。然后您可以检查之后的数据,看到该questions$Answer
列现在由我们的值组成labels()
,而不是数字。
questions$Answer <- factor(questions$Answer,
levels=1:5,
labels=c('Approve','Slightly Approve','Neutral','Slightly Disapprove','Disapprove'))
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用该包绘制绘图ggplot2
。GGplot 使用 绘制数据到绘图区域geoms
。在这种情况下,我们可以使用geom_bar()
它来绘制条形图(总计每个项目的数量/计数),并且x
仅需要美观。如果我们将fill
每个条形的颜色设置为等于该Answer
列,那么它将对条形进行颜色编码,以与每个问题的每个答案的编号相关联。默认情况下,条形按照我们之前为列levels
参数设置的顺序堆叠在一起questions$Answer
。
ggplot(questions, aes(x=Question_num)) +
geom_bar(aes(fill=Answer))
Run Code Online (Sandbox Code Playgroud)
这个情节有很多地方是正确的,总体布局看起来也不错。剩下的就是通过几种方式改变外观。我们可以通过扩展绘图代码来更改绘图的这些方面来做到这一点。也就是说,我想做以下事情:
完整的绘图代码现在如下所示。您应该能够识别代码的哪些部分正在执行上面提到的每件事。
ggplot(questions, aes(x=Question_num)) +
geom_bar(aes(fill=Answer)) +
scale_fill_brewer(palette='Spectral', direction=-1) +
scale_y_continuous(expand=expansion(0)) +
labs(
title='My Likert Plot', subtitle='Twenty Questions!',
x='Questions', y='Number Answered'
) +
theme_classic() +
theme(legend.position='top')
Run Code Online (Sandbox Code Playgroud)
很酷,是吗?
至于“有没有一个简单的函数可以完成我想要的功能?”。答案是不”。您可以编写一个,但这可能取决于数据的最初格式化方式。如果您需要经常绘制这些图,请设置一个 R 脚本来自动为您执行此操作:)。
OP 在评论中请求通过百分比显示相同的信息。这也相当简单,而且通常是人们想要用李克特图做的事情……所以让我们这样做吧!我们将分两个阶段将计数转换为百分比。首先,我们将设置轴和条来执行此操作。其次,我们将在每个栏的顶部覆盖文本,以显示每个问题的回答百分比。
首先,我们将条形图和 y 轴设置为百分比,而不是计数。我们绘制条形几何的线是geom_bar(aes(fill=Answer))
。该函数内部也有一个隐藏的默认值position = "stack"
(我们不必指定)。这position
论点涉及ggplot
当需要在特定 x 值处绘制多个条形时应如何处理这种情况。在这种情况下,它确定如何处理与questions$Answer
每个问题对应的每个值对应的 5 个条。
正如您可能想象的那样,“堆栈”只是将它们堆叠在一起。由于我们有 20 个人回答每个问题,因此每个问题的所有条形的总高度都相同 (20)。如果只有 19 个人回答问题 3 该怎么办?好吧,总的条形高度会比其他条形要短。
通常,李克特图都显示相同高度的条形,因为它们是根据整体的比例堆叠的它们占整体在本例中,我们希望每一堆条形图的总数达到 1。这意味着 10 个人以一种方式回答应该映射到 0.5 (50%) 的条形图高度。
这就是其他position
价值观发挥作用的地方。我们想用position = "fill"
参考,我们希望需要在相同 x 轴位置绘制的条形堆叠......但不是根据它们的值,而是根据该 x 轴位置的总值的比例。
最后,我们想要确定我们的规模。如果我们只使用position="fill"
y 轴刻度,则值为“0、0.25、0.50、0.75 和 1.0”或类似的值。我们希望它看起来像“0%、25%、50%、75%、100%”。您可以在scale_y_continuous()
函数内执行此操作并指定labels
参数。在这种情况下,该scales
包有一个方便的percent_format()
功能就是为了这个目的。将这些放在一起,您将得到以下结果:
ggplot(questions, aes(x=Question_num)) +
geom_bar(aes(fill=Answer), position="fill") +
scale_fill_brewer(palette='Spectral', direction=-1) +
scale_y_continuous(expand=expansion(0), labels=scales::percent_format()) +
labs(
title='My Likert Plot', subtitle='Twenty Questions!',
x='Questions', y='Number Answered'
) +
theme_classic() +
theme(legend.position='top')
Run Code Online (Sandbox Code Playgroud)
不幸的是,要将文本以百分比形式放在顶部,这并不那么简单。为此,我们需要汇总数据,在这种情况下,最简单的方法是预先汇总到一个单独的数据集中,然后使用映射到我们的汇总数据框的文本几何来标记文本。
摘要数据框是通过指定我们希望如何将数据分组在一起,然后分配n()
或每个答案的计数作为freq
列值来创建的。
questions_summary <- questions %>%
group_by(Question_num, Answer) %>%
summarize(freq = n()) %>% ungroup()
Run Code Online (Sandbox Code Playgroud)
然后我们用它来映射到一个新的几何对象geom_text
:该y
值需要再次表示为比例。就像geom_bar
上面的原因一样,我们必须使用这个"fill"
位置。我还想确保每个条的位置垂直设置为“中间”,因此我们必须通过使用position_fill(vjust=0.5)
而不是仅指定一点"fill"
指定更进一步。
您会注意到最后一个关键部分是我们正在使用group
美学。这个非常重要。对于文本几何,ggplot
需要知道数据如何分组。就条形几何而言,“显而易见”(可以这么说),由于条形的颜色不同,因此条形的每种颜色都是分离的。对于文本,这总是需要指定(如何分割值),我们通过group
美观来做到这一点。
ggplot(questions, aes(x=Question_num)) +
geom_bar(aes(fill=Answer), position="fill") +
geom_text(
data=questions_summary,
aes(y=freq, label=percent(freq/20,1), group=Answer),
position=position_fill(vjust=0.5),
color='gray25', size=3.5
) +
scale_fill_brewer(palette='Spectral', direction=-1) +
scale_y_continuous(expand=expansion(0), labels=scales::percent_format()) +
labs(
title='My Likert Plot', subtitle='Twenty Questions!',
x='Questions', y='Number Answered'
) +
theme_classic() +
theme(legend.position='top')
Run Code Online (Sandbox Code Playgroud)
瞧!
归档时间: |
|
查看次数: |
3718 次 |
最近记录: |