【问题标题】:Alter Variables in List with purrr使用 purrr 更改列表中的变量
【发布时间】:2020-01-01 02:07:44
【问题描述】:

我有三个具有相同变量名称的数据集abc。我想检查这些变量是否包含缺失/无效值。

我有一个检查函数check_variables(),用于检查缺失值或无效值(例如,该函数可能只是is.na)。

虽然我可以将我的检查函数 check_variables() 显式应用于每个数据集,例如:

check.output = list(
    a = check_variables(a),
    b = check_variables(b),
    c = check_variables(c)
)

purrr 为这个问题提供了一个很好的一站式解决方案:

list(a,b,c) %>%
    map(~ .x %>% check_variables())

但此步骤仅将check_variables() 映射到列表中的数据集元素。相反,我希望函数 check_variables() 映射到每个数据集。有没有办法有效地将函数映射到列表中的数据集而不是每个数据集中的元素?

【问题讨论】:

  • R(和大多数其他函数式语言)实际上并不是这样工作的。最好始终将相关的 data.frames 保留在列表中,而不是尝试使用三个不同的变量。函数应始终返回新对象,而不是替换现有对象。以这种方式处理数据会更安全。
  • 欢迎来到 SO。请edit您的问题并提供minimal reproducible example,包括示例数据集abccheck_variables() 的示例实现。以及您的预期输出。正如目前所写的那样,我很难理解你所追求的问题。谢谢。

标签: r purrr


【解决方案1】:

如果目的是应用check_variables(),它接收数据集(表)并返回单个TRUEFALSE,那么问题可能与矢量化函数的使用有关。

R 和 R 的包有许多向量化函数,例如is.na,这意味着当将这些函数应用于列表c(1, NA, 2) 或数据框时,该函数将应用于列表的每个元素,从而产生@ 987654326@ 而不是 TRUE(任何元素 is.na)或 FALSE(所有元素 is.na)。

check_variable() 函数由这些向量化函数组成时,我们需要“聚合”这些向量化函数使用allany 等函数。此外,我们还需要控制聚合范围,以控制 check_variables() 函数是应用于元素、变量(列)还是整个表(数据框):

require(tidyverse) # in production code, import only `dplyr` and `tidyr`
require(purrr)

a = data.frame(x = c(1,2,3), y =c(3,NA,5))
b = data.frame(x = c(1,NA,3), y =c(3,4,5))
c = data.frame(x = c(1,NA,3), y =c(3,NA,4))

# apply `check.func` on varaibles(columns)
# aggregation has to be limited within scope of each varaible (column)
# `dplyr::summarize_all` happens to functioning like this
check.vars = function(list.tbls, check.func) list.tbls %>% map(~ .x %>% summarize_all(check.func) )

# apply `check.func` on the entire table
# as long as `check.func` takes a table and returns a single value
# we can directly apply this function
check.tbls = function(list.tbls, check.func) list.tbls %>% map(~ check.func(.x))


## Some sample functions

# check if all elements under the scope, has no NA
# take in either a vector or a table, return a boolean
has.no.na = . %>% is.na %>% any %>% `!`

# check if all elements under the scope is less than 5, NAs are counted as False
# take in either a vector or a table, return a boolean
has.no.na = . %>% is.na %>% any %>% `!` 
is.lt.5  = . %>% `<`(5) %>% all %>% replace_na(F)

# check if all elements under the scope is less than 5, NAs are ignored, all NA means TRUE
# take in either a vector or a table, return a boolean
is.lt.5.rm.na  = . %>% `<`(5) %>% all(na.rm=T)


## Use of sample functions to check variables within each dataset
list(a,b,c) %>% check.vars(has.no.na)
list(a,b,c) %>% check.vars(is.lt.5)

## Use of sample functions to check each dataset
list(a,b,c) %>% check.tbls(has.no.na)
list(a,b,c) %>% check.tbls(is.lt.5)
list(a,b,c) %>% check.tbls(is.lt.5.rm.na)

【讨论】:

    【解决方案2】:

    如果您想修改自变量,您可以传递变量名称列表进行编辑,然后使用getassign 访问和修改它们。

    library(purrr)
    library(magrittr)
    
    a = list(var = 1)
    b = list(var = 2)
    c = list(var = 3)
    
    # get the current environment. alternative is to use functions like 
    # parent.frame from within the loop but that can get confusing
    e = environment()
    
    c('a','b','c') %>% 
        map(function(x){
            ls = get(x,envir = e)
            # whatever modification you want to make on the list
            ls$var = ls$var+1
            assign(x,ls,envir = e)
        })
    

    注意在现实生活中,正如@MrFlick 所说,您可能不想这样做。将 a、b、c 保留在一个列表中,您的下游分析会更容易,因为我认为它们必须通过同一管道进行处理。 map 将愉快地返回您修改后的列表,您可以使用它来覆盖原始列表或分配给新变量。或者,在列表索引上使用for 循环来随时修改原始列表或填充预先分配的新变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 2021-04-24
      • 2021-01-13
      • 2021-10-09
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      相关资源
      最近更新 更多