对于示例数据框:
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数据框列出了不同年龄类别的三个国家/地区的人口比例。需要在调查数据框中添加相应的国家/年龄类别作为附加列。以前,当我使用单个国家/地区时,我使用合并,但这不会在我理解,因为我需要在列和行上进行匹配。
有没有人有任何想法?
答案 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
是空的。因此,它会返回dt1
和dt2
中的所有列(导致右连接)。
换句话说,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
运算符将相应的值分配给:=
。
有关
:=
运营商的历史记录的详细信息,请参阅有关this,this和this的信息。有关
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)
您可以使用包reshape2
和dplyr
执行此操作:
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
这就是你想要的吗?