Наиболее эффективный список для метода data.frame? - PullRequest
32 голосов
/ 10 мая 2011

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

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

Ответы [ 2 ]

27 голосов
/ 10 мая 2011

Поскольку 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

Обновление - это примерно в 2 раза быстрее, чем создание 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
10 голосов
/ 12 сентября 2012

Похоже, что требуется предложение 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))

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