Crear datos vacíos.marco


Estoy tratando de inicializar un dato.marco sin filas. Básicamente, quiero especificar los tipos de datos para cada columna y nombrarlos, pero no tener filas creadas como resultado.

Lo mejor que he podido hacer hasta ahora es algo como:

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

Que crea un dato.marco con una sola fila que contiene todos los tipos de datos y nombres de columna que quería, pero también crea una fila inútil que luego debe eliminarse.

¿Hay una mejor manera de hacer esto?

Author: Jaap, 2012-05-21

13 answers

Simplemente inicializarlo con vectores vacíos:

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

Aquí hay otro ejemplo con diferentes tipos de columna:

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

Inicializar un data.frame con una columna vacía del tipo incorrecto no impide nuevas adiciones de filas que tengan columnas de diferentes tipos.
Este método es un poco más seguro en el sentido de que tendrá los tipos de columna correctos desde el principio, por lo tanto, si su código se basa en alguna comprobación de tipo de columna, lo hará trabajar incluso con un data.frame con cero filas.

 490
Author: digEmAll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-11 17:49:03

Si ya tiene un marco de datos existente , digamos df que tiene las columnas que desea, entonces puede crear un marco de datos vacío eliminando todas las filas:

empty_df = df[FALSE,]

Observe que df todavía contiene los datos, pero empty_df no.

Encontré esta pregunta buscando cómo crear una nueva instancia con filas vacías, así que creo que podría ser útil para algunas personas.

 78
Author: toto_tico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 01:42:53

Puede hacerlo sin especificar los tipos de columna

df = data.frame(matrix(vector(), 0, 3,
                dimnames=list(c(), c("Date", "File", "User"))),
                stringsAsFactors=F)
 70
Author: zeleniy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-10 12:03:05

Puedes usar read.table con una cadena vacía para la entrada text de la siguiente manera:

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

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

Alternativamente especificar el col.names como una cadena:

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

Gracias a Richard Scriven por la mejora

 46
Author: Rentrop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-28 21:22:59

La forma más eficiente de hacerlo es usar structure para crear una lista que tenga la clase "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)

Para poner esto en perspectiva en comparación con la respuesta actualmente aceptada, aquí hay un punto de referencia 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
 20
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-20 20:15:02

Si usted está buscando la brevedad :

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

Por lo que no es necesario especificar los nombres de las columnas por separado. Obtiene el tipo de columna predeterminado lógico hasta que rellene el marco de datos.

 15
Author: Marc van Oudheusden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-19 08:48:25

He creado un marco de datos vacío usando el siguiente código

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

Y trató de unir algunas filas para poblar el mismo de la siguiente manera.

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

Pero comenzó a dar nombres de columna incorrectos de la siguiente manera

  X3 X4
1  3  4

La solución a esto es convertir newrow a tipo df de la siguiente manera

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

Ahora da el marco de datos correcto cuando se muestra con nombres de columna de la siguiente manera

  id nobs
1  3   4 
 9
Author: Shrikant Prabhu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-18 07:17:41

Sólo declarar

table = data.frame()

Cuando intente rbind la primera línea creará las columnas

 8
Author: Daniel Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-24 15:42:49

Si desea crear un dato vacío.frame con nombres dinámicos (colnames en una variable), esto puede ayudar:

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

También puede cambiar los tipos si lo necesita. como:

names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()
 3
Author: Ali Khosro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-11 16:34:22

Si desea declarar tal data.frame con muchas columnas, probablemente será un dolor escribir todas las clases de columna a mano. Especialmente si puede hacer uso de rep, este enfoque es fácil y rápido (aproximadamente un 15% más rápido que la otra solución que se puede generalizar de esta manera):

Si sus clases de columna deseadas están en un vector colClasses, puede hacer lo siguiente:

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

lapply dará lugar a una lista de longitud deseada, cada elemento de los cuales es simplemente un vector de tipo vacío como numeric() o integer().

setDF convierte este list por referencia a un data.frame.

setnames añade los nombres deseados por referencia.

Comparación de velocidad:

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 

También es más rápido que usar structure de una manera similar:

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
Author: MichaelChirico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 01:51:25

Si no le importa no especificar explícitamente los tipos de datos, puede hacerlo de esta manera:

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)
 2
Author: Odysseus Ithaca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-10 17:25:37

Para crear un marco de datos vacío , pase el número de filas y columnas necesarias a la siguiente función:

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

Para crear un marco vacío mientras se especifica la clase de cada columna , simplemente pase un vector de los tipos de datos deseados a la siguiente función:

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

Utilizar como sigue:

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

Que da:

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

Para confirmar sus elecciones, ejecute lo siguiente:

lapply(df, class)

#output
$X1
[1] "character"

$X2
[1] "logical"

$X3
[1] "numeric"
 1
Author: Cybernetic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-11 19:50:51

Digamos que los nombres de las columnas son dinámicos, puede crear una matriz vacía con nombre de fila y transformarla en un marco de datos.

nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))
 0
Author: jpmarindiaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-13 04:37:41