Производительность rbind.data.frame - PullRequest
8 голосов
/ 12 мая 2011

У меня есть список фреймов данных, для которых я уверен, что все они содержат хотя бы одну строку (на самом деле, некоторые содержат только одну строку, а другие содержат заданное количество строк), и что все они имеют одинаковые столбцы(имена и типы).В случае, если это имеет значение, я также уверен, что в строках нет NA.

Ситуацию можно смоделировать так:

#create one row
onerowdfr<-do.call(data.frame, c(list(), rnorm(100) , lapply(sample(letters[1:2], 100, replace=TRUE), function(x){factor(x, levels=letters[1:2])})))
colnames(onerowdfr)<-c(paste("cnt", 1:100, sep=""), paste("cat", 1:100, sep=""))
#reuse it in a list
someParts<-lapply(rbinom(200, 1, 14/200)*6+1, function(reps){onerowdfr[rep(1, reps),]})

Я установил параметры (израндомизацию), чтобы они приблизили мою истинную ситуацию.

Теперь я хочу объединить все эти кадры данных в одном кадре данных.Я думал, что использование rbind поможет, как это:

system.time(
result<-do.call(rbind, someParts)
)

Теперь, в моей системе (что не особенно медленно), и с указанными выше настройками, это вывод system.time:

   user  system elapsed 
   5.61    0.00    5.62

Почти 6 секунд для повторного связывания 254 (в моем случае) строк из 200 переменных?Конечно, должен быть способ улучшить производительность здесь?В моем коде мне приходится делать подобные вещи очень часто (это результат многократного вменения), поэтому мне нужно, чтобы это было как можно быстрее.

Ответы [ 5 ]

13 голосов
/ 12 мая 2011

Можете ли вы построить свои матрицы только с числовыми переменными и преобразовать в коэффициент в конце? rbind намного быстрее на числовых матрицах.

В моей системе, используя фреймы данных:

> system.time(result<-do.call(rbind, someParts))
   user  system elapsed 
  2.628   0.000   2.636 

Построение списка со всеми числовыми матрицами вместо:

onerowdfr2 <- matrix(as.numeric(onerowdfr), nrow=1)
someParts2<-lapply(rbinom(200, 1, 14/200)*6+1, 
                   function(reps){onerowdfr2[rep(1, reps),]})

результаты намного быстрее rbind.

> system.time(result2<-do.call(rbind, someParts2))
   user  system elapsed 
  0.001   0.000   0.001

РЕДАКТИРОВАТЬ: Вот еще одна возможность; он просто объединяет каждый столбец по очереди.

> system.time({
+   n <- 1:ncol(someParts[[1]])
+   names(n) <- names(someParts[[1]])
+   result <- as.data.frame(lapply(n, function(i) 
+                           unlist(lapply(someParts, `[[`, i))))
+ })
   user  system elapsed 
  0.810   0.000   0.813  

Все же не так быстро, как при использовании матриц.

РЕДАКТИРОВАТЬ 2:

Если у вас есть только цифры и коэффициенты, нетрудно преобразовать все в числовые значения, rbind, и преобразовать необходимые столбцы обратно в коэффициенты. Это предполагает, что все факторы имеют одинаковые уровни. Преобразование в множитель из целого числа также происходит быстрее, чем из числового значения, поэтому сначала я делаю целое число.

someParts2 <- lapply(someParts, function(x)
                     matrix(unlist(x), ncol=ncol(x)))
result<-as.data.frame(do.call(rbind, someParts2))
a <- someParts[[1]]
f <- which(sapply(a, class)=="factor")
for(i in f) {
  lev <- levels(a[[i]])
  result[[i]] <- factor(as.integer(result[[i]]), levels=seq_along(lev), labels=lev)
}

Время в моей системе:

   user  system elapsed 
   0.090    0.00    0.091 
5 голосов
/ 12 мая 2011

Не огромный прирост, но замена rbind на rbind.fill из пакета plyr выбивает примерно 10% времени работы (с образцом набора данных на моей машине).

4 голосов
/ 10 июля 2013

Если вы действительно хотите быстрее управлять data.frame, я бы предложил использовать пакет data.table и функцию rbindlist().Я не проводил обширные тесты, но для моего набора данных (3000 кадров данных, 1000 строк по 40 столбцов в каждом) rbindlist() занимает всего 20 секунд.

3 голосов
/ 12 мая 2011

Это на 25% быстрее, но должен быть лучший способ ...

system.time({
  N <- do.call(sum, lapply(someParts, nrow))
  SP <- as.data.frame(lapply(someParts[[1]], function(x) rep(x,N)))
  k <- 0
  for(i in 1:length(someParts)) {
    j <- k+1
    k <- k + nrow(someParts[[i]])
    SP[j:k,] <- someParts[[i]]
  }
})
1 голос
/ 24 июня 2016

Убедитесь, что вы привязываете фрейм данных к фрейму данных. При связывании списка с фреймом данных произошла огромная деградация производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...