【问题标题】:Setting many values in large matrix in R在R中的大矩阵中设置许多值
【发布时间】:2021-12-17 03:05:00
【问题描述】:

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

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

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

## 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 等代表具有 5 行、50 行等的 data.table)。一旦我们到达较长的 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

【问题讨论】:

  • 是稀疏矩阵的一种选择吗?例如Matrix::sparseMatrix(i = dt$xval, j = dt$yval, x=1, dims=c(iwidth,iheight))
  • 索引矩阵时,dt$xvaldt$yval 应该交换。 yval 是行索引,xval 是列索引。
  • 非常感谢@user20650! sparseMatrix 使之成为可能。我不得不写一点不同:mx=Matrix::sparseMatrix(i = tra$xval+1, j = tra$yval+1, x=1, dims=c(iheight+1,iwidth+1))。 +1 对于 data.table 中的值是必需的,并且由于第二条评论中提到的内容,iheight 和 iwidth 的切换是必要的(非常感谢@RuiBarradas)。之后,我使用mx=as.matrix(mx[-1,-1]) 将其转换为常规矩阵(-1 用于删除之前添加的行和列)
  • 几个挑剔的东西:你指的是data.table,但你创建了一个data.framedata.table 的优势之一是处理大型数据集的速度,因此了解这是否真的是您正在使用的内容会很有帮助。此外,也许您的示例与您实际使用的示例相比有所简化,但我不知道百万行数据框或百万项数字矩阵本身会构成“大数据”。大到足以考虑性能,当然,但大到需要计算机之外的特殊工具?不一定。
  • @camille,感谢您的评论,是的,我在示例中使用了 data.frame,但在现实世界中,我使用了 data.table,但仍然存在相同的问题(另请参阅第一条评论在我的第一个代码块中;)至于“大数据”的定义,很好,当然可以拥有比这更多的数据,但是对于我使用的 PC,即使是 500,000 行也需要花费大量时间来完成这项任务,而且我将不得不编写这个脚本供其他人使用,我不知道他们有什么 PC 能力。所以数据量很大,我得注意我采用的方法。

标签: r matrix bigdata


【解决方案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

【讨论】:

  • 非常感谢您的回答!为了方便起见,我将使用评论中的 sparseMatrix (更容易在脚本中编写并且速度非常快),但这实际上对于其他问题也很好。我一定会用 Rcpp 扩展我的视野;)
猜你喜欢
  • 1970-01-01
  • 2016-06-29
  • 2019-04-16
  • 2021-02-19
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多