data.table cheatsheet.pdf

data.table 是 data.frame 的扩展,兼容 dplyr 包中的函数,被其操作后,返回值仍为 data.table。故今后凡是操作数据框都可以默认先转换为 data.table. 需要保留原始数据时,使用 dplyr 系列函数;不需要保留时,使用 data.table 的象牙操作符,追求速度和简洁。

data.table 的高性能来源于内存管理(引用语法)、并行化和大量精细优化。

创建

直接创建

data.table()

dt <- data.table(
  a = c("A", "B", "C", "A", "A", "B"),
  b = 1:6
)
class(dt)
#> [1] "data.table" "data.frame"

将 data.frame 转化为 data.table

as.data.table()setDT(),前者创建一份拷贝并转换,后者原地转换。

df <- data.frame(
  a = c("A", "B", "C", "A", "A", "B"),
  b = 1:6
)
df %>%
  setDT() %>%
  class()
#> [1] "data.table" "data.frame"
library(sqldf)
options(sqldf.driver = "SQLite")
channel <- dbConnect(
  SQLite(),
  dbname = "C:/Users/humoo/OneDrive/ICT/DataBase/SQLite/trade.db"
)
total <- dbReadTable(channel, "total") %>%
  select(-time) %>%
  setDT()
class(total)
#> [1] "data.table" "data.frame"
dbDisconnect(channel)

set*()函数族不复制数据,节省时间

setnames(total, "trade", "total") # 将列名trade改为total
head(total)
#>    year month export import  total surplus
#> 1: 2018     1 1994.9 1810.7 3805.6   184.2
#> 2: 2018     2 1706.4 1383.4 3089.8   323.0
#> 3: 2018     3 1739.1 1796.6 3535.7   -57.5
#> 4: 2018     4 1989.5 1726.7 3716.2   262.8
#> 5: 2018     5 2116.1 1881.8 3997.9   234.3
#> 6: 2018     6 2156.2 1747.0 3903.2   409.2
total[, id := .I] # .I意为1:.N,这句为total加了一个序号索引列

setcolorder(total, neworder = "id") 
# setcolorder() 列的重排序
# 默认第二个参数是一个列名向量。如果只写出了一部分列,则其他列按照原来的顺序
head(total)
#>    id year month export import  total surplus
#> 1:  1 2018     1 1994.9 1810.7 3805.6   184.2
#> 2:  2 2018     2 1706.4 1383.4 3089.8   323.0
#> 3:  3 2018     3 1739.1 1796.6 3535.7   -57.5
#> 4:  4 2018     4 1989.5 1726.7 3716.2   262.8
#> 5:  5 2018     5 2116.1 1881.8 3997.9   234.3
#> 6:  6 2018     6 2156.2 1747.0 3903.2   409.2
set*()函数族
setDT(), setDF()
setattr() setnames() Set attributes of objects by reference
setkey() setkeyv() setindex() setindexv() key() indices() haskey() Create key on a data.table
setcolorder() Fast column reordering of a data.table by reference
setorder(dt, a, -b) setorderv() Fast row reordering of a data.table by reference

读取文件创建 data.table

  • fread()可以将能直接将.csv .txt 等格式的文件读为 data.table

  • fwrite()可以写 data.table 到文件

在处理大数据时,它们的速度非常快。建议读写文件统一使用这两个函数

data.table 查询语法

语法

dt[i, j, by = list(), ...]

很像 SQL 语句

  1. i: subset 行(就像 WHERE 子句)
  2. by/keyby1 : 分组(就像 GROUP BY 子句)
  3. j: subset2 或 manipulate 列 (就像 SELECT 子句),甚至可以是一个匿名函数

特殊符号

特殊符号 说明
.() 代替list()
.N 总行数
.SD 代表分组后每个组的数据框
.SDcols 在 by 处,与.SD连用,用来选择哪些列包含在.SD中
.BY 包含所有by分组变量的list
.I 整数向量 1:.N
.GRP 分组索引
.NGRP 分组数
.EACHI 用于by/keyby = .EACHI,表示根据i表达式的每一行分组

链式操作

DT[...][...][...]

或写为:

DT[
  ...
][
  ...
][
  ...
]

与管道%>%配合的链式操作

由于 R 中所有的操作符都是函数,因此可以将`[`作为一个函数,贯穿于%>%中,获得代码风格的一致性。

mtcars %>% 
    as.data.table() %>% 
    `[`(, .(mpg = mean(mpg)), by = cyl) %>% 
    `[`(.N,) 
#>    cyl  mpg
#> 1:   8 15.1

Subset rows using i

选择行可以接受的运算符除了常用的逻辑运算符,还包括 %like% (匹配模式,有点像正则表达式) 和 %between%

data.table[] 方括号中只有一个参数时,默认为 i 参数,表示对行操作。

dt3 <- data.table(
  a = 1:6,
  b = c("A", "B", "C", "A", "A", "B")
)

dt3[1:2, ]
#>    a b
#> 1: 1 A
#> 2: 2 B
# 选出最后一行
dt3[.N]
#>    a b
#> 1: 6 B
# 选出第一行和最后一行
dt3[c(1, .N)]
#>    a b
#> 1: 1 A
#> 2: 6 B
# 选出满足特定条件的行
dt3[b == "A", ]
#>    a b
#> 1: 1 A
#> 2: 4 A
#> 3: 5 A
# 行排序,负号表示降序
total[order(-month)] %>% head()
#>    year month export import  trade surplus
#> 1: 2018    12 2212.5 1641.9 3854.4   570.6
#> 2: 2018    11 2243.3 1824.7 4068.0   418.6
#> 3: 2019    11 2217.4 1830.1 4047.5   387.3
#> 4: 2018    10 2148.0 1816.7 3964.7   331.3
#> 5: 2019    10 2129.3 1701.2 3830.5   428.1
#> 6: 2018     9 2254.3 1949.9 4204.2   304.4
total %>% head()
#>    year month export import  trade surplus
#> 1: 2018     1 1994.9 1810.7 3805.6   184.2
#> 2: 2018     2 1706.4 1383.4 3089.8   323.0
#> 3: 2018     3 1739.1 1796.6 3535.7   -57.5
#> 4: 2018     4 1989.5 1726.7 3716.2   262.8
#> 5: 2018     5 2116.1 1881.8 3997.9   234.3
#> 6: 2018     6 2156.2 1747.0 3903.2   409.2

Manipulate columns with j

subset

dt[, c(2)] # 提取第2列
#>    b
#> 1: 1
#> 2: 2
#> 3: 3
#> 4: 4
#> 5: 5
#> 6: 6

象牙操作符 :=3

这个操作符有点像 SQL 语句中的 AS 关键词,但左为列名,右为表达式。

  • 给data.table对象增加一列,data.table[, colname := var1]
  • 给data.table对象删除一列,就是给这列赋值为空,data.table[, colname := NULL]
  • 修改data.table对象的值,通过索引定位后进行值的替换,data.table[condition, colname := value]

单个列名不用加引号,多个列名组成的向量,必须加引号,如c('d1', 'd2')

dt <- data.table(
  a = c("A", "B", "C", "A", "A", "B"),
  b = 1:6
)

# 1列,不必加引号
dt[, c := b + 2] # (b + 2) AS c
dt
#>    a b c
#> 1: A 1 3
#> 2: B 2 4
#> 3: C 3 5
#> 4: A 4 6
#> 5: A 5 7
#> 6: B 6 8
# 增加2列,第1种写法
dt[, c('d1', 'd2') := list(1:6, 2:7)] # := 左边是一个name向量,右边是一个value列表
dt
#>    a b c d1 d2
#> 1: A 1 3  1  2
#> 2: B 2 4  2  3
#> 3: C 3 5  3  4
#> 4: A 4 6  4  5
#> 5: A 5 7  5  6
#> 6: B 6 8  6  7
# 增加2列,第2种写法
dt[,`:=`(c1 = 1:6, c2 = 2:7)] # := 很像一个update函数
dt
#>    a b c d1 d2 c1 c2
#> 1: A 1 3  1  2  1  2
#> 2: B 2 4  2  3  2  3
#> 3: C 3 5  3  4  3  4
#> 4: A 4 6  4  5  4  5
#> 5: A 5 7  5  6  5  6
#> 6: B 6 8  6  7  6  7
# 删除c1列
dt[, c1 := NULL]
dt
#>    a b c d1 d2 c2
#> 1: A 1 3  1  2  2
#> 2: B 2 4  2  3  3
#> 3: C 3 5  3  4  4
#> 4: A 4 6  4  5  5
#> 5: A 5 7  5  6  6
#> 6: B 6 8  6  7  7
# 同时删除d1,d2列
dt[, c("d1", "d2") := NULL]
dt
#>    a b c c2
#> 1: A 1 3  2
#> 2: B 2 4  3
#> 3: C 3 5  4
#> 4: A 4 6  5
#> 5: A 5 7  6
#> 6: B 6 8  7
# 给b赋值为30
dt[, b := 30]
dt
#>    a  b c c2
#> 1: A 30 3  2
#> 2: B 30 4  3
#> 3: C 30 5  4
#> 4: A 30 6  5
#> 5: A 30 7  6
#> 6: B 30 8  7
# 对a列值为B、c2列值大于3的行,其b列赋值为100
dt[a == "B" & c2 > 3, b := 100]
dt
#>    a   b c c2
#> 1: A  30 3  2
#> 2: B  30 4  3
#> 3: C  30 5  4
#> 4: A  30 6  5
#> 5: A  30 7  6
#> 6: B 100 8  7
# 另一种写法
dt[, b := ifelse(a == "B" & c2 > 3, 50, b)]
dt
#>    a  b c c2
#> 1: A 30 3  2
#> 2: B 30 4  3
#> 3: C 30 5  4
#> 4: A 30 6  5
#> 5: A 30 7  6
#> 6: B 50 8  7
# 更新列
dt[, b := as.character(b)]
dt
#>    a  b c c2
#> 1: A 30 3  2
#> 2: B 30 4  3
#> 3: C 30 5  4
#> 4: A 30 6  5
#> 5: A 30 7  6
#> 6: B 50 8  7

=而非象牙操作符

不是在原来的数据框基础上改动,而是新创建一个数据框。

此时,j 中的列不能写成向量,而要写成列表,用.(列名...)list(列名...)

total[, .(year, month, export, import, ratio = export / import)]
#>     year month export import     ratio
#>  1: 2018     1 1994.9 1810.7 1.1017286
#>  2: 2018     2 1706.4 1383.4 1.2334827
#>  3: 2018     3 1739.1 1796.6 0.9679951
#>  4: 2018     4 1989.5 1726.7 1.1521978
#>  5: 2018     5 2116.1 1881.8 1.1245084
#>  6: 2018     6 2156.2 1747.0 1.2342301
#>  7: 2018     7 2144.2 1869.8 1.1467537
#>  8: 2018     8 2169.6 1907.1 1.1376435
#>  9: 2018     9 2254.3 1949.9 1.1561106
#> 10: 2018    10 2148.0 1816.7 1.1823636
#> 11: 2018    11 2243.3 1824.7 1.2294076
#> 12: 2018    12 2212.5 1641.9 1.3475242
#> 13: 2019     1 2179.7 1788.0 1.2190716
#> 14: 2019     2 1351.2 1317.0 1.0259681
#> 15: 2019     3 1979.0 1657.3 1.1941109
#> 16: 2019     4 1934.8 1797.8 1.0762042
#> 17: 2019     5 2138.5 1721.2 1.2424471
#> 18: 2019     6 2128.4 1618.6 1.3149635
#> 19: 2019     7 2215.3 1764.8 1.2552697
#> 20: 2019     8 2148.0 1799.7 1.1935323
#> 21: 2019     9 2181.2 1784.7 1.2221662
#> 22: 2019    10 2129.3 1701.2 1.2516459
#> 23: 2019    11 2217.4 1830.1 1.2116278
#>     year month export import     ratio
total # 原始数据不变
#>     year month export import  trade surplus
#>  1: 2018     1 1994.9 1810.7 3805.6   184.2
#>  2: 2018     2 1706.4 1383.4 3089.8   323.0
#>  3: 2018     3 1739.1 1796.6 3535.7   -57.5
#>  4: 2018     4 1989.5 1726.7 3716.2   262.8
#>  5: 2018     5 2116.1 1881.8 3997.9   234.3
#>  6: 2018     6 2156.2 1747.0 3903.2   409.2
#>  7: 2018     7 2144.2 1869.8 4014.0   274.4
#>  8: 2018     8 2169.6 1907.1 4076.7   262.5
#>  9: 2018     9 2254.3 1949.9 4204.2   304.4
#> 10: 2018    10 2148.0 1816.7 3964.7   331.3
#> 11: 2018    11 2243.3 1824.7 4068.0   418.6
#> 12: 2018    12 2212.5 1641.9 3854.4   570.6
#> 13: 2019     1 2179.7 1788.0 3967.7   391.7
#> 14: 2019     2 1351.2 1317.0 2668.2    34.2
#> 15: 2019     3 1979.0 1657.3 3636.3   321.7
#> 16: 2019     4 1934.8 1797.8 3732.6   137.0
#> 17: 2019     5 2138.5 1721.2 3859.7   417.3
#> 18: 2019     6 2128.4 1618.6 3747.0   509.8
#> 19: 2019     7 2215.3 1764.8 3980.1   450.5
#> 20: 2019     8 2148.0 1799.7 3947.7   348.3
#> 21: 2019     9 2181.2 1784.7 3965.9   396.5
#> 22: 2019    10 2129.3 1701.2 3830.5   428.1
#> 23: 2019    11 2217.4 1830.1 4047.5   387.3
#>     year month export import  trade surplus

匿名函数

data("diamonds", package = "ggplot2")
setDT(diamonds)

# 按 cut 分组做回归
diamonds[,
  {
    m <- lm(log(price) ~ carat + depth)
    as.list(coef(m))
  },
  keyby = cut
]
#>          cut (Intercept)    carat        depth
#> 1:      Fair    7.730010 1.264588 -0.014982439
#> 2:      Good    7.077469 1.973600 -0.014601101
#> 3: Very Good    6.293642 2.087957 -0.002890208
#> 4:   Premium    5.934310 1.852778  0.005939651
#> 5:     Ideal    8.495409 2.125605 -0.038080022

Group according to by

by 分组

keyby 不仅分组且立刻定义键,并按照键对行排序

dt <- data.table(
  a = c("A", "B", "C", "A", "A", "B"),
  b = 1:6
)

# 对整个b列数据求和
dt[, sum(b)]
#> [1] 21
# 按a列分组,对b列按分组求和,运算结果自动命名为 V1
dt[, sum(b), by = a]
#>    a V1
#> 1: A 10
#> 2: B  8
#> 3: C  3
# 提取每一组的第一行,组成新数据框
dt[, .SD[1], by = a]
#>    a b
#> 1: A 1
#> 2: B 2
#> 3: C 3
# 提取每一组的最后一行,组成新数据框
dt[, .SD[.N], by = a]
#>    a b
#> 1: A 5
#> 2: B 6
#> 3: C 3
# 先分组,再显示每组行数
total[, .N, by = year] 
#>    year  N
#> 1: 2018 12
#> 2: 2019 11
total[, .N, by = .(year, month)] # year 和 month 是有先后顺序的
#>     year month N
#>  1: 2018     1 1
#>  2: 2018     2 1
#>  3: 2018     3 1
#>  4: 2018     4 1
#>  5: 2018     5 1
#>  6: 2018     6 1
#>  7: 2018     7 1
#>  8: 2018     8 1
#>  9: 2018     9 1
#> 10: 2018    10 1
#> 11: 2018    11 1
#> 12: 2018    12 1
#> 13: 2019     1 1
#> 14: 2019     2 1
#> 15: 2019     3 1
#> 16: 2019     4 1
#> 17: 2019     5 1
#> 18: 2019     6 1
#> 19: 2019     7 1
#> 20: 2019     8 1
#> 21: 2019     9 1
#> 22: 2019    10 1
#> 23: 2019    11 1
#>     year month N
total[, .(avg_ex = mean(export)), by = year] # 即使只有一列,也要用列表
#>    year   avg_ex
#> 1: 2018 2072.842
#> 2: 2019 2054.800

独立辅助函数

行排序

setorder(dt, a, -b) 重新排列行,先a按升序,再b按降序

行去重

unique(dt, by = c("a","b")) 检查by的列,提取不重复的行

uniqueN(dt, by = c("a","b")) 检查by的列,计算每个不重复的行出现了多少次

列重命名

setnames(dt, c("a","b"), c("x","y") 列重命名

提前和滞后 lead and lag

dt[, c := shift(a, 1)] # 默认 lag
dt
#>    a b    c
#> 1: A 1 <NA>
#> 2: B 2    A
#> 3: C 3    B
#> 4: A 4    C
#> 5: A 5    A
#> 6: B 6    A
dt[, c := shift(a, 1, type = "lead")]
dt
#>    a b    c
#> 1: A 1    B
#> 2: B 2    C
#> 3: C 3    A
#> 4: A 4    A
#> 5: A 5    B
#> 6: B 6 <NA>

这样可以很容易地加入提前或滞后列,进行同比增长率等计算

遍历操作各列

dt <- data.table(a = c(1, 2, 3), b = c(4, 5, 6))
# 遍历a和b两列,都求平均值
dt[, map(.SD, mean), .SDcols = c("a", "b")]
#>    a b
#> 1: 2 5
cols <- c("a", "b")
# 遍历 cols 各列,求平均值,且赋给新加入的a_m列
dt[, str_c(cols, "_m") := map(.SD, mean), .SDcols = cols]
dt
#>    a b a_m b_m
#> 1: 1 4   2   5
#> 2: 2 5   2   5
#> 3: 3 6   2   5

连接操作

setkey(d1, v1, v3, ...)

将某些列定义为 keys后,就可以通过键选择行,以及进行关系数据连接,比无键时快170倍。同时,可以用 mult 参数对查询结果增加过滤条件,使代码更高效。

dt <- data.table(a = c("A", "B", "C", "A", "A", "B"),
                 b = 1:6)

# 设置a列为主键
setkey(dt, a)
# setkeyv(dt, 'a') 函数仅接受字符串作为第二个参数
key(dt)
#> [1] "a"
# 打印dt对象,发现数据已经按照a列字母对应ASCII码值进行了排序。
dt
#>    a b
#> 1: A 1
#> 2: A 4
#> 3: A 5
#> 4: B 2
#> 5: B 6
#> 6: C 3
# 取a列中值为B的行,设置主键后可省略`a==`
dt["B"]
#>    a b
#> 1: B 2
#> 2: B 6
# 取a列中值为B的行,并保留查询结果的第一行
dt["B", mult = "first"]
#>    a b
#> 1: B 2
# 取a列中值为B的行,并保留查询结果的最后一行
dt["B", mult = "last"]
#>    a b
#> 1: B 6
# 取a列中值为b的行,没有数据则为NA
dt["b"]
#>    a  b
#> 1: b NA
setkey(total, year, month)
total[.(2019, 3)] # 省略了 year == 2019 & month == 3
#>    id year month export import  total surplus
#> 1: 15 2019     3   1979 1657.3 3636.3   321.7
total[.(2019)] # 省略了 year == 2019,可以只根据一列 subset
#>     id year month export import  total surplus
#>  1: 13 2019     1 2179.7 1788.0 3967.7   391.7
#>  2: 14 2019     2 1351.2 1317.0 2668.2    34.2
#>  3: 15 2019     3 1979.0 1657.3 3636.3   321.7
#>  4: 16 2019     4 1934.8 1797.8 3732.6   137.0
#>  5: 17 2019     5 2138.5 1721.2 3859.7   417.3
#>  6: 18 2019     6 2128.4 1618.6 3747.0   509.8
#>  7: 19 2019     7 2215.3 1764.8 3980.1   450.5
#>  8: 20 2019     8 2148.0 1799.7 3947.7   348.3
#>  9: 21 2019     9 2181.2 1784.7 3965.9   396.5
#> 10: 22 2019    10 2129.3 1701.2 3830.5   428.1
#> 11: 23 2019    11 2217.4 1830.1 4047.5   387.3
# 不能写total[list(3)],得不出有意义的结果

Join

基本语法为dt1[dt2, on = .(b = y)],其中b为dt1的列,y为dt2的列

  • 若连个表都定义了 key, 第二个参数不用填

  • 若没有定义key,…要写 on = “commonColumn” 或 by.dt1 = “c1”, by.dt2 = “c2”

例:学生考试的场景。按照ER设计方法,我们通常会按照实体进行数据划分。这里存在2个实体,一个是学生,一个是成绩。学生实体包括学生姓名等基本资料,而成绩实体包括考试的科目和成绩。通过设置两个主键,对2个数据集进行连接。

# 6个学生
student <- data.table(
  id = 1:6,
  name = c("Dan", "Mike", "Ann", "Yang", "Li", "Kate")
)
student
#>    id name
#> 1:  1  Dan
#> 2:  2 Mike
#> 3:  3  Ann
#> 4:  4 Yang
#> 5:  5   Li
#> 6:  6 Kate
# 分别参加A和B两门考试
score <- data.table(
  id = 1:12, stuId = rep(1:6, 2),
  score = runif(12, 60, 99),
  course = c(rep("A", 6), rep("B", 6))
)
score
#>     id stuId    score course
#>  1:  1     1 95.12819      A
#>  2:  2     2 80.36217      A
#>  3:  3     3 60.48816      A
#>  4:  4     4 88.57715      A
#>  5:  5     5 79.29332      A
#>  6:  6     6 89.29934      A
#>  7:  7     1 92.13274      B
#>  8:  8     2 97.45311      B
#>  9:  9     3 72.97279      B
#> 10: 10     4 84.86577      B
#> 11: 11     5 82.29908      B
#> 12: 12     6 61.60280      B
# 设置student数据集的key
setkey(student, "id")

# 设置score数据集的key
setkey(score, "stuId")

# 连接
student[score, nomatch = NA, mult = "all"]
#>     id name i.id    score course
#>  1:  1  Dan    1 95.12819      A
#>  2:  1  Dan    7 92.13274      B
#>  3:  2 Mike    2 80.36217      A
#>  4:  2 Mike    8 97.45311      B
#>  5:  3  Ann    3 60.48816      A
#>  6:  3  Ann    9 72.97279      B
#>  7:  4 Yang    4 88.57715      A
#>  8:  4 Yang   10 84.86577      B
#>  9:  5   Li    5 79.29332      A
#> 10:  5   Li   11 82.29908      B
#> 11:  6 Kate    6 89.29934      A
#> 12:  6 Kate   12 61.60280      B
rm(list = ls())

集合运算

  fintersect(dt1, dt2)
  fsetdiff(dt1, dt2)
  funion(dt1, dt2)
  fsetequal(dt1, dt2)

高级应用实例

## 月K线图数据的整理

# 1. 产生日期序列
market_data <- data.table(date = (as.Date("2015-05-01") + 0:299))
set.seed(125)

# 2. 产生成交价格和数量序列
# 股市价格是一个随机游走过程,将300天的变化率累乘起来
# 成交量 volume 取随机数
market_data[, `:=`(
  price = ((1 + rnorm(300, 0.001, 0.05)) %>% cumprod() * 30) %>% round(2),
  volume = rbinom(300, 5000, 0.8)
)]
head(market_data)
#>          date price volume
#> 1: 2015-05-01 31.43   4000
#> 2: 2015-05-02 30.64   4022
#> 3: 2015-05-03 33.45   4006
#> 4: 2015-05-04 33.62   3976
#> 5: 2015-05-05 34.32   4044
#> 6: 2015-05-06 30.59   4010
# 3. 股价走势图
plot(price ~ date,
  data = market_data,
  type = "l",
  main = "Market data"
)

# 4. 获取横轴 domain
market_data[, range(date)] # 没有:=,所以这不是update,而是summarize
#> [1] "2015-05-01" "2016-02-24"
# 5. 按月分组,每组统计最大、最小、最早、最晚价格,这就是月K线图!!!
monthly <- market_data[,
  # 这样写 list 就很像键值对了
  .(
    open = price[[1]],
    high = max(price),
    low = min(price),
    close = price[[.N]]
  ),
  keyby = .(
    year = year(date),
    month = month(date)
  )
]
head(monthly)
#>    year month  open  high   low close
#> 1: 2015     5 31.43 39.36 30.03 38.13
#> 2: 2015     6 42.74 50.54 39.08 39.47
#> 3: 2015     7 37.21 46.96 31.70 43.42
#> 4: 2015     8 46.98 57.06 42.24 42.24
#> 5: 2015     9 45.79 47.19 34.02 37.09
#> 6: 2015    10 34.75 58.65 34.75 56.38
# 6. 自定义函数,针对不同的列可以分组计算平均值
average <- function(column) {
  market_data[,
    .(
      average = mean(.SD[[column]])
    ),
    by = .(year = year(date))
  ]
}
average("price")
#>    year  average
#> 1: 2015 42.13637
#> 2: 2016 24.56982
average("volume")
#>    year  average
#> 1: 2015 4003.596
#> 2: 2016 3996.564

  1. 能自动将分组变量设为key.↩︎

  2. 要用list()或.()↩︎

  3. 象牙操作符不拷贝被修改的列。原地修改的性能更高,避免了对数据的复制。↩︎

LS0tDQp0aXRsZTogImRhdGEudGFibGUiDQpzdWJ0aXRsZTogJycNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywgDQogIGV2YWwgPSBjb25maWckZXZhbCwgDQogIHRpZHkgPSBjb25maWckdGlkeSwgDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwgDQogIGNvbGxhcHNlID0gY29uZmlnJGNvbGxhcHNlLCANCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCg0KIyMgdXNlIG5lY2Vzc2FyeSBwYWNrYWdlcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCmBgYA0KDQoNCjxhIGhyZWY9Ii4uL3BkZi9jaGVhdHNoZWV0LWRhdGEudGFibGUucGRmIj4qZGF0YS50YWJsZSBjaGVhdHNoZWV0LnBkZio8L2E+DQoNCjxvYmplY3QgZGF0YT0iLi4vcGRmL2NoZWF0c2hlZXQtZGF0YS50YWJsZS5wZGYiIHR5cGU9ImFwcGxpY2F0aW9uL3BkZiIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSI+PC9vYmplY3Q+DQoNCmRhdGEudGFibGUg5pivIGRhdGEuZnJhbWUg55qE5omp5bGV77yM5YW85a65IGRwbHlyIOWMheS4reeahOWHveaVsO+8jOiiq+WFtuaTjeS9nOWQju+8jOi/lOWbnuWAvOS7jeS4uiBkYXRhLnRhYmxl44CC5pWF5LuK5ZCO5Yeh5piv5pON5L2c5pWw5o2u5qGG6YO95Y+v5Lul6buY6K6k5YWI6L2s5o2i5Li6IGRhdGEudGFibGUuIOmcgOimgeS/neeVmeWOn+Wni+aVsOaNruaXtu+8jOS9v+eUqCBkcGx5ciDns7vliJflh73mlbDvvJvkuI3pnIDopoHkv53nlZnml7bvvIzkvb/nlKggZGF0YS50YWJsZSDnmoTosaHniZnmk43kvZznrKbvvIzov73msYLpgJ/luqblkoznroDmtIHjgIINCg0KZGF0YS50YWJsZSDnmoTpq5jmgKfog73mnaXmupDkuo7lhoXlrZjnrqHnkIbvvIjlvJXnlKjor63ms5XvvInjgIHlubbooYzljJblkozlpKfph4/nsr7nu4bkvJjljJbjgIINCg0KIyMg5Yib5bu6DQoNCiMjIyDnm7TmjqXliJvlu7oNCg0KYGRhdGEudGFibGUoKWANCg0KYGBge3J9DQpkdCA8LSBkYXRhLnRhYmxlKA0KICBhID0gYygiQSIsICJCIiwgIkMiLCAiQSIsICJBIiwgIkIiKSwNCiAgYiA9IDE6Ng0KKQ0KY2xhc3MoZHQpDQpgYGANCg0KIyMjIOWwhiBkYXRhLmZyYW1lIOi9rOWMluS4uiBkYXRhLnRhYmxlDQoNCmBhcy5kYXRhLnRhYmxlKClg5ZKMYHNldERUKClg77yM5YmN6ICF5Yib5bu65LiA5Lu95ou36LSd5bm26L2s5o2i77yM5ZCO6ICF5Y6f5Zyw6L2s5o2i44CCDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZSgNCiAgYSA9IGMoIkEiLCAiQiIsICJDIiwgIkEiLCAiQSIsICJCIiksDQogIGIgPSAxOjYNCikNCmRmICU+JQ0KICBzZXREVCgpICU+JQ0KICBjbGFzcygpDQoNCg0KbGlicmFyeShzcWxkZikNCm9wdGlvbnMoc3FsZGYuZHJpdmVyID0gIlNRTGl0ZSIpDQpjaGFubmVsIDwtIGRiQ29ubmVjdCgNCiAgU1FMaXRlKCksDQogIGRibmFtZSA9ICJDOi9Vc2Vycy9odW1vby9PbmVEcml2ZS9JQ1QvRGF0YUJhc2UvU1FMaXRlL3RyYWRlLmRiIg0KKQ0KdG90YWwgPC0gZGJSZWFkVGFibGUoY2hhbm5lbCwgInRvdGFsIikgJT4lDQogIHNlbGVjdCgtdGltZSkgJT4lDQogIHNldERUKCkNCmNsYXNzKHRvdGFsKQ0KZGJEaXNjb25uZWN0KGNoYW5uZWwpDQpgYGANCg0KYHNldCooKWDlh73mlbDml4/kuI3lpI3liLbmlbDmja7vvIzoioLnnIHml7bpl7QNCg0KYGBge3J9DQpzZXRuYW1lcyh0b3RhbCwgInRyYWRlIiwgInRvdGFsIikgIyDlsIbliJflkI10cmFkZeaUueS4unRvdGFsDQpoZWFkKHRvdGFsKQ0KdG90YWxbLCBpZCA6PSAuSV0gIyAuSeaEj+S4ujE6Lk7vvIzov5nlj6XkuLp0b3RhbOWKoOS6huS4gOS4quW6j+WPt+e0ouW8leWIlw0KDQpzZXRjb2xvcmRlcih0b3RhbCwgbmV3b3JkZXIgPSAiaWQiKSANCiMgc2V0Y29sb3JkZXIoKSDliJfnmoTph43mjpLluo8NCiMg6buY6K6k56ys5LqM5Liq5Y+C5pWw5piv5LiA5Liq5YiX5ZCN5ZCR6YeP44CC5aaC5p6c5Y+q5YaZ5Ye65LqG5LiA6YOo5YiG5YiX77yM5YiZ5YW25LuW5YiX5oyJ54Wn5Y6f5p2l55qE6aG65bqPDQpoZWFkKHRvdGFsKQ0KYGBgDQoNCg0KDQp8IGBzZXQqKClg5Ye95pWw5pePICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYHNldERUKClgLCBgc2V0REYoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHNldGF0dHIoKWAgYHNldG5hbWVzKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgU2V0IGF0dHJpYnV0ZXMgb2Ygb2JqZWN0cyBieSByZWZlcmVuY2UgICAgICAgICAgICAgIHwNCnwgYHNldGtleSgpYCBgc2V0a2V5digpYCBgc2V0aW5kZXgoKWAgYHNldGluZGV4digpYCBga2V5KClgIGBpbmRpY2VzKClgIGBoYXNrZXkoKWAgfCBDcmVhdGUga2V5IG9uIGEgZGF0YS50YWJsZSAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgc2V0Y29sb3JkZXIoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBGYXN0IGNvbHVtbiByZW9yZGVyaW5nIG9mIGEgZGF0YS50YWJsZSBieSByZWZlcmVuY2UgfA0KfCBgc2V0b3JkZXIoZHQsIGEsIC1iKWAgYHNldG9yZGVydigpYCAgICAgICAgICAgICAgICAgICAgICAgICAgfCBGYXN0IHJvdyByZW9yZGVyaW5nIG9mIGEgZGF0YS50YWJsZSBieSByZWZlcmVuY2UgICAgfA0KDQojIyMg6K+75Y+W5paH5Lu25Yib5bu6IGRhdGEudGFibGUNCg0KLSBgZnJlYWQoKSBg5Y+v5Lul5bCG6IO955u05o6l5bCGLmNzdiAudHh0IOetieagvOW8j+eahOaWh+S7tuivu+S4uiBkYXRhLnRhYmxlDQoNCi0gYGZ3cml0ZSgpIGDlj6/ku6XlhpkgZGF0YS50YWJsZSDliLDmlofku7YNCg0K5Zyo5aSE55CG5aSn5pWw5o2u5pe277yM5a6D5Lus55qE6YCf5bqm6Z2e5bi45b+r44CCKirlu7rorq7or7vlhpnmlofku7bnu5/kuIDkvb/nlKjov5nkuKTkuKrlh73mlbAqKg0KDQojIyBkYXRhLnRhYmxlIOafpeivouivreazlQ0KDQojIyMg6K+t5rOVDQoNCmBgYGBSDQpkdFtpLCBqLCBieSA9IGxpc3QoKSwgLi4uXQ0KYGBgYA0KDQrlvojlg48gU1FMIOivreWPpQ0KDQoxLiBgaWA6IHN1YnNldCDooYzvvIjlsLHlg48gV0hFUkUg5a2Q5Y+l77yJDQoyLiBgYnlgL2BrZXlieWBbXjFdIDog5YiG57uE77yI5bCx5YOPIEdST1VQIEJZIOWtkOWPpe+8iQ0KMy4gYGpgOiBzdWJzZXRbXjJdIOaIliBtYW5pcHVsYXRlIOWIlyDvvIjlsLHlg48gU0VMRUNUIOWtkOWPpe+8ie+8jOeUmuiHs+WPr+S7peaYr+S4gOS4quWMv+WQjeWHveaVsA0KDQpbXjFdOiDog73oh6rliqjlsIbliIbnu4Tlj5jph4/orr7kuLprZXkuDQoNClteMl06IOimgeeUqGxpc3QoKeaIli4oKQ0KDQojIyMg54m55q6K56ym5Y+3DQoNCnwg54m55q6K56ym5Y+3IHwg6K+05piOICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfA0KfCAuKCkgICAgICB8IOS7o+abv2xpc3QoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAuTiAgICAgICB8IOaAu+ihjOaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLlNEICAgICAgfCDku6PooajliIbnu4TlkI7mr4/kuKrnu4TnmoTmlbDmja7moYYgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC5TRGNvbHMgIHwg5ZyoIGJ5IOWkhO+8jOS4ji5TROi/nueUqO+8jOeUqOadpemAieaLqeWTquS6m+WIl+WMheWQq+WcqC5TROS4rSAgICAgICAgICAgICAgIHwNCnwgLkJZICAgICAgfCDljIXlkKvmiYDmnIlieeWIhue7hOWPmOmHj+eahGxpc3QgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC5JICAgICAgIHwg5pW05pWw5ZCR6YePIDE6Lk4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLkdSUCAgICAgfCDliIbnu4TntKLlvJUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAuTkdSUCAgICB8IOWIhue7hOaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLkVBQ0hJICAgfCDnlKjkuo5ieS9rZXlieSA9IC5FQUNISe+8jOihqOekuuagueaNrmnooajovr7lvI/nmoTmr4/kuIDooYzliIbnu4QgfA0KDQojIyMg6ZO+5byP5pON5L2cDQoNCmBEVFsuLi5dWy4uLl1bLi4uXWANCg0K5oiW5YaZ5Li677yaDQoNCmBgYFINCkRUWw0KICAuLi4NCl1bDQogIC4uLg0KXVsNCiAgLi4uDQpdDQpgYGANCg0KIyMjIOS4jueuoemBk2AlPiVg6YWN5ZCI55qE6ZO+5byP5pON5L2cDQoNCueUseS6jiBSIOS4reaJgOacieeahOaTjeS9nOespumDveaYr+WHveaVsO+8jOWboOatpOWPr+S7peWwhlxgW1xg5L2c5Li65LiA5Liq5Ye95pWw77yM6LSv56m/5LqOYCU+JWDkuK3vvIzojrflvpfku6PnoIHpo47moLznmoTkuIDoh7TmgKfjgIINCg0KYGBge3J9DQptdGNhcnMgJT4lIA0KCWFzLmRhdGEudGFibGUoKSAlPiUgDQoJYFtgKCwgLihtcGcgPSBtZWFuKG1wZykpLCBieSA9IGN5bCkgJT4lIA0KCWBbYCguTiwpIA0KYGBgDQoNCiMjIyBTdWJzZXQgcm93cyB1c2luZyBpDQoNCumAieaLqeihjOWPr+S7peaOpeWPl+eahOi/kOeul+espumZpOS6huW4uOeUqOeahOmAu+i+kei/kOeul+espu+8jOi/mOWMheaLrCBgJWxpa2UlYCAo5Yy56YWN5qih5byP77yM5pyJ54K55YOP5q2j5YiZ6KGo6L6+5byPKSDlkowgYCViZXR3ZWVuJWANCg0KZGF0YS50YWJsZVtdIOaWueaLrOWPt+S4reWPquacieS4gOS4quWPguaVsOaXtu+8jOm7mOiupOS4uiBpIOWPguaVsO+8jOihqOekuuWvueihjOaTjeS9nOOAgg0KDQpgYGB7cn0NCmR0MyA8LSBkYXRhLnRhYmxlKA0KICBhID0gMTo2LA0KICBiID0gYygiQSIsICJCIiwgIkMiLCAiQSIsICJBIiwgIkIiKQ0KKQ0KDQpkdDNbMToyLCBdDQoNCiMg6YCJ5Ye65pyA5ZCO5LiA6KGMDQpkdDNbLk5dDQoNCiMg6YCJ5Ye656ys5LiA6KGM5ZKM5pyA5ZCO5LiA6KGMDQpkdDNbYygxLCAuTildDQoNCiMg6YCJ5Ye65ruh6Laz54m55a6a5p2h5Lu255qE6KGMDQpkdDNbYiA9PSAiQSIsIF0NCg0KIyDooYzmjpLluo/vvIzotJ/lj7fooajnpLrpmY3luo8NCnRvdGFsW29yZGVyKC1tb250aCldICU+JSBoZWFkKCkNCg0KdG90YWwgJT4lIGhlYWQoKQ0KYGBgDQoNCiMjIyBNYW5pcHVsYXRlIGNvbHVtbnMgd2l0aCBqDQoNCiMjIyMgc3Vic2V0DQoNCmBgYHtyfQ0KZHRbLCBjKDIpXSAjIOaPkOWPluesrDLliJcNCmBgYA0KDQojIyMjIOixoeeJmeaTjeS9nOespiBgOj1gW14zXQ0KDQpbXjNdOiDosaHniZnmk43kvZznrKbkuI3mi7fotJ3ooqvkv67mlLnnmoTliJfjgILljp/lnLDkv67mlLnnmoTmgKfog73mm7Tpq5jvvIzpgb/lhY3kuoblr7nmlbDmja7nmoTlpI3liLbjgIINCg0K6L+Z5Liq5pON5L2c56ym5pyJ54K55YOPIFNRTCDor63lj6XkuK3nmoQgYEFTYCDlhbPplK7or43vvIzkvYblt6bkuLrliJflkI3vvIzlj7PkuLrooajovr7lvI/jgIINCg0KLSAgIOe7mWRhdGEudGFibGXlr7nosaHlop7liqDkuIDliJfvvIxgZGF0YS50YWJsZVssIGNvbG5hbWUgOj0gdmFyMV1gDQotICAg57uZZGF0YS50YWJsZeWvueixoeWIoOmZpOS4gOWIl++8jOWwseaYr+e7mei/meWIl+i1i+WAvOS4uuepuu+8jGBkYXRhLnRhYmxlWywgY29sbmFtZSA6PSBOVUxMXWANCi0gICDkv67mlLlkYXRhLnRhYmxl5a+56LGh55qE5YC877yM6YCa6L+H57Si5byV5a6a5L2N5ZCO6L+b6KGM5YC855qE5pu/5o2i77yMYGRhdGEudGFibGVbY29uZGl0aW9uLCBjb2xuYW1lIDo9IHZhbHVlXWANCg0K5Y2V5Liq5YiX5ZCN5LiN55So5Yqg5byV5Y+377yM5aSa5Liq5YiX5ZCN57uE5oiQ55qE5ZCR6YeP77yM5b+F6aG75Yqg5byV5Y+377yM5aaCYGMoJ2QxJywgJ2QyJylgDQoNCmBgYHtyfQ0KZHQgPC0gZGF0YS50YWJsZSgNCiAgYSA9IGMoIkEiLCAiQiIsICJDIiwgIkEiLCAiQSIsICJCIiksDQogIGIgPSAxOjYNCikNCg0KIyAx5YiX77yM5LiN5b+F5Yqg5byV5Y+3DQpkdFssIGMgOj0gYiArIDJdICMgKGIgKyAyKSBBUyBjDQpkdA0KDQojIOWinuWKoDLliJfvvIznrKwx56eN5YaZ5rOVDQpkdFssIGMoJ2QxJywgJ2QyJykgOj0gbGlzdCgxOjYsIDI6NyldICMgOj0g5bem6L655piv5LiA5LiqbmFtZeWQkemHj++8jOWPs+i+ueaYr+S4gOS4qnZhbHVl5YiX6KGoDQpkdA0KDQojIOWinuWKoDLliJfvvIznrKwy56eN5YaZ5rOVDQpkdFssYDo9YChjMSA9IDE6NiwgYzIgPSAyOjcpXSAjIDo9IOW+iOWDj+S4gOS4qnVwZGF0ZeWHveaVsA0KZHQNCg0KIyDliKDpmaRjMeWIlw0KZHRbLCBjMSA6PSBOVUxMXQ0KZHQNCg0KIyDlkIzml7bliKDpmaRkMSxkMuWIlw0KZHRbLCBjKCJkMSIsICJkMiIpIDo9IE5VTExdDQpkdA0KDQojIOe7mWLotYvlgLzkuLozMA0KZHRbLCBiIDo9IDMwXQ0KZHQNCg0KIyDlr7lh5YiX5YC85Li6QuOAgWMy5YiX5YC85aSn5LqOM+eahOihjO+8jOWFtmLliJfotYvlgLzkuLoxMDANCmR0W2EgPT0gIkIiICYgYzIgPiAzLCBiIDo9IDEwMF0NCmR0DQoNCiMg5Y+m5LiA56eN5YaZ5rOVDQpkdFssIGIgOj0gaWZlbHNlKGEgPT0gIkIiICYgYzIgPiAzLCA1MCwgYildDQpkdA0KDQojIOabtOaWsOWIlw0KZHRbLCBiIDo9IGFzLmNoYXJhY3RlcihiKV0NCmR0DQpgYGANCg0KIyMjIyDnlKhgPWDogIzpnZ7osaHniZnmk43kvZznrKYNCg0K5LiN5piv5Zyo5Y6f5p2l55qE5pWw5o2u5qGG5Z+656GA5LiK5pS55Yqo77yM6ICM5piv5paw5Yib5bu65LiA5Liq5pWw5o2u5qGG44CCDQoNCuatpOaXtu+8jGog5Lit55qE5YiX5LiN6IO95YaZ5oiQ5ZCR6YeP77yM6ICM6KaB5YaZ5oiQ5YiX6KGo77yM55SoYC4o5YiX5ZCNLi4uKWDmiJZgbGlzdCjliJflkI0uLi4pYA0KDQpgYGB7cn0NCnRvdGFsWywgLih5ZWFyLCBtb250aCwgZXhwb3J0LCBpbXBvcnQsIHJhdGlvID0gZXhwb3J0IC8gaW1wb3J0KV0NCg0KdG90YWwgIyDljp/lp4vmlbDmja7kuI3lj5gNCmBgYA0KDQojIyMjIOWMv+WQjeWHveaVsA0KDQpgYGB7cn0NCmRhdGEoImRpYW1vbmRzIiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikNCnNldERUKGRpYW1vbmRzKQ0KDQojIOaMiSBjdXQg5YiG57uE5YGa5Zue5b2SDQpkaWFtb25kc1ssDQogIHsNCiAgICBtIDwtIGxtKGxvZyhwcmljZSkgfiBjYXJhdCArIGRlcHRoKQ0KICAgIGFzLmxpc3QoY29lZihtKSkNCiAgfSwNCiAga2V5YnkgPSBjdXQNCl0NCmBgYA0KDQoNCiMjIyBHcm91cCBhY2NvcmRpbmcgdG8gYnkNCg0KYnkg5YiG57uEDQoNCmtleWJ5IOS4jeS7heWIhue7hOS4lOeri+WIu+WumuS5iemUru+8jOW5tuaMieeFp+mUruWvueihjOaOkuW6jw0KDQpgYGB7cn0NCmR0IDwtIGRhdGEudGFibGUoDQogIGEgPSBjKCJBIiwgIkIiLCAiQyIsICJBIiwgIkEiLCAiQiIpLA0KICBiID0gMTo2DQopDQoNCiMg5a+55pW05LiqYuWIl+aVsOaNruaxguWSjA0KZHRbLCBzdW0oYildDQoNCiMg5oyJYeWIl+WIhue7hO+8jOWvuWLliJfmjInliIbnu4TmsYLlkozvvIzov5Dnrpfnu5Pmnpzoh6rliqjlkb3lkI3kuLogVjENCmR0Wywgc3VtKGIpLCBieSA9IGFdDQoNCiMg5o+Q5Y+W5q+P5LiA57uE55qE56ys5LiA6KGM77yM57uE5oiQ5paw5pWw5o2u5qGGDQpkdFssIC5TRFsxXSwgYnkgPSBhXQ0KDQojIOaPkOWPluavj+S4gOe7hOeahOacgOWQjuS4gOihjO+8jOe7hOaIkOaWsOaVsOaNruahhg0KZHRbLCAuU0RbLk5dLCBieSA9IGFdDQoNCg0KIyDlhYjliIbnu4TvvIzlho3mmL7npLrmr4/nu4TooYzmlbANCnRvdGFsWywgLk4sIGJ5ID0geWVhcl0gDQp0b3RhbFssIC5OLCBieSA9IC4oeWVhciwgbW9udGgpXSAjIHllYXIg5ZKMIG1vbnRoIOaYr+acieWFiOWQjumhuuW6j+eahA0KdG90YWxbLCAuKGF2Z19leCA9IG1lYW4oZXhwb3J0KSksIGJ5ID0geWVhcl0gIyDljbPkvb/lj6rmnInkuIDliJfvvIzkuZ/opoHnlKjliJfooagNCmBgYA0KDQoNCiMjIOeLrOeri+i+heWKqeWHveaVsA0KDQojIyMg6KGM5o6S5bqPDQoNCmBzZXRvcmRlcihkdCwgYSwgLWIpYCDph43mlrDmjpLliJfooYzvvIzlhYhh5oyJ5Y2H5bqP77yM5YaNYuaMiemZjeW6jw0KDQoNCiMjIyDooYzljrvph40NCg0KYHVuaXF1ZShkdCwgYnkgPSBjKCJhIiwiYiIpKWAg5qOA5p+lYnnnmoTliJfvvIzmj5Dlj5bkuI3ph43lpI3nmoTooYwNCg0KYHVuaXF1ZU4oZHQsIGJ5ID0gYygiYSIsImIiKSlgIOajgOafpWJ555qE5YiX77yM6K6h566X5q+P5Liq5LiN6YeN5aSN55qE6KGM5Ye6546w5LqG5aSa5bCR5qyhDQoNCiMjIyDliJfph43lkb3lkI0NCg0KYHNldG5hbWVzKGR0LCBjKCJhIiwiYiIpLCBjKCJ4IiwieSIpYCDliJfph43lkb3lkI0NCg0KIyMjIOaPkOWJjeWSjOa7nuWQjiBsZWFkIGFuZCBsYWcNCg0KYGBge3J9DQpkdFssIGMgOj0gc2hpZnQoYSwgMSldICMg6buY6K6kIGxhZw0KZHQNCg0KZHRbLCBjIDo9IHNoaWZ0KGEsIDEsIHR5cGUgPSAibGVhZCIpXQ0KZHQNCmBgYA0KDQoqKui/meagt+WPr+S7peW+iOWuueaYk+WcsOWKoOWFpeaPkOWJjeaIlua7nuWQjuWIl++8jOi/m+ihjOWQjOavlOWinumVv+eOh+etieiuoeeulyoqDQoNCiMjIyDpgY3ljobmk43kvZzlkITliJcNCg0KYGBge3J9DQpkdCA8LSBkYXRhLnRhYmxlKGEgPSBjKDEsIDIsIDMpLCBiID0gYyg0LCA1LCA2KSkNCiMg6YGN5Y6GYeWSjGLkuKTliJfvvIzpg73msYLlubPlnYflgLwNCmR0WywgbWFwKC5TRCwgbWVhbiksIC5TRGNvbHMgPSBjKCJhIiwgImIiKV0NCg0KY29scyA8LSBjKCJhIiwgImIiKQ0KIyDpgY3ljoYgY29scyDlkITliJfvvIzmsYLlubPlnYflgLzvvIzkuJTotYvnu5nmlrDliqDlhaXnmoRhX23liJcNCmR0Wywgc3RyX2MoY29scywgIl9tIikgOj0gbWFwKC5TRCwgbWVhbiksIC5TRGNvbHMgPSBjb2xzXQ0KZHQNCmBgYA0KDQojIyDov57mjqXmk43kvZwNCg0KIyMjIOmUrg0KDQpgc2V0a2V5KGQxLCB2MSwgdjMsIC4uLilgDQoNCuWwhuafkOS6m+WIl+WumuS5ieS4uiBrZXlz5ZCO77yM5bCx5Y+v5Lul6YCa6L+H6ZSu6YCJ5oup6KGM77yM5Lul5Y+K6L+b6KGM5YWz57O75pWw5o2u6L+e5o6l77yM5q+U5peg6ZSu5pe25b+rMTcw5YCN44CC5ZCM5pe277yM5Y+v5Lul55SoIG11bHQg5Y+C5pWw5a+55p+l6K+i57uT5p6c5aKe5Yqg6L+H5ruk5p2h5Lu277yM5L2/5Luj56CB5pu06auY5pWI44CCDQoNCmBgYHtyfQ0KZHQgPC0gZGF0YS50YWJsZShhID0gYygiQSIsICJCIiwgIkMiLCAiQSIsICJBIiwgIkIiKSwNCiAgICAgICAgICAgICAgICAgYiA9IDE6NikNCg0KIyDorr7nva5h5YiX5Li65Li76ZSuDQpzZXRrZXkoZHQsIGEpDQojIHNldGtleXYoZHQsICdhJykg5Ye95pWw5LuF5o6l5Y+X5a2X56ym5Liy5L2c5Li656ys5LqM5Liq5Y+C5pWwDQprZXkoZHQpDQoNCiMg5omT5Y2wZHTlr7nosaHvvIzlj5HnjrDmlbDmja7lt7Lnu4/mjInnhadh5YiX5a2X5q+N5a+55bqUQVNDSUnnoIHlgLzov5vooYzkuobmjpLluo/jgIINCmR0DQoNCiMg5Y+WYeWIl+S4reWAvOS4ukLnmoTooYzvvIzorr7nva7kuLvplK7lkI7lj6/nnIHnlaVgYT09YA0KZHRbIkIiXQ0KDQojIOWPlmHliJfkuK3lgLzkuLpC55qE6KGM77yM5bm25L+d55WZ5p+l6K+i57uT5p6c55qE56ys5LiA6KGMDQpkdFsiQiIsIG11bHQgPSAiZmlyc3QiXQ0KDQojIOWPlmHliJfkuK3lgLzkuLpC55qE6KGM77yM5bm25L+d55WZ5p+l6K+i57uT5p6c55qE5pyA5ZCO5LiA6KGMDQpkdFsiQiIsIG11bHQgPSAibGFzdCJdDQoNCiMg5Y+WYeWIl+S4reWAvOS4umLnmoTooYzvvIzmsqHmnInmlbDmja7liJnkuLpOQQ0KZHRbImIiXQ0KYGBgDQoNCg0KYGBge3J9DQpzZXRrZXkodG90YWwsIHllYXIsIG1vbnRoKQ0KdG90YWxbLigyMDE5LCAzKV0gIyDnnIHnlaXkuoYgeWVhciA9PSAyMDE5ICYgbW9udGggPT0gMw0KDQp0b3RhbFsuKDIwMTkpXSAjIOecgeeVpeS6hiB5ZWFyID09IDIwMTnvvIzlj6/ku6Xlj6rmoLnmja7kuIDliJcgc3Vic2V0DQojIOS4jeiDveWGmXRvdGFsW2xpc3QoMyld77yM5b6X5LiN5Ye65pyJ5oSP5LmJ55qE57uT5p6cDQpgYGANCg0KIyMjIEpvaW4NCg0K5Z+65pys6K+t5rOV5Li6YGR0MVtkdDIsIG9uID0gLihiID0geSldYO+8jOWFtuS4rWLkuLpkdDHnmoTliJfvvIx55Li6ZHQy55qE5YiXDQoNCi0gICDoi6Xov57kuKrooajpg73lrprkuYnkuoYga2V5LCDnrKzkuozkuKrlj4LmlbDkuI3nlKjloasNCg0KLSAgIOiLpeayoeacieWumuS5iWtlee+8jC4uLuimgeWGmSBvbiA9ICJjb21tb25Db2x1bW4iIOaIliBieS5kdDEgPSAiYzEiLCBieS5kdDIgPSAiYzIiDQoNCuS+i++8muWtpueUn+iAg+ivleeahOWcuuaZr+OAguaMieeFp0VS6K6+6K6h5pa55rOV77yM5oiR5Lus6YCa5bi45Lya5oyJ54Wn5a6e5L2T6L+b6KGM5pWw5o2u5YiS5YiG44CC6L+Z6YeM5a2Y5ZyoMuS4quWunuS9k++8jOS4gOS4quaYr+WtpueUn++8jOS4gOS4quaYr+aIkOe7qeOAguWtpueUn+WunuS9k+WMheaLrOWtpueUn+Wnk+WQjeetieWfuuacrOi1hOaWme+8jOiAjOaIkOe7qeWunuS9k+WMheaLrOiAg+ivleeahOenkeebruWSjOaIkOe7qeOAgumAmui/h+iuvue9ruS4pOS4quS4u+mUru+8jOWvuTLkuKrmlbDmja7pm4bov5vooYzov57mjqXjgIINCg0KYGBge3J9DQojIDbkuKrlrabnlJ8NCnN0dWRlbnQgPC0gZGF0YS50YWJsZSgNCiAgaWQgPSAxOjYsDQogIG5hbWUgPSBjKCJEYW4iLCAiTWlrZSIsICJBbm4iLCAiWWFuZyIsICJMaSIsICJLYXRlIikNCikNCnN0dWRlbnQNCg0KIyDliIbliKvlj4LliqBB5ZKMQuS4pOmXqOiAg+ivlQ0Kc2NvcmUgPC0gZGF0YS50YWJsZSgNCiAgaWQgPSAxOjEyLCBzdHVJZCA9IHJlcCgxOjYsIDIpLA0KICBzY29yZSA9IHJ1bmlmKDEyLCA2MCwgOTkpLA0KICBjb3Vyc2UgPSBjKHJlcCgiQSIsIDYpLCByZXAoIkIiLCA2KSkNCikNCnNjb3JlDQoNCiMg6K6+572uc3R1ZGVudOaVsOaNrumbhueahGtleQ0Kc2V0a2V5KHN0dWRlbnQsICJpZCIpDQoNCiMg6K6+572uc2NvcmXmlbDmja7pm4bnmoRrZXkNCnNldGtleShzY29yZSwgInN0dUlkIikNCg0KIyDov57mjqUNCnN0dWRlbnRbc2NvcmUsIG5vbWF0Y2ggPSBOQSwgbXVsdCA9ICJhbGwiXQ0KDQpybShsaXN0ID0gbHMoKSkNCmBgYA0KDQojIyMg6ZuG5ZCI6L+Q566XDQoNCmBgYFINCiAgZmludGVyc2VjdChkdDEsIGR0MikNCiAgZnNldGRpZmYoZHQxLCBkdDIpDQogIGZ1bmlvbihkdDEsIGR0MikNCiAgZnNldGVxdWFsKGR0MSwgZHQyKQ0KYGBgDQoNCiMjIOmrmOe6p+W6lOeUqOWunuS+iw0KDQpgYGB7cn0NCg0KIyMg5pyIS+e6v+WbvuaVsOaNrueahOaVtOeQhg0KDQojIDEuIOS6p+eUn+aXpeacn+W6j+WIlw0KbWFya2V0X2RhdGEgPC0gZGF0YS50YWJsZShkYXRlID0gKGFzLkRhdGUoIjIwMTUtMDUtMDEiKSArIDA6Mjk5KSkNCnNldC5zZWVkKDEyNSkNCg0KIyAyLiDkuqfnlJ/miJDkuqTku7fmoLzlkozmlbDph4/luo/liJcNCiMg6IKh5biC5Lu35qC85piv5LiA5Liq6ZqP5py65ri46LWw6L+H56iL77yM5bCGMzAw5aSp55qE5Y+Y5YyW546H57Sv5LmY6LW35p2lDQojIOaIkOS6pOmHjyB2b2x1bWUg5Y+W6ZqP5py65pWwDQptYXJrZXRfZGF0YVssIGA6PWAoDQogIHByaWNlID0gKCgxICsgcm5vcm0oMzAwLCAwLjAwMSwgMC4wNSkpICU+JSBjdW1wcm9kKCkgKiAzMCkgJT4lIHJvdW5kKDIpLA0KICB2b2x1bWUgPSByYmlub20oMzAwLCA1MDAwLCAwLjgpDQopXQ0KaGVhZChtYXJrZXRfZGF0YSkNCg0KIyAzLiDogqHku7fotbDlir/lm74NCnBsb3QocHJpY2UgfiBkYXRlLA0KICBkYXRhID0gbWFya2V0X2RhdGEsDQogIHR5cGUgPSAibCIsDQogIG1haW4gPSAiTWFya2V0IGRhdGEiDQopDQoNCiMgNC4g6I635Y+W5qiq6L20IGRvbWFpbg0KbWFya2V0X2RhdGFbLCByYW5nZShkYXRlKV0gIyDmsqHmnIk6Pe+8jOaJgOS7pei/meS4jeaYr3VwZGF0Ze+8jOiAjOaYr3N1bW1hcml6ZQ0KDQojIDUuIOaMieaciOWIhue7hO+8jOavj+e7hOe7n+iuoeacgOWkp+OAgeacgOWwj+OAgeacgOaXqeOAgeacgOaZmuS7t+agvO+8jOi/meWwseaYr+aciEvnur/lm77vvIHvvIHvvIENCm1vbnRobHkgPC0gbWFya2V0X2RhdGFbLA0KICAjIOi/meagt+WGmSBsaXN0IOWwseW+iOWDj+mUruWAvOWvueS6hg0KICAuKA0KICAgIG9wZW4gPSBwcmljZVtbMV1dLA0KICAgIGhpZ2ggPSBtYXgocHJpY2UpLA0KICAgIGxvdyA9IG1pbihwcmljZSksDQogICAgY2xvc2UgPSBwcmljZVtbLk5dXQ0KICApLA0KICBrZXlieSA9IC4oDQogICAgeWVhciA9IHllYXIoZGF0ZSksDQogICAgbW9udGggPSBtb250aChkYXRlKQ0KICApDQpdDQpoZWFkKG1vbnRobHkpDQoNCg0KIyA2LiDoh6rlrprkuYnlh73mlbDvvIzpkojlr7nkuI3lkIznmoTliJflj6/ku6XliIbnu4TorqHnrpflubPlnYflgLwNCmF2ZXJhZ2UgPC0gZnVuY3Rpb24oY29sdW1uKSB7DQogIG1hcmtldF9kYXRhWywNCiAgICAuKA0KICAgICAgYXZlcmFnZSA9IG1lYW4oLlNEW1tjb2x1bW5dXSkNCiAgICApLA0KICAgIGJ5ID0gLih5ZWFyID0geWVhcihkYXRlKSkNCiAgXQ0KfQ0KYXZlcmFnZSgicHJpY2UiKQ0KYXZlcmFnZSgidm9sdW1lIikNCmBgYA0K