Создание R-кадра данных построчно

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

Хотя это работает, это кажется очень громоздким. Разве нет более простого способа достижения той же цели?

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

7 ответов

Решение

Вы можете увеличивать их построчно, добавляя или используя rbind(),

Это не значит, что вы должны. Динамически растущие структуры - один из наименее эффективных способов кодирования в R.

Если вы можете, выделите весь data.frame заранее:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

а затем во время ваших операций вставьте строку за раз

DF[i, ] <- list(1.4, "foo")

Это должно работать для произвольных data.frame и быть намного более эффективным. Если вы выбрасываете N, вы всегда можете уменьшить пустые строки в конце.

Можно добавить строки в NULL:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

например

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)

Это глупый пример того, как использовать do.call(rbind,) на выходе Map() [который похож на lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

Я использую эту конструкцию довольно часто.

Причина, по которой мне так нравится Rcpp, заключается в том, что я не всегда понимаю, как думает R Core, а с Rcpp чаще всего не приходится.

Говоря философски, вы находитесь в состоянии греха в отношении функциональной парадигмы, которая пытается гарантировать, что каждое значение кажется независимым от любого другого значения; изменение одного значения никогда не должно вызывать видимого изменения другого значения, как это происходит с указателями, совместно использующими представление в C.

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

В C++ STL, push_back() это образ жизни. Он не пытается быть функциональным, но он пытается эффективно приспособиться к общим идиомам программирования.

С некоторой сообразительностью за кулисами вы можете иногда договориться, что в каждом мире будет одна нога. Файловые системы, основанные на снимках, являются хорошим примером (который основан на таких понятиях, как объединение монтировок, которые также объединяют обе стороны).

Если бы R Core хотел сделать это, базовое векторное хранилище могло бы функционировать как объединение. Одна ссылка на векторное хранилище может быть действительной для подписчиков 1:N в то время как другая ссылка на то же хранилище действительна для подписчиков 1:(N+1), Могут быть зарезервированные хранилища, на которые пока что нет действительной ссылки, но удобные для быстрого push_back(), Вы не нарушаете функциональную концепцию при добавлении за пределы диапазона, который любая существующая ссылка считает действительным.

Постепенно добавляя строки постепенно, вы исчерпываете зарезервированное хранилище. Вам нужно будет создавать новые копии всего, с объемом памяти, умноженным на некоторое приращение. Реализации STL, которые я использую, имеют тенденцию умножать объем памяти на 2 при расширении выделения. Мне показалось, что я прочитал в R Internals, что есть структура памяти, где хранилище увеличивается на 20%. В любом случае, операции роста происходят с логарифмической частотой по отношению к общему количеству добавленных элементов. На амортизированной основе это обычно приемлемо.

За кулисами я видел худшее. Каждый раз, когда вы push_back() новая строка на фрейм данных, должна быть скопирована структура индекса верхнего уровня. Новая строка может добавляться к общему представлению, не затрагивая старые функциональные значения. Я даже не думаю, что это сильно усложнит сборщик мусора; так как я не предлагаю push_front() все ссылки являются префиксными ссылками на начало выделенного векторного хранилища.

Я нашел этот способ для создания dataframe по сырью без матрицы.

С автоматическим названием столбца

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

С именем столбца

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )

Ответ Дирка Эддельбюттеля самый лучший; здесь я просто отмечаю, что вы можете избежать предварительного указания измерений или типов данных в фрейме, что иногда полезно, если у вас несколько типов данных и много столбцов:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(d,row2) #now this works as you'd expect.

Если у вас есть векторы, предназначенные стать строками, объедините их, используя c()передать их в матрицу строка за строкой и преобразовать эту матрицу в кадр данных.

Например, строки

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

может быть преобразован в фрейм данных таким образом:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Надо признать, я вижу 2 основных ограничения: (1) это работает только с одномодовыми данными, и (2) вы должны знать свои последние # столбцы, чтобы это работало (т.е. я предполагаю, что вы не работаете с рваный массив, наибольшая длина строки которого априори неизвестна).

Это решение кажется простым, но из моего опыта с преобразованиями типов в R, я уверен, что оно создает новые проблемы в будущем. Кто-нибудь может прокомментировать это?

В зависимости от формата вашей новой строки вы можете использовать tibble::add_rowесли ваша новая строка проста и может быть указана в "парах значений". Или вы могли бы использоватьdplyr::bind_rows, "эффективная реализация общего шаблона do.call(rbind, dfs)".

Другие вопросы по тегам