使用 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.call
和 merge
来获取最终数据集。
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_each
和 rename_
,然后使用带有 lapply
循环的 interp
用于同时对所有缩放变量进行最终过滤。
在下面的代码中,我利用了 mutate_each
允许命名从 dplyr_0.4.3.9001 开始的单个函数这一事实。 rename_
中的事情看起来有点复杂,因为我正在为新列命名您想要的名称。为了简化事情,您可以让它们从 mutate_each
以 _z
结尾,并省去 rename_
与 gsub
和 grepl
.
的复杂步骤
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(.))]))))
完成后,您只需按多列进行筛选。我认为最简单的方法是使用 interp
和 lapply
列出要过滤的条件,然后将其提供给 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
稍后再保留它们。
我有一个两部分的问题。我搜索了整个堆栈并找到了与我的问题相关的答案,但我尝试过的任何变体都没有奏效。在此先感谢您的帮助!
我有一个包含许多变量的大型数据框。
首先,我想(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.call
和 merge
来获取最终数据集。
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_each
和 rename_
,然后使用带有 lapply
循环的 interp
用于同时对所有缩放变量进行最终过滤。
在下面的代码中,我利用了 mutate_each
允许命名从 dplyr_0.4.3.9001 开始的单个函数这一事实。 rename_
中的事情看起来有点复杂,因为我正在为新列命名您想要的名称。为了简化事情,您可以让它们从 mutate_each
以 _z
结尾,并省去 rename_
与 gsub
和 grepl
.
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(.))]))))
完成后,您只需按多列进行筛选。我认为最简单的方法是使用 interp
和 lapply
列出要过滤的条件,然后将其提供给 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
稍后再保留它们。