Вопрос по dataframe, r – Создать пустой data.frame

401

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

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

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

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

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

Ваш Ответ

15   ответов
11

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

table = data.frame()

когда ты пытаешьсяrbind первая строка будет создавать столбцы

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededIfError: User Rate Limit ExceededrbindError: User Rate Limit Exceeded
Error: User Rate Limit Exceededrbind().
89

если тыalready have an existent data frameскажем такdf в котором есть нужные столбцы, тогда вы можете просто создать пустой фрейм данных, удалив все строки:

empty_df = df[FALSE,]

Заметить, чтоdf все еще содержит данные, ноempty_df Безразлично & APOS; т.

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

Любые комментарии о том, почему ответ был отклонен?
Не уверен, почему отказался; Я считаю это простым и блестящим
Прекрасная идея. Не храните ни одной строки, но ВСЕ столбцы. Кто бы ни понизил голосование, что-то пропустил.
Не уверен - это хорошая техника, и я проголосовал за нее.
Хорошее решение, однако я обнаружил, что получаю фрейм данных с 0 строками. Чтобы размер кадра данных оставался неизменным, я предлагаю new_df = df [NA,]. Это также позволяет сохранить любой предыдущий столбец в новом фрейме данных. Например, чтобы получить «Дата» столбец из исходного df (с сохранением остальных NA): new_df $ Date & lt; - df $ Date.
16

Если вы ищете краткость:

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

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

read.csv анализирует текстовый аргумент, чтобы вы получили имена столбцов. Он более компактен, чем read.table (text = & quot ;, col.names = c (& quot; col1 & quot ;, & quot; col2 & quot;))
Это не соответствует требованиям OP,"I want to specify the data types for each column"хотя, вероятно, это можно изменить для этого.
Я получил :Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 0, 2
Еще несколько объяснений было бы неплохо.
0

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

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

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

...the reason [coercion] occurs [when cbinding matrices and their constituent types] is that a matrix can only have a single data type. When you cbind 2 matrices, the result is still a matrix and so the variables are all coerced into a single type before converting to a data.frame

3

а столбцов в переменной), это может помочь:

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

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 с пустым столбцом неправильного типа не препятствует дальнейшему добавлению строк, имеющих столбцы разных типов.
Этот метод только немногоsafer в том смысле, что у вас с самого начала будут правильные типы столбцов, следовательно, если ваш код использует некоторую проверку типов столбцов, он будет работать даже сdata.frame с нулевыми рядами.

@yosukesabai:data.frameстолбцы набраны, так что да, если вы хотите инициализироватьdata.frame Вы должны решить тип столбцов ...
@ user4050: вопрос был о создании пустого data.frame, поэтому, когда число строк равно нулю ... может быть, вы хотите создать data.frame, полный для NA ... в этом случае вы можете использовать, например, например.data.frame(Doubles=rep(as.double(NA),numberOfRow), Ints=rep(as.integer(NA),numberOfRow))
@yosukesabai: нет, если вы инициализируете столбец с NULL, столбец не будет добавлен :)
Хороший сas.Date(character()) , Спасибо.
Было бы то же самое, если бы я инициализировал все поля с NULL?
20

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 и предположил, что Google действительно нашел то, что я хотел, и все здесьdata.table-связанные с.
@PatrickT Нет никакого смысла проверять, что делает ваш код.data.frame() обеспечивает проверку имен, строк и т. д.
data.table обычно содержит.internal.selfref атрибут, который не может быть подделан без вызоваdata.table функции. Вы уверены, что не полагаетесь на недокументированное поведение здесь?
Есть идеи, почему разница в производительности?
@ AdamRyczkowski Я думаю, что вы путаете основание "data.frame" класс и надстройка «data.table» класс изdata.table package.
2

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
48

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)

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

Error: User Rate Limit Exceededmany потенциальные столбцы
Error: User Rate Limit Exceeded
Или дажеread.table(text = "", ...)Error: User Rate Limit Exceeded
read.csvError: User Rate Limit Exceededreadr::read_csv, как вread_csv("Date,File,User\n", col_types = "Dcc")Error: User Rate Limit Exceeded
3

вы можете сделать это следующим образом:

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

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

nms <- sample(LETTERS,sample(1:10))
as.data.frame(t(matrix(nrow=length(nms),ncol=0,dimnames=list(nms))))
Error: User Rate Limit Exceeded"I want to specify the data types for each column"
72

df = data.frame(matrix(vector(), 0, 3,
                dimnames=list(c(), c("Date", "File", "User"))),
                stringsAsFactors=F)
В этом случае типы столбцов по умолчанию считаются логическими для вектора (), но затем переопределяются с типами элементов, добавляемых в df. Попробуйте str (df), df [1,1] & lt; - 'x' apos;
10

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 
0

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

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

create an empty data frame, передайте оличество необходимых стро и столбцов в следующую фунцию:

create_empty_table <- function(num_rows, num_cols) {
    frame <- data.frame(matrix(NA, nrow = num_rows, ncol = num_cols))
    return(frame)
}

Создать пустую рамуwhile specifying the class of each columnпросто передайте ветор нужных типов данных в следующую фунцию:

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"
Это не соответствует требованиям OP,"I want to specify the data types for each column"

Похожие вопросы