Создание R-кадра данных построчно - PullRequest
100 голосов
/ 04 сентября 2010

Я бы хотел построчно построить в Data R. к списку однострочный фрейм данных и продвиньте индекс списка на один. Наконец, do.call(rbind,) в списке.

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

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

Ответы [ 7 ]

89 голосов
/ 04 сентября 2010

Вы можете увеличивать их построчно, добавляя или используя 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, вы всегда можете уменьшить пустые строки в конце.

47 голосов
/ 04 сентября 2010

Можно добавить строки в 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)
9 голосов
/ 06 сентября 2010

Это глупый пример того, как использовать 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"

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

8 голосов
/ 31 августа 2011

Причина, по которой мне так нравится 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(), все ссылки являются префиксными ссылками на начало выделенного векторного хранилища.

1 голос
/ 21 января 2019

Я нашел этот способ создания фрейма данных в формате raw без матрицы.

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

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")
    )
1 голос
/ 30 июня 2018

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

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.
0 голосов
/ 24 мая 2017

Если у вас есть векторы, предназначенные стать строками, объедините их, используя 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) вы должны знать свои последние # столбцы, чтобы это работало (то есть я предполагаю, что вы не работа с рваным массивом, наибольшая длина строки которого неизвестна a priori ).

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

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