【问题标题】:Reducing the number of columns using a condition in R使用 R 中的条件减少列数
【发布时间】:2017-08-17 14:37:12
【问题描述】:

我有一个超过 1000 行和 100 列的大矩阵。在每一行中只有 6-10 列有值,其余的都是零。我想创建一个只有 5 列的矩阵,每行取 5 个连续列的值。例如:

A = structure(c(0, 1L, 6L, 0, 2L, 0, 2L, 0, 1L, 4L, 1L, 3L, 7L, 2L, 6L, 2L, 4L, 0, 3L, 0, 3L, 5L, 1L, 4L, 0, 4L, 6L, 2L, 0, 0, 5L, 0, 3L, 5L, 0, 0, 0, 4L, 6L, 7L, 0, 7L, 5L, 7L, 8L, 6L, 0, 0, 8L, 9L, 0, 0, 0, 9L, 1L, 0 , 0, 0, 0, 2L, 7L, 0, 2L, 0, 0, 1L, 8L, 4, 0, 0), .Dim = c(5L, 14L))

#A =
#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
#[1,]    0    0    1    2    3    4    5    0    0     6     0     0     7     1
#[2,]    1    2    3    4    5    6    0    0    7     0     0     0     0     8
#[3,]    6    0    7    0    1    2    3    4    5     0     0     0     2     4
#[4,]    0    1    2    3    4    0    5    6    7     8     9     0     0     0
#[5,]    2    4    6    0    0    0    0    7    8     9     1     2     0     0

我想要这个矩阵:

B = structure(c(1L, 1L, 1L, 5L, 7L, 2L, 2L, 2L, 6L, 8L, 3L, 3L, 3L, 7L, 9L, 4L, 4L, 4L, 8L, 1L, 5L, 5L, 5L, 9L, 2L), .Dim = c(5L, 5L))


#B = 
#     [,1] [,2] [,3] [,4] [,5]
#[1,]    1    2    3    4    5
#[2,]    1    2    3    4    5
#[3,]    1    2    3    4    5
#[4,]    5    6    7    8    9
#[5,]    7    8    9    1    2

我的代码:

df = data.frame(A)
B = do.call(rbind, lapply(1:NROW(df), function(i) df[i,][(df[i,])!=0][1:5]))
# or
B = t(apply(X = df, MARGIN = 1, function(x) x[x!=0][1:5]))

我的代码在 A 的前两行运行良好,但在其余行中运行失败。我还考虑过获取非零的列索引,然后查看是否有 5 个连续列(它们之间没有任何间隙)并检索它们的值。非常感谢任何帮助!

【问题讨论】:

  • 如果行没有5个连续值怎么办?
  • 如果一行没有 5 个后续值(然后打印 5 个 NA),此版本也可以工作:t(apply(A,1,function(z) z[(-4:0)+match(5,Reduce(function(x,y) ifelse(y,x+y,0),z!=0,acc=TRUE))]))

标签: r matrix


【解决方案1】:

这是一个使用rle的选项

t(apply(A, 1, function(x) {
      rl <- rle(x !=0)
    head(x[inverse.rle(within.list(rl, values[!(values & lengths >= 5)] <- FALSE))], 5)}))
#      [,1] [,2] [,3] [,4] [,5]
#[1,]    1    2    3    4    5
#[2,]    1    2    3    4    5
#[3,]    1    2    3    4    5
#[4,]    5    6    7    8    9
#[5,]    7    8    9    1    2

【讨论】:

    【解决方案2】:

    你可以使用rollapply:

    library(zoo)
    t(apply(A,1,function(x) {x[match(T,rollapply(!!x,5,all)) + (0:4)]}))
    
    #      [,1] [,2] [,3] [,4] [,5]
    # [1,]    1    2    3    4    5
    # [2,]    1    2    3    4    5
    # [3,]    1    2    3    4    5
    # [4,]    5    6    7    8    9
    # [5,]    7    8    9    1    2
    

    如果您有没有任何 5 序列的行会崩溃,如果您希望处理它,请更新您的帖子。

    或相同但更漂亮:

    library(purrr)
    Adf       <- as.data.frame(t(A)) # data.frame fits more this data conceptually, you have different series, and it's better to put them in columns
    res_df  <- map_df(Adf,~ {.x[match(T,rollapply(.x!=0,5,all))+(0:4)]})
    res_mat <- as.matrix(t(unname(res_df))) # if you want to go back to ugly :)
    

    【讨论】:

      【解决方案3】:

      编辑:遗漏了一些细节,这是我使用 apply 和基础库的新尝试:

      cumfun <- function(x){           
        y<-ifelse(x>0,1,0)
        tmp<-cumsum(y)
        pos<-which(tmp-cummax((!y)*tmp)==5)
        x[(pos-4) : pos]
      }
      
      B<-t(apply(A,1,cumfun))
      
      > B
           [,1] [,2] [,3] [,4] [,5]
      [1,]    1    2    3    4    5
      [2,]    1    2    3    4    5
      [3,]    1    2    3    4    5
      [4,]    5    6    7    8    9
      [5,]    7    8    9    1    2
      

      【讨论】:

      • 是的,但结果不匹配
      • 这不是 OP 想要的(必须连续有 5 个非零值)
      • 感谢您指出,误读了问题。现在应该是正确的! @PoGibas
      【解决方案4】:
      library(zoo)
      t(apply(A, MAR = 1, function(x, n = 5) x[which(rollsum(!!x, n)==n)[1]+seq_len(n)-1]))
      
           [,1] [,2] [,3] [,4] [,5]
      [1,]    1    2    3    4    5
      [2,]    1    2    3    4    5
      [3,]    1    2    3    4    5
      [4,]    5    6    7    8    9
      [5,]    7    8    9    1    2
      

      【讨论】:

        猜你喜欢
        • 2021-02-02
        • 1970-01-01
        • 1970-01-01
        • 2021-03-08
        • 2015-06-09
        • 1970-01-01
        • 1970-01-01
        • 2016-02-28
        • 2015-11-30
        相关资源
        最近更新 更多