假设我有data.frame df
df<-data.frame(a=1:5,b=101:105,c=201:205)
我可以同时调用这些数据的子集,同时对其中一列(或行)进行某种修改(例如,算术运算)吗?
例如,如果我想返回df
的第一列和第二列,但返回第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
答案 0 :(得分:10)
这是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) })
答案 1 :(得分:6)
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
答案 2 :(得分:4)
或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()
将仅返回对其进行调用时命名的变量。在这里,我们实际上只转换了两个变量中的一个,但我们通过创建它的副本在结果中包含了第二个变量。与transmute()
相比,mutate()
将返回原始数据框的整体以及创建的变量。如果您为新变量指定与现有变量相同的名称,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
答案 3 :(得分:3)
`[`(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和其他方法将基本上掩盖相同的行为。如果你想要完成空间和代码高尔夫,这应该会有所帮助。我喜欢Mr.Flick建议的外观,但这有点快(位)。
答案 4 :(得分:2)
我不相信这些都比两步法更快,只需用更少的击键来做。以下是一些基准测试:
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])})
差异消失了。