如何使用另一个tibble的多个条件有条件地更新R tibble

How to conditionally update a R tibble using multiple conditions of another tibble

我有两个 table。我想使用多个条件使用第二个 table 更新第一个 table。在基础 R 中,我会使用 if...else 类型构造来执行此操作,但想知道如何使用 dplyr 来实现此操作。

要更新的table(添加了一个字段)如下所示:

> Intvs
# A tibble: 12 x 3
   Group  From    To
   <chr> <dbl> <dbl>
 1 A         0     1
 2 A         1     2
 3 A         2     3
 4 A         3     4
 5 A         4     5
 6 A         5     6
 7 B         0     1
 8 B         1     2
 9 B         2     3
10 B         3     4
11 B         4     5
12 B         5     6

我想用来进行更新的标题如下所示:

 >Zns
# A tibble: 2 x 4
  Group From     To  Zone
  <chr> <chr> <dbl> <dbl>
1 A     X         1     5
2 B     Y         3     4

我想用 Zns tibble 更新 Intvs tibble,使用字段 == Group、>= From 和 <= To 来控制更新。预期的输出应该是这样的

> Intvs
# A tibble: 12 x 4
   Group  From    To  Zone
   <chr> <dbl> <dbl> <chr>
 1 A         0     1  NA
 2 A         1     2  X
 3 A         2     3  X
 4 A         3     4  X
 5 A         4     5  X
 6 A         5     6  NA
 7 B         0     1  NA
 8 B         1     2  NA
 9 B         2     3  NA
10 B         3     4  Y
11 B         4     5  NA
12 B         5     6  NA

使用 dplyr 执行此操作的最有效方法是什么?

下面的代码应该使虚拟 tables Intv 和 Zns

# load packages
require(tidyverse)

# Intervals table
a <- c(rep("A", 6), rep("B", 6))
b <- c(seq(0,5,1), seq(0,5,1) )
c <- c(seq(1,6,1), seq(1,6,1))
Intvs <- bind_cols(a, b, c) 
names(Intvs) <- c("Group", "From", "To")

# Zones table
a <- c("A", "B")
b <- c("X", "Y")
c <- c(1, 3)
d <- c(5, 4)
Zns <- bind_cols(a, b, c, d) 
names(Zns) <- c("Group", "From", "To", "Zone")

这是我得到的最接近的。它没有给出预期的输出:

library(dplyr)
left_join(Intvs, Zns, by="Group") %>% 
  group_by(Group) %>% 
  mutate(Zone1 = case_when(From.x <= Zone & From.x >= To.y ~ From.y)) %>% 
  select(Group, From=From.x, To=To.x, Zone = Zone1)
   Group  From    To Zone 
   <chr> <dbl> <dbl> <chr>
 1 A         0     1 NA   
 2 A         1     2 X    
 3 A         2     3 X    
 4 A         3     4 X    
 5 A         4     5 X    
 6 A         5     6 X    
 7 B         0     1 NA   
 8 B         1     2 NA   
 9 B         2     3 NA   
10 B         3     4 Y    
11 B         4     5 Y    
12 B         5     6 NA 

不确定为什么第一行没有给出 NA,因为 0 - 1 不在 1 - 5 的范围内。

首先 left_join 使用 Group 列的两个数据帧。在这里,我将后缀“_Zns”分配给 Zns 数据帧中的值。然后使用单个 case_when 或 (ifelse) 语句将 NA 分配给不适合该范围的行。最后,删除以 Zns.

结尾的列
library(dplyr)

left_join(Intvs, Zns, by = "Group", suffix = c("", "_Zns")) %>% 
  mutate(Zone = case_when(From >= From_Zns & To <= To_Zns ~ Zone,
                           TRUE ~ NA_character_)) %>% 
  select(-ends_with("Zns"))

# A tibble: 12 × 4
   Group  From    To Zone 
   <chr> <dbl> <dbl> <chr>
 1 A         0     1 NA   
 2 A         1     2 X    
 3 A         2     3 X    
 4 A         3     4 X    
 5 A         4     5 X    
 6 A         5     6 NA   
 7 B         0     1 NA   
 8 B         1     2 NA   
 9 B         2     3 NA   
10 B         3     4 Y    
11 B         4     5 NA   
12 B         5     6 NA   

数据

请注意,我已经更改了您在 Zns 数据框中的列名顺序。

a <- c(rep("A", 6), rep("B", 6))
b <- c(seq(0,5,1), seq(0,5,1) )
c <- c(seq(1,6,1), seq(1,6,1))
Intvs <- bind_cols(a, b, c) 
names(Intvs) <- c("Group", "From", "To")

# Zones table
a <- c("A", "B")
b <- c("X", "Y")
c <- c(1, 3)
d <- c(5, 4)
Zns <- bind_cols(a, b, c, d)
colnames(Zns) <- c("Group", "Zone", "From", "To")

使用 non-equi 从 data.table

加入
library(data.table)
setDT(Intvs)[Zns, Zone := Zone, on = .(Group, From >= From, To <= To)]

-输出

> Intvs
     Group  From    To   Zone
    <char> <num> <num> <char>
 1:      A     0     1   <NA>
 2:      A     1     2      X
 3:      A     2     3      X
 4:      A     3     4      X
 5:      A     4     5      X
 6:      A     5     6   <NA>
 7:      B     0     1   <NA>
 8:      B     1     2   <NA>
 9:      B     2     3   <NA>
10:      B     3     4      Y
11:      B     4     5   <NA>
12:      B     5     6   <NA>