Maak een leeg data.frame

Ik probeer een data.frame te initialiseren zonder rijen. Kortom, ik wil de gegevenstypen voor elke kolom specificeren en ze een naam geven, maar er worden geen rijen als resultaat gemaakt.

Het beste wat ik tot nu toe heb kunnen doen, is zoiets als:

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

Dat creëert een data.frame met een enkele rij die alle gegevenstypen en kolomnamen bevat die ik wilde, maar creëert ook een nutteloze rij die vervolgens moet worden verwijderd.

Is er een betere manier om dit te doen?


Antwoord 1, autoriteit 100%

Initialiseer het gewoon met lege vectoren:

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

Hier is een ander voorbeeld met verschillende kolomtypes:

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 :

Het initialiseren van een data.framemet een lege kolom van het verkeerde type verhindert niet verdere toevoegingen van rijen met kolommen van verschillende typen.
Deze methode is net een beetje veiligerin die zin dat u vanaf het begin over de juiste kolomtypen beschikt, dus als uw code afhankelijk is van een kolomtypecontrole, werkt deze zelfs met een data.framemet nul rijen.


Antwoord 2, autoriteit 24%

Als u al een bestaand dataframehebt, laten we zeggen dfmet de gewenste kolommen, dan kunt u gewoon een leeg dataframe maken door alle rijen te verwijderen :

empty_df = df[FALSE,]

Merk op dat dfnog steeds de gegevens bevat, maar empty_dfniet.

Ik vond deze vraag op zoek naar hoe je een nieuwe instantie met lege rijen kunt maken, dus ik denk dat het voor sommige mensen nuttig kan zijn.


Antwoord 3, autoriteit 12%

U kunt het doen zonder kolomtypen op te geven

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

Antwoord 4, autoriteit 9%

U kunt als volgt read.tablegebruiken met een lege tekenreeks voor de invoer text:

colClasses = c("Date", "character", "character")
col.names = c("Date", "File", "User")
df <- read.table(text = "",
                 colClasses = colClasses,
                 col.names = col.names)

Als alternatief specificeren van de col.namesals een string:

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

Met dank aan Richard Scriven voor de verbetering


Antwoord 5, autoriteit 6%

Gewoon aangeven

table = data.frame()

wanneer u de eerste regel probeert te rbind, worden de kolommen gemaakt


Antwoord 6, autoriteit 4%

De meest efficiënte manier om dit te doen is door structurete gebruiken om een lijst te maken met de klasse "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)

Om dit in perspectief te plaatsen in vergelijking met het momenteel geaccepteerde antwoord, hier is een eenvoudige benchmark:

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

Antwoord 7, autoriteit 2%

Als u op zoek bent naar kortheid:

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

u hoeft de kolomnamen dus niet apart op te geven. U krijgt het standaard kolomtype logisch totdat u het gegevensframe vult.


Antwoord 8, autoriteit 2%

Ik heb een leeg dataframe gemaakt met de volgende code

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

en probeerde als volgt enkele rijen te binden om dezelfde te vullen.

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

maar het begon als volgt onjuiste kolomnamen te geven

 X3 X4
1  3  4

Oplossing hiervoor is om newrow als volgt naar type df te converteren

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

geeft nu het juiste dataframe wanneer weergegeven met kolomnamen als volgt

 id nobs
1  3   4 

Antwoord 9

Om een leeg gegevensframe te maken, geeft u het aantal benodigde rijen en kolommen door aan de volgende functie:

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

Als u een leeg frame wilt maken terwijl u de klasse van elke kolom specificeert, geeft u eenvoudig een vector van de gewenste gegevenstypen door aan de volgende functie:

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

Gebruik als volgt:

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

Wat geeft:

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

Voer het volgende uit om uw keuzes te bevestigen:

lapply(df, class)
#output
$X1
[1] "character"
$X2
[1] "logical"
$X3
[1] "numeric"

Antwoord 10

Als u een leeg data.frame met dynamische namen (colnamen in een variabele) wilt maken, kan dit helpen:

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

Je kunt de typen ook wijzigen als dat nodig is. zoals:

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

Antwoord 11

Als u het niet erg vindt om gegevenstypen niet expliciet op te geven, kunt u dit op deze manier doen:

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)

Antwoord 12

Door data.tablete gebruiken, kunnen we gegevenstypen voor elke kolom specificeren.

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

Antwoord 13

Als je zo’n data.framemet veel kolommen wilt declareren, zal het waarschijnlijk lastig zijn om alle kolomklassen met de hand te typen. Vooral als je gebruik kunt maken van rep, is deze aanpak gemakkelijk en snel (ongeveer 15% sneller dan de andere oplossing die zo kan worden gegeneraliseerd):

Als uw gewenste kolomklassen zich in een vector colClassesbevinden, kunt u het volgende doen:

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

lapplyzal resulteren in een lijst met de gewenste lengte, waarvan elk element gewoon een leeg getypte vector is zoals numeric()of integer().

setDFconverteert deze listdoor te verwijzen naar een data.frame.

setnamesvoegt de gewenste namen toe als referentie.

Snelheidsvergelijking:

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 

Het is ook sneller dan het gebruik van structureop een vergelijkbare manier:

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

Antwoord 14

Als u al een dataframe heeft, kunt u de metadata (kolomnamen en typen) uit een dataframe halen(bijv. als u een BUG beheertdie alleen wordt geactiveerd met bepaalde invoer en hebben een leeg dummy-dataframe nodig):

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

En gebruik dan de read.tableom het lege dataframe te maken

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

Antwoord 15

Stel dat uw kolomnamen dynamisch zijn, u kunt een lege matrix met rijnamen maken en deze omzetten in een gegevensframe.

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

Antwoord 16

Deze vraag ging niet specifiek in op mijn zorgen (beschreven hier) maar voor het geval iemand dit wil doen met een geparametriseerd aantal kolommen en zonder dwang:

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

Zoals divibisan zegt over de gekoppelde vraag,

…de reden dat [dwang] optreedt [bij het binden van matrices en hun samenstellende typen] is dat een matrix alleen een
enkel gegevenstype. Als je 2 matrices bindt, is het resultaat nog steeds a
matrix en dus worden de variabelen allemaal eerder in een enkel type gedwongen
converteren naar een data.frame


Antwoord 17

Ik houd deze functie bij de hand voor wanneer ik hem nodig heb, en verander de kolomnamen en klassen om ze aan te passen aan het gebruik:

make_df <- function() { data.frame(name=character(),
                     profile=character(),
                     sector=character(),
                     type=character(),
                     year_range=character(),
                     link=character(),
                     stringsAsFactors = F)
}
make_df()
[1] name       profile    sector     type       year_range link      
<0 rows> (or 0-length row.names)

Other episodes