在 R 中创建多个计数器的矩阵

Creating a matrix of multiple counters in R

所以,我的目标是采用输入向量并制作不同计数器的输出矩阵。所以每次我的输入中出现一个值时,我都想找到那个计数器并将它迭代 1。我知道我不擅长解释这个,所以我在下面展示了一个简单的版本。但是,我想进行 2 处更改,我将在示例之后列举这些更改,以便它有意义。

nums = c(1,2,3,4,5,1,2,4,3,5)
unis = unique(nums)
counter = matrix(NA, nrow = length(nums), ncol = length(unis))
colnames(counter) = unis
for (i in 1:length(nums)){
  temp = nums[i]
  if (i == 1){
    counter[1,] = 0
    counter[1,temp] = 1
  } else {
    counter[i,] = counter[i-1,]
    counter[i,temp] = counter[i-1,temp]+1
  }
}
counter

输出

 > counter
      1 2 3 4 5
 [1,] 1 0 0 0 0
 [2,] 1 1 0 0 0
 [3,] 1 1 1 0 0
 [4,] 1 1 1 1 0
 [5,] 1 1 1 1 1
 [6,] 2 1 1 1 1
 [7,] 2 2 1 1 1
 [8,] 2 2 1 2 1
 [9,] 2 2 2 2 1
[10,] 2 2 2 2 2

2次修改。 1) 由于真实数据要大得多,我想使用 apply 来做到这一点,或者比我更了解 R 的人说应该这样做。 2)虽然输入是一个向量,其中每个元素只是一个元素,但如果向量的一个元素是一个元组,这怎么能被推广呢?例如(如果 nums 是 4 和 5 的元组,那么它会在该步骤中迭代这两个步骤,输出的最后一行将是 2,2,2,3,2)

谢谢,如果您不明白,请提出问题,我会尽力澄清

对于您的第一个查询,您可以通过以下方式到达那里:

sapply(unique(nums), function(x) cumsum(nums==x) )

 #      [,1] [,2] [,3] [,4] [,5]
 # [1,]    1    0    0    0    0
 # [2,]    1    1    0    0    0
 # [3,]    1    1    1    0    0
 # [4,]    1    1    1    1    0
 # [5,]    1    1    1    1    1
 # [6,]    2    1    1    1    1
 # [7,]    2    2    1    1    1
 # [8,]    2    2    1    2    1
 # [9,]    2    2    2    2    1
 #[10,]    2    2    2    2    2

使用 Matrix 包(随 R 的标准安装一起提供)

nums <- c(1,2,3,4,5,1,2,4,3,5)
apply(Matrix::sparseMatrix(i=seq_along(nums), j=nums), 2, cumsum)
#      [,1] [,2] [,3] [,4] [,5]
#  [1,]    1    0    0    0    0
#  [2,]    1    1    0    0    0
#  [3,]    1    1    1    0    0
#  [4,]    1    1    1    1    0
#  [5,]    1    1    1    1    1
#  [7,]    2    2    1    1    1
#  [8,]    2    2    1    2    1
#  [9,]    2    2    2    2    1
# [10,]    2    2    2    2    2

请注意,这与 thelatemail 建议的解决方案在几个方面有所不同。您喜欢哪种行为取决于您将其用于什么目的。

这是一个说明差异的小例子:

nums <- c(5,2,1,1)

# My suggestion
apply(Matrix::sparseMatrix(i=seq_along(nums), j=nums), 2, cumsum)
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    0    0    0    0    1
# [2,]    0    1    0    0    1
# [3,]    1    1    0    0    1
# [4,]    2    1    0    0    1

# @thelatemail's suggestion
sapply(unique(nums), function(x) cumsum(nums==x) )
#      [,1] [,2] [,3]
# [1,]    1    0    0
# [2,]    1    1    0
# [3,]    1    1    1
# [4,]    1    1    2

对于你的第二个问题,你可以这样做:

nums <- list(1,2,3,4,5,1,2,4,3,c(4,5))

ii <- rep(seq_along(nums), times=lengths(nums)) ## lengths() is in R>=3.2.0
jj <- unlist(nums)
apply(Matrix::sparseMatrix(i=ii, j=jj), 2, cumsum)
#       [,1] [,2] [,3] [,4] [,5]
#  [1,]    1    0    0    0    0
#  [2,]    1    1    0    0    0
#  [3,]    1    1    1    0    0
#  [4,]    1    1    1    1    0
#  [5,]    1    1    1    1    1
#  [6,]    2    1    1    1    1
#  [7,]    2    2    1    1    1
#  [8,]    2    2    1    2    1
#  [9,]    2    2    2    2    1
# [10,]    2    2    2    3    2

另一个想法:

do.call(rbind, Reduce("+", lapply(nums, tabulate, max(unlist(nums))), accumulate = TRUE))
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    0    0    0    0
# [2,]    1    1    0    0    0
# [3,]    1    1    1    0    0
# [4,]    1    1    1    1    0
# [5,]    1    1    1    1    1
# [6,]    2    1    1    1    1
# [7,]    2    2    1    1    1
# [8,]    2    2    1    2    1
# [9,]    2    2    2    2    1
#[10,]    2    2    2    2    2

一般来说:

x = list(1, 3, 6, c(6, 3), 2, c(4, 6, 1), c(1, 2), 3)
do.call(rbind, Reduce("+", lapply(x, tabulate, max(unlist(x))), accumulate = TRUE))
#     [,1] [,2] [,3] [,4] [,5] [,6]
#[1,]    1    0    0    0    0    0
#[2,]    1    0    1    0    0    0
#[3,]    1    0    1    0    0    1
#[4,]    1    0    2    0    0    2
#[5,]    1    1    2    0    0    2
#[6,]    2    1    2    1    0    3
#[7,]    3    2    2    1    0    3
#[8,]    3    2    3    1    0    3