Tạo một data.frame trống


480

Tôi đang cố gắng khởi tạo data.frame mà không có hàng nào. Về cơ bản, tôi muốn chỉ định các loại dữ liệu cho mỗi cột và đặt tên cho chúng, nhưng không có bất kỳ hàng nào được tạo như là kết quả.

Điều tốt nhất tôi có thể làm cho đến nay là một cái gì đó như:

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

Điều này tạo ra một data.frame với một hàng chứa tất cả các kiểu dữ liệu và tên cột tôi muốn, nhưng cũng tạo ra một hàng vô dụng mà sau đó cần phải được loại bỏ.

Có cách nào tốt hơn để làm điều này?

Câu trả lời:


652

Chỉ cần khởi tạo nó với các vectơ trống:

df <- data.frame(Date=as.Date(character()),
                 File=character(), 
                 User=character(), 
                 stringsAsFactors=FALSE) 

Đây là một ví dụ khác với các loại cột khác nhau:

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 

Lưu ý:

Khởi tạo a data.framevới một cột trống không đúng loại sẽ không ngăn thêm các hàng có các cột khác nhau.
Phương pháp này an toàn hơn một chút theo nghĩa là bạn sẽ có các loại cột chính xác ngay từ đầu, do đó nếu mã của bạn dựa vào một số kiểm tra loại cột, nó sẽ hoạt động ngay cả với một data.framehàng không.


3
Nó sẽ giống nhau nếu tôi khởi tạo tất cả các trường bằng NULL?
yosukesabai

8
@yosukesabai: không, nếu bạn khởi tạo một cột bằng NULL thì cột sẽ không được thêm vào :)
digEmAll

6
@yosukesabai: data.frameđã nhập các cột, vì vậy, nếu bạn muốn khởi tạo, data.framebạn phải quyết định loại cột ...
digEmAll

1
@jxramos: tốt, thực sự data.framekhông hạn chế về "tính nguyên thủy" của các loại cột (ví dụ: bạn có thể thêm một cột ngày hoặc thậm chí một cột chứa danh sách các phần tử). Ngoài ra, câu hỏi này không phải là một tài liệu tham khảo tuyệt đối, vì ví dụ nếu bạn không chỉ định đúng loại cột, bạn sẽ không chặn thêm hàng có các loại khác nhau ... vì vậy, tôi sẽ thêm một ghi chú, nhưng không một ví dụ với tất cả các loại nguyên thủy vì nó không bao gồm tất cả các khả năng ...
digEmAll

3
@ user4050: câu hỏi là về việc tạo ra một data.frame rỗng, vì vậy khi số hàng là zero ... có lẽ bạn muốn tạo một data.frame đầy đủ về NA ... trong trường hợp đó bạn có thể sử dụng ví dụdata.frame(Doubles=rep(as.double(NA),numberOfRow), Ints=rep(as.integer(NA),numberOfRow))
digEmAll

140

Nếu bạn đã có khung dữ liệu tồn tại , giả sử dfcó các cột bạn muốn, thì bạn chỉ có thể tạo khung dữ liệu trống bằng cách xóa tất cả các hàng:

empty_df = df[FALSE,]

Lưu ý rằng dfvẫn chứa dữ liệu, nhưng empty_dfkhông.

Tôi thấy câu hỏi này đang tìm cách tạo một thể hiện mới với các hàng trống, vì vậy tôi nghĩ nó có thể hữu ích cho một số người.


2
Ý tưởng tuyệt vời. Không giữ hàng nào, nhưng TẤT CẢ các cột. Bất cứ ai bị bỏ rơi đã bỏ lỡ một cái gì đó.
Ram Narasimhan

1
Giải pháp tuyệt vời, tuy nhiên tôi thấy rằng tôi nhận được một khung dữ liệu với 0 hàng. Để giữ kích thước của khung dữ liệu giống nhau, tôi đề xuất new_df = df [NA,]. Điều này cũng cho phép lưu trữ bất kỳ cột trước đó vào khung dữ liệu mới. Ví dụ: để lấy cột "Ngày" từ df gốc (trong khi vẫn giữ NA): new_df $ Ngày <- df $ Ngày.
Katya

2
@Katya, nếu bạn làm df[NA,]điều này cũng sẽ ảnh hưởng đến chỉ số (không chắc là điều bạn muốn), thay vào đó tôi sẽ sử dụng df[TRUE,] = NA; tuy nhiên lưu ý rằng điều này sẽ ghi đè lên bản gốc. Bạn sẽ cần sao chép khung dữ liệu trước copy_df = data.frame(df)và sau đócopy_df[TRUE,] = NA
toto_tico

3
@Katya, hoặc bạn cũng có thể dễ dàng thêm hàng rỗng đến empty_dfvới empty_df[0:nrow(df),] <- NA.
toto_tico

1
@Katya, bạn sử dụng backquote (`) xung quanh những gì bạn muốn đánh dấu là mã và có những thứ khác là chữ nghiêng sử dụng * và in đậm bằng **. Bạn có thể muốn đọc tất cả Cú pháp Markdown của SO . Hầu hết chỉ có ý nghĩa cho câu trả lời mặc dù.
toto_tico

79

Bạn có thể làm điều đó mà không chỉ định các loại cột

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

4
Trong trường hợp đó, các kiểu cột mặc định là logic trên mỗi vectơ (), nhưng sau đó được ghi đè bằng các loại phần tử được thêm vào df. Hãy thử str (df), df [1,1] <- 'x'
Dave X

58

Bạn có thể sử dụng read.tablevới một chuỗi trống cho đầu vào textnhư sau:

colClasses = c("Date", "character", "character")
col.names = c("Date", "File", "User")

df <- read.table(text = "",
                 colClasses = colClasses,
                 col.names = col.names)

Hoặc chỉ định col.namesdưới dạng một chuỗi:

df <- read.csv(text="Date,File,User", colClasses = colClasses)

Cảm ơn Richard Scriven vì sự cải tiến


4
Hoặc thậm chí read.table(text = "", ...)vì vậy bạn không cần phải mở một kết nối rõ ràng.
Rich Scriven

sốt sắng. có lẽ là cách mở rộng / tự động nhất để thực hiện việc này cho nhiều cột tiềm năng
MichaelChirico

3
Cách read.csvtiếp cận cũng hoạt động với readr::read_csv, như trong read_csv("Date,File,User\n", col_types = "Dcc"). Bằng cách này, bạn có thể trực tiếp tạo ra một cấu trúc trống của cấu trúc được yêu cầu.
Heather Turner

27

Cách hiệu quả nhất để làm điều này là sử dụng structuređể tạo một danh sách có lớp "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)

Để đưa vấn đề này vào quan điểm so với câu trả lời hiện được chấp nhận, đây là một điểm chuẩn đơn giản:

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.tablethường chứa một .internal.selfrefthuộc tính, không thể làm giả mà không gọi các data.tablehàm. Bạn có chắc chắn rằng bạn không dựa vào một hành vi không có giấy tờ ở đây?
Adam Ryczkowski

@AdamRyczkowski Tôi nghĩ rằng bạn đang nhầm lẫn giữa lớp "data.frame" cơ bản và lớp "data.table" bổ trợ từ gói data.table .
Thomas

Đúng. Chắc chắn rồi. Lỗi của tôi. Bỏ qua bình luận cuối cùng của tôi. Tôi đã xem qua chủ đề này khi tìm kiếm data.tablevà cho rằng Google đã tìm thấy những gì tôi muốn và mọi thứ ở đây đều data.tableliên quan.
Adam Ryczkowski

1
@PatrickT Không có kiểm tra xem mã của bạn đang làm gì có ý nghĩa gì. data.frame()cung cấp kiểm tra về đặt tên, tên gọi, v.v.
Thomas

26

Chỉ cần khai báo

table = data.frame()

Khi bạn cố gắng đến rbinddòng đầu tiên, nó sẽ tạo các cột


2
Không thực sự đáp ứng các yêu cầu của OP về "Tôi muốn chỉ định loại dữ liệu cho từng cột và đặt tên cho chúng". Nếu bước tiếp theo là rbindđiều này sẽ hoạt động tốt, nếu không ...
Gregor Thomas

Dù sao, cảm ơn cho giải pháp đơn giản này. Tôi cũng muốn khởi tạo data.frame với các cột cụ thể vì tôi nghĩ rbind chỉ có thể được sử dụng nếu các cột tương ứng giữa hai data.frame. Điều này dường như không phải là trường hợp. Tôi đã ngạc nhiên khi tôi có thể chỉ cần khởi tạo data.frame khi sử dụng rbind. Cảm ơn.
giordano

4
Giải pháp đề xuất tốt nhất ở đây. Đối với tôi, sử dụng cách đề xuất, đã làm việc hoàn hảo vớirbind() .
Kots

17

Nếu bạn đang tìm kiếm sự ngắn gọn:

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

vì vậy bạn không cần chỉ định tên cột riêng. Bạn nhận được kiểu cột mặc định hợp lý cho đến khi bạn điền vào khung dữ liệu.


read.csv phân tích cú pháp đối số văn bản để bạn có được tên cột. Nó nhỏ gọn hơn read.table (text = "", col.names = c ("col1", "col2"))
marc

Tôi nhận được:Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 0, 2
Climbs_lika_Spyder 17/05/2015

Điều này không đáp ứng yêu cầu của OP, "Tôi muốn chỉ định các loại dữ liệu cho từng cột" , mặc dù nó có thể được sửa đổi để làm như vậy.
Gregor Thomas

14

Tôi đã tạo khung dữ liệu trống bằng cách sử dụng mã sau

df = data.frame(id = numeric(0), jobs = numeric(0));

và cố gắng liên kết một số hàng để điền giống như sau.

newrow = c(3, 4)
df <- rbind(df, newrow)

nhưng nó bắt đầu cho tên cột không chính xác như sau

  X3 X4
1  3  4

Giải pháp cho vấn đề này là chuyển đổi newrow sang gõ df như sau

newrow = data.frame(id=3, jobs=4)
df <- rbind(df, newrow)

bây giờ cung cấp khung dữ liệu chính xác khi được hiển thị với tên cột như sau

  id nobs
1  3   4 

7

Để tạo khung dữ liệu trống , hãy chuyển số lượng hàng và cột cần thiết vào chức năng sau:

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

Để tạo một khung trống trong khi chỉ định lớp của mỗi cột , chỉ cần chuyển một vectơ của các loại dữ liệu mong muốn vào hàm sau:

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

Sử dụng như sau:

df <- create_empty_table(3, 3, c('character','logical','numeric'))

Cung cấp cho:

   X1  X2 X3
1 <NA> NA NA
2 <NA> NA NA
3 <NA> NA NA

Để xác nhận lựa chọn của bạn, hãy chạy như sau:

lapply(df, class)

#output
$X1
[1] "character"

$X2
[1] "logical"

$X3
[1] "numeric"

1
Điều này không đáp ứng yêu cầu của OP, "Tôi muốn chỉ định loại dữ liệu cho từng cột"
Gregor Thomas

6

Nếu bạn muốn tạo một data.frame trống với các tên động (tên trong một biến), điều này có thể giúp:

names <- c("v","u","w")
df <- data.frame()
for (k in names) df[[k]]<-as.numeric()

Bạn có thể thay đổi các loại là tốt nếu bạn cần như vậy. giống:

names <- c("u", "v")
df <- data.frame()
df[[names[1]]] <- as.numeric()
df[[names[2]]] <- as.character()

4

Nếu bạn không nhớ không chỉ định rõ ràng các loại dữ liệu, bạn có thể thực hiện theo cách này:

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)

4

Bằng cách sử dụng, data.tablechúng tôi có thể chỉ định các loại dữ liệu cho mỗi cột.

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

3

Nếu bạn muốn khai báo như vậy data.framevới nhiều cột, có thể sẽ rất khó để nhập tất cả các lớp cột bằng tay. Đặc biệt nếu bạn có thể sử dụng rep, phương pháp này rất dễ dàng và nhanh chóng (nhanh hơn khoảng 15% so với giải pháp khác có thể khái quát như thế này):

Nếu các lớp cột mong muốn của bạn nằm trong một vectơ colClasses, bạn có thể làm như sau:

library(data.table)
setnames(setDF(lapply(colClasses, function(x) eval(call(x)))), col.names)

lapplysẽ dẫn đến một danh sách độ dài mong muốn, mỗi phần tử chỉ đơn giản là một vectơ gõ trống như numeric()hoặc integer().

setDFchuyển đổi điều này listbằng cách tham chiếu đến a data.frame.

setnames thêm tên mong muốn bằng cách tham khảo.

So sánh tốc độ:

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 

Nó cũng nhanh hơn sử dụng structuretheo cách tương tự:

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

1

Giả sử tên cột của bạn là động, bạn có thể tạo một ma trận tên hàng trống và chuyển đổi nó thành khung dữ liệu.

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

Điều này không đáp ứng yêu cầu của OP, "Tôi muốn chỉ định loại dữ liệu cho từng cột"
Gregor Thomas

1

Câu hỏi này không giải quyết cụ thể mối quan tâm của tôi (được nêu ở đây ) nhưng trong trường hợp bất kỳ ai cũng muốn làm điều này với số lượng cột được tham số hóa và không ép buộc:

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

Như divibisan nói về câu hỏi liên kết,

... lý do [ép buộc] xảy ra [khi ma trận kết hợp và các kiểu cấu thành của chúng] là một ma trận chỉ có thể có một kiểu dữ liệu duy nhất. Khi bạn cbind 2 ma trận, kết quả vẫn là một ma trận và do đó tất cả các biến được ép buộc thành một loại duy nhất trước khi chuyển đổi thành data.frame


1

Nếu bạn đã có một khung dữ liệu , bạn có thể trích xuất siêu dữ liệu (tên và loại cột) từ một khung dữ liệu (ví dụ: nếu bạn đang kiểm soát BUG chỉ được kích hoạt với một số đầu vào nhất định và cần một Dataframe trống):

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

Và sau đó sử dụng read.tableđể tạo khung dữ liệu trống

read.table(text = "",
   colClasses = c('integer', 'factor'),
   col.names = c('col1', 'col2'))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.