【问题标题】:R - Split data frame row into two rowsR - 将数据框行分成两行
【发布时间】:2015-09-01 15:24:30
【问题描述】:

我有 2 个表格(数据和参考;下面的玩具示例)。这些表具有我想检查重叠的 START 和 END 位置(使用 data.table 包中的 foverlaps 之类的东西),然后拆分值,如下所示。

>data  <- data.table(ID=c(1,2,3), Chrom=c(1,1,2), Start=c(1,500,1000), End=c(900,5000,5000), Probes=c(899,4500,4500))
>Ref.table <- data.table(Chrom=c(1,2), Split=c(1000,2000))

>Ref.table
Chrom    Split
1        1000
2        2000

>data
ID    Chrom    Start    End    Probes
1     1        1        900    899
2     1        500      5000   4500
3     2        1000     5000   4000

如您所见,ID 1 与参考表没有重叠,因此将单独放置。但是,ID 2&3,我想根据 Ref.table 进行拆分。

我想得到的结果表是:

>result
ID    Chrom    Start    End    Probes
1     1        1        900    899
2     1        500      1000   500
2     1        1001     5000   4000
3     2        1000     2000   1000
3     2        2001     5000   3000

我相信您可以看到,这有两个部分: 1. 根据单独的表将范围拆分为两列 2. 在两部分之间按比例拆分 # 个探针

我一直在寻找可以做到这一点的 R 包(按染色体臂分割范围),但找不到如上所示的。任何指向函数包的链接都将不胜感激,但我也愿意自己编写代码......在一些帮助下。

到目前为止,我只能使用 foverlaps 来确定是否存在重叠: 示例:

>foverlaps(Ref.table[data[14]$Chrom], data[14], which=TRUE)
     xid   yid
1:    1     1

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    这是一个可能的 foverlaps 解决方案(如 Q 中所述)。

    前两个步骤很简单,而且非常惯用,将 End 列添加到 Ref.table,这样我们就会有重叠的区间,然后通过 Chrom 和区间列键入两个数据集 (在 v 1.9.5+ 中,您现在可以指定 by.xby.y)并简单地运行 foverlaps

    library(data.table)
    setDT(Ref.table)[, End := Split]
    setkey(Ref.table)
    setkey(setDT(data), Chrom, Start, End)
    res <- foverlaps(data, Ref.table)
    res
    #    Chrom Split  End ID Start i.End Probes
    # 1:     1    NA   NA  1     1   900    899
    # 2:     1  1000 1000  2   500  5000   4500
    # 3:     2  2000 2000  3  1000  5000   4000
    

    现在我们有了重叠,我们需要根据我们的匹配增加数据集的大小。我们可以以is.na(Split) 为条件(这意味着没有发现重叠)。我不确定这部分是否可以更有效地完成

    res2 <- res[, if(is.na(Split)) .SD else rbind(.SD, .SD), by = .(ID, Chrom)]
    ## Or, if you only have one row per group, maybe
    ## res2 <- res[, if(is.na(Split)) .SD else .SD[c(1L,1L)], by = .(ID, Chrom)]
    

    现在,最后两步将根据新的列值更新EndStart 列,然后更新Probes

    res2[!is.na(Split), `:=`(i.End = c(Split[1L], i.End[-1L]),
                             Start = c(Start[-1L], Split[1L] + 1L)), 
         by = .(ID, Chrom)]
    res2[!is.na(Split), Probes := i.End - Start]
    res2
    #    ID Chrom Split  End Start i.End Probes
    # 1:  1     1    NA   NA     1   900    899
    # 2:  2     1  1000 1000   500  1000    500
    # 3:  2     1  1000 1000  1001  5000   3999
    # 4:  3     2  2000 2000  1000  2000   1000
    # 5:  3     2  2000 2000  2001  5000   2999
    

    (如果您愿意,可以删除不需要的列)

    【讨论】:

    • 这很好用,谢谢。不能正常工作的一件事是 Proportion of Probes (不确定我是否在问题中正确指定了这一点)。我添加了一个“长度”列来获取原始长度,然后在你的答案中没有“i.End - Start”,我做了“Probes * ((i.End - Start) / (Length))”。万一其他人为此提出这个问题,我想我会把它放在这里。
    • @GaiusAugustus 感谢您的评论。我不记得这个问题是关于什么了,但我很高兴你明白了:)
    【解决方案2】:

    先定义一个分裂函数:

    splitter<-function(data, reftable){
      splitsite <- which(reftable$Chrom == data$Chrom)
      if(reftable$Split[splitsite] > data$Start && reftable$Split[splitsite] <= data$End){
        return(data.frame(ID = data$ID,
                          Chrom = data$Chrom,
                          Start = c(data$Start, reftable$Split[splitsite] + 1),
                          End = c(reftable$Split[splitsite],data$End),
                          Probes = c((reftable$Split[splitsite]- data$Start)*data$Probes/(data$End-data$Start),
                                     ((data$End - (reftable$Split[splitsite] + 1))*data$Probes/(data$End-data$Start)))))
      } else {
        return(data)
      }  
    }
    

    然后我们可以使用dplyr在每一行上运行它:

    library(dplyr)
    data %>% group_by(ID) %>%
             do(splitter(., ref.table))
    

    给出以下内容。你可以看到它有 3999 和 2999 而不是你的 4000 和 3000,根据你的第 1 行,我不确定你想要哪个。你可以通过在 ((data$End - (reftable$Split[splitsite] + 1)) 中删除 +1 来修复它

      ID Chrom Start  End Probes
    1  1     1     1  900    899
    2  2     1   500 1000    500
    3  2     1  1001 5000   3999
    4  3     2  1000 2000   1000
    5  3     2  2001 5000   2999
    

    【讨论】:

      【解决方案3】:

      这是我的方法:

      merge(data, Ref.table, by = "Chrom") %>% 
        mutate(
          end = ifelse(Split > Start & Split < End, Split, End),
          start2 = ifelse(Split > Start & Split < End, end + 1, NA),
          end2 = ifelse(Split > Start & Split < End, End, NA)
          ) %>%
        select(-End, -Probes, -Split) %>% 
        gather(label, value, Start, end, start2, end2, na.rm = TRUE) %>% 
        mutate(
          rep = ifelse(label %in% c("Start", "end"), 1, 2),
          label = as.character(label),
          label = ifelse(label %in% c("Start", "start2"), "start", label),
          label = ifelse(label %in% c("end", "end2"), "end", label)
          ) %>%
        spread(label, value) %>%
        select(ID, Chrom, start, end) %>%
        mutate(probes = end - start)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-18
        • 2019-02-09
        • 2018-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-23
        相关资源
        最近更新 更多