data frame. reset_index什么意思_data.table的使用—高级篇

9bf800bd066d96e4af1ccf1498602651.png

在上一篇文章里面,我们已经讲了data.table的基础,在这篇文章里,主要讲一些高级技巧。

在by关键字后面使用表达式

我们先创造一个data.table对象,基于mtcars数据框

> mtcars_dt=data.table(mtcars)
> head(mtcars_dt)
    mpg cyl disp  hp drat    wt  qsec vs am gear carb
1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

按照初级篇介绍的语法,我们可以把不同的车型按照vs(引擎类型)和am(自动还是手动挡)分组,并计算每一组的平均mpg:

> mtcars_dt[,.(mean_mpg=mean(mpg)),by=.(vs,am)]
   vs am mean_mpg
1:  0  1 19.75000
2:  1  1 28.37143
3:  1  0 20.74286
4:  0  0 15.05000

我们不仅可以对不同的列直接分组,也可以在by后面直接使用表达式(expression),比如下面的代码:

> mtcars_dt[,.(mean_mpg=mean(mpg)),by=.(vs==1,am==1)]
      vs    am mean_mpg
1: FALSE  TRUE 19.75000
2:  TRUE  TRUE 28.37143
3:  TRUE FALSE 20.74286
4: FALSE FALSE 15.05000

setindex和setindexv

初级篇里面我们介绍了setkey和setkeyv的使用。每一次setkey,data.table都会被排序,排序的复杂度是O(nlogn),如果可以避免还是应该避免的。setindex和setindexv则不会对data.table对象进行重新排序。另外,一个data.table只能有一组key,但是可以有不同的indices,这也是key与index的一个主要区别。

我们可以给予index快速地对一个data.table进行筛选,比如我们可以选择gear=4的所有行:

> mtcars_dt[.(4), on='gear']
     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
 1: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
 2: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
 3: 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
 4: 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
 5: 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
 6: 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
 7: 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
 8: 32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
 9: 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
10: 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
11: 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
12: 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

请注意以上语法,on='gear'表明我们要基于gear这一列进行过滤,过滤的条件就是gear=4,4需要放在.()里面。如果gear不是numeric类型,那我们不需要把4放在.()中,比如下面的代码:

> setindex(iris_dt,Species)
> head(iris_dt['virginica',on='Species'])
   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1:          4.9         2.5          4.5         1.7 virginica
2:          5.6         2.8          4.9         2.0 virginica
3:          5.7         2.5          5.0         2.0 virginica
4:          5.8         2.7          5.1         1.9 virginica
5:          5.8         2.7          5.1         1.9 virginica
6:          5.8         2.8          5.1         2.4 virginica

也许读到这你会有一个疑问,为什么需要通过setindex来过滤,为什么不直接用gear==4来过滤呢,因为setindex之后data.table会使用二分查找(复杂度是O(logn)),而不是线性的筛选(复杂度O(n))。如果一个index只被用到一次,那在生成index的时候,会有一个排序的操作,复杂度是O(nlogn),所以并不是很合算,但是index在data.table中是可以重复使用的,也就是一次排序之后可以进行多次二分查找。

我们也可以基于index进行更复杂的操作,比如我们想从mtcars数据中选择gear==4的车型,根据发动机的缸数分组,计算每一组的平均mpg并且对这一结果命名为mean_mpg,并且我们想要结果按照发动机的缸数进行排序。对于这样的一个复杂操作,在data.table里面只要简介的一行代码:

> mtcars_dt[.(4L),.(gear,mean_mpg=mean(mpg)),keyby='cyl',on='gear']
    cyl gear mean_mpg
 1:   4    4   26.925
 2:   4    4   26.925
 3:   4    4   26.925
 4:   4    4   26.925
 5:   4    4   26.925
 6:   4    4   26.925
 7:   4    4   26.925
 8:   4    4   26.925
 9:   6    4   19.750
10:   6    4   19.750
11:   6    4   19.750
12:   6    4   19.750

上面的keyby和by的区别在与keyby不仅会分组,还会根据分组的列进行排序,所以我们看到结果是按照cyl进行排序的。看到这里你有没有觉得data.table优雅又强大?

值得注意的一点是,data.table对象可能会自动创造index;比如当我们对某一列使用==或者 %in%,那么给予这一列的index会被自动创造。

set函数

我们在初级篇里面介绍了:=操作符可以在原始的data.table对象中创造新的列。如果这一列已经存在,按:=可以修改这一列的值。在data.table中还有一个set()函数,也可以用来修改data.table中的元素的值。:=的速度已经很快,但是如果你需要在循环中改变元素的值,也许set()函数的速度更快。下面的例子里,我们先创造一个1000✖️1000的数组,然后创造一个同样形状的data.table对象,然后我们分别用:=和set()函数修改data.table对象的元素。

> a=array(0,c(1000,1000))
> a_dt=data.table(a)
> system.time(for (i in 1:1000) a[i,1L] = i)
   user  system elapsed 
  0.003   0.000   0.003 
> system.time(for (i in 1:1000) a_dt[i,V1:=i])
   user  system elapsed 
  1.448   0.020   0.377 
> system.time(for (i in 1:1000) set(a_dt,i,1L,i))
   user  system elapsed 
  0.003   0.000   0.003 

上面的结果显示,和for loop联合使用时,set()的性能远超:=,几乎和对数组直接操作的速度一致。当然,我的建议是如果可以用array类型,就不要去用data.frame或者data.table,避免任何的额外性能开销。

shift

>stock_dt=data.table(company=c('apple','apple','apple','orange','orange'),price=c(200,220,250,200,240),date=c(as.Date('2011-01-01'),as.Date('2011-01-02'),as.Date('2011-01-03'),as.Date('2011-01-01'),as.Date('2011-01-02')))
> stock_dt
   company price       date
1:   apple   200 2011-01-01
2:   apple   220 2011-01-02
3:   apple   250 2011-01-03
4:  orange   200 2011-01-01
5:  orange   240 2011-01-02

我们想计算每一只股票每日较前一个交易日的价格变化

> stock_dt[,change:=price-shift(price,1),by=company]
> stock_dt
   company price       date change
1:   apple   200 2011-01-01     NA
2:   apple   220 2011-01-02     20
3:   apple   250 2011-01-03     30
4:  orange   200 2011-01-01     NA
5:  orange   240 2011-01-02     40

使用:=同时创造多个新的列

我们可以使用:=操作符生成新的一列,那么如果想同时生成多列呢?直接的办法就是使用:=多次,每一次产生一列。其实我们可以用:=同时完成多列的生成,下面的代码就是一个例子:

> mtcars_dt[, `:=`(avg=mean(mpg), med=median(mpg), min=min(mpg)), by=cyl]
> mtcars_dt[1:5,]
    mpg cyl disp  hp drat    wt  qsec vs am gear carb      avg  med  min
1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4 19.74286 19.7 17.8
2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4 19.74286 19.7 17.8
3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1 26.66364 26.0 21.4
4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1 19.74286 19.7 17.8
5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 15.10000 15.2 10.4

注意上面的:=放在 `` 里面,是把 :=当作函数名来调用。

{}

括号{}在data.table里面很神奇,举个例子,适当的使用{}可以帮我们隐藏一些过渡的中间变量。比如我想在一个data.table里面加入新的两列——x和y,x=z^2+1, y=z^2-1,我可以用下面的办法实现:

> z_dt=data.table(z=1:5)
> z_dt
   z
1: 1
2: 2
3: 3
4: 4
5: 5
 
> z_dt[, .(x=z^1+1,y=z^2-1)]
   x  y
1: 2  0
2: 3  3
3: 4  8
4: 5 15
5: 6 24

这段代码里,我们计算了z^2两次,如果是更复杂的操作,更大的数据,这样做的效率比较低。更有效的办法是创造一个中间过渡的变量,temp=z^2,然后 x=temp+1, y=temp-1。最后我们又不想temp出现在结果中,怎么实现呢?

> z_dt[, {temp=z^2; .(x=temp+1,y=temp-1)}]
    x  y
1:  2  0
2:  5  3
3: 10  8
4: 17 15
5: 26 24

data.table还有一些其他的高级技巧,限于篇幅就不在这里介绍了,如果大家喜欢我们的文章并且希望看到更多可以留言,我们可以再写一篇关于data.table的使用文章。

在此也希望大家点赞或者转发支持,你们的支持就是我们创作的最大动力!

也欢迎关注我们的微信公众号 - 机器会学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值