Buat data.frame kosong

480

Saya mencoba menginisialisasi data.frame tanpa baris. Pada dasarnya, saya ingin menentukan tipe data untuk setiap kolom dan menamainya, tetapi hasilnya tidak ada baris yang dibuat.

Yang terbaik yang bisa saya lakukan sejauh ini adalah sesuatu seperti:

df <- data.frame(Date=as.Date("01/01/2000", format="%m/%d/%Y"), 
                 File="", User="", stringsAsFactors=FALSE)
df <- df[-1,]

Yang membuat data.frame dengan satu baris berisi semua tipe data dan nama kolom yang saya inginkan, tetapi juga membuat baris yang tidak berguna yang kemudian perlu dihapus.

Apakah ada cara yang lebih baik untuk melakukan ini?

Jeff Allen
sumber

Jawaban:

652

Inisialisasi dengan vektor kosong:

df <- data.frame(Date=as.Date(character()),
                 File=character(), 
                 User=character(), 
                 stringsAsFactors=FALSE) 

Berikut ini contoh lain dengan berbagai jenis kolom:

df <- data.frame(Doubles=double(),
                 Ints=integer(),
                 Factors=factor(),
                 Logicals=logical(),
                 Characters=character(),
                 stringsAsFactors=FALSE)

str(df)
> str(df)
'data.frame':   0 obs. of  5 variables:
 $ Doubles   : num 
 $ Ints      : int 
 $ Factors   : Factor w/ 0 levels: 
 $ Logicals  : logi 
 $ Characters: chr 

NB:

Menginisialisasi data.framedengan kolom kosong dari jenis yang salah tidak mencegah penambahan lebih lanjut dari baris memiliki kolom dari jenis yang berbeda.
Metode ini hanya sedikit lebih aman dalam arti bahwa Anda akan memiliki jenis kolom yang benar dari awal, maka jika kode Anda bergantung pada beberapa jenis pemeriksaan kolom, itu akan bekerja bahkan dengan data.framedengan nol baris.

menggali semua
sumber
3
Apakah akan sama jika saya menginisialisasi semua bidang dengan NULL?
yosukesabai
8
@yosukesabai: tidak, jika Anda menginisialisasi kolom dengan NULL kolom tidak akan ditambahkan :)
digEmAll
6
@yosukesabai: data.frametelah mengetik kolom, jadi ya, jika Anda ingin menginisialisasi, data.frameAnda harus menentukan jenis kolom ...
digEmAll
1
@ jxramos: yah, sebenarnya data.frametidak benar-benar membatasi "primitif" dari jenis kolom (misalnya, Anda dapat menambahkan kolom tanggal atau bahkan kolom yang berisi daftar elemen). Selain itu, pertanyaan ini bukan referensi absolut, karena misalnya jika Anda tidak menentukan jenis kolom yang benar, Anda tidak akan memblokir tambahan baris tambahan yang memiliki kolom dari jenis yang berbeda ... jadi, saya akan menambahkan catatan, tetapi tidak contoh dengan semua tipe primitif karena tidak mencakup semua kemungkinan ...
digEmAll
3
@ user4050: pertanyaannya adalah tentang membuat data.frame kosong, jadi ketika jumlah baris adalah nol ... mungkin Anda ingin membuat data.frame penuh pada NAS ... dalam hal ini Anda dapat menggunakan misdata.frame(Doubles=rep(as.double(NA),numberOfRow), Ints=rep(as.integer(NA),numberOfRow))
digEmAll
140

Jika Anda sudah memiliki bingkai data yang ada , katakanlah dfyang memiliki kolom yang Anda inginkan, maka Anda bisa membuat bingkai data kosong dengan menghapus semua baris:

empty_df = df[FALSE,]

Perhatikan bahwa dfmasih berisi data, tetapiempty_df tidak.

Saya menemukan pertanyaan ini mencari cara membuat contoh baru dengan baris kosong, jadi saya pikir ini mungkin bermanfaat bagi sebagian orang.

toto_tico
sumber
2
Ide bagus Pertahankan tidak ada baris, tetapi SEMUA kolom. Siapa pun yang menurunkan nilai melewatkan sesuatu.
Ram Narasimhan
1
Solusi yang bagus, namun saya menemukan bahwa saya mendapatkan bingkai data dengan 0 baris. Untuk menjaga ukuran frame data tetap sama, saya sarankan new_df = df [NA,]. Ini juga memungkinkan untuk menyimpan kolom sebelumnya ke dalam bingkai data baru. Misalnya untuk mendapatkan kolom "Tanggal" dari df asli (sambil tetap beristirahat NA): new_df $ Date <- df $ Date.
Katya
2
@ Katya, jika Anda melakukan df[NA,]ini akan mempengaruhi indeks juga (yang tidak mungkin seperti yang Anda inginkan), saya akan menggunakan df[TRUE,] = NA; Namun perhatikan bahwa ini akan menimpa yang asli. Anda harus menyalin kerangka data terlebih dahulu copy_df = data.frame(df)dan kemudiancopy_df[TRUE,] = NA
toto_tico
3
@ Katya, atau Anda juga dapat dengan mudah menambahkan baris kosong ke empty_dfwith empty_df[0:nrow(df),] <- NA.
toto_tico
1
@ Katya, Anda menggunakan tanda kutip mundur (`) di sekitar apa yang ingin Anda tandai sebagai kode, dan ada hal-hal lain seperti huruf miring menggunakan *, dan cetak tebal menggunakan **. Anda mungkin ingin membaca semua Sintaks Markdown SO . Sebagian besar hanya masuk akal untuk jawaban.
toto_tico
79

Anda dapat melakukannya tanpa menentukan jenis kolom

df = data.frame(matrix(vector(), 0, 3,
                dimnames=list(c(), c("Date", "File", "User"))),
                stringsAsFactors=F)
zeleniy
sumber
4
Dalam kasus itu, kolom mengetikkan default sebagai logis per vektor (), tetapi kemudian diganti dengan jenis elemen yang ditambahkan ke df. Coba str (df), df [1,1] <- 'x'
Dave X
58

Anda dapat menggunakan read.tabledengan string kosong untuk input textsebagai berikut:

colClasses = c("Date", "character", "character")
col.names = c("Date", "File", "User")

df <- read.table(text = "",
                 colClasses = colClasses,
                 col.names = col.names)

Cara lainnya menentukan col.namesstring:

df <- read.csv(text="Date,File,User", colClasses = colClasses)

Terima kasih kepada Richard Scriven untuk perbaikannya

Rentrop
sumber
4
Atau bahkan read.table(text = "", ...)Anda tidak perlu secara eksplisit membuka koneksi.
Rich Scriven
manis. mungkin cara yang paling dapat dikembangkan / dikerjakan secara otomatis untuk banyak kolom potensial
MichaelChirico
3
The read.csvPendekatan juga bekerja dengan readr::read_csv, seperti dalam read_csv("Date,File,User\n", col_types = "Dcc"). Dengan cara ini Anda dapat langsung membuat tibble kosong dari struktur yang diperlukan.
Heather Turner
27

Cara paling efisien untuk melakukan ini adalah menggunakan structureuntuk membuat daftar yang memiliki kelas "data.frame":

structure(list(Date = as.Date(character()), File = character(), User = character()), 
          class = "data.frame")
# [1] Date File User
# <0 rows> (or 0-length row.names)

Untuk menempatkan ini dalam perspektif dibandingkan dengan jawaban yang saat ini diterima, berikut ini adalah tolok ukur sederhana:

s <- function() structure(list(Date = as.Date(character()), 
                               File = character(), 
                               User = character()), 
                          class = "data.frame")
d <- function() data.frame(Date = as.Date(character()),
                           File = character(), 
                           User = character(), 
                           stringsAsFactors = FALSE) 
library("microbenchmark")
microbenchmark(s(), d())
# Unit: microseconds
#  expr     min       lq     mean   median      uq      max neval
#   s()  58.503  66.5860  90.7682  82.1735 101.803  469.560   100
#   d() 370.644 382.5755 523.3397 420.1025 604.654 1565.711   100
Thomas
sumber
data.tablebiasanya berisi .internal.selfrefatribut, yang tidak dapat dipalsukan tanpa memanggil data.tablefungsi. Apakah Anda yakin tidak mengandalkan perilaku tidak berdokumen di sini?
Adam Ryczkowski
@AdamRyczkowski Saya pikir Anda membingungkan basis "data.frame" kelas dan add-on "data.table" kelas dari paket data.table .
Thomas
Iya. Pastinya. Salahku. Abaikan komentar terakhir saya. Saya menemukan utas ini ketika mencari data.tabledan berasumsi bahwa Google memang menemukan apa yang saya inginkan dan semua yang ada di sini data.tableterkait.
Adam Ryczkowski
1
@ Patrick Tidak ada yang mengecek apa yang sedang dilakukan oleh kode Anda. data.frame()memberikan cek untuk penamaan, nama pengguna, dll.
Thomas
26

Nyatakan saja

table = data.frame()

ketika Anda mencoba ke rbindbaris pertama itu akan membuat kolom

Daniel Fischer
sumber
2
Tidak benar-benar memenuhi persyaratan OP tentang "Saya ingin menentukan tipe data untuk setiap kolom dan menamainya". Jika langkah selanjutnya adalah rbindini akan bekerja dengan baik, jika tidak ...
Gregor Thomas
Bagaimanapun, terima kasih atas solusi sederhana ini. Saya juga ingin menginisialisasi data.frame dengan kolom tertentu karena saya pikir rbind hanya dapat digunakan jika kolom sesuai antara dua data.frame. Sepertinya tidak demikian. Saya terkejut bahwa saya bisa begitu saja menginisialisasi data.frame saat menggunakan rbind. Terima kasih.
giordano
4
Solusi yang diusulkan terbaik di sini. Bagi saya, menggunakan cara yang diusulkan, bekerja dengan sempurna rbind().
Kots
17

Jika Anda mencari kekurangan:

read.csv(text="col1,col2")

jadi Anda tidak perlu menentukan nama kolom secara terpisah. Anda mendapatkan tipe kolom default logis sampai Anda mengisi bingkai data.

marc
sumber
read.csv mem-parsing argumen teks sehingga Anda mendapatkan nama kolom. Ini lebih kompak daripada read.table (text = "", col.names = c ("col1", "col2"))
marc
Saya mendapatkan:Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 0, 2
Climbs_lika_Spyder
Ini tidak memenuhi persyaratan OP, "Saya ingin menentukan tipe data untuk setiap kolom" , meskipun mungkin dapat dimodifikasi untuk melakukannya.
Gregor Thomas
14

Saya membuat bingkai data kosong menggunakan kode berikut

df = data.frame(id = numeric(0), jobs = numeric(0));

dan mencoba mengikat beberapa baris untuk mengisi yang sama sebagai berikut.

newrow = c(3, 4)
df <- rbind(df, newrow)

tetapi mulai memberikan nama kolom yang salah sebagai berikut

  X3 X4
1  3  4

Solusi untuk ini adalah untuk mengkonversi newrow ke tipe df sebagai berikut

newrow = data.frame(id=3, jobs=4)
df <- rbind(df, newrow)

sekarang memberikan bingkai data yang benar ketika ditampilkan dengan nama kolom sebagai berikut

  id nobs
1  3   4 
Shrikant Prabhu
sumber
7

Untuk membuat bingkai data kosong , masukkan jumlah baris dan kolom yang diperlukan ke fungsi berikut:

create_empty_table <- function(num_rows, num_cols) {
    frame <- data.frame(matrix(NA, nrow = num_rows, ncol = num_cols))
    return(frame)
}

Untuk membuat bingkai kosong sambil menentukan kelas setiap kolom , cukup lewati vektor tipe data yang diinginkan ke dalam fungsi berikut:

create_empty_table <- function(num_rows, num_cols, type_vec) {
  frame <- data.frame(matrix(NA, nrow = num_rows, ncol = num_cols))
  for(i in 1:ncol(frame)) {
    print(type_vec[i])
    if(type_vec[i] == 'numeric') {frame[,i] <- as.numeric(frame[,i])}
    if(type_vec[i] == 'character') {frame[,i] <- as.character(frame[,i])}
    if(type_vec[i] == 'logical') {frame[,i] <- as.logical(frame[,i])}
    if(type_vec[i] == 'factor') {frame[,i] <- as.factor(frame[,i])}
  }
  return(frame)
}

Gunakan sebagai berikut:

df <- create_empty_table(3, 3, c('character','logical','numeric'))

Pemberian yang mana:

   X1  X2 X3
1 <NA> NA NA
2 <NA> NA NA
3 <NA> NA NA

Untuk mengkonfirmasi pilihan Anda, jalankan yang berikut:

lapply(df, class)

#output
$X1
[1] "character"

$X2
[1] "logical"

$X3
[1] "numeric"
Berhubung dgn sibernetika
sumber
1
Ini tidak memenuhi persyaratan OP, "Saya ingin menentukan tipe data untuk setiap kolom"
Gregor Thomas
6

Jika Anda ingin membuat bingkai data kosong dengan nama dinamis (nama dalam variabel), ini dapat membantu:

names <- c("v","u","w")
df <- data.frame()
for (k in names) df[[k]]<-as.numeric()

Anda dapat mengubah jenisnya juga jika Anda membutuhkannya. Suka:

names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()
Ali Khosro
sumber
4

Jika Anda tidak keberatan tidak menentukan tipe data secara eksplisit, Anda dapat melakukannya dengan cara ini:

headers<-c("Date","File","User")
df <- as.data.frame(matrix(,ncol=3,nrow=0))
names(df)<-headers

#then bind incoming data frame with col types to set data types
df<-rbind(df, new_df)
Odysseus Ithaca
sumber
4

Dengan Menggunakan, data.tablekami dapat menentukan tipe data untuk setiap kolom.

library(data.table)    
data=data.table(a=numeric(), b=numeric(), c=numeric())
Rushabh Patel
sumber
3

Jika Anda ingin mendeklarasikannya data.framedengan banyak kolom, mungkin akan merepotkan untuk mengetikkan semua kelas kolom dengan tangan. Terutama jika Anda dapat memanfaatkannya rep, pendekatan ini mudah dan cepat (sekitar 15% lebih cepat daripada solusi lain yang dapat digeneralisasi seperti ini):

Jika kelas kolom yang Anda inginkan dalam vektor colClasses, Anda dapat melakukan hal berikut:

library(data.table)
setnames(setDF(lapply(colClasses, function(x) eval(call(x)))), col.names)

lapplyakan menghasilkan daftar panjang yang diinginkan, setiap elemen yang hanya berupa vektor diketik kosong seperti numeric()atau integer().

setDFmengonversinya listdengan mengacu pada a data.frame.

setnames menambahkan nama yang diinginkan dengan referensi.

Perbandingan kecepatan:

classes <- c("character", "numeric", "factor",
             "integer", "logical","raw", "complex")

NN <- 300
colClasses <- sample(classes, NN, replace = TRUE)
col.names <- paste0("V", 1:NN)

setDF(lapply(colClasses, function(x) eval(call(x))))

library(microbenchmark)
microbenchmark(times = 1000,
               read = read.table(text = "", colClasses = colClasses,
                                 col.names = col.names),
               DT = setnames(setDF(lapply(colClasses, function(x)
                 eval(call(x)))), col.names))
# Unit: milliseconds
#  expr      min       lq     mean   median       uq      max neval cld
#  read 2.598226 2.707445 3.247340 2.747835 2.800134 22.46545  1000   b
#    DT 2.257448 2.357754 2.895453 2.401408 2.453778 17.20883  1000  a 

Ini juga lebih cepat daripada menggunakan structuredengan cara yang serupa:

microbenchmark(times = 1000,
               DT = setnames(setDF(lapply(colClasses, function(x)
                 eval(call(x)))), col.names),
               struct = eval(parse(text=paste0(
                 "structure(list(", 
                 paste(paste0(col.names, "=", 
                              colClasses, "()"), collapse = ","),
                 "), class = \"data.frame\")"))))
#Unit: milliseconds
#   expr      min       lq     mean   median       uq       max neval cld
#     DT 2.068121 2.167180 2.821868 2.211214 2.268569 143.70901  1000  a 
# struct 2.613944 2.723053 3.177748 2.767746 2.831422  21.44862  1000   b
MichaelChirico
sumber
1

Katakanlah nama kolom Anda dinamis, Anda dapat membuat matriks bernama baris kosong dan mengubahnya menjadi bingkai data.

nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))
jpmarindiaz
sumber
Ini tidak memenuhi persyaratan OP, "Saya ingin menentukan tipe data untuk setiap kolom"
Gregor Thomas
1

Pertanyaan ini tidak secara khusus membahas masalah saya (dijelaskan di sini ) tetapi jika ada orang yang ingin melakukan ini dengan jumlah kolom parameter dan tanpa paksaan:

> require(dplyr)
> dbNames <- c('a','b','c','d')
> emptyTableOut <- 
    data.frame(
        character(), 
        matrix(integer(), ncol = 3, nrow = 0), stringsAsFactors = FALSE
    ) %>% 
    setNames(nm = c(dbNames))
> glimpse(emptyTableOut)
Observations: 0
Variables: 4
$ a <chr> 
$ b <int> 
$ c <int> 
$ d <int>

Seperti yang divibisan nyatakan pada pertanyaan terkait,

... alasan [pemaksaan] terjadi [ketika matriks mengikat dan tipe-tipe pembentuknya] adalah bahwa sebuah matriks hanya dapat memiliki tipe data tunggal. Saat Anda mengikat 2 matriks, hasilnya masih berupa matriks sehingga semua variabel dipaksa menjadi satu jenis sebelum dikonversi ke data.frame

d8aninja
sumber
1

Jika Anda sudah memiliki kerangka data, Anda dapat mengekstrak metadata (nama kolom dan jenis) dari kerangka data (misalnya jika Anda mengendalikan BUG yang hanya dipicu dengan input tertentu dan memerlukan kerangka Data boneka kosong):

colums_and_types <- sapply(df, class)

# prints: "c('col1', 'col2')"
print(dput(as.character(names(colums_and_types))))

# prints: "c('integer', 'factor')"
dput(as.character(as.vector(colums_and_types)))

Dan kemudian gunakan read.tableuntuk membuat kerangka data kosong

read.table(text = "",
   colClasses = c('integer', 'factor'),
   col.names = c('col1', 'col2'))
toto_tico
sumber