R - 将数据框行拆分为两行
R - Split data frame row into two rows
我有 2 tables(数据和参考;下面的玩具示例)。我想检查这些 table 的 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 与引用 table 没有重叠,因此它会被单独保留。但是,ID 2 和 3,我想根据 Ref.table 进行拆分。
我想要得到的结果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.根据单独的table将范围分成两列
2. 在两部分之间按比例拆分 # 探针
我一直在寻找可以执行此操作的 R 程序包(按染色体臂划分范围),但未能找到如上所示的程序包。任何指向函数包的链接都将不胜感激,但我也愿意自己编写代码......在一点帮助下。
到目前为止,我只能使用重叠来确定是否存在重叠:
示例:
>foverlaps(Ref.table[data[14]$Chrom], data[14], which=TRUE)
xid yid
1: 1 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,根据你的第 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
这是我的方法:
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)
这是一个可能的 foverlaps
解决方案(如 Q 中所述)。
前两个步骤很简单而且非常地道,将 End 列添加到 Ref.table
这样我们就会有重叠的间隔,然后通过 Chrom
和间隔列(在 v 1.9.5+ 中,您现在可以指定 by.x
和 by.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)]
现在,最后两个步骤将根据新的列值更新 End
和 Start
列,然后更新 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
(您可以根据需要删除不需要的列)
我有 2 tables(数据和参考;下面的玩具示例)。我想检查这些 table 的 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 与引用 table 没有重叠,因此它会被单独保留。但是,ID 2 和 3,我想根据 Ref.table 进行拆分。
我想要得到的结果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.根据单独的table将范围分成两列 2. 在两部分之间按比例拆分 # 探针
我一直在寻找可以执行此操作的 R 程序包(按染色体臂划分范围),但未能找到如上所示的程序包。任何指向函数包的链接都将不胜感激,但我也愿意自己编写代码......在一点帮助下。
到目前为止,我只能使用重叠来确定是否存在重叠: 示例:
>foverlaps(Ref.table[data[14]$Chrom], data[14], which=TRUE)
xid yid
1: 1 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,根据你的第 1 行我不确定你想要哪个。你可以通过删除 ((data$End - (reftable$Split[splitsite] + 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
这是我的方法:
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)
这是一个可能的 foverlaps
解决方案(如 Q 中所述)。
前两个步骤很简单而且非常地道,将 End 列添加到 Ref.table
这样我们就会有重叠的间隔,然后通过 Chrom
和间隔列(在 v 1.9.5+ 中,您现在可以指定 by.x
和 by.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)]
现在,最后两个步骤将根据新的列值更新 End
和 Start
列,然后更新 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
(您可以根据需要删除不需要的列)