在 R 中的大矩阵中设置多个值

Setting many values in large matrix in R

问题: 我有一个很大的 data.table dt,其中包含大约一百万个 x 和 y 值。这些 x-y 组合代表二维平面上的事件。我知道那个平面的尺寸 (iwidth, iheight).

我想创建一个矩阵,除了 data.table 中列出的 x-y 值外,其他所有地方都为 0。在这些点,矩阵值应为 1。通常这很容易完成,但是要设置一百万个 x-y 值,常规方法不适合。

方法: 由于并非每个 x-y 组合都会在 data.table 中表示,我首先创建一个具有正确维度的 0 矩阵。然后我将 data.table 指示的点处的 0 替换为 1s.

## initial setup (for easier testing we just use a data.frame, not a data.table)
iwidth = 4288
iheight = 8576
dt = data.frame( xval=sample(iwidth ,10), yval=sample(iheight ,10) )

## simple approach
mx = matrix(ncol=iwidth, nrow=iheight, data=0)
mx[dt$xval, dt$yval] = 1

## biganalytics approach
library(biganalytics)
mx = as.big.matrix(matrix(ncol=iwidth, nrow=iheight, data=0))
mx[dt$xval, dt$yval] = 1

失败: 对于小数据,这工作得很好。但是,当您实际拥有一个包含一百万行的 data.table 时,它需要很长时间。我认为 biganalytics 包可能会有所帮助,但这仅适用于小数据,而大数据实际上更糟(参见下面的基准)。我也试过 applywith 但对我来说它们也不起作用(我认为它们应该更慢)。

这些是上述方法的微基准测试结果(n=1)(dt5、dt50 等代表 data.table 有 5 行、50 行等)。一旦我们达到长data.tables(即矩阵中要替换的许多值),所需的时间就会大大增加。

## Regular matrix:
Unit: milliseconds
   expr       min        lq      mean    median        uq       max neval
    dt5  130.8255  130.8255  130.8255  130.8255  130.8255  130.8255     1
   dt50   87.2308   87.2308   87.2308   87.2308   87.2308   87.2308     1
  dt500   86.7591   86.7591   86.7591   86.7591   86.7591   86.7591     1
 dt5000  129.6120  129.6120  129.6120  129.6120  129.6120  129.6120     1
dt50000 4340.6080 4340.6080 4340.6080 4340.6080 4340.6080 4340.6080     1

## Biganalytics matrix:
Unit: milliseconds
   expr          min           lq         mean       median           uq          max neval
    dt5     0.988101     0.988101     0.988101     0.988101     0.988101     0.988101     1
   dt50     0.779401     0.779401     0.779401     0.779401     0.779401     0.779401     1
  dt500     9.814602     9.814602     9.814602     9.814602     9.814602     9.814602     1
 dt5000   202.574901   202.574901   202.574901   202.574901   202.574901   202.574901     1
dt50000 19939.191600 19939.191600 19939.191600 19939.191600 19939.191600 19939.191600     1

以下 Rcpp 函数可能是您要找的:

Rcpp::cppFunction("NumericMatrix 
                  coords_to_matrix(int ncols, int nrows, 
                                   NumericVector x_coords,
                                   NumericVector y_coords) {
                  if(x_coords.size() != y_coords.size()) 
                    stop(\"x_coords and y_coords must be same length\");
                  NumericMatrix m(nrows, ncols);
                  for(int i = 0; i < x_coords.size(); i++)
                  {
                    if((x_coords[i] > ncols - 1) || 
                       (y_coords[i] > nrows - 1)) continue;
                    m[y_coords[i] - 1 + (x_coords[i] - 1) * m.nrow()] = 1;
                  }
                  return m; 
                  }")

这似乎可以满足您的需求。

例如:

set.seed(1)
dt <- data.frame(x = sample(10), y = sample(10))
dt
#>     x  y
#> 1   9  3
#> 2   4  1
#> 3   7  5
#> 4   1  8
#> 5   2  2
#> 6   5  6
#> 7   3 10
#> 8  10  9
#> 9   6  4
#> 10  8  7

mat <- coords_to_matrix(10, 10, dt$x, dt$y)
mat
#>       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#>  [1,]    0    0    0    1    0    0    0    0    0     0
#>  [2,]    0    1    0    0    0    0    0    0    0     0
#>  [3,]    0    0    0    0    0    0    0    0    1     0
#>  [4,]    0    0    0    0    0    1    0    0    0     0
#>  [5,]    0    0    0    0    0    0    1    0    0     0
#>  [6,]    0    0    0    0    1    0    0    0    0     0
#>  [7,]    0    0    0    0    0    0    0    1    0     0
#>  [8,]    1    0    0    0    0    0    0    0    0     0
#>  [9,]    0    0    0    0    0    0    0    0    0     1
#> [10,]    0    0    1    0    0    0    0    0    0     0

它似乎 运行 比您当前的任何选项都快得多:

iwidth = 4288
iheight = 8576
dt5 = data.frame( xval=sample(iwidth ,5), yval=sample(iheight ,5) )
dt50 = data.frame( xval=sample(iwidth ,50), yval=sample(iheight ,50) )
dt500 = data.frame( xval=sample(iwidth ,500), yval=sample(iheight ,500) )
dt5000 = data.frame( xval=sample(iwidth ,5000, replace = TRUE), 
                     yval=sample(iheight ,5000, replace = TRUE) )
dt50000 =  data.frame( xval=sample(iwidth ,50000, replace = TRUE), 
                       yval=sample(iheight ,50000, replace = TRUE) )

microbenchmark::microbenchmark(
  m5 = m5 <- coords_to_matrix(iwidth, iheight, dt5$xval, dt5$yval),
  m50 = m50 <- coords_to_matrix(iwidth, iheight, dt50$xval, dt50$yval),
  m500 = m500 <- coords_to_matrix(iwidth, iheight, dt500$xval, dt500$yval),
  m5000 = m5000 <- coords_to_matrix(iwidth, iheight, dt5000$xval, dt5000$yval),
  m50000 = m50000 <- coords_to_matrix(iwidth, iheight, dt50000$xval, dt50000$yval),
  times = 10)
#> Unit: milliseconds
#>    expr     min      lq      mean   median       uq      max neval cld
#>      m5 45.5397 55.2420 105.15879 60.25800  83.0363 284.8644    10   a
#>     m50 45.3205 53.1242 127.77022 58.02275 294.3918 305.8922    10   a
#>    m500 45.3013 45.4073  98.20344 53.51115  55.8047 292.2100    10   a
#>   m5000 45.4192 45.7605  76.51107 54.57740  55.7256 278.7359    10   a
#>  m50000 46.2567 49.4814 104.44953 56.87705  78.4683 302.9901    10   a