基于列和行在R中合并

KT_*_*T_1 1 merge r dataframe

对于示例数据框:

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数据框列出了不同年龄类别的三个国家/地区的人口比例.需要在调查数据框中添加相应的国家/年龄类别作为附加列.以前,例如,当我使用单个国家/地区时,我使用合并,但这不会在我理解,因为我需要匹配列和行.

有没有人有任何想法?

Aru*_*run 5

使用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是空的.因此,它返回两者中的所有列dt1dt2(导致右连接).

换句话说,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.

希望这可以帮助.