การสร้าง R dataframe ทีละแถว


108

ฉันต้องการสร้างดาต้าเฟรมทีละแถวใน R ฉันได้ทำการค้นหาแล้วและสิ่งที่ฉันคิดขึ้นมาคือคำแนะนำในการสร้างรายการว่างเก็บสเกลาร์ดัชนีรายการจากนั้นทุกครั้งที่เพิ่มลงในรายการ ดาต้าเฟรมแถวเดียวและเลื่อนดัชนีรายการทีละรายการ สุดท้ายdo.call(rbind,)ในรายการ

ในขณะที่ใช้งานได้ดูเหมือนจะยุ่งยากมาก ไม่มีวิธีที่ง่ายกว่าในการบรรลุเป้าหมายเดียวกันหรือไม่?

เห็นได้ชัดว่าฉันอ้างถึงกรณีที่ฉันไม่สามารถใช้applyฟังก์ชันบางอย่างได้และจำเป็นต้องสร้างแถวดาต้าเฟรมทีละแถวอย่างชัดเจน อย่างน้อยก็มีวิธีที่จะpushเข้าสู่จุดสิ้นสุดของรายการแทนที่จะติดตามดัชนีล่าสุดที่ใช้อย่างชัดเจนหรือไม่?


1
คุณสามารถใช้append()[ซึ่งน่าจะใช้ชื่อว่า insert] หรือc()เพิ่มรายการต่อท้ายรายการ แต่จะไม่ช่วยคุณได้ที่นี่
hatmatrix

มีไม่หลายหน้าที่ในการวิจัยที่เฟรมข้อมูลกลับมาจนกว่าคุณจะกลับพวกเขา [แถวฉลาด] จากlapply(), Map()และอื่น ๆ แต่คุณยังอาจต้องการที่จะดูที่aggregate(), dapply() {heR.Misc}และcast() {reshape}เพื่อดูว่างานของคุณไม่สามารถจัดการกับเหล่านี้ ฟังก์ชัน (เหล่านี้ส่งคืนเฟรมข้อมูลทั้งหมด)
hatmatrix

คำตอบ:


96

rbind()คุณสามารถเจริญเติบโตแถวพวกเขาโดยแถวโดยการผนวกหรือใช้

นั่นไม่ได้หมายความว่าคุณควร โครงสร้างที่เติบโตแบบไดนามิกเป็นวิธีหนึ่งที่มีประสิทธิภาพน้อยที่สุดในการเขียนโค้ดใน R

หากทำได้ให้จัดสรร data.frame ทั้งหมดของคุณด้านหน้า:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

จากนั้นในระหว่างการดำเนินการของคุณให้แทรกแถวทีละแถว

DF[i, ] <- list(1.4, "foo")

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


6
คุณไม่ได้ตั้งใจที่จะใส่ N แทน 10 และใส่รายการ (1.4, "foo") แทน c (1.4, "foo") เพื่อที่จะไม่บังคับให้ 1.4 เข้าสู่โหมดอักขระ?
hatmatrix

ใช่ฉันตั้งใจจะใช้ N ในการสร้าง data.frame นอกจากนี้การบีบบังคับในการแชทที่ดีมาก - ฉันพลาดสิ่งนั้นไปแล้ว
Dirk Eddelbuettel

1
การแก้ไขคำตอบจะดีกว่าปล่อยไว้ในความคิดเห็น ฉันสับสนในการพยายามคร่ำครวญคำตอบนี้
ผู้ใช้

4
data.tableดูเหมือนว่าจะเร็วกว่าการจัดสรรล่วงหน้าโดยใช้ data.frames ด้วยซ้ำ ทดสอบที่นี่: stackoverflow.com/a/11486400/636656
Ari B. Friedman

นี่ยังคงเป็นจริงใน R 3.1 ซึ่งควรเร็วกว่านี้หรือไม่?
userJT

49

หนึ่งสามารถเพิ่มแถวในNULL:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

เช่น

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)

3
มันส่งออกเมทริกซ์ไม่ใช่เฟรมข้อมูล
Olga

1
@Olga เฉพาะในกรณีที่คุณผูกแถวขององค์ประกอบประเภทที่เท่ากัน - BTW ในกรณีนี้จะดีกว่าที่จะsapply(หรือ vectorise) และเปลี่ยน
mbq

1
@mbq สิ่งที่ฉันทำ ฉันยังพบว่าถ้าคุณเริ่มต้นด้วย df <-data.frame () มันจะแสดงกรอบข้อมูล
Olga

9

นี่เป็นตัวอย่างโง่ ๆ ของวิธีใช้do.call(rbind,)กับเอาต์พุตของMap()[ซึ่งคล้ายกับlapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

ฉันใช้โครงสร้างนี้ค่อนข้างบ่อย


8

เหตุผลที่ฉันชอบ Rcpp มากคือฉันไม่เข้าใจว่า R Core คิดอย่างไรและด้วย Rcpp บ่อยกว่านั้นฉันไม่จำเป็นต้องทำ

เมื่อพูดในเชิงปรัชญาคุณอยู่ในสถานะของบาปเกี่ยวกับกระบวนทัศน์เชิงหน้าที่ซึ่งพยายามทำให้แน่ใจว่าทุกคุณค่าปรากฏขึ้นโดยไม่ขึ้นกับคุณค่าอื่นใด การเปลี่ยนค่าหนึ่งค่าไม่ควรทำให้เกิดการเปลี่ยนแปลงที่มองเห็นได้ในอีกค่าหนึ่งวิธีที่คุณได้รับจากการแสดงพอยน์เตอร์ร่วมกันใน C

ปัญหาเกิดขึ้นเมื่อการเขียนโปรแกรมเชิงฟังก์ชันส่งสัญญาณให้ยานลำเล็กเคลื่อนที่ออกนอกเส้นทางและยานลำเล็กตอบว่า "ฉันคือประภาคาร" การเปลี่ยนแปลงเล็ก ๆ เป็นชุดยาว ๆ กับวัตถุขนาดใหญ่ที่คุณต้องการดำเนินการในระหว่างนี้จะทำให้คุณกลายเป็นพื้นที่ของประภาคาร

ใน C ++ STL push_back()คือวิถีชีวิต มันไม่ได้พยายามที่จะทำงาน แต่ก็ไม่พยายามที่จะรองรับสำนวนการเขียนโปรแกรมร่วมกันได้อย่างมีประสิทธิภาพ

ด้วยความชาญฉลาดเบื้องหลังบางครั้งคุณสามารถจัดให้มีเท้าข้างเดียวในแต่ละโลก ระบบไฟล์ที่ใช้สแน็ปช็อตเป็นตัวอย่างที่ดี (ซึ่งพัฒนามาจากแนวคิดเช่นการเมานต์แบบยูเนี่ยนซึ่งซ้อนทั้งสองด้านด้วย)

หาก R Core ต้องการทำสิ่งนี้หน่วยเก็บข้อมูลเวกเตอร์ที่อยู่ภายใต้อาจทำหน้าที่เหมือนการต่อเชื่อมแบบยูเนี่ยน หนึ่งในการอ้างอิงถึงการจัดเก็บเวกเตอร์อาจจะถูกต้องสำหรับห้อยขณะที่อ้างอิงอีกครั้งเพื่อให้การจัดเก็บข้อมูลเดียวกันที่ถูกต้องสำหรับห้อย1:N 1:(N+1)อาจจะมีการจัดเก็บข้อมูลที่สงวนไว้ยังไม่ได้อ้างถึงการสั่งจ่ายยาโดยอะไร push_back()แต่สะดวกสำหรับการอย่างรวดเร็ว คุณไม่ละเมิดแนวคิดการทำงานเมื่อต่อท้ายนอกช่วงที่การอ้างอิงที่มีอยู่ถือว่าถูกต้อง

ในที่สุดการต่อท้ายแถวเพิ่มขึ้นคุณจะใช้พื้นที่เก็บข้อมูลที่สงวนไว้หมด คุณจะต้องสร้างสำเนาใหม่ของทุกสิ่งโดยที่พื้นที่เก็บข้อมูลจะคูณด้วยส่วนเพิ่มบางส่วน การใช้งาน STL ที่ฉันใช้มักจะคูณพื้นที่เก็บข้อมูลด้วย 2 เมื่อขยายการจัดสรร ฉันคิดว่าฉันอ่านใน R Internals ว่ามีโครงสร้างหน่วยความจำที่การจัดเก็บเพิ่มขึ้น 20% ไม่ว่าจะด้วยวิธีใดก็ตามการดำเนินการเติบโตเกิดขึ้นพร้อมกับความถี่ลอการิทึมที่สัมพันธ์กับจำนวนองค์ประกอบทั้งหมดที่ต่อท้าย โดยปกติแล้วค่าตัดจำหน่ายมักจะยอมรับได้

ในฐานะที่เป็นกลเม็ดเบื้องหลังไปฉันเห็นว่าแย่ลง ทุกครั้งที่คุณpush_back()สร้างแถวใหม่ในดาต้าเฟรมโครงสร้างดัชนีระดับบนสุดจะต้องถูกคัดลอก แถวใหม่สามารถผนวกเข้ากับการแสดงที่ใช้ร่วมกันได้โดยไม่กระทบกับค่าการทำงานเก่าใด ๆ ฉันไม่คิดว่ามันจะยุ่งยากกับคนเก็บขยะมากนัก เนื่องจากฉันไม่ได้เสนอpush_front()การอ้างอิงทั้งหมดเป็นการอ้างอิงคำนำหน้าที่ด้านหน้าของพื้นที่จัดเก็บเวกเตอร์ที่จัดสรร


2

คำตอบของ Dirk Eddelbuettel นั้นดีที่สุด ที่นี่ฉันเพิ่งทราบว่าคุณสามารถหลีกเลี่ยงการไม่ระบุมิติข้อมูลหรือประเภทข้อมูลล่วงหน้าซึ่งบางครั้งก็มีประโยชน์หากคุณมีข้อมูลหลายประเภทและมีคอลัมน์จำนวนมาก:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(df,row2) #now this works as you'd expect.

คุณหมายถึงdf<-rbind(df, row2)?
Timothy C. Quinn

1

ฉันพบวิธีนี้ในการสร้าง dataframe โดย raw โดยไม่มีเมทริกซ์

ด้วยชื่อคอลัมน์อัตโนมัติ

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

พร้อมชื่อคอลัมน์

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )

0

หากคุณมีเวกเตอร์ที่กำหนดให้กลายเป็นแถวให้เชื่อมต่อโดยใช้c()ส่งผ่านไปยังเมทริกซ์ทีละแถวและแปลงเมทริกซ์นั้นเป็นดาต้าเฟรม

ตัวอย่างเช่นแถว

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

สามารถแปลงเป็น data frame ได้ดังนี้:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

เป็นที่ยอมรับว่าฉันเห็นข้อ จำกัด สำคัญ 2 ประการ: (1) สิ่งนี้ใช้ได้กับข้อมูลโหมดเดียวเท่านั้นและ (2) คุณต้องรู้ # คอลัมน์สุดท้ายของคุณเพื่อให้สิ่งนี้ใช้งานได้ (เช่นฉันสมมติว่าคุณไม่ได้ทำงานกับ a อาร์เรย์มอมแมมมีความยาวแถวที่ยิ่งใหญ่ที่สุดเป็นที่รู้จักเบื้องต้น )

วิธีนี้ดูเหมือนง่าย แต่จากประสบการณ์ของฉันกับการแปลงประเภทใน R ฉันแน่ใจว่ามันสร้างความท้าทายใหม่ ๆ ใครสามารถแสดงความคิดเห็นเกี่ยวกับเรื่องนี้?


0

ขึ้นอยู่กับรูปแบบของแถวใหม่ของคุณคุณอาจใช้tibble::add_rowถ้าแถวใหม่ของคุณเรียบง่ายและสามารถระบุเป็น "คู่ค่า" หรือคุณสามารถใช้dplyr::bind_rows"การใช้งานรูปแบบทั่วไปของ do.call (rbind, dfs) ​​อย่างมีประสิทธิภาพ"

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