将数据框行拆分为两行

5

我有两张表格(data和reference),这些表格都有开始和结束位置。我想使用像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与参考表没有重叠,因此将保持原样。但是,我希望根据Ref.table拆分ID 2&3。

我想要的结果表格如下:

>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
3个回答

5
这里有一个可能的foverlaps解决方案(如在问题中提到的)。
前两个步骤非常简单且惯用,为Ref.table添加一个End列以使我们的区间重叠,然后按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

(You can remove unwanted columns if you wish)


这个方法很好用,谢谢。唯一不太好的是探针比例(我不确定我在问题中是否正确指定了)。我添加了一个“长度”列来获取原始长度,然后在你的答案中,我用“探针 * ((i.End - Start) / (Length))”代替了“i.End - Start”。以防其他人也遇到同样的问题,我把解决方法写在这里。 - Gaius Augustus
@GaiusAugustus 感谢您的评论。我已经不记得问题是关于什么了,但很高兴您解决了它 :) - David Arenburg

1
首先定义一个切割函数:

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,根据您的第一行,我不确定您想要哪个。您可以通过在((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

0

这是我的方法:

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接