Создайте пустой файл data.frame

Я пытаюсь инициализировать data.frame без каких-либо строк. В принципе, я хочу указать типы данных для каждого столбца и называть их, но не иметь никаких строк, созданных в результате.

Лучшее, что я смог сделать до сих пор, это что-то вроде:

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

Что создает data.frame с одной строкой, содержащей все типы данных и имена столбцов, которые я хотел, но также создает бесполезную строку, которая затем должна быть удалена.

Есть ли лучший способ сделать это?

Ответ 1

Просто инициализируйте его пустыми векторами:

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 с нулевыми строками.

Ответ 2

Если у вас уже есть существующий фрейм данных, скажем df, который имеет нужные вам столбцы, тогда вы можете просто создать пустой фрейм данных, удалив все строки:

empty_df = df[FALSE,]

Обратите внимание, что df все еще содержит данные, но empty_df нет.

Я нашел этот вопрос, чтобы узнать, как создать новый экземпляр с пустыми строками, поэтому я думаю, что это может быть полезно для некоторых людей.

Ответ 3

Вы можете сделать это без указания типов столбцов

df = data.frame(matrix(vector(), 0, 3,
                dimnames=list(c(), c("Date", "File", "User"))),
                stringsAsFactors=F)

Ответ 4

Вы можете использовать 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)

Спасибо Ричарду Скривену за улучшение

Ответ 5

Самый эффективный способ сделать это - использовать 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

Ответ 6

Просто объявите

table = data.frame()

при попытке rbind в первой строке он создаст столбцы

Ответ 7

Если вы ищете одолжение:

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

поэтому вам не нужно указывать имена столбцов отдельно. Вы получаете логический столбец по умолчанию, пока вы не заполните кадр данных.

Ответ 8

Я создал пустой фрейм данных, используя следующий код

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 

Ответ 9

Чтобы создать пустой фрейм данных, укажите количество строк и столбцов, необходимых для следующей функции:

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"

Ответ 10

Если вы хотите создать пустой 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()

Ответ 11

Если вы не возражаете не указывать типы данных явно, вы можете сделать это следующим образом:

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)

Ответ 12

Если вы хотите объявить такой 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

Ответ 13

Используя data.table, мы можем указать типы данных для каждого столбца.

library(data.table)    
data=data.table(a=numeric(), b=numeric(), c=numeric())

Ответ 14

Скажите, что имена столбцов динамические, вы можете создать пустую матрицу с именами строк и преобразовать ее в кадр данных.

nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))

Ответ 15

Этот вопрос конкретно не касался моих проблем (обрисованных в общих чертах здесь), но в случае, если кто-то захочет сделать это с параметризованным числом столбцов и без принуждения:

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

Как утверждает дивибисан по связанному вопросу,

... причина [принуждения] возникает [когда матрицы связывания и их составляющие типы] заключаются в том, что матрица может иметь только единый тип данных. Когда вы связываете 2 матрицы, результат все еще матрица и поэтому переменные все приводятся в один тип, прежде чем преобразование в data.frame

Ответ 16

Если у вас уже есть фрейм данных, вы можете извлечь метаданные (имена и типы столбцов) из фрейма данных (например, если вы контролируете ошибку, которая запускается только при определенных входных данных и необходимости пустой фиктивный кадр данных):

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

А затем используйте read.table для создания пустого фрейма данных

read.table(text = "",
   colClasses = c('integer', 'factor'),
   col.names = c('col1', 'col2'))