对于示例数据框:
survey <- structure(list(id = 1:10, cntry = structure(c(2L, 3L, 1L, 2L,
2L, 3L, 1L, 1L, 3L, 2L), .Label = c("DE", "FR", "UK"), class = "factor"),
age.cat = structure(c(1L, 1L, 2L, 4L, 1L, 3L, 4L, 4L, 1L,
2L), .Label = c("Y_15.24", "Y_40.54", "Y_55.plus", "Y_less.15"
), class = "factor")), .Names = c("id", "cntry", "age.cat"
), class = "data.frame", row.names = c(NA, -10L))
Run Code Online (Sandbox Code Playgroud)
我想添加一个名为'age.cat'的额外列,该列由另一个数据帧填充:
age.cat <- structure(list(cntry = structure(c(2L, 3L, 1L), .Label = c("DE",
"FR", "UK"), class = "factor"), Y_less.15 = c(0.2, 0.2, 0.3),
Y_15.24 = c(0.2, 0.1, 0.2), Y_25.39 = c(0.2, 0.3, 0.1), Y_40.54 = c(0.3,
0.2, 0.1), Y_55.plus = c(0.1, 0.2, 0.3)), .Names = c("cntry",
"Y_less.15", "Y_15.24", "Y_25.39", "Y_40.54", "Y_55.plus"), class = "data.frame", row.names = c(NA,
-3L))
Run Code Online (Sandbox Code Playgroud)
age.cat数据框列出了不同年龄类别的三个国家/地区的人口比例.需要在调查数据框中添加相应的国家/年龄类别作为附加列.以前,例如,当我使用单个国家/地区时,我使用合并,但这不会在我理解,因为我需要匹配列和行.
有没有人有任何想法?
使用data.table,我将直接执行如下操作:
require(data.table) # v1.9.6+
dt1[dt2, ratio := unlist(mget(age.cat)), by=.EACHI, on="cntry"]
Run Code Online (Sandbox Code Playgroud)
哪里,
dt1 = as.data.table(survey)[, age.cat := as.character(age.cat)]
dt2 = as.data.table(age.cat)
Run Code Online (Sandbox Code Playgroud)
对于每一行dt2,dt1$cntry找到对应的匹配行dt2$cntry(通过匹配列,有助于将其视为子集操作cntry).age.cat提取那些匹配行的值并将其传递给mget()函数,该函数查找以值in命名的变量age.cat,并在其中查找dt2(我们允许列中的列dt2也可用于此目的),并提取相应的值.因为它返回一个list,我们unlist它.这些值ratio通过引用分配给列.
由于这避免了通过熔化/收集不必要地实现中间数据,因此非常有效.此外,由于它在连接时通过引用添加了新的列,因此避免了另一种中间实现并且具有双重效率.
就个人而言,我发现代码更容易理解正在发生的事情(当然有足够的基础R知识),但这当然是主观的.
data.table语法的一般形式是DT[i, j, by]:
取
DT,子集行i,然后计算j分组by.
data.table中的i参数除了是子集操作之外,还可以是另一个data.table.dt1[cntry == "FR"]
考虑一下表达式:dt1[dt2, on="cntry"].
它做的第一件事是通过匹配=中提供的列,为每一行计算dt2所有匹配的行索引.例如,对于,匹配的行索引是.这些行索引是使用快速二进制搜索在内部计算的.dt1on"cntry"dt2$cntry == "FR"dt1c(1,4,5,10)
一旦计算出匹配的行索引,就会查看是否在j参数中提供了表达式.在上面的表达式j是空的.因此,它返回两者中的所有列dt1和dt2(导致右连接).
换句话说,data.table允许以与子集类似的方式执行连接操作(因为在两个操作中,参数的目的是获得匹配的行).例如,首先计算匹配的行索引,然后提取这些行的所有列(因为参数中没有提供列).这有几个优点.例如,如果我们只想返回列的子集,那么我们可以这样做,例如:idt1[cntry == "FR"]j
dt1[dt2, .(cntry, Y_less.15), on="cntry"]
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为我们查看j表达式并注意到只需要那两列.因此,在计算的行索引上,我们只提取所需的列,从而避免所有其他列的不必要的实现.因此效率高
此外,就像我们如何选择列一样,我们也可以在列上进行计算.例如,如果你想得到sum(Y_less.15)什么?
dt1[dt2, sum(Y_less.15), on="cntry"]
# [1] 2.3
Run Code Online (Sandbox Code Playgroud)
这很好,但它计算所有匹配行的总和.如果您想获得sum 每一行,该dt2$cntry怎么办?这是by = .EACHI进来的地方.
dt1[dt2, sum(Y_less.15), on="cntry", by=.EACHI]
# cntry V1
# 1: FR 0.2
# 2: UK 0.2
# 3: DE 0.3
Run Code Online (Sandbox Code Playgroud)
by=.EACHI确保为每行j计算表达式.i = dt2
同样,我们也可以在使用运算符加入时添加/更新列:=.这就是上面给出的答案.唯一棘手的部分是从中提取匹配行的值dt2,因为它们存储在单独的列中.因此我们使用mget().并且在匹配列时对表达式unlist(mget(.))进行评估.并且使用运算符分配相应的值.dt2"cntry"ratio:=
有关历史的详细信息
:=运营商看到这个,这个和这个职位上的SO.有关更多信息
by=.EACHI,请参阅此帖子.有关data.table语法简介和引用语义的更多信息,请参阅vignettes.
希望这可以帮助.
| 归档时间: |
|
| 查看次数: |
97 次 |
| 最近记录: |