【问题标题】:Condtional match columns across different dataframes跨不同数据框的条件匹配列
【发布时间】:2022-01-11 14:46:09
【问题描述】:

我正在处理两个数据集 - 一组有成对的项目:

original <- data.frame(label1 = c("cat", "cat", "dog", "dog", "cat", "tiger", "tiger", "cow"),
                      label2 = c("dog", "dog", "cat", "cat", "dog", "cow", "cow", "tiger"))
original
  label1 label2
1    cat    dog
2    cat    dog
3    dog    cat
4    dog    cat
5    cat    dog
6  tiger    cow
7  tiger    cow
8    cow  tiger

第二个数据集包含第一组项目的索引代码:

index <- data.frame(item = c("cat", "dog", "tiger", "cow"),
                    code = c(1, 0, 1, 0))
index
   item code
1   cat    1
2   dog    0
3 tiger    1
4   cow    0

我正在寻找一种方法来创建两个新列:tag0 和 tag1,使其看起来像这样:

new <- data.frame(label1 = c("cat", "cat", "dog", "dog", "cat", "tiger", "tiger", "cow"),
                  label2 = c("dog", "dog", "cat", "cat", "dog", "cow", "cow", "tiger"),
                  tag1 = c("cat", "cat", "cat", "cat", "cat", "tiger", "tiger", "tiger"),
                  tag0 = c("dog", "dog", "dog", "dog", "dog", "cow", "cow", "cow"))
new
  label1 label2  tag1 tag0
1    cat    dog   cat  dog
2    cat    dog   cat  dog
3    dog    cat   cat  dog
4    dog    cat   cat  dog
5    cat    dog   cat  dog
6  tiger    cow tiger  cow
7  tiger    cow tiger  cow
8    cow  tiger tiger  cow

tag0指的是code=0对应的标签,tag1指的是index数据帧中code=1对应的标签。

谁能帮助我提供基于tidyverse 的解决方案?

【问题讨论】:

  • 这个index 架构有点奇怪。 {label1, label2} 到 (tag1, tag0) 的逻辑映射是什么?我的猜测是,无论顺序如何,{cat, dog} 与 {dog, cat} 的处理方式相同,其中 cat 和 dog 总是配对在一起; tiger 和 cow 也是如此。但这在问题中并没有说清楚......
  • index 包含来自original 的所有唯一标签的列表,这些标签存储为item,具有code 值。虽然项目(狗或猫)可能以相同的概率出现在一行内的label1 或label2,但tag 指的是它们的code 值。
  • 感谢您接受我的回答!我刚刚用一个额外的解决方案更新了我的答案,它具有更优雅的工作流程并且更灵活。
  • 基本上,c_across (label1 | label2) 从行1 获取c("cat", "dog"),从行c("cow", "tiger") 获取c("cow", "tiger"),等等;与rowwise() 一起使用时。这让我们检查item 是否与该行中的一个标签匹配。具体来说,它允许我们对匹配正则表达式 label\d+ 的所有 label* 列执行此操作。
  • 然而如果你尝试c(label1 | label2),你会得到一个长向量,基本上是label1 列堆叠在label2 列上:c(c("cat", "cat", "dog", ..., "tiger", "cow"), c("dog", "dog", "cat", ..., "cow", "tiger"))。当然,这对于每行内的比较是无用的。

标签: r string-matching


【解决方案1】:

tidyverse 中有两个解决方案。虽然第一种适用于这种特殊情况,但我更喜欢第二种,它更优雅且可扩展。

解决方案 1:JOIN 对应每个 label*

首先导入tidyverse 并生成数据集original 和index。

library(tidyverse)


# ...
# Code to generate 'original' and 'index' datasets.
# ...

然后应用此工作流程。

original %>%
  # Uniquely identify each row (for pivoting later).
  mutate(row_id = row_number()) %>%
  # Match 'label1' to the tags.
  left_join(
    index,
    by = c("label1" = "item"),
    keep = TRUE
  ) %>%
  # Match 'label2' to the tags.
  left_join(
    index,
    by = c("label2" = "item"),
    keep = TRUE,
    suffix = c(".1", ".2")
  ) %>%
  # Pivot 'item.1 | ... | item.n | code.1 | ... | code.n' into a consolidated
  # 'item | code' form.
  pivot_longer(
    cols = matches("^(item|code)\\.(\\d+)?$"),
    names_pattern = "^(item|code)\\.(\\d+)?$",
    names_to = c(".value", NA)
  ) %>%
  # Pivot back into a 'tag1 | tag0' form.
  pivot_wider(
    values_from = item,
    names_from = code,
    names_glue = "tag{code}"
  ) %>%
  # Omit unique identifier.
  select(!row_id)

结果

鉴于 original 和 index 数据集,就像这里复制的那样

original <- data.frame(
  label1 = c("cat", "cat", "dog", "dog", "cat", "tiger", "tiger", "cow"),
  label2 = c("dog", "dog", "cat", "cat", "dog", "cow", "cow", "tiger")
)

index <- data.frame(
  item = c("cat", "dog", "tiger", "cow"),
  code = c(1, 0, 1, 0)
)

此解决方案应产生以下结果:

# A tibble: 8 x 4
  label1 label2 tag1  tag0 
  <chr>  <chr>  <chr> <chr>
1 cat    dog    cat   dog  
2 cat    dog    cat   dog  
3 dog    cat    cat   dog  
4 dog    cat    cat   dog  
5 cat    dog    cat   dog  
6 tiger  cow    tiger cow  
7 tiger  cow    tiger cow  
8 cow    tiger  tiger cow  

注意

如果您的original 数据集还有更多label* 列,则您需要为这些列中的每一列执行一个额外的JOIN。


解决方案 2:单个 CROSS JOIN

这是一个更优雅的工作流程,它也更灵活:它适用于original 中任意数量的label* 列和任意一组@ 987654340@s in index.

original %>%
  # Uniquely identify each row (for pivoting later).
  mutate(row_id = row_number()) %>%
  # Perform a cross-join compare every 'item' to every 'label*'.
  full_join(
    index,
    by = character()
  ) %>%
  # Keep only those rows where 'item' matches a 'label*'.
  rowwise() %>%
  filter(item %in% c_across(matches("^label\\d+"))) %>%
  # Pivot into a 'tag1 | tag0' form.
  pivot_wider(
    values_from = item,
    names_from = code,
    names_glue = "tag{code}"
  ) %>%
  # Omit unique identifier.
  select(!row_id)

结果

结果保持不变。

# A tibble: 8 x 4
  label1 label2 tag1  tag0 
  <chr>  <chr>  <chr> <chr>
1 cat    dog    cat   dog  
2 cat    dog    cat   dog  
3 dog    cat    cat   dog  
4 dog    cat    cat   dog  
5 cat    dog    cat   dog  
6 tiger  cow    tiger cow  
7 tiger  cow    tiger cow  
8 cow    tiger  tiger cow  

注意

唯一的缺点是它必须执行 CROSS JOIN,这可能会影响更大数据集的性能。

【讨论】:

    【解决方案2】:

    另一种可能的解决方案:

    library(tidyverse)
    
    original <- data.frame(label1 = c("cat", "cat", "dog", "dog", "cat", "tiger", "tiger", "cow"),
                           label2 = c("dog", "dog", "cat", "cat", "dog", "cow", "cow", "tiger"))
    
    index <- data.frame(item = c("cat", "dog", "tiger", "cow"),
                        code = c(1, 0, 1, 0))
    
    original %>% 
      full_join(index, by=c("label1" = "item")) %>% 
      full_join(index, by=c("label2" = "item")) %>% 
      mutate(tag1 = if_else(code.x == 1, label1, label2)) %>% 
      mutate(tag2 = if_else(code.y == 1, label1, label2)) %>% 
      select(!starts_with("code"))
    
    #>   label1 label2  tag1 tag2
    #> 1    cat    dog   cat  dog
    #> 2    cat    dog   cat  dog
    #> 3    dog    cat   cat  dog
    #> 4    dog    cat   cat  dog
    #> 5    cat    dog   cat  dog
    #> 6  tiger    cow tiger  cow
    #> 7  tiger    cow tiger  cow
    #> 8    cow  tiger tiger  cow
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-14
      • 2021-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-30
      相关资源
      最近更新 更多