example - Crear un data.frame vacío
r create dataframe with column names (14)
Creé un marco de datos vacío usando el siguiente código
df = data.frame(id = numeric(0), jobs = numeric(0));
y trató de enlazar algunas filas para rellenar lo mismo de la siguiente manera.
newrow = c(3, 4)
df <- rbind(df, newrow)
pero comenzó dando nombres de columna incorrectos de la siguiente manera
X3 X4
1 3 4
La solución a esto es convertir newrow al 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 los nombres de columna de la siguiente manera
id nobs
1 3 4
Estoy tratando de inicializar un data.frame sin ninguna fila. 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,]
Lo que crea un data.frame 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?
Esta pregunta no abordó específicamente mis preocupaciones (descritas here ), pero en caso de que alguien quiera hacer esto con un número de columnas parametrizado y sin coerción:
> 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>
Como los estados divibisanos en la pregunta vinculada,
... la razón por la que [la coacción] ocurre [cuando las matrices de unión y sus tipos de constituyentes] es que una matriz solo puede tener un único tipo de datos. Cuando se unen 2 matrices, el resultado sigue siendo una matriz y, por lo tanto, todas las variables se convierten en un solo tipo antes de convertirlas en un data.frame
La forma más eficiente de hacer esto es usar la 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
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)
}
Use de la siguiente manera:
df <- create_empty_table(3, 3, c(''character'',''logical'',''numeric''))
Lo 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"
Puede usar read.table
con una cadena vacía para el text
entrada 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, especificando los col.names
como una cadena:
df <- read.csv(text="Date,File,User", colClasses = colClasses)
Gracias a Richard Scriven por la mejora.
Puedes hacerlo sin especificar tipos de columnas.
df = data.frame(matrix(vector(), 0, 3,
dimnames=list(c(), c("Date", "File", "User"))),
stringsAsFactors=F)
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 lo hace.
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.
Si desea crear un data.frame vacío con nombres dinámicos (nombres de columna en una variable), esto puede ayudar:
names <- c("v","u","w")
df <- data.frame()
for (k in names) df[[k]]<-as.numeric()
Puede cambiar los tipos también si lo necesita. me gusta:
names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()
Si desea declarar dicho data.frame
con muchas columnas, probablemente será una data.frame
escribir todas las clases de columnas a mano. Especialmente si puede hacer uso de la rep
, este enfoque es fácil y rápido (aproximadamente un 15% más rápido que la otra solución que puede generalizarse 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á como resultado una lista de la longitud deseada, cada elemento de la cual es simplemente un vector escrito vacío como numeric()
o integer()
.
setDF
convierte esta list
por referencia a un data.frame
.
setnames
agrega 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 la 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
Si estás buscando la brevedad:
read.csv(text="col1,col2")
por lo que no es necesario especificar los nombres de columna por separado. Obtendrá el tipo de columna predeterminado lógico hasta que complete el marco de datos.
Si no le importa no especificar tipos de datos explícitamente, 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)
Solo declara
table = data.frame()
Cuando intentes volver a rbind
la primera línea, se crearán las columnas.
Solo inicialízalo 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 columnas:
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:
La inicialización de 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 la comprobación de algún tipo de columna, funcionará incluso con un data.frame
con cero filas.
Supongamos que los nombres de las columnas son dinámicos, puede crear una matriz de nombre de fila vacía 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))))