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.frame
met 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.frame
met nul rijen.
Antwoord 2, autoriteit 24%
Als u al een bestaand dataframehebt, laten we zeggen df
met de gewenste kolommen, dan kunt u gewoon een leeg dataframe maken door alle rijen te verwijderen :
empty_df = df[FALSE,]
Merk op dat df
nog steeds de gegevens bevat, maar empty_df
niet.
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.table
gebruiken 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.names
als 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 structure
te 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.table
te 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.frame
met 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 colClasses
bevinden, kunt u het volgende doen:
library(data.table)
setnames(setDF(lapply(colClasses, function(x) eval(call(x)))), col.names)
lapply
zal resulteren in een lijst met de gewenste lengte, waarvan elk element gewoon een leeg getypte vector is zoals numeric()
of integer()
.
setDF
converteert deze list
door te verwijzen naar een data.frame
.
setnames
voegt 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 structure
op 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.table
om 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)