对数据分组汇总

data.table 中另一个非常重要的参数是 by,它用于将数据分割成多个部分(即按
照 by 的值进行分组),并且对每个部分(组)计算第 2 个参数。在本节中,我们将会演示
如何通过 by 以更简便的方式实现数据的分组汇总。例如,by 的最简单用法就是计算每组
的记录条数。在下面的代码中,我们将分别统计已发布和未发布的产品数量:
product_info[, .N, by = released]
## released N
## 1: TRUE 4
## 2: FALSE 2
分组变量可以不止一个。例如,由 type 和 class 确定一个组,然后计算每组的记录条数:
product_info[, .N, by = .(type, class)]
## type class N
## 1: model vehicle 2
## 2: model people 2
## 3: toy vehicle 2
我们也可以对每个组进行统计计算。这里,分别计算防水产品和非防水产品两组的质
量得分均值:
product_tests[, mean(quality, na.rm = TRUE),
by = .(waterproof)]
## waterproof V1
## 1: no 10.00
## 2: yes 5.75
注意到,均值存储在 V1 列中,因为没有提供列名,所以使用了默认的列名 V1。为了
避免这种情况的出现,需要改用 .(y = f(x)) 形式的表达式:
product_tests[, .(mean_quality = mean(quality, na.rm = TRUE)),
by = .(waterproof)]
## waterproof mean_quality
## 1: no 10.00
## 2: yes 5.75
我们可以将多个 [ ] 按顺序连接起来。在接下来的例子中,首先使用通用的键 id,
将 product_info 和 product_tests 连接起来,然后筛选出已发布的产品,再按
照 type 和 class 进行分组,最后计算每组的 quality 和 durability 的均值:
type_class_tests0 <- product_info[product_tests][released == TRUE,
.(mean_quality = mean(quality, na.rm = TRUE),
mean_durability = mean(durability, na.rm = TRUE)),
by = .(type, class)]
## type class mean_quality mean_durability
## 1: toy vehicle NaN 10.0
## 2: model vehicle 6 4.5
## 3: model people 5 NaN
值得注意的是,在返回的 data.table 中,by 所对应的组合中的值是唯一的。虽然
成功实现了目标,但是 type_class_tests0 中并没有设置键:
key(type_class_tests0)
## NULL
此时,我们可以使用 keyby(而不是 by)来确保在结果的 data.table 中自动地将
keyby 对应的分组向量设置为键。一般情况下,data.table 的默认返回结果中,会保持
原来的顺序。但有时,我们想让返回结果根据分组变量自动排序。keyby 也可以实现自动
排序,即按照分组变量自动排序(默认升序),可谓一举两得:
type_class_tests <- product_info[product_tests][released == TRUE,
.(mean_quality = mean(quality, na.rm = TRUE),
mean_durability = mean(durability, na.rm = TRUE)),
keyby = .(type, class)]
type_class_tests
## type class mean_quality mean_durability
## 1: model people 5 NaN
## 2: model vehicle 6 4.5
## 3: toy vehicle NaN 10.0
key(type_class_tests)
## [1] "type" "class"
这样,我们便可以直接用一组键—值来获取记录:
type_class_tests[.("model", "vehicle"), mean_quality]
## [1] 6
可以清楚地看到,要找到表中的特定记录,使用键比使用逻辑比较更加方便。而且,
因为数据集还不够大,键的真正优势还未展现出来。对大数据集使用键进行搜索记录,能
够比迭代使用逻辑比较快得多。因为键搜索利用了二进制搜索,而迭代则在不必要的计算
上浪费了很多时间。
这里我们举个例子进行对比。首先,我们创建一个有 1000 万行的数据,其中一列是索
引列 id,其他两列是随机数:
n <- 10000000
test1 <- data.frame(id = 1:n, x = rnorm(n), y = rnorm(n))
现在我们想要找到 id 为 876543 的行,看一下这要花多久:
system.time(row <- test1[test1$id == 876543, ])
## user system elapsed
## 0.156 0.036 0.192
row
## id x y
## 876543 876543 0.02300419 1.291588
看起来,好像不是很久。但是,如果需要频繁地这样做,例如每秒几百次,那么计算
机根本无法及时返回结果。
作为对比,我们使用 data.table 来完成这个任务。先调用 setDT( ) 将数据框转
换为 data.table。这个函数可以将对象原地转换,不需要复制。使用 setDT( ) 函数时,
我们还将 id 作为一个键,因此最终的结果 data.table 以 id 列作为键:
setDT(test1, key = "id")
class(test1)
## [1] "data.table" "data.frame"
现在,test1 已经被转换成 data.table 了。接着,我们搜索相同的元素:
system.time(row <- test1[.(8765432)])
## user system elapsed
## 0.000 0.000 0.001
row
## id x y
## 1: 8765432 0.2532357 -2.121696
结果相同,但 data.table 用的时间比 data.frame 少很多。

posted @ 2019-02-11 14:05  NAVYSUMMER  阅读(190)  评论(0编辑  收藏  举报
交流群 编程书籍