Подтвердить что ты не робот

Самый эффективный список методов data.frame?

Просто общался с коллегами по этому поводу, и мы подумали, что стоит посмотреть, что должны сказать люди в SO-стране. Предположим, у меня был список с N элементами, где каждый элемент был вектором длины X. Теперь предположим, что я хотел преобразовать его в data.frame. Как и в большинстве вещей в R, существует множество способов скинирования пресловутого кота, например as.dataframe, используя пакет plyr, комбинируя do.call с cbind, предварительно распределяя DF и заполняя его, и другие.

Проблема, которая была представлена, - это то, что происходит, когда либо N, либо X (в нашем случае это X) становятся чрезвычайно большими. Существует ли один метод скин-кошки, который заметно превосходит эффективность (особенно в плане памяти)?

4b9b3361

Ответ 1

Так как a data.frame уже является списком, и вы знаете, что каждый элемент списка имеет одинаковую длину (X), самой быстрой задачей, вероятно, было бы просто обновить атрибуты class и row.names:

set.seed(21)
n <- 1e6
x <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
x <- c(x,x,x,x,x,x)

system.time(a <- as.data.frame(x))
system.time(b <- do.call(data.frame,x))
system.time({
  d <- x  # Skip 'c' so Joris doesn't down-vote me! ;-)
  class(d) <- "data.frame"
  rownames(d) <- 1:n
  names(d) <- make.unique(names(d))
})

identical(a, b)  # TRUE
identical(b, d)  # TRUE

Обновить - это ~ 2x быстрее, чем создание d:

system.time({
  e <- x
  attr(e, "row.names") <- c(NA_integer_,n)
  attr(e, "class") <- "data.frame"
  attr(e, "names") <- make.names(names(e), unique=TRUE)
})

identical(d, e)  # TRUE

Обновление 2. Я забыл о потреблении памяти. Последнее обновление делает две копии e. Использование функции attributes уменьшает ее до одной копии.

set.seed(21)
f <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
f <- c(f,f,f,f,f,f)
tracemem(f)
system.time({  # makes 2 copies
  attr(f, "row.names") <- c(NA_integer_,n)
  attr(f, "class") <- "data.frame"
  attr(f, "names") <- make.names(names(f), unique=TRUE)
})

set.seed(21)
g <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
g <- c(g,g,g,g,g,g)
tracemem(g)
system.time({  # only makes 1 copy
  attributes(g) <- list(row.names=c(NA_integer_,n),
    class="data.frame", names=make.names(names(g), unique=TRUE))
})

identical(f,g)  # TRUE

Ответ 2

Кажется, что требуется предложение data.table, учитывая, что для больших наборов данных требуется эффективность. Примечательно setattr устанавливает по ссылке и не копирует

library(data.table)
set.seed(21)
n <- 1e6
h <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
h <- c(h,h,h,h,h,h)
tracemem(h)

system.time({h <- as.data.table(h)
            setattr(h, 'names', make.names(names(h), unique=T))})

as.data.table, однако делает копию.


Изменить - без копирования версии

Использование предложения @MatthewDowle setattr(h,'class','data.frame'), которое будет конвертировать в data.frame по ссылке (без копий)

set.seed(21)
n <- 1e6
i <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
i <- c(i,i,i,i,i,i)
tracemem(i)

system.time({  
  setattr(i, 'class', 'data.frame')
  setattr(i, "row.names", c(NA_integer_,n))

  setattr(i, "names", make.names(names(i), unique=TRUE))

})