通过参考R中的向量进行子分配

时间:2015-05-20 08:26:01

标签: r data.table r6

我能否以某种方式使用原子矢量参考子分配?
当然没有将它包装在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解决方案,因为它已经是我的一个解决方案了。
我已经检查过<-对象中的R6仍然会复制:gist

2 个答案:

答案 0 :(得分:9)

在最近的R版本(3.1-3.1.2 +左右)中,向量的赋值不会复制。你不会通过运行OP的代码来看到它,原因如下。因为您重用了x并将其分配给其他某个对象,所以不会通知R在该点复制x,并且必须假定它不会(在上面的特定情况下,我认为在data.table::data.table中更改它并通知R已经制作了副本会很好,但这是一个单独的问题 - data.frame遭受同样的问题),并因此复制{{ 1}}首次使用。如果你稍微改变命令的顺序,你会发现没有区别:

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

答案 1 :(得分:5)

根据@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"

与base和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