同时对数据框的特定列进行子集化和操作
Simultaneously subsetting and operating on a specific column of a data frame
假设我有 data.frame df
df<-data.frame(a=1:5,b=101:105,c=201:205)
我可以在调用此数据的子集的同时对其中一列(或行)执行某种修改(例如算术)吗?
例如,如果我想 return df
的第一列和第二列,但 return 第 1 列值的日志。是否有一些符号可以修改 df[,1:2]
以即时生成以下所有内容?:
a b
>1 0.0000000 101
>2 0.6931472 102
>3 1.0986123 103
>4 1.3862944 104
>5 1.6094379 105
`[`(transform(df, a = log(a)),1:2)
# a b
#1 0.0000000 101
#2 0.6931472 102
#3 1.0986123 103
#4 1.3862944 104
#5 1.6094379 105
您可以在执行功能时调用子集。但它比同时操作更巧妙。但是 dplyr 和其他方法基本上会掩盖相同的行为。如果您要完成的是 space 和代码高尔夫,这应该会有所帮助。我喜欢 Mr.Flick 的建议,但这有点快(位)。
或dplyr
版本:
library(dplyr)
transmute(df, a = log(a), b = b)
a b
1 0.0000000 101
2 0.6931472 102
3 1.0986123 103
4 1.3862944 104
5 1.6094379 105
在 dplyr
中,transmute()
将 return 只有在调用它时命名的变量。在这里,我们实际上只转换了两个变量之一,但我们通过创建它的副本将第二个变量包含在结果中。与 transmute()
相比,mutate()
将 return 整个原始数据框以及创建的变量。如果您为新变量赋予与现有变量相同的名称,mutate()
将覆盖这些变量。
dplyr
版本的一个好处是,它很容易混合转换并给结果起好听的名字,像这样:
> transmute(df, a.log = log(a), b.sqrt = sqrt(b))
a.log b.sqrt
1 0.0000000 10.04988
2 0.6931472 10.09950
3 1.0986123 10.14889
4 1.3862944 10.19804
5 1.6094379 10.24695
这是within()
的一个很好的例子
within(df[1:2], a <- log(a))
# a b
# 1 0.0000000 101
# 2 0.6931472 102
# 3 1.0986123 103
# 4 1.3862944 104
# 5 1.6094379 105
或者如果您不想在通话中使用 <-
,您可以使用括号
within(df[1:2], { a = log(a) })
我不相信这些比两步法更快,只是用更少的击键来完成。以下是一些基准:
library(microbenchmark)
microbenchmark(dplyr = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-transmute(df, a = log(a), b = b)},
transform = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-transform(df, a = log(a))},
within = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-within(df[1:2], a <- log(a))},
twosteps = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-df[,1:2];df[,1]<-log(df[,1])})
Unit: microseconds
expr min lq mean median uq max neval
dplyr 1374.710 1438.453 1657.3807 1534.0680 1658.2910 5231.572 100
transform 489.597 508.413 764.6921 524.9240 569.4680 18127.718 100
within 493.436 518.396 593.6254 534.9085 585.7880 1554.420 100
twosteps 421.245 438.909 501.6850 450.6210 491.5165 2101.231 100
为了演示下面 Gregor 的评论,首先是 5 行,但将对象创建置于基准测试之外:
n = 5
df = data.frame(a = runif(n), b = rnorm(n), c = 1:n)
microbenchmark(dplyr = {df2 <- transmute(df, a = log(a), b = b)},
subset = {df2 <- `[`(transform(df, a = log(a)),1:2)},
within = {df2 <- within(df[1:2], a <- log(a))},
twosteps = {df2 <- df[,1:2]; df2[,1]<-log(df2[,1])})
# twosteps looks much better!
但是如果您将行数增加到足够大,您可能会关心速度差异:
n = 1e6
df = data.frame(a = runif(n), b = rnorm(n), c = 1:n)
microbenchmark(dplyr = {df2 <- transmute(df, a = log(a), b = b)},
subset = {df2 <- `[`(transform(df, a = log(a)),1:2)},
within = {df2 <- within(df[1:2], a <- log(a))},
twosteps = {df2 <- df[,1:2]; df2[,1]<-log(df2[,1])})
差异消失了。
data.table
的方法如下:
library(data.table)
setDT(df)[, .(a=log(a),b)]
大型数据集测试:
library(data.table)
dt1 <- CJ(a = seq(1, 1e3, by=1), b = sample(1e2L), c = sample(1e2L))
df1 <- copy(dt1)
setDF(df1)
基准:
library(rbenchmark)
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"),
dt = dt1[, .(a=log(a),b)],
dplyr = transmute(df1, a = log(a), b = b),
transform = transform(df1, a = log(a), b = b),
within = within(df1, a <- log(a))[,1:2],
twosteps = {df1<-df1[,1:2];df1[,1]<-log(df1[,1])})
test elapsed relative
5 twosteps 0.249 1.000
4 within 0.251 1.008
3 transform 0.251 1.008
2 dplyr 0.300 1.205
1 dt 0.462 1.855
令我惊讶的是,data.table
方法是最慢的方法。而在大多数其他情况下(例如:, two),这是更快的方法。
假设我有 data.frame df
df<-data.frame(a=1:5,b=101:105,c=201:205)
我可以在调用此数据的子集的同时对其中一列(或行)执行某种修改(例如算术)吗?
例如,如果我想 return df
的第一列和第二列,但 return 第 1 列值的日志。是否有一些符号可以修改 df[,1:2]
以即时生成以下所有内容?:
a b
>1 0.0000000 101
>2 0.6931472 102
>3 1.0986123 103
>4 1.3862944 104
>5 1.6094379 105
`[`(transform(df, a = log(a)),1:2)
# a b
#1 0.0000000 101
#2 0.6931472 102
#3 1.0986123 103
#4 1.3862944 104
#5 1.6094379 105
您可以在执行功能时调用子集。但它比同时操作更巧妙。但是 dplyr 和其他方法基本上会掩盖相同的行为。如果您要完成的是 space 和代码高尔夫,这应该会有所帮助。我喜欢 Mr.Flick 的建议,但这有点快(位)。
或dplyr
版本:
library(dplyr)
transmute(df, a = log(a), b = b)
a b
1 0.0000000 101
2 0.6931472 102
3 1.0986123 103
4 1.3862944 104
5 1.6094379 105
在 dplyr
中,transmute()
将 return 只有在调用它时命名的变量。在这里,我们实际上只转换了两个变量之一,但我们通过创建它的副本将第二个变量包含在结果中。与 transmute()
相比,mutate()
将 return 整个原始数据框以及创建的变量。如果您为新变量赋予与现有变量相同的名称,mutate()
将覆盖这些变量。
dplyr
版本的一个好处是,它很容易混合转换并给结果起好听的名字,像这样:
> transmute(df, a.log = log(a), b.sqrt = sqrt(b))
a.log b.sqrt
1 0.0000000 10.04988
2 0.6931472 10.09950
3 1.0986123 10.14889
4 1.3862944 10.19804
5 1.6094379 10.24695
这是within()
within(df[1:2], a <- log(a))
# a b
# 1 0.0000000 101
# 2 0.6931472 102
# 3 1.0986123 103
# 4 1.3862944 104
# 5 1.6094379 105
或者如果您不想在通话中使用 <-
,您可以使用括号
within(df[1:2], { a = log(a) })
我不相信这些比两步法更快,只是用更少的击键来完成。以下是一些基准:
library(microbenchmark)
microbenchmark(dplyr = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-transmute(df, a = log(a), b = b)},
transform = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-transform(df, a = log(a))},
within = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-within(df[1:2], a <- log(a))},
twosteps = {df<-data.frame(a=1:5,b=101:105,c=201:205);df<-df[,1:2];df[,1]<-log(df[,1])})
Unit: microseconds
expr min lq mean median uq max neval
dplyr 1374.710 1438.453 1657.3807 1534.0680 1658.2910 5231.572 100
transform 489.597 508.413 764.6921 524.9240 569.4680 18127.718 100
within 493.436 518.396 593.6254 534.9085 585.7880 1554.420 100
twosteps 421.245 438.909 501.6850 450.6210 491.5165 2101.231 100
为了演示下面 Gregor 的评论,首先是 5 行,但将对象创建置于基准测试之外:
n = 5
df = data.frame(a = runif(n), b = rnorm(n), c = 1:n)
microbenchmark(dplyr = {df2 <- transmute(df, a = log(a), b = b)},
subset = {df2 <- `[`(transform(df, a = log(a)),1:2)},
within = {df2 <- within(df[1:2], a <- log(a))},
twosteps = {df2 <- df[,1:2]; df2[,1]<-log(df2[,1])})
# twosteps looks much better!
但是如果您将行数增加到足够大,您可能会关心速度差异:
n = 1e6
df = data.frame(a = runif(n), b = rnorm(n), c = 1:n)
microbenchmark(dplyr = {df2 <- transmute(df, a = log(a), b = b)},
subset = {df2 <- `[`(transform(df, a = log(a)),1:2)},
within = {df2 <- within(df[1:2], a <- log(a))},
twosteps = {df2 <- df[,1:2]; df2[,1]<-log(df2[,1])})
差异消失了。
data.table
的方法如下:
library(data.table)
setDT(df)[, .(a=log(a),b)]
大型数据集测试:
library(data.table)
dt1 <- CJ(a = seq(1, 1e3, by=1), b = sample(1e2L), c = sample(1e2L))
df1 <- copy(dt1)
setDF(df1)
基准:
library(rbenchmark)
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"),
dt = dt1[, .(a=log(a),b)],
dplyr = transmute(df1, a = log(a), b = b),
transform = transform(df1, a = log(a), b = b),
within = within(df1, a <- log(a))[,1:2],
twosteps = {df1<-df1[,1:2];df1[,1]<-log(df1[,1])})
test elapsed relative
5 twosteps 0.249 1.000
4 within 0.251 1.008
3 transform 0.251 1.008
2 dplyr 0.300 1.205
1 dt 0.462 1.855
令我惊讶的是,data.table
方法是最慢的方法。而在大多数其他情况下(例如: