今天我开始使用purrr函数,所以我可以尝试从更实用的方法中使用R.我目前有一个数据框,其中包含一个带有许多其他变量的响应变量。我的目标是将数据帧拆分为响应列中的级别,然后对所有拆分数据帧运行shapiro.test()。
例如,此代码有效:
# fake data
df = data.frame(y = c(rep(1,10), rep(2, 10)),
a = rnorm(20),
b = runif(20),
c = rnorm(20))
df$y <- factor(df$y)
df %>%
select(y, a) %>%
split(.$y) %>%
map(~shapiro.test(.x$a))
这会返回:
$`1`
Shapiro-Wilk normality test
data: .x$a
W = 0.93455, p-value = 0.4941
$`2`
Shapiro-Wilk normality test
data: .x$a
W = 0.7861, p-value = 0.009822
因此,我希望它在单个列上运行,但我希望它在任何列的给定向量上运行。我现在的想法是创建一个我想要运行的列名的向量,并在map()中使用它。我觉得我很接近这个权利,但我只是有点卡住了。
# Function that splits the df into two groups based on y levels and run shapiro test on the split dfs
shapiro <- function(var) {
df_list = df %>%
select(y, var) %>%
split(.$y) %>%
map(~shapiro.test(.x$var))
return(df_list)
}
这失败了:
> shapiro(a)
Error in .f(.x[[i]], ...) : object 'a' not found
这是有道理的,因为a没有保存在环境中。这是我设想的方向,但我不知道是否有更好的方法。
# the column names I want the function to take
columns = c(a, b, c)
# map it
map(columns, shapiro)
但是,由于列名不在环境中,因此会出错。有没有人有关于如何解决或改善它的建议?
谢谢!
如果你想用一个函数做这个,你可能需要进入tidyeval,就像@MauritsEvers一样。对于这样一个相对较小的任务,你可以通过几个map
呼叫逃脱。映射通过y
拆分创建的数据框列表,然后使用map_at
将测试应用于您选择的列。
在第一种方法中,你最终会有一些过剩 - 任何不在map_at
中的列都只是挂在那里。更干净的方法是选择所需的列,然后在所有列上选择map
以应用测试。
library(tidyverse)
test_list1 <- df %>%
split(.$y) %>%
map(function(split_by_y) {
split_by_y %>%
map_at(vars(a, b, c), shapiro.test)
})
test_list2 <- df %>%
split(.$y) %>%
map(function(split_by_y) {
split_by_y %>%
select(a, b, c) %>%
map(shapiro.test)
})
test_list2[[2]]$a
#>
#> Shapiro-Wilk normality test
#>
#> data: .x[[i]]
#> W = 0.95281, p-value = 0.7018
由reprex package创建于2019-03-05(v0.2.1)
这是一个tidyverse
方式,有三个更正/改进:
shapiro(a)
,您将列作为符号提供,因此我们需要确保正确引用a
,然后不引用以引用dplyr
s非标准评估。split
更多tidyverse
一致的方法是使用nest
。df
作为shapiro
的函数参数,从而避免对全局变量的依赖。这是改进版
shapiro <- function(df, var) {
var <- enquo(var)
df_list <- df %>%
select(y, !!var) %>%
group_by(y) %>%
nest() %>%
mutate(test = map(setNames(data, y), ~shapiro.test(.x[[1]]))) %>%
pull(test)
return(df_list)
}
所以对于列df$a
shapiro(df, a)
#$`1`
#
# Shapiro-Wilk normality test
#
#data: .x[[1]]
#W = 0.93049, p-value = 0.4527
#
#
#$`2`
#
# Shapiro-Wilk normality test
#
#data: .x[[1]]
#W = 0.9268, p-value = 0.4171
和列df$b
shapiro(df, b)
#$`1`
#
# Shapiro-Wilk normality test
#
#data: .x[[1]]
#W = 0.90313, p-value = 0.237
#
#
#$`2`
#
# Shapiro-Wilk normality test
#
#data: .x[[1]]
#W = 0.88552, p-value = 0.1509
您可以使用for循环将结果附加到列表:
shapiro <- function(var) {
myList = list()
for (i in 1:length(var)) {
myList[[i]] = df %>%
select(y, var = var[i]) %>%
split(.$y) %>%
map(~shapiro.test(.x$var))
}
return(myList)
}
只需确保为列使用字符向量:
shapiro(c("a", "b"))