我正在尝试找到一种方法,使用df2中的信息向df1添加列,条件是df1中每行的内容,而不通过df1循环。
具体来说,我想将df2("哺乳动物")中的一般信息作为新列添加到df1中已有的特定信息(" tiger")。
以下代码有效,但我正在寻找它更快/矢量化/更优雅的版本,因为它(当然)非常慢。
for (i in (1:nrow(df1))) {
subCategories <- unlist(df1$categories_split[i])
currentAggrCategories <- unique(df2[df2$subcategory %in% subCategories, 2])
if (length(currentAggrCats) == 0 ) {
currentAggrCats <- NA
}
df1$aggregatedCategories[[i]] <- currentAggrCats
}
数据如下所示:
df1:
name sex categories_split
===== === ================
john m c(tiger)
clara f c(crocodile)
ben m c(butterfly, metalmarks)
df2:
subcategory category
=========== ============
tiger mammal
crocodile reptile
butterfly insect
metalmark insect
请注意,由于数据结构(不幸的是给出),df2中可能有多个匹配,可能是唯一的。
非常感谢你的帮助!
答案 0 :(得分:0)
这是一个基本解决方案:
#unlist the categories_split column
namedf <- do.call(rbind, by(df1, df1$name, function(x) {
data.frame(name=x$name, sex=x$sex, categories_split=unlist(x$categories_split))
}))
rownames(namedf) <- NULL
#perform lookup
namedf$category <- df2$category[match(namedf$categories_split, df2$subcategory)]
namedf
或data.table
解决方案:
library(data.table)
setDT(df1)
setDT(df2)
df2[
df1[.(name, sex), .(categories_split=unlist(categories_split)), by=.(name, sex), on=.(name, sex)],
on=c("subcategory"="categories_split")]
数据:
df1 <- data.frame(name=c("john","clara","ben"),
sex=c("m","f","m"))
df1$categories_split <- list("tiger", "crocodile", c("butterfly","metalmark"))
df2 <- read.table(text="subcategory category
tiger mammal
crocodile reptile
butterfly insect
metalmark insect", header=TRUE)
答案 1 :(得分:0)
取消列出并匹配df1
中的列与df2
idx <- match(unlist(df1$categories_split), df2$subcategory)
将重新列出的匹配项添加到原始数据中;这利用unlist()
/ relist()
语义来保留原始几何。
df1$aggregate <- relist(df2$category[idx], df1$categories_split)
在构建df1时使用stringsAsFactors = FALSE
,或在重定位期间使用as.character(df2$category[idx1])
以避免将因子强制转换为整数。根据需要进行后处理,例如,
df1$aggregate = lapply(df1$aggregate, unique)
如果期望聚合列包含单个元素,请使用vapply(df1$aggregate, unique, character(1))
。