Створення фрейму даних R за рядком


107

Я хотів би побудувати кадр даних за рядком у Р. Я здійснив пошук, і все, що я придумав, - це пропозиція створити порожній список, тримати скалярний індекс списку, а потім кожного разу додавати до списку однорядний кадр даних і просуньте індекс списку по одному. Нарешті, do.call(rbind,)у списку.

Хоча це працює, це здається дуже громіздким. Чи не існує простішого способу досягнення тієї ж мети?

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


1
Ви можете використовувати append()[який, ймовірно, повинен бути названий вставкою] або c()додати елементи до кінця списку, хоча тут вам не допоможуть.
hatmatrix

Там не багато функцій в R , що кадри повернення даних , якщо не повернути їх [ряди] з lapply(), Map()і так далі, але ви також можете поглянути на aggregate(), dapply() {heR.Misc}і cast() {reshape}щоб побачити , якщо ваші завдання не можуть бути оброблені цими функції (ці всі кадри повернення даних).
hatmatrix

Відповіді:


96

Ви можете вирощувати їх рядком за рядком, додаючи або використовуючи rbind().

Це не означає, що слід. Динамічно зростаючі структури - один з найменш ефективних способів кодування в Р.

Якщо ви можете, виділіть весь файл даних 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, ви завжди можете зменшити порожні рядки в кінці.


6
Хіба ви не мали намір поставити N замість 10, а список (1.4, "foo") замість c (1.4, "foo"), щоб не примусити 1.4 в символьному режимі?
hatmatrix

Так, я мав на увазі використовувати N у створенні data.frame. Крім того, дуже хороший вилов примусу в чаті - я цього пропустив.
Дірк Еддельбуеттель

1
Краще відредагувати відповідь, ніж залишити її в коментарях. Я був розгублений, намагаючись зробити цю відповідь.
Користувач

4
data.tableздається, навіть швидше, ніж попереднє виділення за допомогою data.frames. Тестування тут: stackoverflow.com/a/11486400/636656
Арі Б. Фрідман

це все ще вірно в R 3.1, де це повинно бути швидшим?
userJT

49

Можна додати рядки до 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
він видає матрицю, а не кадр даних
Ольга

1
@Olga Тільки якщо ви прив'язуєте рядки елементів рівного типу - BTW, у цьому випадку краще sapply(або векторизувати) та перемістити.
mbq

1
@mbq Точно те, що я роблю. Я також виявив, що якщо ініціалізувати його з df <-data.frame (), він виводить кадр даних.
Ольга

9

Це нерозумний приклад того, як використовувати 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

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

Якщо говорити по-філософськи, ви перебуваєте у гріху щодо функціональної парадигми, яка намагається забезпечити, щоб кожна цінність виглядала незалежною від будь-якої іншої цінності; зміна одного значення ніколи не повинна спричиняти видимих ​​змін у іншому значенні, як ви отримуєте вказівники, що діляться представленням у C.

Проблеми виникають, коли функціональне програмування сигналізує малому кораблю про те, щоб рухатись із шляху, а маленьке судно відповідає "Я маяк". Внесення довгих серій невеликих змін до великого об’єкта, який ви хочете обробити тим часом, ставить вас на територію маяка.

У С ++ STL push_back()- це спосіб життя. Він не намагається бути функціональним, але він дійсно намагається пристосувати загальні ідіоми програмування ефективно .

Маючи кмітливість за лаштунками, іноді можна домовитись, щоб мати одну ногу в кожному світі. Файлові системи на основі знімків є хорошим прикладом (який розвинувся з таких понять, як з'єднання кріплення, які також є обома сторонами).

Якщо R Core хотів це зробити, базове векторне зберігання може функціонувати як з'єднання з'єднання. Одне посилання на векторне сховище може бути дійсним для підписок 1:N, тоді як інше посилання на те саме сховище справедливе для підписок 1:(N+1). Тут може бути зарезервоване сховище, на яке ще не вірно посилається нічого, але зручне для швидкого push_back(). Ви не порушуєте функціональну концепцію при додаванні за межі діапазону, який будь-яка існуюча посилання вважає дійсною.

Згодом додаючи рядки поступово, у вас закінчується зарезервоване сховище. Вам потрібно буде створити нові копії всього, при цьому обсяг пам’яті помножиться на деякий приріст. Реалізації STL, які я використовую, як правило, примножують сховище на 2 при розширенні розподілу. Я подумав, що я прочитав в "Внутрішній", що існує структура пам'яті, де збільшення сховища на 20%. Так чи інакше, операції з ростом відбуваються з логарифмічною частотою відносно загальної кількості доданих елементів. На амортизованій основі це, як правило, прийнятно.

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


2

Відповідь Дірка Еддельбуеттеля найкраща; тут я лише зазначу, що ви можете уникнути, не задаючи розміри даних або типи даних, що іноді корисно, якщо у вас є кілька типів даних і безліч стовпців:

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(df,row2) #now this works as you'd expect.

Ви мали на увазі df<-rbind(df, row2)?
Тімоті К. Куінн

1

Я знайшов такий спосіб створити кадр даних сировиною без матриці.

З автоматичною назвою стовпця

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")
    )

0

Якщо у вас є вектори, призначені стати рядками, об'єднайте їх за допомогою 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, я впевнений, що це створює нові виклики в усьому світі. Хтось може прокоментувати це?


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.