2015-10-15 6 views
0

Я пытаюсь скопировать один вектор в другой, используя следующий синтаксис:Эффективное использование векторов

data<-NULL 
for(i in 1:nrow(line)){ 
    data=append(data,line[i*4]) 
} 

Из того, что я видел, использование append таким образом приводит много копирования данных, что делает R очень медленным. Каков синтаксис для копирования 4-го элемента одного массива в другой, учитывая, что список, который вы копируете, имеет заданный размер?

+0

Лучший способ перейти - предварительно выделить «вне» вектор и скопировать все там. –

+3

Непонятно, что вы спрашиваете. Не могли бы вы создать вектор индексов, например 'j' и использовать его для подмножества вашей векторной строки? Затем выполните 'data <- line [j]'. –

+1

Сделайте воспроизводимый пример, включая желаемый результат. – Frank

ответ

2

Вот три метода с их ориентирами. Вы можете видеть, что предварительное распределение вектора, как и в функции method2, довольно быстро, а метод lapply - средний, а ваша функция является самой медленной.

Конечно, это 1D векторы, а не массивы n-D, но я ожидал, что эталонные тесты будут похожи или даже более выражены.

method1 <- function(line) { 
    data<-NULL 
    for(i in 1:length(line)){ 
    data=append(data,line[i]) 
    } 
} 

method2 <- function(line) { 
    data <- vector(mode="numeric", length = length(line)) 
    for (i in 1:length(line)) { 
    data[i] <- line[i] 
    } 
} 

library(microbenchmark) 
r <- rnorm(1000) 
microbenchmark(method2(r), unit="ms") 
#> Unit: milliseconds 
#>  expr  min  lq  mean median  uq  max neval 
#> method2(r) 2.18085 2.279676 2.428731 2.371593 2.500495 5.24888 100 
microbenchmark(lapply(r, function(x) { data<-append(data, x) }), unit="ms") 
#> Unit: milliseconds 
#>             expr  min  lq 
#> lapply(r, function(x) {  data <- append(data, x) }) 3.014673 3.091299 
#>  mean median  uq  max neval 
#> 3.287216 3.150052 3.260199 6.036501 100 
microbenchmark(method1(r), unit="ms") 
#> Unit: milliseconds 
#>  expr  min  lq mean median  uq  max neval 
#> method1(r) 3.938684 3.978002 5.71831 4.020001 4.280521 98.58584 100 

Не осознавал, что ОП требуется только каждый четвертый. Почему бы просто не использовать кадр данных или таблицу данных?

d <- data.frame(matrix(rnorm(1000), ncol=1)) 
microbenchmark(d2 <- d[seq(1,nrow(d), 4),]) 
#> Unit: microseconds 
#>       expr min  lq  mean median  uq 
#> d2 <- d[seq(1, nrow(d), 4), ] 64.846 65.9915 73.08007 67.225 73.8225 
#>  max neval 
#> 220.438 100 
library(data.table) 
dt <- data.table(d) 
microbenchmark(d2 <- dt[seq(1,nrow(d), 4),]) 
#> Unit: microseconds 
#>       expr  min  lq  mean median  uq 
#> d2 <- dt[seq(1, nrow(d), 4), ] 298.163 315.2025 324.8793 320.554 330.416 
#>  max neval 
#> 655.124 100 
+0

Все три этих метода, похоже, просто копируют всю строку в данные, а не копируют каждый четвертый элемент по запросу OP. – josliber

+0

Не понял, отредактировал и поблагодарил. – potterzot

+0

Спасибо, это объясняется очень красиво – Dave

2

Если вы пытаетесь извлечь каждый четвертый элемент из вектора, можно индексировать с помощью seq захватить правильные элементы:

data <- letters[seq(4, length(letters), by=4)] 
data 
# [1] "d" "h" "l" "p" "t" "x" 

Растущие вектор один на один раз, как вы показываете в вашем вопросе будет быть медленным, потому что вам нужно будет перераспределить ваш вектор (см. второй круг The R Inferno). Однако даже предварительное выделение вашего вектора и построение его циклом for будет медленным по сравнению с его построением в одной операции векторизации индексации.

Чтобы получить ощущение улучшения скорости, рассмотрит сравнение с подобным методом вы описали, за исключением использования предварительного выделения:

for.prealloc <- function(x) { 
    data <- vector(mode="numeric", length = floor(length(x)/4)) 
    for (i in 1:floor(length(x)/4)) { 
    data[i] <- x[i*4] 
    } 
    data 
} 
josilber <- function(x) x[seq(4, length(x), by=4)] 
r <- rnorm(10000) 
all.equal(for.prealloc(r), josilber(r)) 
# [1] TRUE 

library(microbenchmark) 
microbenchmark(for.prealloc(r), josilber(r)) 
# Unit: microseconds 
#    expr  min  lq  mean median  uq  max neval 
# for.prealloc(r) 1846.014 2035.7890 2351.9681 2094.804 2244.56 5283.285 100 
#  josilber(r) 95.757 97.4125 125.9877 113.179 138.96 259.606 100 

подхода я предлагаю 20x быстрее, чем при использовании for и предварительно выделенный вектор (и он будет даже быстрее, чем с использованием append и нераспределенного вектора).

Смежные вопросы