web-dev-qa-db-fra.com

Créer un data.frame vide

J'essaye d'initialiser un data.frame sans aucune ligne. En gros, je veux spécifier les types de données pour chaque colonne et les nommer, mais aucune ligne ne doit être créée en conséquence.

Le mieux que j'ai pu faire jusqu'à présent est quelque chose comme:

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

Ce qui crée un data.frame avec une seule ligne contenant tous les types de données et les noms de colonnes que je voulais, mais crée également une ligne inutile qui doit ensuite être supprimée.

Y a-t-il une meilleure manière de faire cela?

440
Jeff Allen

Il suffit de l'initialiser avec des vecteurs vides:

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

Voici un autre exemple avec différents types de colonnes:

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 

N.B.:

L'initialisation d'un data.frame avec une colonne vide de type incorrect n'empêche pas l'ajout supplémentaire de lignes comportant des colonnes de types différents.
Cette méthode est juste un peu plus sûre dans le sens où vous aurez les bons types de colonnes depuis le début, donc si votre code repose sur une vérification du type de colonne, cela fonctionnera même avec un data.frame avec zéro ligne.

587
digEmAll

Si vous avez déjà un cadre de données existant, supposons que df comporte les colonnes souhaitées, vous pouvez simplement créer un cadre de données vide en supprimant toutes les lignes:

empty_df = df[FALSE,]

Notez que df contient toujours les données, mais pas empty_df.

J'ai trouvé cette question en cherchant comment créer une nouvelle instance avec des lignes vides, alors je pense que cela pourrait être utile pour certaines personnes.

109
toto_tico

Vous pouvez le faire sans spécifier de type de colonne

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

Vous pouvez utiliser read.table avec une chaîne vide pour l'entrée text comme suit:

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

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

Vous pouvez également spécifier le col.names en tant que chaîne:

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

Merci à Richard Scriven pour l'amélioration

51
Rentrop

La méthode la plus efficace consiste à utiliser structure pour créer une liste contenant la classe "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)

Pour mettre cela en perspective par rapport à la réponse actuellement acceptée, voici un repère simple:

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

Il suffit de déclarer

table = data.frame()

lorsque vous essayez de rbind la première ligne, il créera les colonnes

18
Daniel Fischer

Si vous cherchez de la brièveté:

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

vous n'avez donc pas besoin de spécifier les noms de colonne séparément. Vous obtenez le type de colonne par défaut logique jusqu'à ce que vous remplissiez le bloc de données.

16
marc

J'ai créé un bloc de données vide en utilisant le code suivant

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

et essayé de lier certaines lignes pour remplir le même comme suit.

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

mais il a commencé à donner des noms de colonne incorrects comme suit

  X3 X4
1  3  4

La solution à cela est de convertir newrow en type df comme suit

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

donne maintenant le cadre de données correct lorsqu'il est affiché avec les noms de colonne comme suit

  id nobs
1  3   4 
10
Shrikant Prabhu

Pour créer un bloc de données vide, indiquez le nombre de lignes et de colonnes nécessaires dans la fonction suivante:

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

Pour créer un cadre vide tout en spécifiant la classe de chaque colonne, passez simplement un vecteur des types de données souhaités dans la fonction suivante:

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(df[,i])}
    if(type_vec[i] == 'character') {frame[,i] <- as.character(df[,i])}
    if(type_vec[i] == 'logical') {frame[,i] <- as.logical(df[,i])}
    if(type_vec[i] == 'factor') {frame[,i] <- as.factor(df[,i])}
  }
  return(frame)
}

Utilisez comme suit:

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

Qui donne:

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

Pour confirmer vos choix, exécutez ce qui suit:

lapply(df, class)

#output
$X1
[1] "character"

$X2
[1] "logical"

$X3
[1] "numeric"
4
Cybernetic

Si vous souhaitez créer un data.frame vide avec des noms dynamiques (noms de colonnes dans une variable), cela peut aider:

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

Vous pouvez également modifier les types si vous en avez besoin. comme:

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

Si cela ne vous dérange pas de ne pas spécifier explicitement les types de données, vous pouvez le faire comme suit:

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)
3
Odysseus Ithaca

Si vous voulez déclarer un tel data.frame avec plusieurs colonnes, il vous sera probablement pénible de taper manuellement toutes les classes de colonnes. Surtout si vous pouvez utiliser rep, cette approche est simple et rapide (environ 15% plus rapide que l'autre solution pouvant être généralisée de la manière suivante):

Si vos classes de colonnes souhaitées sont dans un vecteur colClasses, vous pouvez effectuer les opérations suivantes:

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

lapply donnera une liste de longueur désirée, dont chaque élément est simplement un vecteur typé vide comme numeric() ou integer().

setDF convertit cette list par référence à un data.frame.

setnames ajoute les noms souhaités par référence.

Comparaison de vitesse:

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 

C'est aussi plus rapide que d'utiliser structure de la même manière:

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

En utilisant data.table, nous pouvons spécifier des types de données pour chaque colonne.

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

Vous pouvez également extraire les métadonnées (noms et types de colonne)) à partir d'une image (par exemple, if vous contrôlez un bogue qui n'est déclenché qu'avec certaines entrées et nécessite une image vide de données factice ):

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

Et puis utilisez le read.table pour créer le dataframe vide

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

Supposons que vos noms de colonnes soient dynamiques, vous pouvez créer une matrice vide nommée et la transformer en un cadre de données.

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

Cette question ne répondait pas spécifiquement à mes préoccupations (soulignées ici ), mais au cas où quelqu'un voudrait le faire avec un nombre de colonnes paramétré et sans contrainte:

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

Comme le dit le divibisan sur la question liée,

... la raison [la coercition] se produit [lorsque les matrices de liaison et leurs types constituants] est qu'une matrice ne peut avoir qu'un seul type de données. Lorsque vous cbind 2 matrices, le résultat est toujours une matrice et les variables sont donc toutes forcées dans un seul type avant la conversion en data.frame.

0
d8aninja