【问题标题】:How to merge lists of vectors based on one vector belonging to another vector?如何基于属于另一个向量的一个向量合并向量列表?
【发布时间】:2018-01-06 11:59:51
【问题描述】:

在 R 中,我有两个包含列表列的数据框

d1 <- data.table(
  group_id1=1:4
)
d1$Cat_grouped <- list(letters[1:2],letters[3:2],letters[3:6],letters[11:12] )

d_grouped <- data.table(
  group_id2=1:4
)
d_grouped$Cat_grouped <- list(letters[1:5],letters[6:10],letters[1:2],letters[1] )

我想根据d1$Cat_grouped 中的向量包含在d_grouped$Cat_grouped 中的向量中来合并这两个data.tables

更准确地说,可能有两个匹配条件:

a)d1$Cat_grouped的每个向量的所有元素必须在d_grouped$Cat_grouped的匹配向量中

导致以下匹配:

result_a <- data.table(
   group_id1=c(1,2)
   group_id2=c(1,1)
)

b)d1$Cat_grouped的每个向量中至少有一个元素必须在d_grouped$Cat_grouped的匹配向量中

导致以下匹配:

result_b <- data.table(
  group_id1=c(1,2,3,3),
  group_id2=c(1,1,1,2)
)

如何实现 a) 或 b) ?最好以 data.table 的方式。

EDIT1:添加了a)和b)的预期结果

EDIT2:向 d_grouped 添加了更多组,因此分组变量重叠。这打破了一些建议的解决方案

【问题讨论】:

    标签: r list merge data.table


    【解决方案1】:

    所以我认为长格式更好,虽然我的回答有点迂回。我敢打赌,如果有人使用更简洁的数据表,可以在更少的步骤中完成此操作,但这是我所拥有的:

    首先,让我们解压缩示例数据中的向量:

    d1_long <- d1[, list(cat=unlist(Cat_grouped)), group_id1]
    d_grouped_long <- d_grouped[, list(cat=unlist(Cat_grouped)), group_id2]
    

    现在,我们可以合并各个元素:

    result_b <- merge(d1_long, d_grouped_long, by='cat')
    

    根据我们的示例,您似乎实际上不需要知道哪些元素是匹配的一部分...

    result_b[, cat := NULL]
    

    最后,我的答案是重复的 group_id 对,因为它为每个成对匹配获得一个连接,而不仅仅是向量级匹配。所以我们可以将它们独一无二。

    result_b <- unique(result_b)
    

    这是我的结果_b:

       group_id.1 group_id.2
    1:          1          1
    2:          2          1
    3:          3          1
    4:          3          2
    

    我们可以使用 b 作为 a 的中间步骤,因为拥有任何共同的元素是拥有所有共同元素的子集。

    让我们合并原始表,看看候选者在子向量和向量方面是什么

    result_a <- merge(result_b, d1, by = 'group_id1')
    result_a <- merge(result_a, d_grouped, by = 'group_id2')
    

    所以现在,如果 Cat_grouped.x 的长度与关于 Cat_grouped.x 为 %in% Cat_grouped.y 的 TRUE 数匹配,那就是宾果游戏。

    我尝试了一些干净的方法,但在数据表中包含列表的怪异挫败了最明显的尝试。不过这似乎可行:

    我们添加一个row列来操作by

    result_a[, row := 1:.N]
    

    现在让我们获取匹配的长度和数量...

    result_a[, x.length := length(Cat_grouped.x[[1]]), row]
    result_a[, matches := sum(Cat_grouped.x[[1]] %in% Cat_grouped.y[[1]]), row]
    

    并过滤到长度和匹配项相同的行

    result_a <- result_a[x.length==matches]
    

    【讨论】:

    • 太棒了。谢了。您提供了问题 b) 的结果(我放错了预期的输出)。你敢解决问题 a) 吗?
    • 哈哈,我停了下来,因为您在问题中说“或”,所以我认为这可能就足够了。我一直在玩它,似乎更难,但我会继续戳它。
    • 对于 a) ,我想您可以以左连接方式进行合并,因此 d1_long 的所有元素都会被保留,即使是不匹配的元素。然后计算每个 group_id.x 的 group_id.y 的不同元素的数量。然后仅在此计数 ==1 并且 group_id.y 中没有 NA 时才保留
    • 我已经编辑了这个答案以包括我在 a) 中的破解。尽管您的描述听起来可能需要更少的中间步骤,因此可能更好!
    • 您的代码不适用于 OP 提供的数据:object 'group_id' not found
    【解决方案2】:

    此答案侧重于问题的 a) 部分。

    它遵循Harland's approach,但出于性能原因试图更好地利用data.table 成语,因为 OP 提到他的生产数据可能包含数百万个观察值。

    样本数据

    library(data.table)
    d1 <- data.table(
      group_id1 = 1:4,
      Cat_grouped = list(letters[1:2], letters[3:2], letters[3:6], letters[11:12]))
    
    d_grouped <- data.table(
      group_id2 = 1:2,
      Cat_grouped = list(letters[1:5], letters[6:10]))
    

    结果a)

    grp_cols <- c("group_id1", "group_id2")
    unique(d1[, .(unlist(Cat_grouped), lengths(Cat_grouped)), by = group_id1][
      d_grouped[, unlist(Cat_grouped), by = group_id2], on = "V1", nomatch = 0L][
        , .(V2, .N), by = grp_cols][V2 == N, ..grp_cols])
    
       group_id1 group_id2
    1:         1         1
    2:         2         1
    

    说明

    在将d1d_grouped 的列表元素扩展为长格式时,使用lengths() 函数确定d1 的列表元素数量。 lengths()(注意与 length() 的区别)获取列表中每个元素的长度,并在 R 3.2.0 中引入。

    在内连接之后(注意nomatch = 0L 参数),对grp_cols 的每个组合计算结果集中的行数(使用特殊符号.N)。只有结果集中的计数与列表的原始长度匹配的那些行才会被考虑。最后返回grp_cols的唯一组合。

    结果 b)

    结果 b) 可以通过省略计数内容从上述解决方案中得出:

    unique(d1[, unlist(Cat_grouped), by = group_id1][
      d_grouped[, unlist(Cat_grouped), by = group_id2], on = "V1", nomatch = 0L][
          , c("group_id1", "group_id2")])
    
       group_id1 group_id2
    1:         1         1
    2:         2         1
    3:         3         1
    4:         3         2
    

    【讨论】:

      【解决方案3】:

      另一种方式:

      交叉加入获取所有组 id 对:

      Y = CJ(group_id1=d1$group_id1, group_id2=d_grouped$group_id2)
      

      然后合并向量:

      Y = Y[d1, on='group_id1'][d_grouped, on='group_id2']
      
      #    group_id1 group_id2 Cat_grouped i.Cat_grouped
      # 1:         1         1         a,b     a,b,c,d,e
      # 2:         2         1         c,b     a,b,c,d,e
      # 3:         3         1     c,d,e,f     a,b,c,d,e
      # 4:         4         1         k,l     a,b,c,d,e
      # 5:         1         2         a,b     f,g,h,i,j
      # 6:         2         2         c,b     f,g,h,i,j
      # 7:         3         2     c,d,e,f     f,g,h,i,j
      # 8:         4         2         k,l     f,g,h,i,j
      

      现在您可以使用mapply 进行任何您喜欢的过滤:

      Y[mapply(function(u,v) all(u %in% v), Cat_grouped, i.Cat_grouped), 1:2]
      #    group_id1 group_id2
      # 1:         1         1
      # 2:         2         1
      
      Y[mapply(function(u,v) length(intersect(u,v)) > 0, Cat_grouped, i.Cat_grouped), 1:2]
      #    group_id1 group_id2
      # 1:         1         1
      # 2:         2         1
      # 3:         3         1
      # 4:         3         2
      

      【讨论】:

      • 这很好,很优雅。但在我的用例中,d1 和 d_grouped 可以有数百万个观察值。因此,就内存使用而言,仅以物理方式创建交叉连接将过于昂贵。
      猜你喜欢
      • 1970-01-01
      • 2020-01-19
      • 2011-04-23
      • 2016-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 1970-01-01
      相关资源
      最近更新 更多