Создание строки данных R по строкам
Я хотел бы построить строку данных по строкам в R. Я сделал несколько поисков, и все, что я придумал, - это предложение создать пустой список, сохранить индексный индексный скаляр, затем каждый раз добавлять в список однострочный фрейм и перечислить индекс списка на единицу. Наконец, do.call(rbind,)
в списке.
Пока это работает, это кажется очень громоздким. Нет ли более простого способа достичь той же цели?
Очевидно, что я имею в виду случаи, когда я не могу использовать некоторую функцию apply
и явно должен создавать строку данных по строкам. По крайней мере, есть ли способ push
в конце списка вместо явного отслеживания последнего используемого индекса?
Ответы
Ответ 1
Вы можете вырастить их по строкам, добавив или используя rbind()
.
Это не значит, что вам следует. Динамически растущие структуры являются одним из наименее эффективных способов кодирования в R.
Если вы можете, просто сохраните все свои данные. Фрейм спереди:
N <- 1e4 # some magic number, 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, вы всегда можете сбрасывать пустые строки в конце.
Ответ 2
Можно добавить строки в 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)
Ответ 3
Это глупый пример использования 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"
Я использую эту конструкцию довольно часто.
Ответ 4
Причина, по которой мне так нравится Rcpp, заключается в том, что я не всегда понимаю, как думает R Core, и с Rcpp, чаще всего, мне не нужно.
Говоря философски, вы находитесь в состоянии греха в отношении функциональной парадигмы, которая старается обеспечить, чтобы каждая ценность оказывалась независимой от любой другой ценности; изменение одного значения никогда не должно вызывать видимое изменение в другом значении, как вы получаете с представлением совместного использования указателей в C.
Проблемы возникают, когда функциональное программирование сигнализирует маленькому кораблю, чтобы он ушел с дороги, а малый корабль отвечает: "Я - маяк". Создание длинной серии небольших изменений большого объекта, который вы хотите обработать, тем временем ставит вас на площадь маяка.
В С++ STL push_back()
- это образ жизни. Он не пытается быть функциональным, но он пытается эффективно использовать общие идиомы программирования.
С некоторой ловкостью за кулисами, вы можете иногда организовать одну ногу в каждом мире. Хорошим примером являются файловые системы на основе снимков (которые развивались из таких понятий, как монтирования монстров, которые также выравнивают обе стороны).
Если R Core захотел сделать это, базовое хранилище векторов могло бы функционировать как объединение монстров. Одна ссылка на хранилище векторов может быть действительной для индексов 1:N
, а другая ссылка на одно и то же хранилище действительна для индексов 1:(N+1)
. Может быть зарезервированное хранилище, на которое пока еще не ссылаются ссылки, но удобно для быстрого push_back()
. Вы не нарушаете функциональную концепцию при добавлении вне диапазона, которое любая существующая ссылка считает действительной.
В конце концов, добавляя строки поэтапно, у вас заканчивается зарезервированное хранилище. Вам нужно будет создать новые копии всего, причем хранилище умножается на некоторый приращение. Используемые мной реализации STL имеют тенденцию умножать память на 2 при расширении распределения. Я думал, что я читаю в R Internals, что есть структура памяти, где память увеличивается на 20%. В любом случае, операции роста происходят с логарифмической частотой относительно общего количества добавленных элементов. На амортизированной основе это обычно приемлемо.
Как трюки за кулисами идут, я видел хуже. Каждый раз, когда вы push_back()
добавляете новую строку в dataframe, необходимо скопировать структуру индекса верхнего уровня. Новая строка может присоединяться к совместному представлению, не влияя на какие-либо старые функциональные значения. Я даже не думаю, что это усложнит сборщик мусора; поскольку я не предлагаю push_front()
, все ссылки являются префиксными ссылками на переднюю часть выделенного векторного хранилища.
Ответ 5
Дирк Эддельбюттель ответ лучший; здесь я просто отмечаю, что вы можете избежать предварительного указания измерений или типов данных в фрейме, что иногда полезно, если у вас несколько типов данных и много столбцов:
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.
Ответ 6
Если у вас есть векторы, которым суждено стать рядами, объедините их с помощью 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))
По общему признанию, я вижу два основных ограничения: (1) это работает только с одномодовыми данными, и (2) вы должны знать свои последние # столбцы, чтобы это работало (т.е. я предполагаю, что вы не работа с оборванным массивом, наибольшая длина строки которого неизвестна априори).
Это решение кажется простым, но, по моему опыту преобразования типов в R, я уверен, что он создает новые задачи по-линии. Может кто-нибудь прокомментировать это?
Ответ 7
Я нашел этот способ для создания 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")
)