使用 ggplot2 在 R 中绘制 Google 表单复选框网格响应
Plotting Google Forms checkbox grid responses in R using ggplot2
有人向我发送了 Google Forms 问卷的 CSV 输出,并要求我在 R 中创建摘要图。但是,在分析来自linked 图片中的复选框网格:https://i.stack.imgur.com/0QXZb.png
每个参与者都被要求指定他们所有 children 的年龄。 Child 数字显示在网格顶部(例如 'Child 1'、'Child 2' 等),年龄段显示在 left-hand 侧下方的一列中(例如 10-13、 14-18 等)。可以从网格中选择多个响应,这让我很头疼。
对于 CSV 输出,问题的结果已经分开,因此它们出现在多个列中。年龄段显示为单独的列,一个单元格内可以出现多个响应(参见下面的一个非常小的示例)。真实数据集包含数百名参与者,结果已根据多个标准进行子集化。
x10a.6.9 x10a.10.13 x10a.14.18 x10a.19.23
child 2;child 3 child 1
child 1
child 3;child 4 child 2 child 1
child 1; child 2
编辑:丑陋的可复制版本 table(感谢 link Mojoesque):
structure(list(x10a.6.9 = c("child 2;child 3", NA, "child 3;child 4",
NA), x10a.10.13 = c(NA, "child 1", "child 2", "child 1;child 2"
), x10a.14.18 = c("child 1", NA, NA, NA), x10a.19.23 = c(NA,
NA, "child 1", NA)), row.names = c(NA, -4L), class = "data.frame")
我想知道我将如何重塑这些数据,以便它可以在简单的条形图中呈现。我不知道如何安排这些数据以使其与 ggplot2 合作。如果可能的话,我希望它看起来像 summary image produced in Google Forms。就目前而言,我不知道如何使用这样的数据沿着 x-axis 绘制年龄并沿着 y-axis 计数。我想在附图中为每个 child 号码分别显示,但不知道从哪里开始。
我们将不胜感激任何帮助。如果问题措辞不当,我深表歉意,也为我难以置信的天真道歉。
编辑:分辨率
我试了一下这个,想出了如何绘制我想要的东西。我将 post 下面使用的步骤,以防它们对其他人有用。
在下面的代码中,data_to_split 对应于上面显示的 table 的小片段。
library(tidyr)
library(dplyr)
library(ggplot2)
data_split <- data_to_split %>% separate_rows(x10a.6.9:x10a.19.23, sep = ";") %>%
select(x10a.6.9:x10a.19.23) %>% na_if(., "") %>%
mutate_all(funs(as.factor))
有必要使用 tidyr 的 separate_rows 函数,因为一个单元格内可能会出现多个响应(例如,一个单元格可以读取 'child 1;child 2')分隔符“;”用于将这些单元格分成多行。
na_if 用于将作为因子读取的空格转换为 NA 值。
在下面的代码中,添加了新列。这些列只是为已读入 R 的列提供了新名称。旧名称很难看且更难使用。
data_split$`6-9` <- data_split$x10a.6.9
data_split$`10-13` <- data_split$x10a.10.13
data_split$`14-18` <- data_split$x10a.14.18
data_split$`19-23` <- data_split$x10a.19.23
在下面的前两行代码中,选择了所有包含年龄段的相关列。然后将数据从宽格式转换为长格式。
在第三行中,na 值被删除,因为我不想显示它们。 Tally() 用于获取 n 值,该值可用于显示沿 y-axis 的计数。
在第四行,有必要re-order 到age-brackets,以便它们按时间顺序沿着x-axis 显示。其余的行帮助开发了一个基本的条形图。
age_plot <- data_split %>% select(`6-9`:`19-23`) %>%
pivot_longer(., cols = c(`6-9`:`19-23`), names_to = "Var", values_to = "Val") %>%
drop_na() %>% group_by(Var, Val) %>% tally() %>%
ggplot(aes(x = factor(Var, level = c("6-9", "10-13", "14-18", "19-23")), y = n,
fill = factor(Val, level = c("child 1", "child 2", "child 3", "child 4")))) +
geom_bar(stat = "identity", position = position_dodge(width = 0.9)) +
theme_classic() + labs(fill = "Child", x = "Age", y = "Number")
age_plot
结果看起来像 this。 (显然这张图看起来有点奇怪,数据点太少了,但实物看起来不错!)
更新:“position_dodge”更改为“position_dodge2”,“geom_bar”中的“preserve = 'single'”因此每个单独的柱现在都是宽度相同。
答案在问题正文中,但也粘贴在这里,以防有用。
在下面的代码中,data_to_split 对应于问题中显示的 table 的小片段。
可复制版本:
structure(list(x10a.6.9 = c("child 2;child 3", NA, "child 3;child 4",
NA), x10a.10.13 = c(NA, "child 1", "child 2", "child 1;child 2"
), x10a.14.18 = c("child 1", NA, NA, NA), x10a.19.23 = c(NA,
NA, "child 1", NA)), row.names = c(NA, -4L), class = "data.frame")
library(tidyr)
library(dplyr)
library(ggplot2)
data_split <- data_to_split %>%
separate_rows(x10a.6.9:x10a.19.23, sep = ";") %>%
select(x10a.6.9:x10a.19.23) %>% na_if(., "") %>%
mutate_all(funs(as.factor))
有必要使用 tidyr 的 separate_rows 函数,因为一个单元格内可能会出现多个响应(例如,一个单元格可以读取 'child 1;child 2')分隔符“;”用于将这些单元格分成多行。
na_if 用于将作为因子读取的空格转换为 NA 值。
在下面的代码中,添加了新列。这些列只是为已读入 R 的列提供了新名称。旧名称很难看且更难使用。
data_split$`6-9` <- data_split$x10a.6.9
data_split$`10-13` <- data_split$x10a.10.13
data_split$`14-18` <- data_split$x10a.14.18
data_split$`19-23` <- data_split$x10a.19.23
在下面的前两行代码中,选择了所有包含年龄段的相关列。然后将数据从宽格式转换为长格式。
在第三行中,na 值被删除,因为我不想显示它们。 Tally() 用于获取可用于沿 y 轴显示计数的 n 值。
在第四行中,有必要重新排列年龄段,以便它们按时间顺序沿 x 轴显示。其余的行帮助开发了一个基本的条形图。
age_plot <- data_split %>% select(`6-9`:`19-23`) %>%
pivot_longer(., cols = c(`6-9`:`19-23`), names_to = "Var", values_to = "Val") %>%
drop_na() %>% group_by(Var, Val) %>% tally() %>%
ggplot(aes(x = factor(Var, level = c("6-9", "10-13", "14-18", "19-23")), y = n,
fill = factor(Val, level = c("child 1", "child 2", "child 3", "child 4")))) +
geom_bar(stat = "identity", position = position_dodge2(width = 0.9, preserve = "single")) +
theme_classic() + labs(fill = "Child", x = "Age", y = "Number")
age_plot
结果看起来像 this。 (显然这张图看起来有点奇怪,数据点太少了,但实物看起来不错!)
有人向我发送了 Google Forms 问卷的 CSV 输出,并要求我在 R 中创建摘要图。但是,在分析来自linked 图片中的复选框网格:https://i.stack.imgur.com/0QXZb.png
每个参与者都被要求指定他们所有 children 的年龄。 Child 数字显示在网格顶部(例如 'Child 1'、'Child 2' 等),年龄段显示在 left-hand 侧下方的一列中(例如 10-13、 14-18 等)。可以从网格中选择多个响应,这让我很头疼。
对于 CSV 输出,问题的结果已经分开,因此它们出现在多个列中。年龄段显示为单独的列,一个单元格内可以出现多个响应(参见下面的一个非常小的示例)。真实数据集包含数百名参与者,结果已根据多个标准进行子集化。
x10a.6.9 x10a.10.13 x10a.14.18 x10a.19.23
child 2;child 3 child 1
child 1
child 3;child 4 child 2 child 1
child 1; child 2
编辑:丑陋的可复制版本 table(感谢 link Mojoesque):
structure(list(x10a.6.9 = c("child 2;child 3", NA, "child 3;child 4",
NA), x10a.10.13 = c(NA, "child 1", "child 2", "child 1;child 2"
), x10a.14.18 = c("child 1", NA, NA, NA), x10a.19.23 = c(NA,
NA, "child 1", NA)), row.names = c(NA, -4L), class = "data.frame")
我想知道我将如何重塑这些数据,以便它可以在简单的条形图中呈现。我不知道如何安排这些数据以使其与 ggplot2 合作。如果可能的话,我希望它看起来像 summary image produced in Google Forms。就目前而言,我不知道如何使用这样的数据沿着 x-axis 绘制年龄并沿着 y-axis 计数。我想在附图中为每个 child 号码分别显示,但不知道从哪里开始。
我们将不胜感激任何帮助。如果问题措辞不当,我深表歉意,也为我难以置信的天真道歉。
编辑:分辨率
我试了一下这个,想出了如何绘制我想要的东西。我将 post 下面使用的步骤,以防它们对其他人有用。
在下面的代码中,data_to_split 对应于上面显示的 table 的小片段。
library(tidyr)
library(dplyr)
library(ggplot2)
data_split <- data_to_split %>% separate_rows(x10a.6.9:x10a.19.23, sep = ";") %>%
select(x10a.6.9:x10a.19.23) %>% na_if(., "") %>%
mutate_all(funs(as.factor))
有必要使用 tidyr 的 separate_rows 函数,因为一个单元格内可能会出现多个响应(例如,一个单元格可以读取 'child 1;child 2')分隔符“;”用于将这些单元格分成多行。
na_if 用于将作为因子读取的空格转换为 NA 值。
在下面的代码中,添加了新列。这些列只是为已读入 R 的列提供了新名称。旧名称很难看且更难使用。
data_split$`6-9` <- data_split$x10a.6.9
data_split$`10-13` <- data_split$x10a.10.13
data_split$`14-18` <- data_split$x10a.14.18
data_split$`19-23` <- data_split$x10a.19.23
在下面的前两行代码中,选择了所有包含年龄段的相关列。然后将数据从宽格式转换为长格式。 在第三行中,na 值被删除,因为我不想显示它们。 Tally() 用于获取 n 值,该值可用于显示沿 y-axis 的计数。 在第四行,有必要re-order 到age-brackets,以便它们按时间顺序沿着x-axis 显示。其余的行帮助开发了一个基本的条形图。
age_plot <- data_split %>% select(`6-9`:`19-23`) %>%
pivot_longer(., cols = c(`6-9`:`19-23`), names_to = "Var", values_to = "Val") %>%
drop_na() %>% group_by(Var, Val) %>% tally() %>%
ggplot(aes(x = factor(Var, level = c("6-9", "10-13", "14-18", "19-23")), y = n,
fill = factor(Val, level = c("child 1", "child 2", "child 3", "child 4")))) +
geom_bar(stat = "identity", position = position_dodge(width = 0.9)) +
theme_classic() + labs(fill = "Child", x = "Age", y = "Number")
age_plot
结果看起来像 this。 (显然这张图看起来有点奇怪,数据点太少了,但实物看起来不错!)
更新:“position_dodge”更改为“position_dodge2”,“geom_bar”中的“preserve = 'single'”因此每个单独的柱现在都是宽度相同。
答案在问题正文中,但也粘贴在这里,以防有用。
在下面的代码中,data_to_split 对应于问题中显示的 table 的小片段。
可复制版本:
structure(list(x10a.6.9 = c("child 2;child 3", NA, "child 3;child 4",
NA), x10a.10.13 = c(NA, "child 1", "child 2", "child 1;child 2"
), x10a.14.18 = c("child 1", NA, NA, NA), x10a.19.23 = c(NA,
NA, "child 1", NA)), row.names = c(NA, -4L), class = "data.frame")
library(tidyr)
library(dplyr)
library(ggplot2)
data_split <- data_to_split %>%
separate_rows(x10a.6.9:x10a.19.23, sep = ";") %>%
select(x10a.6.9:x10a.19.23) %>% na_if(., "") %>%
mutate_all(funs(as.factor))
有必要使用 tidyr 的 separate_rows 函数,因为一个单元格内可能会出现多个响应(例如,一个单元格可以读取 'child 1;child 2')分隔符“;”用于将这些单元格分成多行。
na_if 用于将作为因子读取的空格转换为 NA 值。
在下面的代码中,添加了新列。这些列只是为已读入 R 的列提供了新名称。旧名称很难看且更难使用。
data_split$`6-9` <- data_split$x10a.6.9
data_split$`10-13` <- data_split$x10a.10.13
data_split$`14-18` <- data_split$x10a.14.18
data_split$`19-23` <- data_split$x10a.19.23
在下面的前两行代码中,选择了所有包含年龄段的相关列。然后将数据从宽格式转换为长格式。 在第三行中,na 值被删除,因为我不想显示它们。 Tally() 用于获取可用于沿 y 轴显示计数的 n 值。 在第四行中,有必要重新排列年龄段,以便它们按时间顺序沿 x 轴显示。其余的行帮助开发了一个基本的条形图。
age_plot <- data_split %>% select(`6-9`:`19-23`) %>%
pivot_longer(., cols = c(`6-9`:`19-23`), names_to = "Var", values_to = "Val") %>%
drop_na() %>% group_by(Var, Val) %>% tally() %>%
ggplot(aes(x = factor(Var, level = c("6-9", "10-13", "14-18", "19-23")), y = n,
fill = factor(Val, level = c("child 1", "child 2", "child 3", "child 4")))) +
geom_bar(stat = "identity", position = position_dodge2(width = 0.9, preserve = "single")) +
theme_classic() + labs(fill = "Child", x = "Age", y = "Number")
age_plot
结果看起来像 this。 (显然这张图看起来有点奇怪,数据点太少了,但实物看起来不错!)