【问题标题】:How to group rows in a range and consider a 3rd column?如何对范围内的行进行分组并考虑第三列?
【发布时间】:2021-02-19 20:26:26
【问题描述】:

我有一个基因数据集,我想在其中对基因组中物理上靠得很近的基因变异/行进行分组。我想将基因组中每条染色体的特定位置范围内的基因分组 (chrom)。

我的 'spots' 数据集是变体/行需要在一个范围内的位置,并且看起来像:

 chrom      low       high
   1        500       1700
   1        19500     20600
   5        400       1500

我的 lowhigh 列是我想查看下一个数据集中是否有任何行落入的范围,同时还考虑到染色体 (chrom) 也必须匹配。具有唯一范围和色度组合的每一行都是它自己的组,我正在查看我的其他数据集中是否有任何内容。

我的其他数据集有一个位置值,我希望查看它是否适合上述任何范围并匹配 chrom,以便将其标记为对应于该范围,然后我可以将位置分组到相同的范围和色度:

Gene   chrom position 
Gene1   1    1200          
Gene2   1    10000        
Gene3   5    500 
Gene4   5    560
Gene5   1    20100           

我已经尝试使用 group_by()between() 来设置范围,因为看到其他与日期/时间范围类似的问题,但我很难考虑匹配染色体的需要(@ 987654332@),然后再搜索范围。

输出如下:

Gene   chrom position   Group 
Gene1   1    1200          1  #position is in one of the ranges and matches the chrom so is in a group    
Gene2   1    10000        NA  #does not fit into any range on chrom 2 (no matches)
Gene3   5    500           2  #position is in one of the ranges and matches the chrom so is in a group
Gene4   5    560           2  #position is in the same range and chrom as above so joins that group
Gene5   1    20100         3  #position matches a chrom and range and so gets a group corresponding to that particular chrom and range
  • Gene3 和 Gene4 不在第 1 组中,因为它们位于不同的 chrom 上,但它们确实与 chrom 匹配,并且在我的第一个数据集的第 3 行范围内 - 所以它们属于对应于该范围和色度。
  • Gene5 与 Gene1 不在同一组中,因为它们与 chrom 匹配时,它们位于 lowhigh 的不同范围内,因此请为独特的范围获取自己的组。

因此,我正在为同一lowhigh 上同一chrom 上的同一范围内的所有行创建一个共享编号的Group 列,如果它们的位置在任何范围内都不匹配,则为NA和第一个数据集中的铬。

输入数据:

df1 <- 
structure(list(chrom = c(1L, 1L, 5L), 
   low = c(500L, 19500L, 400L), high = c(1700L, 20600L, 1500L
    )), row.names = c(NA, -3L), class = c("data.table", "data.frame"))

df2 <- 
structure(list(Gene = c("Gene1", "Gene2", "Gene3", "Gene4", "Gene5"
), chrom = c(1L, 1L, 5L, 5L, 1L), position = c(1200L, 10000L, 
500L, 560L, 20100L)), row.names = c(NA, -5L), class = c("data.table", 
"data.frame"))

我还在考虑为每个唯一范围和色度组合提供我的第一个数据集唯一标识符,然后将该标识符分配给数据集 2 中也匹配该组合的任何行,以便该标识符创建我的组号列。虽然我的真实数据是 2.3k 行范围和 82k 行以匹配到共享组,所以我在运行 dplyr 选项时也遇到了问题,我通常会尝试。

【问题讨论】:

  • 这篇文章可能会有所帮助 - stackoverflow.com/questions/24480031/…
  • 谢谢,我会考虑使用重叠
  • 我已更新您的问题,以将提供的数据集与您实际显示的数据集相匹配。关于问题本身,我会做df2[, grp := df1[.SD, which = TRUE, on = .(chrom, low &lt;= position, high &gt;= position)]](如果你真的不关心团体顺序)
  • 这不是 Ronak 链接的帖子的副本:stackoverflow.com/questions/24480031/…
  • 我正在尝试对匹配进行分组,并尝试考虑将在更大的真实数据上运行的代码。重叠选项不适用于我的真实数据,我要么出错,要么无限期运行。

标签: r dplyr data.table


【解决方案1】:

您可以在data.table 中使用非等值连接:

library(data.table)
df1 <- setDT(df1)
df2 <- setDT(df2)

df1[,group := 1:.N]
df1[df2,on = .(chrom, low < position, high > position)]


   chrom   low  high group  Gene
1:     1  1200  1200     1 Gene1
2:     1 10000 10000    NA Gene2
3:     5   500   500     3 Gene3
4:     5   560   560     3 Gene4
5:     1 20100 20100     2 Gene5

这里我先为df1的每一行设置一个组。合并后,如果满足条件,则将该行关联到一个组。

非 equi 合并不是超级直观,但超级强大且明确:合并条件 .(chrom, low &lt; position, high &gt; position) 在字母上是您明确表示的(您想要相同的染色体,并且位置在低和高之间)。

data.table,当你这样做时

df1[df2,on = something]

您将df1df2 的行合并为满足on 所表达的条件。如果something只是df1df2的公共变量,那么就等价于

merge(df1,df2,all.y = T,by = "someting")

something 可以是两个data.tables 的变量之间的变量和条件列表。这里,.() 表示一个列表,.(chrom,low &lt; position, high &gt; position) 表示你在变量chrom(两个data.tables 之间相同)上合并,low &lt; positionhigh &gt; position。当你表达不等式时,你必须从主data.table的变量开始(df1这里),然后是子集data.table的变量(df2)。

使用不等式的非等式合并的输出用子集data.table的变量(即df2这里)替换主data.table(即df1)的不等式表示的变量,等等@ 987654344@ 和 high 变为 position。如果您想保留 lowhigh 值,您应该将它们复制到其他变量中,或者合并到这些变量的副本中。

您实际上可以进行相反的合并,我们将 df2df1 条目合并,条件相同:

df2[df1,on = .(chrom,position >low , position<high)]

    Gene chrom position position.1 group
1: Gene1     1      500       1700     1
2: Gene5     1    19500      20600     2
3: Gene3     5      400       1500     3
4: Gene4     5      400       1500     3

这里你将df1df2的条目满足on = .()表达的条件进行子集化,得到Gene实际属于一个组的列表(Gene2不在这里,因为它不匹配子集)。

和上面解释的一样,这里position变成lowhigh


编辑

我刚刚看到@DavidArenburg 的评论,这是我提出和解释的更浓缩和更好的版本:

df2[, grp := df1[.SD, which = TRUE, on = .(chrom, low <= position, high >= position)]]

使用which = TRUE直接将非等值合并的结果df1[df2,on = .(chrom, low &lt; position, high &gt; position)]关联到组变量,得到满足df1[df2 , on =....]合并条件的df2行。

【讨论】:

    【解决方案2】:

    如果你知道sql,那么这可以用 sql + R 快速解决:

    df1$group_id <- seq(nrow(df1)) #This creates the unique groups for each interval
    
    sqldf::sqldf('
        SELECT df2.*, df1.group_id 
        FROM df2 
        LEFT JOIN df1 
        ON df2.chrom = df1.chrom AND position between low AND high')
    
      Gene chrom position group_id
    1 Gene1     1     1200        1
    2 Gene2     1    10000       NA
    3 Gene3     5      500        3
    4 Gene4     5      560        3
    5 Gene5     1    20100        2
    

    【讨论】:

      【解决方案3】:

      这是一个data.table 解决方案。我们可以使用 Ronak 引用的那篇文章中介绍的 foverlaps 函数。

      library(data.table)
      
      setDT(df1, key = c("chrom", "low", "high"))[
        , c("min", "max", "Group") := .(NULL, NULL, .I)
      ]
      setDT(df2)[, position2 := position]
      res <- foverlaps(
        df2, df1, 
        by.x = c("chrom", "position", "position2"), 
        type = "within"
      )[
        , .(Gene, chrom, position, Group)
      ]
      

      输出

      > res
          Gene chrom position Group
      1: Gene1     1     1200     1
      2: Gene2     1    10000    NA
      3: Gene3     5      500     3
      4: Gene4     1      560     1
      5: Gene5     1    20100     2
      

      【讨论】:

        【解决方案4】:

        正如 cmets 中指出的,您只需使用 GenomicRanges 中的 findOverlaps 即可在您的参考数据框中找到包含第二个数据帧中的行的行

        您的 df2 与示例中显示的有点不同,所以我将其更改为匹配:

        df2 = structure(list(Gene = c("Gene1", "Gene2", "Gene3", "Gene4", "Gene5"
        ), chrom = c(1L, 1L, 5L, 5L, 1L), position = c(1200L, 10000L, 
        500L, 560L, 20100L)), row.names = c(NA, -5L), class = c("data.table", 
        "data.frame"))
        

        你的 df1 有不同的顺序:

          chrom   min   max   low  high
        1     1  1000  1200   500  1700
        2     1 20000 20100 19500 20600
        3     5   900  1000   400  1500
        

        我们可以像下面这样创建一个 GenomicRanges 对象:

        library(GenomicRanges)
        gr1 = makeGRangesFromDataFrame(df1,start.field="low",end.field="high")
        gr1$Group = 1:length(gr1)
        
                GRanges object with 3 ranges and 1 metadata column:
              seqnames      ranges strand |     Group
                 <Rle>   <IRanges>  <Rle> | <integer>
          [1]        1    500-1700      * |         1
          [2]        1 19500-20600      * |         2
          [3]        5    400-1500      * |         3
        

        然后对第二个数据帧做同样的事情并找到重叠:

        gr2 = makeGRangesFromDataFrame(df2,start.field="position",end.field="position")
        ovlp = as.data.frame(findOverlaps(gr2,gr1))
        df2$Group = ovlp$subjectHits[match(1:length(gr2),ovlp$queryHits)]
        
          Gene chrom position Group
        1 Gene1     1     1200     1
        2 Gene2     1    10000    NA
        3 Gene3     5      500     3
        4 Gene4     1      560     1
        5 Gene5     1    20100     2
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-28
          • 2018-12-27
          • 2021-07-30
          • 2013-04-19
          • 2019-02-03
          • 2021-01-19
          • 2012-11-15
          相关资源
          最近更新 更多