在 R 中编写一个循环以根据相同的连续列值折叠行

问题描述 投票:0回答:2

第一次发帖,如有错误请多多包涵。我正在尝试在 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”循环的某种组合,但我被卡住了。也许已经存在具有可以执行此操作的功能的程序包?有人可以帮我指出正确的方向吗?

提前谢谢你!这是我的第一篇文章,我希望我提供了足够的信息并正确发布。

r dataframe loops row
2个回答
0
投票

您可以利用运行长度 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

0
投票

这里有一个

dplyr
tidyr
的方法。工作流程是:

  1. 使用
    consecutive_id()
  2. 为每个“abs.sum”'组创建具有唯一值的“tmp”列
  3. 将数据透视为长格式,将所有“开始”和“结束”值放入单个列中
  4. 按“chr”和“tmp”对数据进行分组,并使用
    filter()
    获取每个子组(“tmp”)的最小值和最大值
  5. 将数据转回宽格式
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
.

中完成它
© www.soinside.com 2019 - 2024. All rights reserved.