使用 dplyr 和 for 循环将标准化变量添加到数据框

Adding standardized variables to a data frame using dplyr and a for loop

我有一个两部分的问题。我搜索了整个堆栈并找到了与我的问题相关的答案,但我尝试过的任何变体都没有奏效。在此先感谢您的帮助!

我有一个包含许多变量的大型数据框。

首先,我想(1)用另一个变量(在我的例子中是说话者)标准化一个变量,以及(2)在变量标准化后过滤掉值(偏离平均值大于 2 个标准差)。 (1) 和 (2) 可以通过使用 dplyr 的函数来处理。

其次,我有很多变量想要这样做,所以我试图找到一种自动化的方法来做到这一点,比如使用 for 循环。

问题 1:编写包含 dplyr 函数的函数

这是我的数据框的示例:

df = data.frame(speaker=c("eng1","eng1","eng1","eng1","eng1","eng1","eng2","eng2","eng2","eng2","eng2"),
            ratio_means001=c(0.56,0.202,0.695,0.436,0.342,10.1,0.257,0.123,0.432,0.496,0.832),
            ratio_means002=c(0.66,0.203,0.943,0.432,0.345,0.439,0.154,0.234,NA,0.932,0.854))

输出:

     speaker ratio_means001 ratio_means002
1     eng1          0.560          0.660
2     eng1          0.202          0.203
3     eng1          0.695          0.943
4     eng1          0.436          0.432
5     eng1          0.342          0.345
6     eng1         10.100          0.439
7     eng2          0.257          0.154
8     eng2          0.123          0.234
9     eng2          0.432             NA
10    eng2          0.496          0.932
11    eng2          0.832          0.854

下面是我想变成函数的基本代码:

standardized_data = group_by(df, speaker) %>%
mutate(zRatio1 = as.numeric(scale(ratio_means001)))%>%
filter(!abs(zRatio1) > 2)

这样数据框现在看起来像这样(例如):

     speaker ratio_means001 ratio_means002   zRatio1
     (fctr)          (dbl)          (dbl)     (dbl)
 1     eng1          0.560          0.660 -0.3792191
 2     eng1          0.202          0.203 -0.4699781
 3     eng1          0.695          0.943 -0.3449943
 4     eng1          0.436          0.432 -0.4106552
 5     eng1          0.342          0.345 -0.4344858
 6     eng2          0.257          0.154 -0.6349445
 7     eng2          0.123          0.234 -1.1325034
 8     eng2          0.432             NA  0.0148525
 9     eng2          0.496          0.932  0.2524926
 10    eng2          0.832          0.854  1.5001028

这是到目前为止我所拥有的功能。变异部分有效,但我一直在努力添加过滤器部分:

library(lazyeval)
standardize_variable = function(col1, new_col_name) {
     mutate_call = lazyeval::interp(b = interp(~ scale(a)), a = as.name(col1))
     group_by(data,speaker) %>% 
     mutate_(.dots = setNames(list(mutate_call), new_col_name)) %>%
     filter_(interp(~ !abs(b) > 2.5, b = as.name(new_col_name))) # this part does not work
}

当我尝试 运行 函数时收到以下错误:

data = standardize_variable("ratio_means001","zRatio1")

Error in substitute_(`_obj`[[2]], values) : 
argument "_obj" is missing, with no default

问题 2:循环函数

我想将上述函数应用于许多变量,因此我想找到一种方法来使用循环或其他有用的函数来帮助自动执行此过程。变量名仅在末尾的数字不同,所以我想出了这样的东西:

d <- data.frame()
for(i in 1:2) 
{ 
 col1 <- paste("ratio_means00", i, sep = "")
 new_col <- paste("zRatio", i, sep = "")
 d <- rbind(d, standardize_variable(col1, new_col))
}

但是,我收到以下错误:

 Error in match.names(clabs, names(xi)) : 
 names do not match previous names 

再次感谢您对这些问题的帮助!

选项 1

我认为您的函数遇到的主要问题与您调用 interp 两次有关。修复导致 filter 的另一个问题,我认为这是由于 scale 添加属性(我使用的是开发版本 dplyr,dplyr_0.4.3.9001).将 as.numeric 环绕在 scale 周围可以摆脱它。

所以通过修复你的函数看起来像:

standardize_variable = function(col1, new_col_name) {
    mutate_call = lazyeval::interp(~as.numeric(scale(a)), a = as.name(col1))
    group_by(df, speaker) %>% 
        mutate_(.dots = setNames(list(mutate_call), new_col_name)) %>%
        filter_(interp(~ !abs(b) > 2, b = as.name(new_col_name)))
}

我发现通过变量的循环比您所拥有的要复杂一些,因为我相信您希望在为每个变量创建一个数据集后将它们重新合并在一起。一种选择是将它们保存到列表中,然后使用 do.callmerge 来获取最终数据集。

d = list()
for(i in 1:2) { 
    col1 <- paste("ratio_means00", i, sep = "")
    new_col <- paste("zRatio", i, sep = "")
    d[[i]] = standardize_variable(col1, new_col)
}

do.call(merge, d)

  speaker ratio_means001 ratio_means002    zRatio1    zRatio2
1    eng1          0.202          0.203 -0.4699781 -1.1490444
2    eng1          0.342          0.345 -0.4344858 -0.6063693
3    eng1          0.436          0.432 -0.4106552 -0.2738853
4    eng1          0.560          0.660 -0.3792191  0.5974521
5    eng1          0.695          0.943 -0.3449943  1.6789806
6    eng2          0.123          0.234 -1.1325034 -0.7620572
7    eng2          0.257          0.154 -0.6349445 -0.9590348
8    eng2          0.496          0.932  0.2524926  0.9565726
9    eng2          0.832          0.854  1.5001028  0.7645194

备选方案 2

所有这些的替代方法是对问题的第一部分使用 mutate_eachrename_,然后使用带有 lapply 循环的 interp用于同时对所有缩放变量进行最终过滤。

在下面的代码中,我利用了 mutate_each 允许命名从 dplyr_0.4.3.9001 开始的单个函数这一事实。 rename_ 中的事情看起来有点复杂,因为我正在为新列命名您想要的名称。为了简化事情,您可以让它们从 mutate_each_z 结尾,并省去 rename_gsubgrepl.

的复杂步骤
df2 = df %>%
    group_by(speaker) %>%
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>%
    rename_(.dots = setNames(names(.)[grepl("z", names(.))], 
                        paste0("zR", gsub("r|_z|_means00", "", names(.)[grepl("z", names(.))]))))

完成后,您只需按多列进行筛选。我认为最简单的方法是使用 interplapply 列出要过滤的条件,然后将其提供给 filter_.[=45 的 .dots 参数=]

dots = lapply(names(df2)[starts_with("z", vars = names(df2))],
                         function(y) interp(~abs(x) < 2, x = as.name(y)))

filter_(df2, .dots = dots)

Source: local data frame [9 x 5]
Groups: speaker [2]

  speaker ratio_means001 ratio_means002    zRatio1    zRatio2
   (fctr)          (dbl)          (dbl)      (dbl)      (dbl)
1    eng1          0.560          0.660 -0.3792191  0.5974521
2    eng1          0.202          0.203 -0.4699781 -1.1490444
3    eng1          0.695          0.943 -0.3449943  1.6789806
4    eng1          0.436          0.432 -0.4106552 -0.2738853
5    eng1          0.342          0.345 -0.4344858 -0.6063693
6    eng2          0.257          0.154 -0.6349445 -0.9590348
7    eng2          0.123          0.234 -1.1325034 -0.7620572
8    eng2          0.496          0.932  0.2524926  0.9565726
9    eng2          0.832          0.854  1.5001028  0.7645194

选项 3

如果我重塑数据集而不是跨列工作,我通常会发现这些问题最直接。例如,仍然使用最新版本的 mutate_each 但为了简单起见跳过重命名步骤,您可以 gather 使用 tidyr[= 中的 gather 函数将所有标准化列放在一起77=] 然后 filter 新列。

library(tidyr)

df %>%
    group_by(speaker) %>%
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>%
    gather(group, zval, ends_with("_z")) %>%
    filter(abs(zval) <2 )

# First 12 lines of output

Source: local data frame [20 x 5]
Groups: speaker [2]

   speaker ratio_means001 ratio_means002            group       zval
    <fctr>          <dbl>          <dbl>            <chr>      <dbl>
1     eng1          0.560          0.660 ratio_means001_z -0.3792191
2     eng1          0.202          0.203 ratio_means001_z -0.4699781
3     eng1          0.695          0.943 ratio_means001_z -0.3449943
4     eng1          0.436          0.432 ratio_means001_z -0.4106552
5     eng1          0.342          0.345 ratio_means001_z -0.4344858
6     eng2          0.257          0.154 ratio_means001_z -0.6349445
7     eng2          0.123          0.234 ratio_means001_z -1.1325034
8     eng2          0.432             NA ratio_means001_z  0.0148525
9     eng2          0.496          0.932 ratio_means001_z  0.2524926
10    eng2          0.832          0.854 ratio_means001_z  1.5001028
11    eng1          0.560          0.660 ratio_means002_z  0.5974521
12    eng1          0.202          0.203 ratio_means002_z -1.1490444
...

如果所需的最终形式是宽格式,您可以使用 spread(也来自 tidyr。一个优点(对我来说)是您可以即使另一个变量未通过过滤步骤,也保留一个变量的所有值。

df %>%
    group_by(speaker) %>%
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>%
    gather(group, zval, ends_with("_z")) %>%
    filter(abs(zval) <2 ) %>%
    spread(group, zval)

Source: local data frame [11 x 5]
Groups: speaker [2]

   speaker ratio_means001 ratio_means002 ratio_means001_z ratio_means002_z
    <fctr>          <dbl>          <dbl>            <dbl>            <dbl>
1     eng1          0.202          0.203       -0.4699781       -1.1490444
2     eng1          0.342          0.345       -0.4344858       -0.6063693
3     eng1          0.436          0.432       -0.4106552       -0.2738853
4     eng1          0.560          0.660       -0.3792191        0.5974521
5     eng1          0.695          0.943       -0.3449943        1.6789806
6     eng1         10.100          0.439               NA       -0.2471337
7     eng2          0.123          0.234       -1.1325034       -0.7620572
8     eng2          0.257          0.154       -0.6349445       -0.9590348
9     eng2          0.432             NA        0.0148525               NA
10    eng2          0.496          0.932        0.2524926        0.9565726
11    eng2          0.832          0.854        1.5001028        0.7645194

如果您不想保留 NA,您可以随时 na.omit 稍后再保留它们。