今天,我开始使用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)
但是,由于列名不在环境中,因此这会产生错误。有人对如何解决或改进它有建议吗?
谢谢!
答案 0 :(得分:2)
这是一种tidyverse
方式,其中包含三个更正/改进之处:
shapiro(a)
中,您将列作为符号提供,因此我们需要确保正确引用a
,然后再取消引用以遵守dplyr
的非标准评估。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
答案 1 :(得分:1)
如果要使用某个函数来执行此操作,则可能需要进入tidyeval,例如@MauritsEvers答案。对于像这样的相对较小的任务,您可以代替几个map
通话。映射到通过y
拆分而创建的数据帧列表,然后使用map_at
将测试应用于您选择的列。
在第一种方法中,最终会产生一些多余的结果– map_at
中的所有 not 列都只是挂在那儿。较干净的方法是选择所需的列,然后在所有列上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(v0.2.1)
创建于2019-03-05答案 2 :(得分:0)
您可以使用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"))