Ich versuche, ein data.frame ohne Zeilen zu initialisieren. Grundsätzlich möchte ich die Datentypen für jede Spalte angeben und benennen, aber keine Zeilen als Ergebnis erstellen lassen.
Das Beste, was ich bisher konnte, ist so etwas wie:
df <- data.frame(Date=as.Date("01/01/2000", format="%m/%d/%Y"),
File="", User="", stringsAsFactors=FALSE)
df <- df[-1,]
Dadurch wird ein data.frame mit einer einzelnen Zeile erstellt, die alle Datentypen und Spaltennamen enthält, die ich wollte, aber es wird auch eine unbrauchbare Zeile erstellt, die dann entfernt werden muss.
Gibt es einen besseren Weg, dies zu tun?
Initialisiere es einfach mit leeren Vektoren:
df <- data.frame(Date=as.Date(character()),
File=character(),
User=character(),
stringsAsFactors=FALSE)
Hier ist ein anderes Beispiel mit verschiedenen Spaltentypen:
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 .:
Das Initialisieren eines data.frame
mit einer leeren Spalte des falschen Typs verhindert nicht das weitere Hinzufügen von Zeilen mit Spalten unterschiedlichen Typs.
Diese Methode ist nur ein bisschen sicherer in dem Sinne, dass Sie von Anfang an die richtigen Spaltentypen haben, also Ihren Code stützt sich auf eine Überprüfung des Spaltentyps, sie funktioniert sogar mit einem data.frame
mit Nullzeilen.
Wenn Sie bereits einen vorhandenen Datenrahmen haben, sagen wir df
mit den gewünschten Spalten, dann können Sie einfach einen leeren Datenrahmen erstellen, indem Sie alle Zeilen entfernen:
empty_df = df[FALSE,]
Beachten Sie, dass df
immer noch die Daten enthält, empty_df
jedoch nicht.
Ich habe diese Frage gefunden, um eine neue Instanz mit leeren Zeilen zu erstellen. Daher denke ich, dass sie für einige Leute hilfreich sein kann.
Sie können dies tun, ohne Spaltentypen anzugeben
df = data.frame(matrix(vector(), 0, 3,
dimnames=list(c(), c("Date", "File", "User"))),
stringsAsFactors=F)
Sie könnten read.table
mit einer leeren Zeichenfolge für die Eingabe text
wie folgt verwenden:
colClasses = c("Date", "character", "character")
col.names = c("Date", "File", "User")
df <- read.table(text = "",
colClasses = colClasses,
col.names = col.names)
Alternativ können Sie den col.names
als Zeichenfolge angeben:
df <- read.csv(text="Date,File,User", colClasses = colClasses)
Vielen Dank an Richard Scriven für die Verbesserung
Am effizientesten ist es, mit structure
eine Liste mit der Klasse "data.frame"
zu erstellen:
structure(list(Date = as.Date(character()), File = character(), User = character()),
class = "data.frame")
# [1] Date File User
# <0 rows> (or 0-length row.names)
Um dies im Vergleich zur derzeit akzeptierten Antwort zu relativieren, hier ein einfacher Maßstab:
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
Einfach deklarieren
table = data.frame()
wenn Sie in der ersten Zeile rbind
versuchen, werden die Spalten erstellt
Wenn Sie nach Kürze suchen:
read.csv(text="col1,col2")
sie müssen die Spaltennamen also nicht separat angeben. Sie erhalten den Standardspaltentyp logisch, bis Sie den Datenrahmen füllen.
Ich habe einen leeren Datenrahmen mit folgendem Code erstellt
df = data.frame(id = numeric(0), jobs = numeric(0));
und haben versucht, einige Zeilen zu binden, um sie wie folgt zu füllen.
newrow = c(3, 4)
df <- rbind(df, newrow)
aber es begann, falsche Spaltennamen wie folgt zu vergeben
X3 X4
1 3 4
Die Lösung hierfür besteht darin, newrow wie folgt in df zu konvertieren
newrow = data.frame(id=3, jobs=4)
df <- rbind(df, newrow)
gibt nun den korrekten Datenrahmen aus, wenn die Spaltennamen wie folgt angezeigt werden
id nobs
1 3 4
Um einen leeren Datenrahmen zu erstellen, übergeben Sie die Anzahl der benötigten Zeilen und Spalten an die folgende Funktion:
create_empty_table <- function(num_rows, num_cols) {
frame <- data.frame(matrix(NA, nrow = num_rows, ncol = num_cols))
return(frame)
}
Um einen leeren Rahmen zu erstellen während Sie die Klasse jeder Spalte angeben, übergeben Sie einfach einen Vektor der gewünschten Datentypen an die folgende Funktion:
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)
}
Verwenden Sie wie folgt:
df <- create_empty_table(3, 3, c('character','logical','numeric'))
Welches gibt:
X1 X2 X3
1 <NA> NA NA
2 <NA> NA NA
3 <NA> NA NA
Führen Sie Folgendes aus, um Ihre Auswahl zu bestätigen:
lapply(df, class)
#output
$X1
[1] "character"
$X2
[1] "logical"
$X3
[1] "numeric"
Wenn Sie einen leeren data.frame mit dynamischen Namen (Spaltennamen in einer Variablen) erstellen möchten, kann dies helfen:
names <- c("v","u","w")
df <- data.frame()
for (k in names) df[[k]]<-as.numeric()
Sie können bei Bedarf auch die Typen ändern. mögen:
names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()
Wenn es Ihnen nichts ausmacht, Datentypen nicht explizit anzugeben, können Sie dies folgendermaßen tun:
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)
Wenn Sie einen solchen data.frame
mit vielen Spalten deklarieren möchten, ist es wahrscheinlich mühsam, alle Spaltenklassen von Hand einzugeben. Insbesondere, wenn Sie rep
verwenden können, ist dieser Ansatz einfach und schnell (etwa 15% schneller als die andere Lösung, die wie folgt verallgemeinert werden kann):
Befinden sich Ihre gewünschten Spaltenklassen in einem Vektor colClasses
, haben Sie folgende Möglichkeiten:
library(data.table)
setnames(setDF(lapply(colClasses, function(x) eval(call(x)))), col.names)
lapply
führt zu einer Liste der gewünschten Länge, wobei jedes Element einfach ein leerer typisierter Vektor wie numeric()
oder integer()
ist.
setDF
konvertiert dieses list
anhand eines data.frame
.
setnames
fügt die gewünschten Namen als Referenz hinzu.
Geschwindigkeitsvergleich:
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
Es ist auch schneller als die Verwendung von structure
auf ähnliche Weise:
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
Mit data.table
können wir Datentypen für jede Spalte angeben.
library(data.table)
data=data.table(a=numeric(), b=numeric(), c=numeric())
Sie können auch extrahieren Sie die Metadaten (Spaltennamen und -typen) aus einem Datenrahmen (z. B. wenn Sie einen BUG steuern, der nur bei bestimmten Eingaben ausgelöst wird und einen leeren Dummy-Datenrahmen benötigt ):
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)))
Verwenden Sie dann den read.table
, um den leeren Datenrahmen zu erstellen
read.table(text = "",
colClasses = c('integer', 'factor'),
col.names = c('col1', 'col2'))
Angenommen, Ihre Spaltennamen sind dynamisch, können Sie eine leere Matrix mit Zeilennamen erstellen und in einen Datenrahmen umwandeln.
nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))
Diese Frage ging nicht speziell auf meine Bedenken ein (skizziert hier ), aber falls jemand dies mit einer parametrisierten Anzahl von Spalten und ohne Zwang tun möchte:
> 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>
Wie Divibisan zu der damit verbundenen Frage sagt,
... der Grund, warum [Zwang] auftritt [beim Verknüpfen von Matrizen und ihren konstituierenden Typen], ist, dass eine Matrix nur einen einzigen Datentyp haben kann. Wenn Sie 2 Matrizen binden, ist das Ergebnis immer noch eine Matrix, und daher werden die Variablen vor der Konvertierung in einen data.frame-Typ zu einem einzigen Typ zusammengefasst