同时对数据帧的特定列进行子集化和操作

时间:2015-08-07 18:53:46

标签: r subset

假设我有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

5 个答案:

答案 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

令我惊讶的是,data.table方法是最慢的方法。在大多数其他情况下(例如:onetwo),这是更快的方法。

答案 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])})

差异消失了。