在函数内部的select语句中使用dataframe列名与map()一起使用

问题描述 投票:1回答:3

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

但是,由于列名不在环境中,因此会出错。有没有人有关于如何解决或改善它的建议?

谢谢!

r dplyr purrr
3个回答
1
投票

如果你想用一个函数做这个,你可能需要进入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)


2
投票

这是一个tidyverse方式,有三个更正/改进:

  1. 在您的示例中调用shapiro(a),您将列作为符号提供,因此我们需要确保正确引用a,然后不引用以引用dplyrs非标准评估。
  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

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"))
© www.soinside.com 2019 - 2024. All rights reserved.