2015-05-20 11 views
17

Posso utilizzare l'assegnazione secondaria per riferimento sui vettori atomici in qualche modo?
Ovviamente senza avvolgerlo in 1 colonna data.table per usare :=.Assegnazione secondaria per riferimento sul vettore in R

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 

Se R6 può aiutare su che sono aperto per soluzione R6 in quanto è uno dei miei dep già.
Ho già controllato che <- all'interno dell'oggetto R6 faccia ancora la copia: gist.

+0

Interessante. mai sentito parlare di R6. Sembra eccitante. –

+1

@DavidArenburg R6 è IMO la migliore classe di riferimento e lo strumento OOP in R. Sicuramente vale la pena imparare e facile da imparare. – jangorecki

+0

Suppongo che tu possa modificare per riferimento con Rcpp. Google ha fornito: http://stackoverflow.com/q/11300048/1191259 La risposta di Ari in questione collegata ha una funzione semplice che supera il resto. – Frank

risposta

8

Nelle versioni R più recenti (3.1-3.1.2 + o così), l'assegnazione a un vettore non viene copiata. Non lo vedrai comunque eseguendo il codice di OP, e la ragione di ciò è la seguente. Poiché si riutilizza lo x e lo si assegna ad un altro oggetto, a R non viene notificato che x viene copiato in quel punto e deve presumere che non lo sarà (nel caso specifico sopra, penso che sarà opportuno cambiarlo in data.table::data.table e notifica a R che è stata effettuata una copia, ma si tratta di un problema separato - data.frame soffre dello stesso problema), e per questo copia al primo utilizzo. Se si modifica l'ordine dei comandi un po ', che si vede alcuna differenza:

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 

(risposta vecchia, per lo più a sinistra come una curiosità)

in realtà si può utilizzare l'operatore data.table:= di modificare il vettore a posto (penso che è necessario R versione 3.1 + per evitare la copia in 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" 
+0

Non posso essere più d'accordo sulla soluzione, l'ho confrontato ed è super veloce. @eddi Penso che sia una buona funzionalità per PR. – jangorecki

+0

Hmm, non sono sicuro del motivo per cui funziona ... Cioè, se si esegue 'res <- setDT (list (v)) [4, V1: = 10]' si ritornerà piuttosto un 'data.table' un vettore Ho chiesto una volta una [domanda simile] (http://stackoverflow.com/questions/24426164/why-doesnt-setdt-have-any-effect-in-this-case) e mi è stato detto da R-dev che questo non dovrebbe funzionare –

+0

@DavidArenburg il motivo per cui funziona è che né 'list' né' setDT' copiano i dati sottostanti. Creano nuovi oggetti anche se stanno avvolgendo i dati, in modo che 'res' sia un nuovo oggetto, ma che contenga il vettore originale all'interno. – eddi

4

Come suggerito da @Frank, è p è possibile farlo usando Rcpp. Ecco una versione con una macro ispirato dispatch.h di Rcpp che gestisce tutti i tipi di vettore atomiche:

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; 
} 

Esempio:

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" 

confronto con base e metodi data.table. Si può vedere che è molto più veloce:

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 
Problemi correlati