通过引用对 R 中的向量进行子分配
Sub-assign by reference on vector in R
我能以某种方式在原子向量上使用引用子赋值吗?
当然不用将其包装在 1 列 data.table 中以使用 :=
.
library(data.table)
N <- 5e7
x <- sample(letters, N, TRUE)
X <- data.table(x = x)
upd_i <- sample(N, 1L, FALSE)
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0.11 0.06 0.17
system.time(X[upd_i, x := NA_character_])
# user system elapsed
# 0.00 0.00 0.03
如果 R6 可以提供帮助,我愿意接受 R6 解决方案,因为它已经是我的部门之一了。
我已经检查过 <-
inside R6
object 仍然复制:gist.
在最近的 R 版本(3.1-3.1.2+ 左右)中,对向量的赋值不会复制。但是,您不会通过 运行 OP 的代码看到这一点,原因如下。因为您重用了 x
并将其分配给其他某个对象,所以 R 不会被告知 x
已在该点被复制,并且必须假设它不会(在上面的特定情况下,我认为在 data.table::data.table
中更改它并通知 R 已经制作了副本会很好,但这是一个单独的问题 - data.frame
遇到同样的问题),因此它复制了 x
第一次使用。如果稍微更改命令的顺序,您将看不出有什么不同:
N <- 5e7
x <- sample(letters, N, TRUE)
upd_i <- sample(N, 1L, FALSE)
# no copy here:
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0 0 0
X <- data.table(x = x)
system.time(X[upd_i, x := NA_character_])
# user system elapsed
# 0 0 0
# but now R will copy:
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0.28 0.08 0.36
(旧答案,大部分留作好奇)
您实际上可以使用 data.table
:=
运算符就地修改您的矢量(我认为您需要 R 3.1+ 版以避免 list
中的复制):
modify.vector = function (v, idx, value) setDT(list(v))[idx, V1 := value]
v = 1:5
address(v)
#[1] "000000002CC7AC48"
modify.vector(v, 4, 10)
v
#[1] 1 2 3 10 5
address(v)
#[1] "000000002CC7AC48"
正如@Frank 所建议的,可以使用 Rcpp
来做到这一点。这是一个版本,其中包含一个受 Rcpp dispatch.h
启发的宏,它处理所有原子向量类型:
mod_vector.cpp
#include <Rcpp.h>
using namespace Rcpp;
template <int RTYPE>
Vector<RTYPE> mod_vector_impl(Vector<RTYPE> x, IntegerVector i, Vector<RTYPE> value) {
if (i.size() != value.size()) {
stop("i and value must have same length.");
}
for (int a = 0; a < i.size(); a++) {
x[i[a] - 1] = value[a];
}
return x;
}
#define __MV_HANDLE_CASE__(__RTYPE__) case __RTYPE__ : return mod_vector_impl(Vector<__RTYPE__>(x), i, Vector<__RTYPE__>(value));
// [[Rcpp::export]]
SEXP mod_vector(SEXP x, IntegerVector i, SEXP value) {
switch(TYPEOF(x)) {
__MV_HANDLE_CASE__(INTSXP)
__MV_HANDLE_CASE__(REALSXP)
__MV_HANDLE_CASE__(RAWSXP)
__MV_HANDLE_CASE__(LGLSXP)
__MV_HANDLE_CASE__(CPLXSXP)
__MV_HANDLE_CASE__(STRSXP)
__MV_HANDLE_CASE__(VECSXP)
__MV_HANDLE_CASE__(EXPRSXP)
}
stop("Not supported.");
return x;
}
示例:
x <- 1:20
address(x)
#[1] "0x564e7e8"
mod_vector(x, 4:5, 12:13)
# [1] 1 2 3 12 13 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
address(x)
#[1] "0x564e7e8"
与基本方法和 data.table 方法的比较。可以看出速度快了很多:
x <- 1:2e7
microbenchmark::microbenchmark(mod_vector(x, 4:5, 12:13), x[4:5] <- 12:13, modify.vector(x, 4:5, 12:13))
#Unit: microseconds
# expr min lq mean median uq
# mod_vector(x, 4:5, 12:13) 5.967 7.3480 15.05259 9.718 21.0135
# x[4:5] <- 12:13 2.953 5.3610 45722.61334 48122.996 52623.1505
# modify.vector(x, 4:5, 12:13) 954.577 988.7785 1177.17925 1021.380 1361.1210
# max neval
# 58.463 100
# 126978.146 100
# 1559.985 100
我能以某种方式在原子向量上使用引用子赋值吗?
当然不用将其包装在 1 列 data.table 中以使用 :=
.
library(data.table)
N <- 5e7
x <- sample(letters, N, TRUE)
X <- data.table(x = x)
upd_i <- sample(N, 1L, FALSE)
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0.11 0.06 0.17
system.time(X[upd_i, x := NA_character_])
# user system elapsed
# 0.00 0.00 0.03
如果 R6 可以提供帮助,我愿意接受 R6 解决方案,因为它已经是我的部门之一了。
我已经检查过 <-
inside R6
object 仍然复制:gist.
在最近的 R 版本(3.1-3.1.2+ 左右)中,对向量的赋值不会复制。但是,您不会通过 运行 OP 的代码看到这一点,原因如下。因为您重用了 x
并将其分配给其他某个对象,所以 R 不会被告知 x
已在该点被复制,并且必须假设它不会(在上面的特定情况下,我认为在 data.table::data.table
中更改它并通知 R 已经制作了副本会很好,但这是一个单独的问题 - data.frame
遇到同样的问题),因此它复制了 x
第一次使用。如果稍微更改命令的顺序,您将看不出有什么不同:
N <- 5e7
x <- sample(letters, N, TRUE)
upd_i <- sample(N, 1L, FALSE)
# no copy here:
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0 0 0
X <- data.table(x = x)
system.time(X[upd_i, x := NA_character_])
# user system elapsed
# 0 0 0
# but now R will copy:
system.time(x[upd_i] <- NA_character_)
# user system elapsed
# 0.28 0.08 0.36
(旧答案,大部分留作好奇)
您实际上可以使用 data.table
:=
运算符就地修改您的矢量(我认为您需要 R 3.1+ 版以避免 list
中的复制):
modify.vector = function (v, idx, value) setDT(list(v))[idx, V1 := value]
v = 1:5
address(v)
#[1] "000000002CC7AC48"
modify.vector(v, 4, 10)
v
#[1] 1 2 3 10 5
address(v)
#[1] "000000002CC7AC48"
正如@Frank 所建议的,可以使用 Rcpp
来做到这一点。这是一个版本,其中包含一个受 Rcpp dispatch.h
启发的宏,它处理所有原子向量类型:
mod_vector.cpp
#include <Rcpp.h>
using namespace Rcpp;
template <int RTYPE>
Vector<RTYPE> mod_vector_impl(Vector<RTYPE> x, IntegerVector i, Vector<RTYPE> value) {
if (i.size() != value.size()) {
stop("i and value must have same length.");
}
for (int a = 0; a < i.size(); a++) {
x[i[a] - 1] = value[a];
}
return x;
}
#define __MV_HANDLE_CASE__(__RTYPE__) case __RTYPE__ : return mod_vector_impl(Vector<__RTYPE__>(x), i, Vector<__RTYPE__>(value));
// [[Rcpp::export]]
SEXP mod_vector(SEXP x, IntegerVector i, SEXP value) {
switch(TYPEOF(x)) {
__MV_HANDLE_CASE__(INTSXP)
__MV_HANDLE_CASE__(REALSXP)
__MV_HANDLE_CASE__(RAWSXP)
__MV_HANDLE_CASE__(LGLSXP)
__MV_HANDLE_CASE__(CPLXSXP)
__MV_HANDLE_CASE__(STRSXP)
__MV_HANDLE_CASE__(VECSXP)
__MV_HANDLE_CASE__(EXPRSXP)
}
stop("Not supported.");
return x;
}
示例:
x <- 1:20
address(x)
#[1] "0x564e7e8"
mod_vector(x, 4:5, 12:13)
# [1] 1 2 3 12 13 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
address(x)
#[1] "0x564e7e8"
与基本方法和 data.table 方法的比较。可以看出速度快了很多:
x <- 1:2e7
microbenchmark::microbenchmark(mod_vector(x, 4:5, 12:13), x[4:5] <- 12:13, modify.vector(x, 4:5, 12:13))
#Unit: microseconds
# expr min lq mean median uq
# mod_vector(x, 4:5, 12:13) 5.967 7.3480 15.05259 9.718 21.0135
# x[4:5] <- 12:13 2.953 5.3610 45722.61334 48122.996 52623.1505
# modify.vector(x, 4:5, 12:13) 954.577 988.7785 1177.17925 1021.380 1361.1210
# max neval
# 58.463 100
# 126978.146 100
# 1559.985 100