在 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
包可能会有所帮助,但这仅适用于小数据,而大数据实际上更糟(参见下面的基准)。我也试过 apply
或 with
但对我来说它们也不起作用(我认为它们应该更慢)。
这些是上述方法的微基准测试结果(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
问题: 我有一个很大的 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
包可能会有所帮助,但这仅适用于小数据,而大数据实际上更糟(参见下面的基准)。我也试过 apply
或 with
但对我来说它们也不起作用(我认为它们应该更慢)。
这些是上述方法的微基准测试结果(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