使用向量中的值和名称对数据框进行子集化的简单有效方法
Simple and efficient way to subset a data frame using values and names in a vector
给定一个数据集(假设存储为数据框),格式为:
> n <- 10
> set.seed(123)
> ds.df <- data.frame(col1 = round(rnorm(n,2,4), digit = 1),
col2 = sample.int(2, n, replace = TRUE),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace = TRUE))
是否有一种简单有效的方法来对其进行子集化,即使用定义子集应遵守的多个等式的值向量?类似于:
> subset_v <- c(col1 = -0.2, col4 = "i")
> ds.subset <- subset(ds.df, subset_v)
> ds.subset
col1 col2 col3 col4
1 -0.2 1 9 i
其中函数 subset(ds.df,subset_v)
应该 return 尊重的子集:
ds.df[ ds.df$col1 == subset_v["col1"] & ds.df$col2 == subset_v["col2"] & ds.df$col4 == subset_v["col4"], ]
但是最后这个表达式不是很方便,我希望能够在不知道它们的情况下拥有任何列。
我做了一些有用的事情:
subset <- function(ds.df,subset_v){
sub = rep(TRUE, nrow(ds.df))
for(cn in names(subset_v)){
sub=sub & (ds.df[,cn] == subset_v[[cn]])
}
ds.df[sub,]
}
但我觉得有更好、更有效的方法(也许以某种方式删除 for loop
)。
就个人而言,我想知道使用命名向量对数据帧进行子集化是否是个好主意,因为它只能用于相等 =
,而 larger than
和 smaller than
不能这样表达。我建议使用带引号的表达式而不是命名向量(参见下面的方法)。
但是,我想出了一个 tidyverse
方法来编写具有上述功能的函数:
library(tidyverse)
set.seed(123)
n <- 10
ds.df <- data.frame(col1 = round(rnorm(n,2,4), digit=1),
col2 = sample.int(2, n, replace=T),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace=T))
new_filter <- function (data, expr) {
exprs_ls <- purrr::imap(expr, ~ rlang::exprs(!! rlang::sym(.y) == !!.x))
filter(data, !!! unname(unlist(exprs_ls)))
}
new_filter(ds.df, c(col1 = -0.2, col4 = "i"))
#> col1 col2 col3 col4
#> 1 -0.2 1 9 i
由 reprex package (v0.3.0) 于 2020-06-17 创建
下面是我的 替代方法 。
在 base R 中,您可以使用 quote
来引用子集表达式(而不是创建向量),然后您可以使用 eval 在 subset
中对其求值。
n <- 10
ds.df=data.frame(col1=round(rnorm(n,2,4),digit=1),
col2=sample.int(2,n,replace=T),
col3=sample.int(n*10,n),
col4=sample(letters,n,replace=T))
subset_v = quote(col1 > 2 & col3 > 40)
subset(ds.df, eval(subset_v))
#> col1 col2 col3 col4
#> 1 6.6 1 93 m
#> 2 7.0 2 62 j
#> 4 3.9 1 94 t
#> 7 4.5 1 46 r
#> 8 2.8 2 98 h
#> 10 4.9 1 78 p
由 reprex package (v0.3.0)
于 2020-06-17 创建
相同的方法,但使用 dplyr filter
library(dplyr)
n <- 10
ds.df = data.frame(col1 = round(rnorm(n,2,4), digit=1),
col2 = sample.int(2, n, replace=T),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace=T))
filter_v = expr(col1 > 2 & col3 > 40)
filter(ds.df, !! filter_v)
#> col1 col2 col3 col4
#> 1 3.3 1 70 a
#> 2 2.5 2 82 q
#> 3 3.6 1 51 z
由 reprex package (v0.3.0)
于 2020-06-17 创建
我想你在找 merge
:
subset <- function(ds.df,subset_v){
filter = data.frame(as.list(subset_v))
merge(ds.df,filter,by=names(filter),all=F)
}
这适用于 data.frame
和 data.table
,并且 data.table
应该与 @sindri_baldur 的答案相同,所以,如果你我们已经在使用 data.table
,主要区别在于您更喜欢输入 merge(x,y,by=z,all=F)
还是 x[y,on=z]
。
在 data.table
中你可以这样做:
setDT(ds.df)
subset_v = list(col1=-3.3, col2=1, col4="e")
ds.df[as.list(subset_v), on = names(subset_v)]
# col1 col2 col3 col4
# 1: -3.3 1 29 e
可重现的数据:
set.seed(20)
n <- 10
ds.df <- data.frame(
col1 = round(rnorm(n, 2, 4), digit = 1),
col2 = sample.int(2, n, replace = TRUE),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace = TRUE)
)
给定一个数据集(假设存储为数据框),格式为:
> n <- 10
> set.seed(123)
> ds.df <- data.frame(col1 = round(rnorm(n,2,4), digit = 1),
col2 = sample.int(2, n, replace = TRUE),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace = TRUE))
是否有一种简单有效的方法来对其进行子集化,即使用定义子集应遵守的多个等式的值向量?类似于:
> subset_v <- c(col1 = -0.2, col4 = "i")
> ds.subset <- subset(ds.df, subset_v)
> ds.subset
col1 col2 col3 col4
1 -0.2 1 9 i
其中函数 subset(ds.df,subset_v)
应该 return 尊重的子集:
ds.df[ ds.df$col1 == subset_v["col1"] & ds.df$col2 == subset_v["col2"] & ds.df$col4 == subset_v["col4"], ]
但是最后这个表达式不是很方便,我希望能够在不知道它们的情况下拥有任何列。
我做了一些有用的事情:
subset <- function(ds.df,subset_v){
sub = rep(TRUE, nrow(ds.df))
for(cn in names(subset_v)){
sub=sub & (ds.df[,cn] == subset_v[[cn]])
}
ds.df[sub,]
}
但我觉得有更好、更有效的方法(也许以某种方式删除 for loop
)。
就个人而言,我想知道使用命名向量对数据帧进行子集化是否是个好主意,因为它只能用于相等 =
,而 larger than
和 smaller than
不能这样表达。我建议使用带引号的表达式而不是命名向量(参见下面的方法)。
但是,我想出了一个 tidyverse
方法来编写具有上述功能的函数:
library(tidyverse)
set.seed(123)
n <- 10
ds.df <- data.frame(col1 = round(rnorm(n,2,4), digit=1),
col2 = sample.int(2, n, replace=T),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace=T))
new_filter <- function (data, expr) {
exprs_ls <- purrr::imap(expr, ~ rlang::exprs(!! rlang::sym(.y) == !!.x))
filter(data, !!! unname(unlist(exprs_ls)))
}
new_filter(ds.df, c(col1 = -0.2, col4 = "i"))
#> col1 col2 col3 col4
#> 1 -0.2 1 9 i
由 reprex package (v0.3.0) 于 2020-06-17 创建
下面是我的 替代方法 。
在 base R 中,您可以使用 quote
来引用子集表达式(而不是创建向量),然后您可以使用 eval 在 subset
中对其求值。
n <- 10
ds.df=data.frame(col1=round(rnorm(n,2,4),digit=1),
col2=sample.int(2,n,replace=T),
col3=sample.int(n*10,n),
col4=sample(letters,n,replace=T))
subset_v = quote(col1 > 2 & col3 > 40)
subset(ds.df, eval(subset_v))
#> col1 col2 col3 col4
#> 1 6.6 1 93 m
#> 2 7.0 2 62 j
#> 4 3.9 1 94 t
#> 7 4.5 1 46 r
#> 8 2.8 2 98 h
#> 10 4.9 1 78 p
由 reprex package (v0.3.0)
于 2020-06-17 创建
相同的方法,但使用 dplyr filter
library(dplyr)
n <- 10
ds.df = data.frame(col1 = round(rnorm(n,2,4), digit=1),
col2 = sample.int(2, n, replace=T),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace=T))
filter_v = expr(col1 > 2 & col3 > 40)
filter(ds.df, !! filter_v)
#> col1 col2 col3 col4
#> 1 3.3 1 70 a
#> 2 2.5 2 82 q
#> 3 3.6 1 51 z
由 reprex package (v0.3.0)
于 2020-06-17 创建我想你在找 merge
:
subset <- function(ds.df,subset_v){
filter = data.frame(as.list(subset_v))
merge(ds.df,filter,by=names(filter),all=F)
}
这适用于 data.frame
和 data.table
,并且 data.table
应该与 @sindri_baldur 的答案相同,所以,如果你我们已经在使用 data.table
,主要区别在于您更喜欢输入 merge(x,y,by=z,all=F)
还是 x[y,on=z]
。
在 data.table
中你可以这样做:
setDT(ds.df)
subset_v = list(col1=-3.3, col2=1, col4="e")
ds.df[as.list(subset_v), on = names(subset_v)]
# col1 col2 col3 col4
# 1: -3.3 1 29 e
可重现的数据:
set.seed(20)
n <- 10
ds.df <- data.frame(
col1 = round(rnorm(n, 2, 4), digit = 1),
col2 = sample.int(2, n, replace = TRUE),
col3 = sample.int(n*10, n),
col4 = sample(letters, n, replace = TRUE)
)