在函数内部的select语句内使用数据框列名,以与map()一起使用

时间:2019-03-06 00:17:02

标签: r dplyr purrr

今天,我开始使用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)

但是,由于列名不在环境中,因此这会产生错误。有人对如何解决或改进它有建议吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

这是一种tidyverse方式,其中包含三个更正/改进之处:

  1. 在示例调用shapiro(a)中,您将列作为符号提供,因此我们需要确保正确引用a,然后再取消引用以遵守dplyr的非标准评估。
  2. 使用split代替tidyverse,更nest更加一致。
  3. 最后,我建议将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"))