使用来自另一个df的匹配信息向df添加列(优雅地,没有循环)

时间:2018-02-27 10:04:46

标签: r dataframe

我正在尝试找到一种方法,使用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中可能有多个匹配,可能是唯一的。

非常感谢你的帮助!

2 个答案:

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