首页 > 解决方案 > 在R中的多个数据帧的函数中结合for循环和变异

问题描述

我希望我能对我的问题有所了解。基本上,我想在几个不同的数据集中创建相同的变量集(它们是基于异常值、抗议答案等的原始数据集的子集)。我已经尝试了很多东西,但我完全被难住了。

首先,我意识到使用 eval(parse( 是违反建议的,但是,我不是最有效的编码器,这符合我的目的。无论如何,我的目标基本上是创建一组变量 - X_3、X_4、X_5 等。这是一组其他变量的年份(x12) - z1_1、z2_1、z3_1 等。为此,我有以下代码,当我单独输入“数据”所在的每个数据集名称时,该代码有效。

编辑:数据预期

#what it looks like now
  responseid   z3_1 z4_1 z5_1
1          1  4.720 7.08   NA
2          2  1.180   NA 1.18
3          3  1.180   NA 1.18
4          4  2.596 3.54   NA
5          5 15.340   NA   NA
6          6  2.360   NA 2.36

#what i'd like it look like:
  responseid   z3_1 z4_1 z5_1    X_3  X_4  X_5
1          1  4.720 7.08   NA  56.640 84.96    NA
2          2  1.180   NA 1.18  14.160    NA 14.16
3          3  1.180   NA 1.18  14.160    NA 14.16
4          4  2.596 3.54   NA  31.152 42.48    NA
5          5 15.340   NA   NA 184.080    NA    NA
6          6  2.360   NA 2.36  28.320    NA 28.32

#dput
#original
structure(list(responseid = c(1L, 2L, 3L, 4L, 5L, 
6L), z3_1 = c(4.72, 1.18, 1.18, 2.596, 15.34, 2.36), z4_1 = c(7.08, 
NA, NA, 3.54, NA, NA), z5_1 = c(NA, 1.18, 1.18, NA, NA, 2.36),  class = "data.frame", row.names = c(NA, 6L))

#expected
structure(list(responseid = c(1L, 2L, 3L, 4L, 5L, 
6L), z3_1 = c(4.72, 1.18, 1.18, 2.596, 15.34, 2.36), z4_1 = c(7.08, 
NA, NA, 3.54, NA, NA), z5_1 = c(NA, 1.18, 1.18, NA, NA, 2.36), 
    X_3 = c(56.64, 14.16, 14.16, 31.152, 184.08, 28.32), X_4 = c(84.96, 
    NA, NA, 42.48, NA, NA), X_5 = c(NA, 14.16, 14.16, NA, NA, 
    28.32)), class = "data.frame", row.names = c(NA, 6L))

但我想对几个数据集执行此操作,因此我希望使用一个函数来运行每个 df。

for (i in 3:9){
    X.varname <- paste0("X_",i)
    data <- data %>%
      mutate(
        !!X.varname := eval(parse(text=paste0("z", i, "_1")))*12
      )
  }

但是,当我尝试将其放入一个函数中(以便我可以在数据帧列表上运行它)时,什么也没有发生:

f.test <- function(data){
for (i in 3:9){
    X.varname <- paste0("X_",i)
    data <- data %>%
      mutate(
        !!X.varname := eval(parse(text=paste0("z", i, "_1")))*12
      )
  }
}

有谁知道为什么会这样?这是我在 StackOverflow 上的第一个问题,因此如果任何格式不正确,我深表歉意。

标签: rfor-loopdplyr

解决方案


什么都没有发生,因为该函数没有返回任何东西。通常您不需要return在 R 中显式指定某些内容,因为 R 会自动返回最后一个对象。但这是一个 for 循环,意味着它什么也不返回。

为了避免这个问题,只需指定你的函数应该返回什么,即。data

f.test <- function(data){
  for (i in 3:5){
    X.varname <- paste0("X_",i)
    data <- data %>%
      mutate(
        !!X.varname := eval(parse(text=paste0("z", i, "_1")))*12
      )
  }
  return(data)
}

顺便说一句,您是对的,eval(parse(x))不建议这样做,因为它既危险又缓慢。

不确定您向我们展示的内容是否真的是您想要做的,但更好的方法是:

library(dplyr)
library(stringr)


my_fun <- function(data) {
  data %>% 
    mutate_at(vars(starts_with("z")), .funs = list(TBC=~.*12)) %>% 
    rename_at(vars(ends_with('TBC')), .funs = ~{.x %>% str_extract('\\d') %>% str_c('X_', .)})
}

此外,由于您需要对多个数据框执行此操作,您可以使用@koenniem 建议来使用purrr::map(). 将每个数据框放在一个列表中,并将该函数应用于列表的每个元素:

library(purrr)
df2 <- df %>% mutate_at(vars(-responseid), ~.+4)

df_t <- list(df, df2)

df_t %>% 
  map_df(my_fun)
#>    responseid   z3_1  z4_1 z5_1     X_3    X_4   X_5
#> 1           1  4.720  7.08   NA  56.640  84.96    NA
#> 2           2  1.180    NA 1.18  14.160     NA 14.16
#> 3           3  1.180    NA 1.18  14.160     NA 14.16
#> 4           4  2.596  3.54   NA  31.152  42.48    NA
#> 5           5 15.340    NA   NA 184.080     NA    NA
#> 6           6  2.360    NA 2.36  28.320     NA 28.32
#> 7           1  8.720 11.08   NA 104.640 132.96    NA
#> 8           2  5.180    NA 5.18  62.160     NA 62.16
#> 9           3  5.180    NA 5.18  62.160     NA 62.16
#> 10          4  6.596  7.54   NA  79.152  90.48    NA
#> 11          5 19.340    NA   NA 232.080     NA    NA
#> 12          6  6.360    NA 6.36  76.320     NA 76.32

推荐阅读