我尝试根据每个数据框中的元素连接两个数据框df和myData。 df中的列有目的地包含嵌套列表,如果嵌套列表中的元素与myData的元素匹配,我想加入。我希望在df(左连接)中保留不匹配的行。
这是一个例子,首先没有df中的嵌套列表。
df = data.frame(a=1:5)
df$x1= c("a", "b", "g", "a", "a")
str(df)
'data.frame': 5 obs. of 2 variables:
$ a : int 1 2 3 4 5
$ x1: chr "a" "b" "g" "a" ...
myData <- data.frame(x1=c("a", "g", "q"), x2= c("za", "zg", "zq"), stringsAsFactors = FALSE)
现在,我们可以加入第x1列:
#using a for loop
df$x2 <- NA
for(id in 1:nrow(myData)){
df$x2[df$x1 %in% myData$x1[id]] <- myData$x2[id]
}
或者使用dplyr:
library(dplyr)
df = data.frame(a=1:5)
df$x1= c("a", "b", "g", "a", "a")
df %>%
left_join(myData)
现在,考虑使用嵌套列表的df。
l1 = list(letters[1:5])
l2 = list(letters[6:10])
df = data.frame(a=1:5)
df$x1= c("a", "b", "g", l1, l2)
使用for循环无法匹配嵌套列表的元素,正如我们所期望的那样:
df$x2 <- NA
for(id in 1:nrow(myData)){
df$x2[df$x1 %in% myData$x1[id]] <- myData$x2[id]
}
输出:
df
a x1 x2
1 1 a za
2 2 b <NA>
3 3 g zg
4 4 a, b, c, d, e <NA>
5 5 f, g, h, i, j <NA>
使用dplyr:
df %>%
left_join(myData)
抛出错误:
Joining by: c("x1", "x2")
Error: cannot join on column 'x1'
我认为解决方案需要取消嵌套列表,但尚未解决如何将unlist函数用于上述策略。
我也尝试过使用data.table包。如何使用data.table完成此操作可能是一个额外的问题。但是,就data.table处理数据框中的列表而言,我想包含它,因为它可能提供最佳解决方案。
我的实际数据大概是100,000行,因此基数为R的列表上的匹配可能是性能烦恼(考虑data.table的另一个原因?)
Fwiw,在数据框中使用嵌套列表(和其他结构)是我经常在Python中做的事情,可能有一种更好的方法来构建R中的数据。
思想?
答案 0 :(得分:3)
这是一个可能的解决方案:
df$x2 <- NA
for(id in 1:nrow(df))
{
df$x2[id] <- ifelse(
length(ff <- myData$x2[which(myData$x1 == intersect(df$x1[[id]], myData$x1))])==0,
NA,
ff)
}
df
# a x1 x2
#1 1 a za
#2 2 b <NA>
#3 3 g zg
#4 4 a, b, c, d, e za
#5 5 f, g, h, i, j zg
上述解决方案存在一些潜在的缺陷。例如,如果我们将l1
更改为有2个可能的匹配项(例如&#34; a&#34;和&#34; g&#34;):
l1 = list(letters[1:7])
df$x1= c("a", "b", "g", l1, l2)
此解决方案不会捕获两个匹配项,因为:
df$x2 <- NA
for(id in 1:nrow(df))
{
df$x2[id] <- ifelse(
length(ff <- myData$x2[which(myData$x1 == intersect(df$x1[[id]], myData$x1))])==0,
NA,
ff)
}
Warning message:
In myData$x1 == intersect(df$x1[[id]], myData$x1) :
longer object length is not a multiple of shorter object length
如果需要,您可以修改它以允许多个匹配。以下是两种不同的方法,一种方法使用paste
,另一种方式使用list
方式。
df$x2 <- NA
for(id in 1:nrow(df))
{
df$x2[id] <-
paste(if (length(ff <- myData$x2[which(myData$x1 %in% intersect(df$x1[[id]], myData$x1))])==0)
NA else
ff, collapse=", ")
}
df$x2 <- NA
for(id in 1:nrow(df))
{
df$x2[id] <-
list(if (length(ff <- myData$x2[which(myData$x1 %in% intersect(df$x1[[id]], myData$x1))])==0)
NA else
ff)
}
两者都会返回以下内容,但底层结构会有所不同:
a x1 x2
1 1 a za
2 2 b NA
3 3 g zg
4 4 a, b, c, d, e, f, g za, zg
5 5 f, g, h, i, j zg
答案 1 :(得分:2)
我认为这可能有效。当你以递归方式操作列表时,最好编写一个辅助函数来获取值。
getMatch <- function(x, y) {
z <- y[[2]][sort(match(x, y[[1]]))]
z[!length(z)] <- NA
z
}
> rapply(unname(df[-1]), getMatch, y = myData)
# [1] "za" NA "zg" "za" "zg"
或者我们可以使用within
> within(df, { x2 <- sapply(df$x1, getMatch, y = myData) })
# a x1 x2
#1 1 a za
#2 2 b <NA>
#3 3 g zg
#4 4 a, b, c, d, e za
#5 5 f, g, h, i, j zg
答案 2 :(得分:2)
这是data.table
选项:
library(data.table)
# convert to data.table in place
setDT(myData)
# using Frank's extended example
l1 = list(letters[1:7])
l2 = list(letters[6:10])
dt = data.table(a=1:5, x1 = c("a", "b", "g", l1, l2))
# unlist the lists (and to be honest, that's how I would store the data,
# I think the column of lists is a bad idea), then set the keys, merge, and
# go back to columns of lists
setkey(dt[, unlist(x1), by = a], V1)[myData, x2 := i.x2][,
list(x1 = list(V1), x2 = list(na.omit(x2))), keyby = a]
# a x1 x2
#1: 1 a za
#2: 2 b
#3: 3 g zg
#4: 4 a,b,c,d,e,f, za,zg
#5: 5 f,g,h,i,j zg