Boş bir data.frame oluşturun


480

Herhangi bir satır olmadan bir data.frame başlatmaya çalışıyorum. Temel olarak, her sütun için veri türlerini belirtmek ve adlandırmak istiyorum, ancak sonuç olarak oluşturulan herhangi bir satır yok.

Şimdiye kadar yapabildiğim en iyi şey şudur:

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

Bu, istediğim tüm veri türlerini ve sütun adlarını içeren tek bir satırla bir data.frame oluşturur, ancak daha sonra kaldırılması gereken işe yaramaz bir satır oluşturur.

Bunu yapmanın daha iyi bir yolu var mı?

Yanıtlar:


652

Boş vektörlerle başlat:

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

Farklı sütun türlerine sahip başka bir örnek:

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 

Not:

A'nın data.frameyanlış türde boş bir sütunla başlatılması , farklı türde sütunlara sahip satırların daha fazla eklenmesini engellemez.
Bu yöntem, başlangıçtan itibaren doğru sütun türlerine sahip olmanız açısından biraz daha güvenlidir , bu nedenle kodunuz bazı sütun türü denetimlerine dayanıyorsa, data.framesıfır satırlı bir bile çalışacaktır .


3
Tüm alanları NULL ile başlatırsam aynı olur mu?
yosukesabai

8
@yosukesabai: hayır, NULL ile bir sütun başlatırsanız sütun eklenmez :)
digEmAll

6
@yosukesabai: data.frame's sütunlar yazdım , bu yüzden evet, bir başlatmak data.frameistiyorsanız sütun türüne karar
vermelisiniz

1
@jxramos: aslında data.frame, sütun türlerinin " önceliği " üzerinde gerçekten kısıtlayıcı değildir (örneğin, bir tarih sütunu veya hatta öğe listesi içeren bir sütun ekleyebilirsiniz). Ayrıca, bu soru mutlak bir referans değildir, çünkü doğru sütun türünü belirtmezseniz, farklı türlerde sütunlara sahip başka satır eklemelerini engellemezsiniz ... bu nedenle, bir not ekleyeceğim, ancak tüm olasılıkları kapsamadığından tüm ilkel türlere bir örnek ...
digEmAll

3
@ user4050: soru boş bir data.frame oluşturmakla ilgiliydi, bu yüzden satır sayısı sıfır olduğunda ... belki data.frame(Doubles=rep(as.double(NA),numberOfRow), Ints=rep(as.integer(NA),numberOfRow))
NA'larla dolu bir data.frame

140

Eğer varsa zaten mevcut veri dilimi var diyelim dfo zaman sadece tüm satırları kaldırarak Boş bir veri çerçevesi oluşturabilir, istediğiniz sütun vardır:

empty_df = df[FALSE,]

Uyarı dfhala verileri içerir, ancak empty_dfdeğil.

Bu soruyu boş satırlarla nasıl yeni bir örnek oluşturacağımı buldum, bu yüzden bazı insanlar için yararlı olabileceğini düşünüyorum.


2
Harika fikir. Satırların hiçbirini değil, TÜM sütunları saklayın. Kim düşürdüğü bir şeyi kaçırdı.
Ram Narasimhan

1
Güzel bir çözüm, ancak 0 satırlı bir veri çerçevesi aldım. Veri çerçevesinin boyutunu aynı tutmak için new_df = df [NA,] öneririm. Bu aynı zamanda önceki sütunların yeni veri çerçevesine depolanmasını sağlar. Örneğin, orijinal df'den "Tarih" sütununu almak için (geri kalan NA değerini korurken): new_df $ Tarih <- df $ Tarih.
Katya

2
@Katya, eğer bunu df[NA,]da endeksi etkileyecektir (ki istediğiniz gibi olması muhtemel değildir), bunun yerine kullanmak istiyorum df[TRUE,] = NA; ancak bunun orijinalin üzerine yazacağına dikkat edin. Önce veri çerçevesini kopyalamanız copy_df = data.frame(df)ve daha sonracopy_df[TRUE,] = NA
toto_tico

3
@Katya ya da kolayca boş satırlar ekleyebilir empty_dfile empty_df[0:nrow(df),] <- NA.
toto_tico

1
@Katya, kod olarak işaretlemek istediğiniz şeyin etrafında bir backquote (`) kullanırsınız ve * ile italik olarak ve ** kullanarak kalın olarak başka şeyler de vardır . Muhtemelen SO'nun tüm Markdown Sözdizimini okumak istiyorsunuz . Çoğu sadece cevaplar için mantıklı.
toto_tico

79

Sütun türlerini belirtmeden yapabilirsiniz

df = data.frame(matrix(vector(), 0, 3,
                dimnames=list(c(), c("Date", "File", "User"))),
                stringsAsFactors=F)

4
Bu durumda, sütun türleri varsayılan olarak vektör başına mantıksal () olur, ancak daha sonra df'ye eklenen öğelerin türleriyle geçersiz kılınır. Str (df), df [1,1] <- 'x' i deneyin
Dave X

58

Sen kullanabilirsiniz read.tablegirişi için boş bir dize ile textaşağıdaki gibi:

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

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

Alternatif col.namesolarak dize olarak belirtmek :

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

Richard Scriven'a iyileştirme için teşekkürler


4
Ya da read.table(text = "", ...)öyle olsa bile , açıkça bir bağlantı açmanıza gerek yoktur.
Zengin Scriven

şık. muhtemelen bunu birçok potansiyel sütun için yapmanın en genişletilebilir / otomatik yolu
MichaelChirico

3
read.csvYaklaşım ile çalışır readr::read_csvgibi read_csv("Date,File,User\n", col_types = "Dcc"). Bu şekilde, gerekli yapının boş bir tibble'ını doğrudan oluşturabilirsiniz.
Heather Turner

27

Bunu yapmanın en etkili yolu structure, sınıfa sahip bir liste oluşturmak için kullanmaktır "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)

Şu anda kabul edilen cevaba kıyasla bunu perspektife sokmak için basit bir kıyaslama:

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

data.tablegenellikle işlevleri .internal.selfrefçağırmadan taklit edilemeyen bir özellik içerir data.table. Burada belgelenmemiş bir davranışa güvenmediğinizden emin misiniz?
Adam Ryczkowski

@AdamRyczkowski Bence data.table paketindeki temel "data.frame" sınıfını ve "data.table" eklentisini karıştırıyorsunuz .
Thomas

Evet. Kesinlikle. Benim hatam. Son yorumumu yoksay. Arama yaparken bu konuya rastladım data.tableve Google'ın istediğimi bulduğunu ve buradaki her şeyin data.tableilgili olduğunu varsaydım .
Adam Ryczkowski

1
@PatrickT Kodunuzun ne yaptığının herhangi bir anlam ifade ettiğine dair bir kontrol yoktur. data.frame()adlandırma, rownames, vb. üzerinde kontrol sağlar
Thomas

26

Sadece beyan et

table = data.frame()

rbindilk satıra çalıştığınızda sütunlar oluşturulur


2
OP'nin "Her sütun için veri türlerini belirtmek ve adlandırmak istiyorum" gereksinimlerini gerçekten karşılamıyor. Bir sonraki adım bir ise, rbindbu iyi çalışır, değilse ...
Gregor Thomas

Her neyse, bu basit çözüm için teşekkürler. Ben de rbind sadece sütunlar iki data.frame arasında karşılık gelirse kullanılabileceğini düşündüm çünkü belirli sütunlarla bir data.frame başlatmak istedim. Durum böyle değil. Rbind kullanırken sadece bir data.frame başlatabildiğim için şaşırdım. Teşekkürler.
Giordano

4
Burada en iyi önerilen çözüm. Benim için, önerilen yolu kullanarak, mükemmel çalıştı rbind().
Kots

17

Kısalık arıyorsanız:

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

bu nedenle sütun adlarını ayrı ayrı belirtmeniz gerekmez. Veri çerçevesini doldurana kadar varsayılan sütun türünü mantıksal olarak alırsınız.


read.csv, metin argümanını ayrıştırır, böylece sütun adlarını alırsınız. Read.table'dan daha kompakttır (text = "", col.names = c ("col1", "col2"))
marc

Anladım:Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 0, 2
Climbs_lika_Spyder

Bu OP'nin "Her sütun için veri türlerini belirtmek istiyorum" gereksinimlerini karşılamıyor olsa da, muhtemelen bunu yapmak için değiştirilebilir.
Gregor Thomas

14

Aşağıdaki kodu kullanarak boş veri çerçevesi oluşturdum

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

ve aşağıdaki gibi doldurmak için bazı satırları bağlamaya çalıştı.

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

ancak aşağıdaki gibi yanlış sütun adları vermeye başladı

  X3 X4
1  3  4

Bunun çözümü, newrow'u df türüne aşağıdaki gibi dönüştürmektir

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

şimdi sütun adları ile aşağıdaki şekilde görüntülendiğinde doğru veri çerçevesi sağlar

  id nobs
1  3   4 

7

İçin boş bir veri çerçevesini oluşturmak aşağıdaki işlev gerekli satır ve sütun sayısı geçmek:

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

Her bir sütunun sınıfını belirtirken boş bir çerçeve oluşturmak için , istenen veri türlerinden bir vektörü aşağıdaki işleve geçirmeniz yeterlidir:

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

Aşağıdaki gibi kullanın:

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

Hangi verir:

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

Seçimlerinizi onaylamak için aşağıdakileri çalıştırın:

lapply(df, class)

#output
$X1
[1] "character"

$X2
[1] "logical"

$X3
[1] "numeric"

1
Bu OP'nin "Her sütun için veri türlerini belirtmek istiyorum"
Gregor Thomas

6

Dinamik adlara (bir değişkendeki colnames) sahip boş bir data.frame oluşturmak istiyorsanız, bu yardımcı olabilir:

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

Gerekirse türleri de değiştirebilirsiniz. sevmek:

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

4

Veri türlerini açıkça belirtmemenin bir sakıncası yoksa, bunu şu şekilde yapabilirsiniz:

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)

4

Kullanarak data.tableher sütun için veri türlerini belirleyebiliriz.

library(data.table)    
data=data.table(a=numeric(), b=numeric(), c=numeric())

3

Böyle bir data.framesütunu birçok sütunla bildirmek isterseniz , tüm sütun sınıflarını elle yazmak bir acı olacaktır. Özellikle yararlanabiliyorsanız rep, bu yaklaşım kolay ve hızlıdır (bu şekilde genelleştirilebilen diğer çözümlerden yaklaşık% 15 daha hızlı):

İstediğiniz sütun sınıfları bir vektördeyse colClassesaşağıdakileri yapabilirsiniz:

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

lapplyher bir elemanı numeric()ya da gibi bir boş tip vektör olan istenen uzunlukta bir liste ile sonuçlanacaktır integer().

setDFBu dönüştüren listbir referans ile data.frame.

setnames istenen adları referans olarak ekler.

Hız karşılaştırması:

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 

Aynı structureşekilde kullanmaktan daha hızlıdır :

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

1

Sütun adlarınızın dinamik olduğunu, boş bir satır adlı matris oluşturabileceğinizi ve bunu veri çerçevesine dönüştürebileceğinizi varsayalım.

nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))

Bu OP'nin "Her sütun için veri türlerini belirtmek istiyorum"
Gregor Thomas

1

Bu soru endişelerimi ( burada ana hatlarıyla ) özellikle ele almadı, ancak herhangi birinin bunu parametreli sütun sayısı ve zorlama olmadan yapmak istemesi durumunda:

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

Divibisan'ın bağlantılı soruda belirttiği gibi,

... [zorlama] 'nın [matrisleri ve bileşen türlerini bağlarken] ortaya çıkmasının nedeni, bir matrisin yalnızca tek bir veri türüne sahip olabilmesidir. 2 matrisi cbind yaptığınızda, sonuç yine de bir matristir ve bu nedenle değişkenler bir veriye dönüştürülmeden önce tek bir türe zorlanır.


1

Zaten bir veri çerçeveniz varsa , meta verileri (sütun adları ve türleri) bir veri çerçevesinden ayıklayabilirsiniz (örneğin , yalnızca belirli girişlerle tetiklenen ve boş bir sahte Veri Çerçevesine ihtiyaç duyan bir HATA kontrol ediyorsanız):

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

Ve sonra read.tableboş veri çerçevesi oluşturmak için

read.table(text = "",
   colClasses = c('integer', 'factor'),
   col.names = c('col1', 'col2'))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.