Я пытаюсь инициализировать data.frame без каких-либо строк. В принципе, я хочу указать типы данных для каждого столбца и называть их, но не иметь никаких строк, созданных в результате.
Лучшее, что я смог сделать до сих пор, это что-то вроде:
df <- data.frame(Date=as.Date("01/01/2000", format="%m/%d/%Y"),
File="", User="", stringsAsFactors=FALSE)
df <- df[-1,]
Что создает data.frame с одной строкой, содержащей все типы данных и имена столбцов, которые я хотел, но также создает бесполезную строку, которая затем должна быть удалена.
Есть ли лучший способ сделать это?
Просто инициализируйте его пустыми векторами:
df <- data.frame(Date=as.Date(character()),
File=character(),
User=character(),
stringsAsFactors=FALSE)
Вот еще один пример с разными типами столбцов:
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.:
Инициализация data.frame
пустым столбцом неправильного типа не препятствует дальнейшим добавлениям строк, имеющих столбцы разных типов.
Этот метод немного более безопасен в том смысле, что у вас будут правильные типы столбцов с самого начала, поэтому, если ваш код зависит от проверки типа столбца, он будет работать даже с data.frame
с нулевыми строками.
Вы можете сделать это без указания типов столбцов
df = data.frame(matrix(vector(), 0, 3,
dimnames=list(c(), c("Date", "File", "User"))),
stringsAsFactors=F)
Если у вас уже есть существующий фрейм данных, скажем df
, который имеет нужные вам столбцы, тогда вы можете просто создать пустой фрейм данных, удалив все строки:
empty_df = df[FALSE,]
Обратите внимание, что df
все еще содержит данные, но empty_df
нет.
Я нашел этот вопрос, чтобы узнать, как создать новый экземпляр с пустыми строками, поэтому я думаю, что это может быть полезно для некоторых людей.
Вы можете использовать read.table
с пустой строкой для ввода text
следующим образом:
colClasses = c("Date", "character", "character")
col.names = c("Date", "File", "User")
df <- read.table(text = "",
colClasses = colClasses,
col.names = col.names)
Альтернативно указывая col.names
как строку:
df <- read.csv(text="Date,File,User", colClasses = colClasses)
Спасибо Ричарду Скривену за улучшение
read.table(text = "", ...)
так что вам не нужно явно открывать соединение.
Самый эффективный способ сделать это - использовать structure
для создания списка с классом "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)
Чтобы представить это в перспективе по сравнению с принятым в настоящее время ответом, здесь простейший тест:
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
data.table
обычно содержит атрибут .internal.selfref
, который не может быть подделан без вызова функций data.table
. Вы уверены, что не полагаетесь на недокументированное поведение здесь?
Если вы ищете одолжение:
read.csv(text="col1,col2")
поэтому вам не нужно указывать имена столбцов отдельно. Вы получаете логический столбец по умолчанию, пока вы не заполните кадр данных.
Я создал пустой фрейм данных, используя следующий код
df = data.frame(id = numeric(0), jobs = numeric(0));
и попытался связать некоторые строки, чтобы заполнить их следующим образом.
newrow = c(3, 4)
df <- rbind(df, newrow)
но он начал давать неправильные имена столбцов следующим образом
X3 X4
1 3 4
Решение этого заключается в том, чтобы преобразовать newrow в тип df следующим образом
newrow = data.frame(id=3, jobs=4)
df <- rbind(df, newrow)
теперь дает правильный фрейм данных при отображении с именами столбцов следующим образом
id nobs
1 3 4
Если вы хотите создать пустой data.frame с динамическими именами (colnames в переменной), это может помочь:
names <- c("v","u","w")
df <- data.frame()
for (k in names) df[[k]]<-as.numeric()
Вы также можете изменить типы, если это необходимо. как:
names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()
просто объявить table = data.frame() когда вы пытаетесь перенести первую строку, он создаст столбцы
rbind
это будет хорошо, если нет ...
Если вы не возражаете не указывать типы данных явно, вы можете сделать это следующим образом:
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)
Если вы хотите объявить такой data.frame
со многими столбцами, вероятно, будет больно вводить все классы столбцов вручную. Особенно, если вы можете использовать rep
, этот подход прост и быстр (примерно на 15% быстрее, чем другое решение, которое можно обобщить следующим образом):
Если требуемые классы столбцов находятся в векторе colClasses
, вы можете сделать следующее:
library(data.table)
setnames(setDF(lapply(colClasses, function(x) eval(call(x)))), col.names)
lapply
приведет к списку желаемой длины, каждый элемент которого представляет собой просто пустой типизированный вектор, такой как numeric()
или integer()
.
setDF
преобразует этот list
по ссылке на data.frame
.
setnames
добавляет нужные имена по ссылке.
Сравнение скорости:
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
Это также быстрее, чем использование structure
аналогичным образом:
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
Чтобы создать пустой фрейм данных, укажите количество строк и столбцов, необходимых для следующей функции:
create_empty_table <- function(num_rows, num_cols) {
frame <- data.frame(matrix(NA, nrow = num_rows, ncol = num_cols))
return(frame)
}
Чтобы создать пустой кадр при указании класса каждого столбца, просто передайте вектор желаемых типов данных в следующую функцию:
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)
}
Используйте следующее:
df <- create_empty_table(3, 3, c('character','logical','numeric'))
Что дает:
X1 X2 X3
1 <NA> NA NA
2 <NA> NA NA
3 <NA> NA NA
Чтобы подтвердить свой выбор, выполните следующие действия:
lapply(df, class)
#output
$X1
[1] "character"
$X2
[1] "logical"
$X3
[1] "numeric"
Скажите, что имена столбцов динамические, вы можете создать пустую матрицу с именами строк и преобразовать ее в кадр данных.
nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))