【问题标题】:Find overlapping ranges by factor level using R使用 R 按因子级别查找重叠范围
【发布时间】:2019-02-21 14:58:42
【问题描述】:

我需要在单个数据集中找到重叠范围,但需要为每个 ID 或因子级别找到它们。任何帮助将不胜感激!

    library(dplyr)

df_foo = read.table(
  textConnection("Class    Min  Max
A    500  630
A    100  200
B    100  200
A    210  310
A    200  210
B    210  310
A    510  530
B    200  210
A    705  800
B    500  630
B    510  530
B    705  800"), header = TRUE
)

c = outer(df_foo$Max, df_foo$Min, ">")
d = outer(df_foo$Min, df_foo$Max, "<")

df_foo %>% 
  mutate(Overlap = apply(c & d, 1, sum) > 1 
  )

我得到的结果如下:

   Class Min Max Overlap
1      A 500 630    TRUE
2      A 100 200    TRUE
3      B 100 200    TRUE
4      A 210 310    TRUE
5      A 200 210    TRUE
6      B 210 310    TRUE
7      A 510 530    TRUE
8      B 200 210    TRUE
9      A 705 800    TRUE
10     B 500 630    TRUE
11     B 510 530    TRUE
12     B 705 800    TRUE

但我想像这样找到 A 和 B 的每个级别之间的重叠:

   Class Min Max Overlap
1      A 500 630    TRUE
2      A 100 200    FALSE
3      B 100 200    FALSE
4      A 210 310    FALSE
5      A 200 210    FALSE
6      B 210 310    FALSE
7      A 510 530    TRUE
8      B 200 210    FALSE
9      A 705 800    FALSE
10     B 500 630    TRUE
11     B 510 530    TRUE
12     B 705 800    FALSE

【问题讨论】:

    标签: r


    【解决方案1】:

    我在data.table 有答案,翻译成dplyr 应该是直截了当的。这个想法是为每个类创建一个先前累积最大值的向量:

    df_foo <- setDT(df_foo)
    df_foo[, shiftedmaxmax := c(NA,cummax(Max)[1:(.N-1)]),by = Class  ]
    
    
        Class Min Max shiftedmaxmax
     1:     A 100 200            NA
     2:     A 200 210           200
     3:     A 210 310           210
     4:     A 500 630           310
     5:     A 510 530           630
     6:     A 705 800           630
     7:     B 100 200            NA
     8:     B 200 210           200
     9:     B 210 310           210
    10:     B 500 630           310
    11:     B 510 530           630
    12:     B 705 800           630
    

    并将最小值与Maximum之前的最大值进行比较(所以如果低于这个值,则当前行在前一行的范围内)

    df_foo[,superposed := Min < shiftedmaxmax]
    
        Class Min Max shiftedmaxmax superposed
     1:     A 100 200            NA         NA
     2:     A 200 210           200      FALSE
     3:     A 210 310           210      FALSE
     4:     A 500 630           310      FALSE
     5:     A 510 530           630       TRUE
     6:     A 705 800           630      FALSE
     7:     B 100 200            NA         NA
     8:     B 200 210           200      FALSE
     9:     B 210 310           210      FALSE
    10:     B 500 630           310      FALSE
    11:     B 510 530           630       TRUE
    12:     B 705 800           630      FALSE
    

    第一个叠加的丢失了,可以这样获取:

    df_foo[,superposedsource :=  Max %in% shiftedmaxmax[superposed],by = Class]
    df_foo[,superposedtot := ifelse((superposed | superposedsource) &,T,F)]
    
        Class Min Max shiftedmaxmax superposed superposedsource superposedtot
     1:     A 100 200            NA         NA            FALSE            NA
     2:     A 200 210           200      FALSE            FALSE         FALSE
     3:     A 210 310           210      FALSE            FALSE         FALSE
     4:     A 500 630           310      FALSE             TRUE          TRUE
     5:     A 510 530           630       TRUE            FALSE          TRUE
     6:     A 705 800           630      FALSE            FALSE         FALSE
     7:     B 100 200            NA         NA            FALSE            NA
     8:     B 200 210           200      FALSE            FALSE         FALSE
     9:     B 210 310           210      FALSE            FALSE         FALSE
    10:     B 500 630           310      FALSE             TRUE          TRUE
    11:     B 510 530           630       TRUE            FALSE          TRUE
    12:     B 705 800           630      FALSE            FALSE         FALSE
    

    【讨论】:

    • 谢谢,不幸的是范围不是升序或降序。他们都混在一起了。我已经编辑了数据集以反映这一点。
    【解决方案2】:

    另一种data.table 方法。
    样本数据/范围的顺序与此答案无关...foverlaps() 为您完成所有艰苦的工作。

    样本数据

    library( data.table )
    dt <- as.data.table( df_foo )
    

    代码

    #set key for the data.table
    setkey(dt, Min, Max)
    #perform overlap join, keep only joined ranges where the class is the same, and Min and Max are not the same.
    result <- foverlaps( dt, dt )[ Class == i.Class & !(Min == i.Min | Max == i.Max | Min == i.Max | Max == i.Min), ]
    #create a logical vector (i.e. Overlap) by checking if the (pasted) combination of
    #Class, Min and Max exists in both 'dt' and 'result'
    dt[ , Overlap := paste0( Class, Min, Max ) %in% paste0( result$Class, result$Min, result$Max) ][]
    
    #     Class Min Max Overlap
    #  1:     A 100 200   FALSE
    #  2:     B 100 200   FALSE
    #  3:     A 200 210   FALSE
    #  4:     B 200 210   FALSE
    #  5:     A 210 310   FALSE
    #  6:     B 210 310   FALSE
    #  7:     A 500 630    TRUE
    #  8:     B 500 630    TRUE
    #  9:     A 510 530    TRUE
    # 10:     B 510 530    TRUE
    # 11:     A 705 800   FALSE
    # 12:     B 705 800   FALSE
    

    【讨论】:

      【解决方案3】:

      dplyr

      df=df_foo%>%group_by(Class)%>%
        mutate(Overlap=if_else(Min<lag(Max,order_by=Class),TRUE,FALSE))
      df$Overlap[which(df$Overlap==TRUE)-1]=TRUE
      df$Overlap[which(is.na(df$Overlap))]=FALSE
      
      > df
      # A tibble: 12 x 4
      # Groups:   Class [2]
         Class   Min   Max Overlap
         <fct> <dbl> <dbl> <lgl>  
       1 A       100   200 FALSE  
       2 A       200   210 FALSE  
       3 A       210   310 FALSE  
       4 A       500   630 TRUE   
       5 A       510   530 TRUE   
       6 A       705   800 FALSE  
       7 B       100   200 FALSE  
       8 B       200   210 FALSE  
       9 B       210   310 FALSE  
      10 B       500   630 TRUE   
      11 B       510   530 TRUE   
      12 B       705   800 FALSE 
      

      此代码假定您的值按升序排列,因为它只检查前一行。

      编辑
      不是最漂亮的,但很有效。

      df_foo$Class=as.character.factor(df_foo$Class)
      df_foo=as.data.frame(df_foo)
      df_foo$Overlap=rep("FALSE",nrow(df_foo))
      for (i in 1:nrow(df_foo)){
        aux=FALSE
        class=df_foo$Class[i]
        df=df_foo[-i,]%>%filter(.,Class==class)
        for (j in 1:nrow(df)){
          if (df_foo[i,"Min"]<df[j,"Max"] & df_foo[i,"Max"] > df[j,"Min"]){
            aux=TRUE
          }
        }
        df_foo[i,"Overlap"]=aux
      }
      
      
      > df_foo
         Class Min Max Overlap
      1      A 500 630    TRUE
      2      A 100 200   FALSE
      3      B 100 200   FALSE
      4      A 210 310   FALSE
      5      A 200 210   FALSE
      6      B 210 310   FALSE
      7      A 510 530    TRUE
      8      B 200 210   FALSE
      9      A 705 800   FALSE
      10     B 500 630    TRUE
      11     B 510 530    TRUE
      12     B 705 800   FALSE
      

      dplyr 一定有办法做到这一点,但我想不通。发生的事情是它循环遍历df_foo 的每一行;它与同一组的所有其他行生成一个dataframe,并比较是否有任何重叠(min&lt;maxmax&lt;min

      【讨论】:

      • 谢谢,我应该更清楚,但在我的实际数据集中,所有值都混合在一起,不遵循升序或降序
      • 可以有多个重叠吗?如果没有,只需按Class 的升序重新排列代码就可以了。如果不是,则应明确更改。
      • 可能有多个重叠。我已经编辑了示例以显示数据是如何混合的。感谢您的帮助!
      猜你喜欢
      • 1970-01-01
      • 2016-12-24
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      • 1970-01-01
      • 2020-08-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多