สร้าง data.frame ที่ว่างเปล่า


480

ฉันพยายามเริ่มต้น data.frame โดยไม่ต้องแถวใด ๆ โดยทั่วไปฉันต้องการระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์และตั้งชื่อ แต่ไม่ได้สร้างแถวใด ๆ

สิ่งที่ดีที่สุดที่ฉันสามารถทำได้จนถึงตอนนี้คือ:

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

ซึ่งสร้าง data.frame ด้วยแถวเดียวที่มีประเภทข้อมูลและชื่อคอลัมน์ทั้งหมดที่ฉันต้องการ แต่ยังสร้างแถวที่ไร้ประโยชน์ซึ่งจะต้องลบออก

มีวิธีที่ดีกว่าในการทำเช่นนี้?

คำตอบ:


652

เพียงกำหนดค่าเริ่มต้นด้วยเวกเตอร์ที่ว่างเปล่า:

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 

หมายเหตุ:

การกำหนดค่าเริ่มต้น a data.frameด้วยคอลัมน์ว่างของประเภทที่ไม่ถูกต้องจะไม่ป้องกันการเพิ่มเติมของแถวที่มีคอลัมน์ประเภทต่าง ๆ เพิ่มเติม
วิธีนี้เป็นวิธีที่ปลอดภัยกว่าในแง่ที่ว่าคุณจะมีประเภทคอลัมน์ที่ถูกต้องตั้งแต่ต้นดังนั้นหากรหัสของคุณต้องอาศัยการตรวจสอบบางประเภทของคอลัมน์วิธีนี้จะใช้ได้แม้กับdata.frameแถวที่มีศูนย์


3
มันจะเหมือนกันไหมถ้าฉันเริ่มต้นเขตข้อมูลทั้งหมดด้วย NULL
yosukesabai

8
@yosukesabai: ไม่มีถ้าคุณเริ่มต้นคอลัมน์ที่มีคอลัมน์โมฆะจะไม่ถูกเพิ่ม :)
digEmAll

6
@yosukesabai: data.frame's พิมพ์คอลัมน์ดังนั้นใช่ถ้าคุณต้องการที่จะเริ่มต้นdata.frameคุณจะต้องตัดสินใจประเภทของคอลัมน์ ...
digEmAll

1
@jxramos: จริง ๆ แล้วdata.frameไม่ได้ จำกัด อยู่ที่ "primitivity" ของประเภทคอลัมน์ (ตัวอย่างเช่นคุณสามารถเพิ่มคอลัมน์วันที่หรือแม้แต่คอลัมน์ที่มีรายการองค์ประกอบ) นอกจากนี้คำถามนี้ไม่ใช่การอ้างอิงแบบสัมบูรณ์เนื่องจากตัวอย่างถ้าคุณไม่ระบุประเภทที่ถูกต้องของคอลัมน์คุณจะไม่ปิดกั้นการเพิ่มแถวเพิ่มเติมที่มีคอลัมน์ประเภทต่าง ๆ ... ดังนั้นฉันจะเพิ่มบันทึกย่อ แต่ไม่ ตัวอย่างที่มีประเภทดึกดำบรรพ์ทั้งหมดเพราะมันไม่ครอบคลุมความเป็นไปได้ทั้งหมด ...
digEmAll

3
@ user4050: คำถามเกี่ยวกับการสร้าง data.frame ว่างดังนั้นเมื่อจำนวนแถวเป็นศูนย์ ... บางทีคุณอาจต้องการสร้าง data.frame แบบเต็มบน NAs ... ในกรณีนี้คุณสามารถใช้เช่นdata.frame(Doubles=rep(as.double(NA),numberOfRow), Ints=rep(as.integer(NA),numberOfRow))
digEmAll

140

หากคุณมี data frame ที่มีอยู่แล้วสมมติdfว่ามีคอลัมน์ที่คุณต้องการจากนั้นคุณสามารถสร้าง data frame ที่ว่างเปล่าโดยการลบแถวทั้งหมด:

empty_df = df[FALSE,]

โปรดสังเกตว่าdfยังคงมีข้อมูลอยู่ แต่empty_dfไม่มี

ฉันพบคำถามนี้เพื่อค้นหาวิธีสร้างอินสแตนซ์ใหม่ที่มีแถวว่างดังนั้นฉันคิดว่าอาจมีประโยชน์สำหรับบางคน


2
ความคิดที่ยอดเยี่ยม ไม่เก็บแถว แต่คอลัมน์ทั้งหมด ใครก็ตามที่ลงคะแนนพลาดบางสิ่งบางอย่าง
รามนภาสมาน

1
วิธีแก้ปัญหาที่ดี แต่ฉันพบว่าฉันได้รับ data frame ที่มี 0 แถว เพื่อรักษาขนาดของกรอบข้อมูลให้คงเดิมฉันแนะนำ new_df = df [NA,] นอกจากนี้ยังอนุญาตให้เก็บคอลัมน์ก่อนหน้าใด ๆ ลงในกรอบข้อมูลใหม่ ตัวอย่างเช่นเพื่อให้ได้คอลัมน์ "วันที่" จากต้นฉบับ df (ขณะที่ยังคงเหลืออยู่ NA): new_df $ Date <- df $ Date
Katya

2
@Katya ถ้าคุณทำdf[NA,]นี้จะส่งผลกระทบต่อดัชนีเช่นกัน (ซึ่งไม่น่าจะเป็นสิ่งที่คุณต้องการ) ผมจะใช้แทนdf[TRUE,] = NA; อย่างไรก็ตามโปรดสังเกตว่าสิ่งนี้จะเขียนทับต้นฉบับ คุณจะต้องคัดลอกชื่อไฟล์ก่อนcopy_df = data.frame(df)แล้วcopy_df[TRUE,] = NA
toto_tico

3
@Katya หรือคุณยังสามารถได้อย่างง่ายดายเพิ่มแถวว่างไปกับempty_df empty_df[0:nrow(df),] <- NA
toto_tico

1
@Katya คุณใช้ backquote (`) รอบสิ่งที่คุณต้องการทำเครื่องหมายเป็นรหัสและมีสิ่งอื่น ๆ เช่นตัวเอียงโดยใช้ * และใช้ตัวหนา ** คุณอาจต้องการที่จะอ่านทุกMarkdown ไวยากรณ์ของ SO ส่วนใหญ่มันสมเหตุสมผลสำหรับคำตอบว่า
toto_tico

79

คุณสามารถทำได้โดยไม่ต้องระบุประเภทคอลัมน์

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

4
ในกรณีนั้นคอลัมน์จะพิมพ์ค่าเริ่มต้นเป็นตรรกะต่อเวกเตอร์ () แต่จะถูกแทนที่ด้วยประเภทขององค์ประกอบที่เพิ่มลงใน df ลองใช้ str (df), df [1,1] <- 'x'
Dave X

58

คุณสามารถใช้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)

ขอบคุณ Richard Scriven สำหรับการปรับปรุง


4
หรือread.table(text = "", ...)คุณไม่จำเป็นต้องเปิดการเชื่อมต่ออย่างชัดเจน
Rich Scriven

โก๋. อาจจะขยายมากที่สุด / วิธี automable การทำเช่นนี้สำหรับหลายคอลัมน์ที่มีศักยภาพ
MichaelChirico

3
read.csvวิธีการยังทำงานร่วมกับในขณะที่readr::read_csv read_csv("Date,File,User\n", col_types = "Dcc")วิธีนี้คุณสามารถสร้าง tibble ที่ว่างเปล่าของโครงสร้างที่ต้องการได้โดยตรง
Heather Turner

27

วิธีที่มีประสิทธิภาพที่สุดในการทำเช่นนี้คือใช้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ฟังก์ชัน คุณแน่ใจหรือว่าคุณไม่ได้พึ่งพฤติกรรมที่ไม่มีเอกสารที่นี่
Adam Ryczkowski

@AdamRyczkowski ฉันคิดว่าคุณกำลังสับสนฐาน "data.frame" ชั้นและ Add-on ชั้น "data.table" จากแพคเกจ data.table
โทมัส

ใช่. อย่างแน่นอน. ความผิดฉันเอง. ละเว้นความคิดเห็นล่าสุดของฉัน ฉันเจอกระทู้นี้เมื่อค้นหาdata.tableและสันนิษฐานว่า Google พบสิ่งที่ฉันต้องการและทุกอย่างที่นี่data.tableเกี่ยวข้องกับ
Adam Ryczkowski

1
@PatrickT ไม่มีการตรวจสอบว่ารหัสของคุณทำอะไรเข้าท่า data.frame()ให้การตรวจสอบเกี่ยวกับการตั้งชื่อชื่อเล่น ฯลฯ
โทมัส

26

เพียงแค่ประกาศ

table = data.frame()

เมื่อคุณลองrbindบรรทัดแรกมันจะสร้างคอลัมน์


2
ไม่ตรงตามข้อกำหนดของ OP ของ "ฉันต้องการระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์และตั้งชื่อ" หากขั้นตอนต่อไปคือการrbindนี้จะทำงานได้ดีถ้าไม่ ...
เกรโทมัส

อย่างไรก็ตามขอขอบคุณสำหรับวิธีแก้ปัญหาง่ายๆนี้ ฉันยังต้องการเริ่มต้น data.frame ด้วยคอลัมน์เฉพาะเนื่องจากฉันคิดว่า rbind สามารถใช้ได้เฉพาะเมื่อคอลัมน์นั้นสอดคล้องกันระหว่าง data.frame สองรายการ นี่ดูเหมือนจะไม่เป็นอย่างนั้น ฉันประหลาดใจที่ฉันสามารถเริ่มต้น data.frame เมื่อใช้ rbind ขอบคุณ
giordano

4
ทางออกที่ดีที่สุดที่นำเสนอที่นี่ rbind()สำหรับผมโดยใช้วิธีการที่เสนอให้ทำงานอย่างสมบูรณ์แบบด้วย
Kots

17

หากคุณกำลังมองหาการขาดแคลน:

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

ดังนั้นคุณไม่จำเป็นต้องระบุชื่อคอลัมน์แยกกัน คุณได้รับชนิดคอลัมน์เริ่มต้นแบบลอจิคัลจนกว่าคุณเติมข้อมูลเฟรม


read.csv แยกวิเคราะห์อาร์กิวเมนต์ข้อความเพื่อให้คุณได้รับชื่อคอลัมน์ มันมีขนาดกะทัดรัดกว่า read.table (text = "", col.names = c ("col1", "col2"))
marc

ฉันได้รับ:Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 0, 2
Climbs_lika_Spyder

สิ่งนี้ไม่เป็นไปตามข้อกำหนดของ OP "ฉันต้องการระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์"แม้ว่าอาจมีการปรับเปลี่ยนให้ทำได้
Gregor Thomas

14

ฉันสร้างกรอบข้อมูลเปล่าโดยใช้รหัสต่อไปนี้

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 

7

ในการสร้างกรอบข้อมูลเปล่าให้ส่งจำนวนแถวและคอลัมน์ที่จำเป็นในฟังก์ชั่นต่อไปนี้:

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

ใช้ดังต่อไปนี้:

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"

1
สิ่งนี้ไม่เป็นไปตามข้อกำหนดของ OP "ฉันต้องการระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์"
Gregor Thomas

6

หากคุณต้องการสร้าง 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()

4

หากคุณไม่รังเกียจที่จะไม่ระบุประเภทข้อมูลอย่างชัดเจนคุณสามารถทำได้ด้วยวิธีนี้:

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

โดยใช้data.tableเราสามารถระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์

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

3

หากคุณต้องการประกาศdata.frameคอลัมน์ดังกล่าวด้วยคอลัมน์จำนวนมากอาจเป็นเรื่องยากที่จะพิมพ์คลาสคอลัมน์ทั้งหมดด้วยมือ โดยเฉพาะอย่างยิ่งหากคุณสามารถใช้ประโยชน์ได้repวิธีนี้ง่ายและรวดเร็ว (เร็วกว่าโซลูชันอื่น ๆ ประมาณ 15% ที่สามารถทำให้เป็นลักษณะทั่วไปได้):

หากคลาสคอลัมน์ที่คุณต้องการอยู่ในเวกเตอร์colClassesคุณสามารถทำสิ่งต่อไปนี้:

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

lapplyจะส่งผลให้ในรายการของความยาวที่ต้องการองค์ประกอบของแต่ละเป็นเพียงเวกเตอร์พิมพ์ที่ว่างเปล่าเหมือนหรือnumeric()integer()

setDFแปลงนี้โดยอ้างอิงถึงlistdata.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

1

สมมติว่าชื่อคอลัมน์ของคุณเป็นแบบไดนามิกคุณสามารถสร้างเมทริกซ์ชื่อแถวที่ว่างเปล่าและแปลงเป็นเฟรมข้อมูล

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

สิ่งนี้ไม่เป็นไปตามข้อกำหนดของ OP "ฉันต้องการระบุชนิดข้อมูลสำหรับแต่ละคอลัมน์"
Gregor Thomas

1

คำถามนี้ไม่ได้ตอบข้อกังวลของฉันโดยเฉพาะ (อธิบายไว้ที่นี่ ) แต่ในกรณีที่ทุกคนต้องการทำเช่นนี้ด้วยจำนวนคอลัมน์ที่มีพารามิเตอร์และไม่มีการบีบบังคับ:

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

ในฐานะที่เป็นหน่วยงานของรัฐในคำถามที่เชื่อมโยง

... เหตุผล [การข่มขู่] เกิดขึ้น [เมื่อเมทริกซ์ cbinding และประเภทขององค์ประกอบ] คือเมทริกซ์สามารถมีชนิดข้อมูลเดียวเท่านั้น เมื่อคุณ cbind 2 เมทริกซ์ผลลัพธ์ยังคงเป็นเมทริกซ์ดังนั้นตัวแปรจึงถูกรวมเข้าเป็นประเภทเดียวก่อนที่จะแปลงเป็น data.frame


1

หากคุณมี dataframe อยู่แล้วคุณสามารถแตก metadata (ชื่อคอลัมน์และประเภท)จาก dataframe (เช่นถ้าคุณกำลังควบคุม BUGซึ่งถูกทริกเกอร์ด้วยอินพุตบางตัวเท่านั้นและต้องมีดัมฟีเปล่า):

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'))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.