สำหรับแต่ละแถวใน R dataframe


173

ฉันมี dataframe และสำหรับแต่ละแถวใน dataframe นั้นฉันต้องทำการค้นหาที่ซับซ้อนและผนวกข้อมูลบางส่วนเข้ากับไฟล์

dataFrame มีผลลัพธ์ทางวิทยาศาสตร์สำหรับหลุมที่เลือกจาก 96 แผ่นหลุมที่ใช้ในการวิจัยทางชีววิทยาดังนั้นฉันต้องการทำบางสิ่งเช่น:

for (well in dataFrame) {
  wellName <- well$name    # string like "H1"
  plateName <- well$plate  # string like "plate67"
  wellID <- getWellID(wellName, plateName)
  cat(paste(wellID, well$value1, well$value2, sep=","), file=outputFile)
}

ในโลกแห่งกระบวนการของฉันฉันจะทำสิ่งที่ชอบ:

for (row in dataFrame) {
    #look up stuff using data from the row
    #write stuff to the file
}

"วิธี R" คืออะไรในการทำเช่นนี้?


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

16
สิ่งที่ฉันทำคือ: สำหรับ (ดัชนีใน 1: nrow (dataFrame)) {row = dataFrame [index,]; # ทำบางสิ่งบางอย่างกับแถว} ซึ่งไม่เคยดูสวยสำหรับฉันเลย
Carl Coryell-Martin

1
getWellID เรียกฐานข้อมูลหรืออะไรอื่น ๆ มิฉะนั้นแล้วโจนาธานอาจจะถูกต้องและคุณสามารถทำให้เป็นแบบนี้ได้
เชน

คำตอบ:


103

คุณสามารถลองสิ่งนี้ได้โดยใช้apply()ฟังก์ชั่น

> d
  name plate value1 value2
1    A    P1      1    100
2    B    P2      2    200
3    C    P3      3    300

> f <- function(x, output) {
 wellName <- x[1]
 plateName <- x[2]
 wellID <- 1
 print(paste(wellID, x[3], x[4], sep=","))
 cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

> apply(d, 1, f, output = 'outputfile')

76
ระวังเนื่องจาก dataframe ถูกแปลงเป็นเมทริกซ์และสิ่งที่คุณลงท้ายด้วย ( x) คือเวกเตอร์ นี่คือเหตุผลที่ตัวอย่างข้างต้นต้องใช้ดัชนีตัวเลข วิธี by () ช่วยให้คุณ data.frame ซึ่งทำให้รหัสของคุณแข็งแกร่งขึ้น
Darren Cook

ไม่ได้ผลสำหรับฉัน ฟังก์ชัน Apply ใช้กับ x ทุกตัวที่ให้กับ f เป็นค่าอักขระไม่ใช่แถว
Zahy

3
โปรดทราบด้วยว่าคุณสามารถอ้างถึงคอลัมน์ตามชื่อ ดังนั้น: wellName <- x[1]อาจเป็นwellName <- x["name"]ได้
Founddrama

1
เมื่อคาร์เรนพูดถึงความแข็งแกร่งเขาหมายถึงการเปลี่ยนคำสั่งของคอลัมน์ คำตอบนี้จะไม่ทำงานในขณะที่คนที่มีโดย () จะยังคงทำงานได้
HelloWorld

120

คุณสามารถใช้by()ฟังก์ชั่น:

by(dataFrame, 1:nrow(dataFrame), function(row) dostuff)

แต่การวนซ้ำแถวโดยตรงเช่นนี้ไม่ค่อยเป็นสิ่งที่คุณต้องการ คุณควรลอง vectorize แทน ฉันสามารถถามสิ่งที่งานจริงในวงกำลังทำอยู่ได้หรือไม่?


5
สิ่งนี้จะไม่ทำงานหากกรอบข้อมูลมี 0 แถวเพราะ1:0ไม่ว่างเปล่า
sds

10
การแก้ไขที่ง่ายสำหรับกรณี 0 แถวคือการใช้seq_len ()แทรกในสถานที่ของseq_len(nrow(dataFrame)) 1:nrow(dataFrame)
Jim

13
คุณใช้งานจริง (แถว) อย่างไร มันคือ dataframe $ column หรือไม่ dataframe [somevariableNamehere] คุณพูดเป็นแถวได้อย่างไร pseudocode "function (row) dostuff" มันจะมีลักษณะอย่างไร?
uh_big_mike_boi

1
@ ไมค์เปลี่ยนdostuffคำตอบนี้เป็นstr(row) คุณจะเห็นหลายบรรทัดที่พิมพ์ในคอนโซลเริ่มต้นด้วย"'data.frame': 1 obs ของตัวแปร x แต่ระวังการเปลี่ยนdostuffเป็นrowไม่ส่งคืนวัตถุ data.frame สำหรับฟังก์ชั่นด้านนอกโดยรวม แต่จะส่งคืนรายการของ data-frames หนึ่งแถว
pwilcox

91

ประการแรกประเด็นของโจนาธานเกี่ยวกับการปรับเวกเตอร์นั้นถูกต้อง หากฟังก์ชัน getWellID () ของคุณเป็นแบบเวกเตอร์คุณสามารถข้ามลูปและใช้ cat หรือ write.csv:

write.csv(data.frame(wellid=getWellID(well$name, well$plate), 
         value1=well$value1, value2=well$value2), file=outputFile)

หาก getWellID () ไม่ได้เป็นแบบเวกเตอร์แสดงว่าโจนาธานแนะนำให้ใช้byหรือคำแนะนำของ knguyen ว่าapplyควรจะได้ผล

มิฉะนั้นถ้าคุณต้องการใช้จริงๆforคุณสามารถทำสิ่งนี้:

for(i in 1:nrow(dataFrame)) {
    row <- dataFrame[i,]
    # do stuff with row
}

คุณสามารถลองใช้foreachแพ็คเกจได้แม้ว่าคุณจะต้องคุ้นเคยกับไวยากรณ์นั้น นี่คือตัวอย่างง่ายๆ:

library(foreach)
d <- data.frame(x=1:10, y=rnorm(10))
s <- foreach(d=iter(d, by='row'), .combine=rbind) %dopar% d

ตัวเลือกสุดท้ายคือการใช้ฟังก์ชั่นนอกplyrแพ็คเกจซึ่งในกรณีนี้การประชุมจะคล้ายกับฟังก์ชั่นการใช้งานมาก

library(plyr)
ddply(dataFrame, .(x), function(x) { # do stuff })

เชนขอบคุณ ฉันไม่แน่ใจว่าจะเขียน getWellID แบบเวกเตอร์ได้อย่างไร สิ่งที่ฉันต้องทำตอนนี้คือการขุดลงในรายการที่มีอยู่เพื่อค้นหามันหรือดึงออกมาจากฐานข้อมูล
Carl Coryell-Martin

อย่าลังเลที่จะโพสต์คำถาม getWellID (เช่นฟังก์ชันนี้สามารถเป็นแบบเวกเตอร์ได้หรือไม่) และฉันแน่ใจว่าฉัน (หรือคนอื่น) จะตอบคำถามนี้
เชน

2
แม้ว่าจะไม่ได้ getWellID vectorized mapply(getWellId, well$name, well$plate)ผมคิดว่าคุณควรไปกับการแก้ปัญหานี้และแทนที่ด้วย
Jonathan Chang Chang

แม้ว่าคุณจะดึงมันออกมาจากฐานข้อมูลคุณสามารถดึงมันทั้งหมดในครั้งเดียวแล้วกรองผลลัพธ์ใน R; ที่จะเร็วกว่าฟังก์ชั่นวนซ้ำ
เชน

+1 สำหรับforeach- ฉันจะใช้นรกจากสิ่งนั้น
Josh Bode

20

ฉันคิดว่าวิธีที่ดีที่สุดในการทำเช่นนี้กับ Basic R คือ:

for( i in rownames(df) )
   print(df[i, "column1"])

ข้อได้เปรียบที่เหนือfor( i in 1:nrow(df))-approach คือการที่คุณไม่ได้รับเป็นปัญหาถ้าเป็นที่ว่างเปล่าและdfnrow(df)=0


17

ฉันใช้ฟังก์ชั่นยูทิลิตี้ง่าย ๆ นี้:

rows = function(tab) lapply(
  seq_len(nrow(tab)),
  function(i) unclass(tab[i,,drop=F])
)

หรือรูปแบบที่เร็วขึ้นและชัดเจนน้อยลง:

rows = function(x) lapply(seq_len(nrow(x)), function(i) lapply(x,"[",i))

ฟังก์ชันนี้แยก data.frame ไปยังรายการของแถว จากนั้นคุณสามารถสร้าง "สำหรับ" ตามปกติในรายการนี้:

tab = data.frame(x = 1:3, y=2:4, z=3:5)
for (A in rows(tab)) {
    print(A$x + A$y * A$z)
}        

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

for (well in rows(dataFrame)) {
  wellName <- well$name    # string like "H1"
  plateName <- well$plate  # string like "plate67"
  wellID <- getWellID(wellName, plateName)
  cat(paste(wellID, well$value1, well$value2, sep=","), file=outputFile)
}

มันเร็วกว่าที่จะเข้าถึงรายการที่ตรงแล้ว data.frame
ŁŁaniewski-Wołłk

1
เพิ่งรู้ว่ามันเร็วยิ่งกว่าที่จะทำสิ่งเดียวกันด้วย lapply สองครั้ง: rows = function (x) lapply (seq_len (nrow (x)), function (i) lapply (x, function (c) c [i]))
Ł iewsaniewski-Wołłk

ดังนั้นด้านในlapplyจะวนซ้ำไปตามคอลัมน์ของชุดข้อมูลทั้งหมดxโดยตั้งชื่อให้แต่ละคอลัมน์cจากนั้นแยกiรายการ th จากเวกเตอร์คอลัมน์นั้น ถูกต้องหรือไม่
Aaron McDaid

ดีมาก! ในกรณีของฉันฉันมีการแปลงจาก "ปัจจัย" wellName <- as.character(well$name)คุณค่ากับมูลค่าพื้นฐาน:
Steve Pitchers

9

ฉันอยากรู้เกี่ยวกับการแสดงเวลาของตัวเลือกที่ไม่ใช่เวกเตอร์ เพื่อจุดประสงค์นี้ฉันได้ใช้ฟังก์ชัน f ที่กำหนดโดย knguyen

f <- function(x, output) {
  wellName <- x[1]
  plateName <- x[2]
  wellID <- 1
  print(paste(wellID, x[3], x[4], sep=","))
  cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

และ dataframe เหมือนในตัวอย่าง:

n = 100; #number of rows for the data frame
d <- data.frame( name = LETTERS[ sample.int( 25, n, replace=T ) ],
                  plate = paste0( "P", 1:n ),
                  value1 = 1:n,
                  value2 = (1:n)*10 )

ฉันรวมฟังก์ชั่น vectorised สองอัน (เพื่อความรวดเร็วกว่าฟังก์ชั่นอื่น) เพื่อเปรียบเทียบ cat () เข้ากับ write.table () หนึ่งตัว ...

library("ggplot2")
library( "microbenchmark" )
library( foreach )
library( iterators )

tm <- microbenchmark(S1 =
                       apply(d, 1, f, output = 'outputfile1'),
                     S2 = 
                       for(i in 1:nrow(d)) {
                         row <- d[i,]
                         # do stuff with row
                         f(row, 'outputfile2')
                       },
                     S3 = 
                       foreach(d1=iter(d, by='row'), .combine=rbind) %dopar% f(d1,"outputfile3"),
                     S4= {
                       print( paste(wellID=rep(1,n), d[,3], d[,4], sep=",") )
                       cat( paste(wellID=rep(1,n), d[,3], d[,4], sep=","), file= 'outputfile4', sep='\n',append=T, fill = F)                           
                     },
                     S5 = {
                       print( (paste(wellID=rep(1,n), d[,3], d[,4], sep=",")) )
                       write.table(data.frame(rep(1,n), d[,3], d[,4]), file='outputfile5', row.names=F, col.names=F, sep=",", append=T )
                     },
                     times=100L)
autoplot(tm)

รูปภาพผลลัพธ์แสดงให้เห็นว่าการใช้นั้นให้ประสิทธิภาพที่ดีที่สุดสำหรับเวอร์ชันที่ไม่ใช่เวกเตอร์ในขณะที่ write.table () ดูเหมือนว่าจะดีกว่า cat () ForEachRunningTime


6

คุณสามารถใช้by_rowฟังก์ชั่นจากแพ็คเกจpurrrlyrสำหรับสิ่งนี้:

myfn <- function(row) {
  #row is a tibble with one row, and the same 
  #number of columns as the original df
  #If you'd rather it be a list, you can use as.list(row)
}

purrrlyr::by_row(df, myfn)

โดยค่าเริ่มต้นค่าที่ส่งคืนจากmyfnจะถูกใส่ลงในคอลัมน์รายการใหม่ใน df ที่เรียกว่า.outที่เรียกว่า

หากนี่เป็นเอาต์พุตที่คุณต้องการเท่านั้นคุณสามารถเขียนได้ purrrlyr::by_row(df, myfn)$.out


2

เนื่องจากคุณขอ R เทียบเท่าภาษาอื่นฉันพยายามทำเช่นนี้ ดูเหมือนว่าจะทำงานแม้ว่าฉันจะไม่ได้ดูว่าเทคนิคใดมีประสิทธิภาพมากกว่าในอาร์

> myDf <- head(iris)
> myDf
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> nRowsDf <- nrow(myDf)
> for(i in 1:nRowsDf){
+ print(myDf[i,4])
+ }
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.4

สำหรับคอลัมน์หมวดหมู่มันจะดึงข้อมูลมาให้คุณซึ่งคุณสามารถพิมพ์ตัวอักษรโดยใช้ as.character () หากต้องการ

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