基于列和行在R中合并

时间:2016-05-25 12:03:00

标签: r dataframe merge

对于示例数据框:

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))

我想添加一个名为&#39; age.cat&#39;的额外列。由另一个数据框填充:

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))

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

有没有人有任何想法?

4 个答案:

答案 0 :(得分:5)

使用data.table,我会直接执行以下操作:

require(data.table) # v1.9.6+
dt1[dt2, ratio := unlist(mget(age.cat)), by=.EACHI, on="cntry"]

其中,

dt1 = as.data.table(survey)[, age.cat := as.character(age.cat)]
dt2 = as.data.table(age.cat)

对于dt2中的每一行,dt1$cntry中的匹配行与dt2$cntry对应(有助于将其视为子集操作匹配cntry列)。提取这些匹配行的age.cat值并将其传递给mget()函数,该函数查找以age.cat中的值命名的变量,并在dt2中找到它(我们允许dt2中的列也可以用于此目的),并提取相应的值。由于它返回list,我们unlist。这些值通过引用分配给列ratio

由于这避免了通过熔化/收集不必要地实现中间数据,因此非常有效。此外,由于它在加入时通过引用添加了新列,因此它避免了另一个中间实现并且效率更高。

就个人而言,我发现代码更容易理解正在发生的事情(当然有足够的基础R知识),但这当然是主观的。

稍微详细的解释:

data.table语法的一般形式是DT[i, j, by],其内容为:

  

DT获取i子集行,然后按j计算by

data.table 中的i参数,除了作为子集操作,例如dt1[cntry == "FR"]之外,还可以另一个data.table

考虑表达式:dt1[dt2, on="cntry"]

它首先做的是为dt2中的每一行计算dt1中所有匹配的行索引,方法是匹配on = "cntry"中提供的列。例如,对于dt2$cntry == "FR"dt1中的匹配行索引为c(1,4,5,10)。这些行索引是使用快速二进制搜索在内部计算的。

一旦计算出匹配的行索引,就会查看j参数中是否提供了表达式。在上面的表达式j是空的。因此,它会返回dt1dt2中的所有列(导致右连接)。

换句话说,data.table允许 join 操作以与子集类似的方式执行(因为在这两个操作中,i的目的参数是获取匹配的行)。例如,dt1[cntry == "FR"]将首先计算匹配的行索引,然后提取这些行的所有列(因为j参数中没有提供列)。这有几个优点。例如,如果我们只想返回列的子集,那么我们可以这样做,例如:

dt1[dt2, .(cntry, Y_less.15), on="cntry"]

这很有效,因为我们查看j表达式并注意到只需要这两列。因此,在计算的行索引上,我们只提取所需的列,从而避免所有其他列的不必要的实现。因此效率很高

此外,就像我们如何选择列一样,我们也可以在列上计算。例如,如果您想获得sum(Y_less.15)该怎么办?

dt1[dt2, sum(Y_less.15), on="cntry"]
# [1] 2.3

这很好,但它会计算所有匹配行的总和。如果您希望获得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

by=.EACHI确保为j 中的每一行评估i = dt2表达式。

同样,我们也可以在使用:=运算符加入时添加/更新列。这就是上面给出的答案。唯一棘手的部分是从dt2中提取匹配行的值,因为它们存储在单独的列中。因此我们使用mget()。表达式unlist(mget(.))会在dt2 的每一行进行评估,同时在"cntry"列上进行匹配。并使用ratio运算符将相应的值分配给:=

  

有关:=运营商的历史记录的详细信息,请参阅有关thisthisthis的信息。

     

有关by=.EACHI的更多信息,请参阅this post

     

有关data.table语法简介和参考语义的更多信息,请参阅vignettes

希望这有帮助。

答案 1 :(得分:1)

我们可以在melt第二个数据集之后进行连接,以“长”格式

library(data.table) #v1.9.7
melt(setDT(age.cat), id.var="cntry")[survey, on = c("cntry",  "variable" = "age.cat")]
#    cntry  variable value id
# 1:    FR   Y_15.24   0.2  1
# 2:    UK   Y_15.24   0.1  2
# 3:    DE   Y_40.54   0.1  3
# 4:    FR Y_less.15   0.2  4
# 5:    FR   Y_15.24   0.2  5
# 6:    UK Y_55.plus   0.2  6
# 7:    DE Y_less.15   0.3  7
# 8:    DE Y_less.15   0.3  8
# 9:    UK   Y_15.24   0.1  9
#10:    FR   Y_40.54   0.3 10

如果我们使用CRAN版本,即data.table_1.9.6

melt(setDT(age.cat), id.var="cntry", variable.name = "age.cat")[survey, 
                        on = c("cntry",  "age.cat")]

答案 2 :(得分:1)

您可以将age.cat转换为长格式,然后按如下方式使用join:

library(dplyr)
library(tidyr)
age.cat <- gather(age.cat, age.cat, proportion, -cntry)
inner_join(survey, age.cat)

答案 3 :(得分:0)

您可以使用包reshape2dplyr执行此操作:

age.cat %>% melt(variable.name="age.cat") %>% left_join(survey, .)
#### id cntry   age.cat value
#### 1  1    FR   Y_15.24   0.2
#### 2  2    UK   Y_15.24   0.1
#### 3  3    DE   Y_40.54   0.1

这就是你想要的吗?