第一次发帖,如有错误请多多包涵。我正在尝试在 R 中编写一个循环来折叠行。假设我正在使用这样的数据框:
r1 <- c(1, 1,1000,2)
r2 <- c(1, 1001,2000, 2)
r3 <- c(1, 2001,3000, 2)
r4 <- c(1, 3001,4000, 1)
r5 <- c(1, 4001,5000, 3)
r6 <- c(1, 5001,6000, 3)
r7 <- c(2, 1,1000,2 )
r8 <- c(2, 1001,2000, 1)
r9 <- c(2, 2001,3000, 2)
r10 <- c(2, 3001,4000, 1)
r11 <- c(2, 4001,5000, 1)
test <- rbind(r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11)
test <- as.data.frame(test)
colnames(test) <- c("chr", "start","end", "abs.sum")
rownames(test) <- NULL
这给了我一个看起来像这样的数据框:
chr start end abs.sum
1 1 1 1000 2
2 1 1001 2000 2
3 1 2001 3000 2
4 1 3001 4000 1
5 1 4001 5000 3
6 1 5001 6000 3
7 2 1 1000 2
8 2 1001 2000 1
9 2 2001 3000 2
10 2 3001 4000 1
11 2 4001 5000 1
对于每个
chr
值,我想根据相同的连续abs.sum
折叠,保持start
中的最低值和end
中的最高值。因此,例如,我希望我的最终数据框看起来像这样:
chr start end abs.sum
1 1 1 3000 2
2 1 3001 4000 1
3 1 4001 6000 3
4 2 1 1000 2
5 2 1001 2000 1
6 2 2001 3000 2
7 2 3001 5000 1
我试着写一个for循环:
for (i in 1:nrow(test)) {
if (test$abs.sum[i] == test$abs.sum[i + 1]) {
test$end[i] <- test$end[i+1]
test <- test[-i + 1]
test <- test[-(i + 1),]
}
}
返回错误:
Error in if (test$abs.sum[i] == test$abs.sum[i + 1]) { : argument is of length zero
我知道这是不正确的,但这是我到目前为止能够拼凑起来的。我认为这可能需要“while”和“for”循环的某种组合,但我被卡住了。也许已经存在具有可以执行此操作的功能的程序包?有人可以帮我指出正确的方向吗?
提前谢谢你!这是我的第一篇文章,我希望我提供了足够的信息并正确发布。
您可以利用运行长度 id 执行此操作:
library(data.table)
setDT(test)[, .(start=min(start), end=max(end), abs.sum=min(abs.sum)), .(chr,rleid(abs.sum))][,-2]
输出:
chr start end abs.sum
<num> <num> <num> <num>
1: 1 1 3000 2
2: 1 3001 4000 1
3: 1 4001 6000 3
4: 2 1 1000 2
5: 2 1001 2000 1
6: 2 2001 3000 2
7: 2 3001 5000 1
这是另一个使用
dplyr
的选项(但请注意我保留使用data.table:rleid
library(dplyr)
test %>%
group_by(chr, id=data.table::rleid(abs.sum)) %>%
summarize(start=min(start), end=max(end), abs.sum=min(abs.sum)) %>%
select(-id)
输出:
chr start end abs.sum
<dbl> <dbl> <dbl> <dbl>
1 1 1 3000 2
2 1 3001 4000 1
3 1 4001 6000 3
4 2 1 1000 2
5 2 1001 2000 1
6 2 2001 3000 2
7 2 3001 5000 1
这里有一个
dplyr
和tidyr
的方法。工作流程是:
consecutive_id()
filter()
获取每个子组(“tmp”)的最小值和最大值library(dplyr)
library(tidyr)
test %>%
mutate(tmp = consecutive_id(abs.sum)) %>%
pivot_longer(!c(chr, abs.sum, tmp)) %>%
group_by(chr, tmp) %>%
filter(value == min(value) | value == max(value)) %>%
pivot_wider(names_from = name,
values_from = value) %>%
select(!tmp) %>%
ungroup()
# A tibble: 7 × 5
tmp chr abs.sum start end
<int> <dbl> <dbl> <dbl> <dbl>
1 1 1 2 1 3000
2 2 1 1 3001 4000
3 3 1 3 4001 6000
4 4 2 2 1 1000
5 5 2 1 1001 2000
6 6 2 2 2001 3000
7 7 2 1 3001 5000
请注意,这种方法比@langtang 的回答中概述的
data.table
方法慢。只是在此处添加它,以防有人好奇如何仅在 tidyverse
. 中完成它