R डेटाफ़्रेम में प्रत्येक पंक्ति के लिए


173

मेरे पास एक डेटाफ़्रेम है, और उस डेटाफ़्रेम में प्रत्येक पंक्ति के लिए मुझे कुछ जटिल लुकअप करने हैं और कुछ डेटा को एक फ़ाइल में जोड़ना है।

डेटाफ्रेम में जैविक अनुसंधान में प्रयुक्त 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
}

ऐसा करने के लिए "आर तरीका" क्या है?


आपका प्रश्न यहाँ क्या है? Data.frame एक द्वि-आयामी वस्तु है और पंक्तियों पर लूप करना चीजों को करने का एक बिल्कुल सामान्य तरीका है क्योंकि पंक्तियाँ आमतौर पर प्रत्येक स्तंभ में 'चर' के 'अवलोकनों' का समूह होती हैं।
डिर्क एडल्डबुलेटेल

16
मैं जो कर रहा हूं वह है: के लिए: (1 में सूचकांक: nrow (dataFrame)) {row = dataFrame [सूचकांक]; # पंक्ति से सामान करो} जो मुझे कभी बहुत सुंदर नहीं लगा।
कार्ल कोरियल-मार्टिन

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
सावधान रहें, क्योंकि डेटाफ्रेम मैट्रिक्स में परिवर्तित हो जाता है, और जो आप समाप्त करते हैं ( x) एक वेक्टर है। यही कारण है कि उपरोक्त उदाहरण में संख्यात्मक सूचकांक का उपयोग करना है; द्वारा () दृष्टिकोण आपको एक डेटा.फ्रेम देता है, जो आपके कोड को अधिक मजबूत बनाता है।
डैरेन कुक

मेरे लिए काम नहीं किया। लागू फ़ंक्शन प्रत्येक x को वर्ण मान के रूप में दिया जाता है और एक पंक्ति नहीं होती है।
ज़ाहि

3
ध्यान दें कि आप कॉलम को नाम से संदर्भित कर सकते हैं। तो: wellName <- x[1]भी हो सकता है wellName <- x["name"]
फाउंड्रमा

1
जब डैरेन ने मजबूत उल्लेख किया, तो उनका मतलब था कि स्तंभों के आदेशों को स्थानांतरित करना। यह जवाब काम नहीं करेगा जबकि () के साथ वाला अभी भी काम करेगा।
हैलोवर्ल्ड

120

आप by()फ़ंक्शन का उपयोग कर सकते हैं :

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

लेकिन इस तरह से सीधे पंक्तियों पर चलना शायद ही कभी आप चाहते हैं; आपको इसके बजाय सदिश करने की कोशिश करनी चाहिए। क्या मैं पूछ सकता हूं कि लूप में वास्तविक काम क्या है?


5
यदि डेटा फ्रेम 0 पंक्तियां हैं यह अच्छी तरह से काम नहीं करेगा क्योंकि 1:0खाली नहीं है
एसडीएस

10
0 पंक्ति मामले के लिए आसान फिक्स seq_len () का उपयोग करना है , के seq_len(nrow(dataFrame))स्थान पर डालें 1:nrow(dataFrame)
जिम

13
आप वास्तव में (पंक्ति) कैसे लागू करते हैं? क्या यह डेटा कॉलम $ कॉलम है? dataframe [somevariableNamehere]? आप वास्तव में इसकी एक पंक्ति कैसे कहते हैं। स्यूडोकोड "फ़ंक्शन (पंक्ति) डस्टफ़" यह वास्तव में कैसे दिखेगा?
uh_big_mike_boi

1
@ माइक, dostuffइस उत्तर में बदलाव करें str(row) आप कंसोल में छपी कई पंक्तियों को "'data.frame' 'से शुरू करते हुए देखेंगे: एक्स चर का 1 अवलोकन। लेकिन, सावधान रहना बदलते dostuffकरने के लिए rowएक पूरे के रूप बाहरी समारोह के लिए एक data.frame वस्तु वापस नहीं करता है। इसके बजाय यह एक पंक्ति डेटा-फ़्रेम की एक सूची देता है।
pwilcox

91

सबसे पहले, योनातन की वेक्टरिंग के बारे में बात सही है। यदि आपका getWellID () फ़ंक्शन सदिश किया गया है, तो आप लूप को छोड़ सकते हैं और सिर्फ कैट या राइट.स्कव का उपयोग कर सकते हैं:

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

यदि getWellID () वेक्टर नहीं है, तो जोनाथन के सुझाव byया 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 लिखना है। मुझे अभी जो करने की आवश्यकता है, वह सूची को देखने या डेटाबेस से बाहर निकालने के लिए मौजूदा सूची में खोदना है।
कार्ल कोरियल-मार्टिन

GetWellID प्रश्न को पोस्ट करने के लिए स्वतंत्र महसूस करें (अर्थात यह फ़ंक्शन वेक्टर हो सकता है?) अलग से, और मुझे यकीन है कि मैं (या कोई और) इसका उत्तर देगा।
शेन

2
यहां तक ​​कि अगर getWellID वेक्टर नहीं है, तो मुझे लगता है कि आपको इस समाधान के साथ जाना चाहिए, और getWellId को प्रतिस्थापित करना चाहिए mapply(getWellId, well$name, well$plate)
जोनाथन चांग

यहां तक ​​कि अगर आप इसे डेटाबेस से खींचते हैं, तो आप उन सभी को एक बार में खींच सकते हैं और फिर परिणाम को आर में फ़िल्टर कर सकते हैं; यह एक पुनरावृत्त समारोह से तेज हो जाएगा।
शेन

+1 के लिए foreach- मैं उस एक से नरक का उपयोग करने जा रहा हूं।
जोश बोडे

20

मुझे लगता है कि बेसिक R के साथ ऐसा करने का सबसे अच्छा तरीका है:

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

for( i in 1:nrow(df))-Approach पर लाभ यह है कि यदि dfआप खाली हैं और मुसीबत में नहीं आते हैं nrow(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 तक पहुँचने के लिए तेज़ है।
Ł skianiewski-Wołłk

1
बस एहसास हुआ कि एक ही चीज़ को दोगुना करने के लिए यह और भी तेज़ है: पंक्तियाँ = फ़ंक्शन (x) lapply (seq_len (nrow) (x)), फ़ंक्शन (i) lapply (x, function (c) c [i])
to 'अनवस्की-वुल्लक

इसलिए आंतरिक lapplyसंपूर्ण डेटासेट के स्तंभों पर पुनरावृत्ति करता है x, प्रत्येक स्तंभ को नाम देता है c, और फिर iउस स्तंभ वेक्टर से वें प्रविष्टि को निकालता है । क्या ये सही है?
हारून मैकडैड

बहुत अच्छा! मेरे मामले में, मुझे "कारक" मानों से अंतर्निहित मूल्य में बदलना था wellName <- as.character(well$name):।
19

9

मैं गैर-वेक्टरित विकल्पों के समय प्रदर्शन के बारे में उत्सुक था। इस उद्देश्य के लिए, मैंने गुयेन द्वारा परिभाषित फ़ंक्शन च का उपयोग किया है

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

और उसके उदाहरण में एक डेटाफ्रेम:

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 )

मैंने बिल्ली () के दृष्टिकोण की तुलना लिखने के लिए दो वेक्टर किए गए कार्यों (दूसरों की तुलना में जल्दी) के लिए किया था।

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)

परिणामी छवि दिखाती है कि लागू होने वाला एक गैर-सदिश संस्करण के लिए सबसे अच्छा प्रदर्शन देता है, जबकि लिखने में सक्षम () बिल्ली () से बेहतर प्रदर्शन करता है। 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)

डिफ़ॉल्ट रूप से, दिए गए मान को df नामक myfnनए सूची स्तंभ में डाल दिया जाता है .out

यदि यह एकमात्र आउटपुट है जो आप चाहते हैं, तो आप लिख सकते हैं purrrlyr::by_row(df, myfn)$.out


2

खैर, जब से आपने अन्य भाषाओं के बराबर आर के लिए कहा, मैंने ऐसा करने की कोशिश की। काम करने लगता है, हालांकि मैंने वास्तव में नहीं देखा है कि आर में कौन सी तकनीक अधिक कुशल है।

> 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 () का उपयोग करके टाइपकास्ट कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.