Membuat R dataframe baris demi baris

107

Saya ingin membuat dataframe baris demi baris di R. Saya telah melakukan beberapa pencarian, dan yang saya dapatkan hanyalah saran untuk membuat daftar kosong, menyimpan skalar indeks daftar, lalu setiap kali menambahkan ke daftar kerangka data baris tunggal dan memajukan indeks daftar satu per satu. Akhirnya, do.call(rbind,)masuk dalam daftar.

Meskipun ini berhasil, tampaknya sangat tidak praktis. Bukankah ada cara yang lebih mudah untuk mencapai tujuan yang sama?

Jelas saya mengacu pada kasus di mana saya tidak dapat menggunakan beberapa applyfungsi dan secara eksplisit perlu membuat dataframe baris demi baris. Setidaknya, apakah ada cara untuk pushmemasuki akhir daftar daripada secara eksplisit melacak indeks terakhir yang digunakan?

David B
sumber
1
Anda dapat menggunakan append()[yang mungkin harus diberi nama sisipkan] atau c()untuk menambahkan item ke akhir daftar, meskipun tidak akan membantu Anda di sini.
hatmatrix
Tidak banyak fungsi dalam R yang frame data kembali kecuali Anda kembali mereka [baris-bijaksana] dari lapply(), Map(), dan sebagainya, tetapi Anda juga mungkin ingin melihat pada aggregate(), dapply() {heR.Misc}, dan cast() {reshape}untuk melihat apakah tugas-tugas Anda tidak dapat ditangani oleh ini fungsi (ini semua frame data kembali).
hatmatrix

Jawaban:

96

Anda dapat mengembangkannya baris demi baris dengan menambahkan atau menggunakan rbind().

Itu tidak berarti Anda harus melakukannya. Struktur yang tumbuh secara dinamis adalah salah satu cara yang paling tidak efisien untuk membuat kode di R.

Jika Anda bisa, alokasikan seluruh data.frame Anda di muka:

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

dan kemudian selama operasi Anda masukkan baris pada satu waktu

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

Itu seharusnya berfungsi untuk data.frame sewenang-wenang dan jauh lebih efisien. Jika Anda melampaui N, Anda selalu dapat memperkecil baris kosong di bagian akhir.

Dirk Eddelbuettel
sumber
6
Bukankah Anda bermaksud untuk meletakkan N bukannya 10, dan daftar (1.4, "foo") daripada c (1.4, "foo") agar tidak memaksa 1.4 ke mode karakter?
hatmatrix
Ya, saya bermaksud menggunakan N dalam pembuatan data.frame. Juga, sangat bagus menangkap kembali paksaan ke dalam obrolan - saya melewatkannya.
Dirk Eddelbuettel
1
Lebih baik mengedit jawaban daripada meninggalkannya di komentar. Saya bingung mencoba memberikan jawaban ini.
Pengguna
4
data.tabletampaknya lebih cepat daripada pra-alokasi menggunakan data.frames. Pengujian di sini: stackoverflow.com/a/11486400/636656
Ari B.Friedman
apakah ini masih benar di R 3.1 di mana ini seharusnya lebih cepat?
userJT
49

Seseorang dapat menambahkan baris ke NULL:

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

misalnya

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)
mbq
sumber
3
itu mengeluarkan matriks, bukan bingkai data
Olga
1
@Olga Hanya jika Anda mengikat baris elemen dengan tipe yang sama - BTW dalam hal ini lebih baik untuk sapply(atau melakukan vektorisasi) dan mentransposisi.
mbq
1
@mbq Persis apa yang saya lakukan. Saya juga menemukan bahwa jika Anda menginisialisasinya dengan df <-data.frame (), ia akan mengeluarkan bingkai data.
Olga
9

Ini adalah contoh konyol bagaimana menggunakan do.call(rbind,)pada keluaran Map()[yang mirip dengan 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"

Saya cukup sering menggunakan konstruksi ini.

hatmatrix
sumber
8

Alasan saya sangat menyukai Rcpp adalah karena saya tidak selalu mengerti cara berpikir R Core, dan dengan Rcpp, lebih sering daripada tidak, saya tidak perlu melakukannya.

Berbicara secara filosofis, Anda berada dalam keadaan berdosa sehubungan dengan paradigma fungsional, yang mencoba memastikan bahwa setiap nilai muncul terlepas dari setiap nilai lainnya; mengubah satu nilai tidak boleh menyebabkan perubahan yang terlihat di nilai lain, seperti yang Anda dapatkan dengan representasi berbagi pointer di C.

Masalah muncul ketika pemrograman fungsional memberi sinyal pada pesawat kecil untuk menyingkir, dan pesawat kecil itu menjawab "Saya adalah mercusuar". Membuat serangkaian panjang perubahan kecil pada objek besar yang ingin Anda proses sementara itu membuat Anda persegi di wilayah mercusuar.

Dalam C ++ STL, push_back()adalah cara hidup. Itu tidak mencoba untuk berfungsi, tetapi mencoba untuk mengakomodasi idiom pemrograman umum secara efisien .

Dengan beberapa kepintaran di balik layar, terkadang Anda dapat mengatur agar memiliki satu kaki di setiap dunia. Sistem file berbasis snapshot adalah contoh yang baik (yang berevolusi dari konsep seperti union mount, yang juga menggunakan kedua sisi).

Jika R Core ingin melakukan ini, penyimpanan vektor yang mendasari dapat berfungsi seperti union mount. Satu referensi ke penyimpanan vektor mungkin valid untuk langganan 1:N, sementara referensi lain ke penyimpanan yang sama berlaku untuk langganan 1:(N+1). Mungkin ada penyimpanan yang dipesan belum secara valid direferensikan oleh apa pun kecuali nyaman untuk cepat push_back(). Anda tidak melanggar konsep fungsional saat menambahkan di luar rentang yang dianggap valid oleh referensi yang ada.

Akhirnya menambahkan baris secara bertahap, Anda kehabisan penyimpanan yang dipesan. Anda harus membuat salinan baru dari semuanya, dengan penyimpanan dikalikan dengan beberapa kenaikan. Implementasi STL yang saya gunakan cenderung melipatgandakan penyimpanan dengan 2 saat memperluas alokasi. Saya pikir saya membaca di R Internal bahwa ada struktur memori di mana penyimpanan bertambah 20%. Bagaimanapun, operasi pertumbuhan terjadi dengan frekuensi logaritmik relatif terhadap jumlah total elemen yang ditambahkan. Secara diamortisasi, ini biasanya dapat diterima.

Saat trik di balik layar berjalan, saya telah melihat yang lebih buruk. Setiap kali Anda push_back()membuat baris baru ke dataframe, struktur indeks tingkat atas perlu disalin. Baris baru dapat ditambahkan ke representasi bersama tanpa memengaruhi nilai fungsional lama apa pun. Saya bahkan tidak berpikir itu akan mempersulit pemulung; karena saya tidak mengusulkan push_front()semua referensi adalah referensi awalan ke depan penyimpanan vektor yang dialokasikan.

Allan Stokes
sumber
2

Jawaban Dirk Eddelbuettel adalah yang terbaik; di sini saya hanya mencatat bahwa Anda dapat lolos dengan tidak menentukan dimensi kerangka data atau tipe data sebelumnya, yang terkadang berguna jika Anda memiliki beberapa tipe data dan banyak kolom:

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.
John
sumber
Apakah maksud Anda df<-rbind(df, row2)?
Timothy C. Quinn
1

Saya telah menemukan cara ini untuk membuat kerangka data dengan mentah tanpa matriks.

Dengan nama kolom otomatis

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

Dengan nama kolom

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")
    )
phili_b
sumber
0

Jika Anda memiliki vektor yang ditakdirkan menjadi baris, gabungkan menggunakan c(), teruskan ke matriks baris demi baris, dan konversikan matriks tersebut ke kerangka data.

Misalnya baris

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)

dapat diubah menjadi bingkai data dengan demikian:

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

Memang, saya melihat 2 batasan utama: (1) ini hanya berfungsi dengan data mode tunggal, dan (2) Anda harus mengetahui # kolom terakhir Anda agar ini berfungsi (yaitu, saya berasumsi bahwa Anda tidak bekerja dengan larik compang-camping yang panjang baris terbesarnya tidak diketahui secara a priori ).

Solusi ini tampaknya sederhana, tetapi dari pengalaman saya dengan jenis konversi di R, saya yakin ini menciptakan tantangan baru di masa mendatang. Adakah yang bisa mengomentari ini?

Keegan Smith
sumber
0

Bergantung pada format baris baru Anda, Anda dapat menggunakan tibble::add_rowjika baris baru Anda sederhana dan dapat ditentukan dalam "pasangan nilai". Atau Anda bisa menggunakan dplyr::bind_rows, "implementasi yang efisien dari pola umum do.call (rbind, dfs)".

Arthur Yip
sumber