г: для работы цикла с вложенными индексами работает супер медленно

голоса
14

У меня операция я хотел бы запустить для каждой строки кадра данных, изменяя один столбец. Я являюсь / применить ddply / sqldf человек, но я буду использовать петлю, когда они имеют смысл, и я думаю, что это одна из тех времен. Этот случай является сложным, так как столбец изменений зависит от информации, которая изменяет по строкам; в зависимости от информации в одной ячейке, я должен внести изменения только один из десяти других ячеек в этой строке. С 75 столбцов и 20000 строк, операция занимает 10 минут, когда все другие операции, в моем сценарии занимает 0-5 секунд, десять секунд макс. Я раздел мою проблему вплоть до очень простого теста ниже.

n <- 20000
t.df <- data.frame(matrix(1:5000, ncol=10, nrow=n) )
system.time(
 for (i in 1:nrow(t.df)) {
 t.df[i,(t.df[i,1]%%10 + 1)] <- 99
 }
)

Это занимает 70 секунд с десятью колоннами, и 360, когда Ncol = 50. Это безумие. Есть петли неправильный подход? Есть ли лучший, более эффективный способ сделать это?

Я уже пытался инициализировать вложенный термин (t.df [я, 1] %% 10 + 1) в виде списка вне для цикла. Это экономит около 30 секунд (из 10 минут), но делает пример кода выше, более сложным. Так что это помогает, но не решение.

Моя текущая лучшая идея пришла во время подготовки этого тестирования. Для меня, только 10 колонн, которые существенны (и 75-11 колонки не имеют значения). Со времен пробега зависят столько от числа столбцов, можно просто запустить описанную выше операцию на кадре данных, который исключает нерелевантные столбцы. Это поможет мне до чуть более минуты. Но это «цикл с вложенными индексами» даже самым лучшим способом думать о моей проблеме?

Задан 30/11/2011 в 19:40
источник пользователем
На других языках...                            


5 ответов

голоса
10

Кажется, что узкое место является имея данные в виде data.frame. Я полагаю, что в вашей реальной проблеме у вас есть веские причины, чтобы использовать data.frame. Любой способ преобразовать данные таким образом, что он может остаться в матрице?

Кстати, большой вопрос, и очень хороший пример.

Вот иллюстрация того, насколько быстрее петли на матрицах, чем на data.frames:

> n <- 20000
> t.df <- (matrix(1:5000, ncol=10, nrow=n) )
> system.time(
+   for (i in 1:nrow(t.df)) {
+     t.df[i,(t.df[i,1]%%10 + 1)] <- 99
+   }
+ )
   user  system elapsed 
  0.084   0.001   0.084 
> 
> n <- 20000
> t.df <- data.frame(matrix(1:5000, ncol=10, nrow=n) )
> system.time(
+   for (i in 1:nrow(t.df)) {
+     t.df[i,(t.df[i,1]%%10 + 1)] <- 99
+   }
+   )
   user  system elapsed 
 31.543  57.664  89.224 
Ответил 30/11/2011 в 19:55
источник пользователем

голоса
6

ОБНОВЛЕНИЕ: Добавлена ​​версия матричного решения Томми к бенчмаркинга.

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

n <- 20000
t.df <- (matrix(1:5000, ncol=10, nrow=n))

f_ramnath <- function(x){
  idx <- x[,1] %% 10 + 1
  x[cbind(1:NROW(x), idx)] <- 99  
  return(x)
}

f_long <- function(t.df){
  for (i in 1:nrow(t.df)) {
    t.df[i,(t.df[i,1]%%10 + 1)] <- 99
  }
  return(t.df)
}

f_joran <- function(t.df){
  t.df[col(t.df) == (row(t.df) %% 10) + 1]  <- 99
  return(t.df)
}

f_tommy <- function(t.df){
  t2.df <- t.df
  # Create a logical matrix with TRUE wherever the replacement should happen
  m <- array(FALSE, dim=dim(t2.df))
  m[cbind(seq_len(nrow(t2.df)), t2.df[,1]%%10L + 1L)] <- TRUE
  t2.df[m] <- 99
  return(t2.df)
}

f_tommy_mat <- function(m){
  m[cbind(seq_len(nrow(m)), m[,1]%%10L + 1L)] <- 99
}

Для сравнения эффективности различных подходов, мы можем использовать rbenchmark.

library(rbenchmark)
benchmark(f_long(t.df), f_ramnath(t.df), f_joran(t.df), f_tommy(t.df), 
  f_tommy_mat(t.df), replications = 20,  order = 'relative',
  columns = c('test', 'elapsed', 'relative')

               test elapsed  relative
5 f_tommy_mat(t.df)   0.135  1.000000
2   f_ramnath(t.df)   0.172  1.274074
4     f_tommy(t.df)   0.311  2.303704
3     f_joran(t.df)   0.705  5.222222
1      f_long(t.df)   2.411 17.859259
Ответил 30/11/2011 в 20:17
источник пользователем

голоса
6

Использование rowи colкажется менее сложным для меня:

t.df[col(t.df) == (row(t.df) %% 10) + 1]  <- 99

Я думаю , что Томми все еще быстрее, но при использовании rowи colможет быть проще для понимания.

Ответил 30/11/2011 в 20:15
источник пользователем

голоса
6

@JD Long является правильным , что если t.dfможет быть представлено в виде матрицы, все будет гораздо быстрее.

... И тогда вы можете векторизации все это так, что это молниеносно:

n <- 20000
t.df <- data.frame(matrix(1:5000, ncol=10, nrow=n) )
system.time({
  m <- as.matrix(t.df)
  m[cbind(seq_len(nrow(m)), m[,1]%%10L + 1L)] <- 99
  t2.df <- as.data.frame(m)
}) # 0.00 secs

К сожалению, матрица индексации Я использую здесь , кажется, не работает на data.frame.

EDIT Разновидности где я создаю логическую матрицу индекс работает на data.frame, и почти так же быстро:

n <- 20000
t.df <- data.frame(matrix(1:5000, ncol=10, nrow=n) )
system.time({
  t2.df <- t.df

  # Create a logical matrix with TRUE wherever the replacement should happen
  m <- array(FALSE, dim=dim(t2.df))
  m[cbind(seq_len(nrow(t2.df)), t2.df[,1]%%10L + 1L)] <- TRUE

  t2.df[m] <- 99
}) # 0.01 secs
Ответил 30/11/2011 в 20:09
источник пользователем

голоса
1

Другой вариант , когда вы нужны смешанные типы столбцов (и поэтому вы не можете использовать matrix) находится :=в data.table . Пример из ?":=":

require(data.table)
m = matrix(1,nrow=100000,ncol=100)
DF = as.data.frame(m)
DT = as.data.table(m)    
system.time(for (i in 1:1000) DF[i,1] <- i)
    # 591 seconds 
system.time(for (i in 1:1000) DT[i,V1:=i])
    # 1.16 seconds  ( 509 times faster )
Ответил 01/12/2011 в 12:51
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more