प्रत्येक पंक्ति से एकाधिक तर्कों के साथ डेटाफ़्रेम की प्रत्येक पंक्ति पर कॉल-अप फ़ंक्शन को कॉल करें


168

मेरे पास कई कॉलम के साथ एक डेटाफ्रेम है। डेटाफ्रेम में प्रत्येक पंक्ति के लिए, मैं पंक्ति पर एक फ़ंक्शन को कॉल करना चाहता हूं, और फ़ंक्शन का इनपुट उस पंक्ति से कई कॉलम का उपयोग कर रहा है। उदाहरण के लिए, मान लें कि मेरे पास यह डेटा है और यह परीक्षण है जो दो आर्गन स्वीकार करता है:

> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
  x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b

मान लें कि मैं इस परीक्षण को कॉलम x और z पर लागू करना चाहता हूं। तो, पंक्ति 1 के लिए मुझे 1 + 5 चाहिए, और पंक्ति 2 के लिए मुझे 2 + 6 चाहिए। क्या लूप के लिए लिखने के बिना ऐसा करने का कोई तरीका है, शायद लागू फ़ंक्शन परिवार के साथ?

मैंने यह कोशिश की:

> df[,c('x','z')]
  x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing

लेकिन त्रुटि मिली, कोई विचार?

संपादित करें: मैं जिस वास्तविक फ़ंक्शन को कॉल करना चाहता हूं वह एक साधारण योग नहीं है, लेकिन यह power.t.test है। मैंने उदाहरण प्रयोजनों के लिए a + b का उपयोग किया। अंतिम लक्ष्य कुछ ऐसा करने में सक्षम होना है (स्यूडोकोड में लिखा गया है):

df = data.frame(
    delta=c(delta_values), 
    power=c(power_values), 
    sig.level=c(sig.level_values)
)

lapply(df, power.t.test(delta_from_each_row_of_df, 
                        power_from_each_row_of_df, 
                        sig.level_from_each_row_of_df
))

जहां परिणाम df के प्रत्येक पंक्ति के लिए power.t.test के लिए आउटपुट का वेक्टर है।


रास्ते के लिए stackoverflow.com/a/24728107/946850 भी देखें dplyr
krlmlr

जवाबों:


137

आप applyमूल डेटा के सबसेट पर आवेदन कर सकते हैं ।

 dat <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
 apply(dat[,c('x','z')], 1, function(x) sum(x) )

या यदि आपका फ़ंक्शन बस वेक्टर संस्करण का उपयोग कर रहा है:

rowSums(dat[,c('x','z')])
[1] 6 8

यदि आप उपयोग करना चाहते हैं testFunc

 testFunc <- function(a, b) a + b
 apply(dat[,c('x','z')], 1, function(x) testFunc(x[1],x[2]))

EDIT नाम से कॉलम एक्सेस करने के लिए और इंडेक्स में आप ऐसा कुछ नहीं कर सकते हैं:

 testFunc <- function(a, b) a + b
 apply(dat[,c('x','z')], 1, function(y) testFunc(y['z'],y['x']))

धन्यवाद @agstudy, कि काम किया! क्या आप जानते हैं कि सूचकांक के बजाय नाम से आर्ग्स निर्दिष्ट करने का कोई तरीका है? इसलिए, TestFunc के लिए, कुछ इस तरह लागू होता है (dat [, c ('x', 'z')], 1, [pseudocode] testFunc (a = x, b = y))? इसका कारण यह है कि मैं इस तरीके से power.t.test को कॉल कर रहा हूं, और मैं डेल्टा, पावर, sig.level params को पहले से निर्दिष्ट पदों के साथ एक सरणी में चिपकाए जाने के बजाय नाम से संदर्भित करने में सक्षम होना पसंद करूंगा। अधिक मजबूत होने के कारण के लिए उन स्थिति को संदर्भित करना। किसी भी मामले में बहुत बहुत धन्यवाद!
vasek1

पिछली टिप्पणी के बारे में खेद है, समाप्त टाइपिंग से पहले हिट दर्ज करें :) इसे हटा दिया और पूर्ण संस्करण पोस्ट किया।
vasek1

21
applyबड़े डेटा पर उपयोग न करें। यह संपूर्ण ऑब्जेक्ट (मैट्रिक्स में कनवर्ट करने के लिए) को कॉपी करेगा। यह समस्याएँ भी पैदा करेगा। यदि आपके पास data.frame के भीतर अलग-अलग वर्ग की वस्तुएँ हैं।
mnel

105

data.frameहै list, इसलिए ...

के लिए vectorized कार्यों do.call आमतौर पर एक अच्छा शर्त है। लेकिन तर्कों के नाम खेलने में आते हैं। यहाँ आपके testFunca और b के स्थान पर args x और y के साथ कॉल किया जाता है। ...बिना किसी त्रुटि के अप्रासंगिक आर्ग को पारित करने की अनुमति देता है:

do.call( function(x,z,...) testFunc(x,z), df )

के लिए गैर vectorized कार्यों , mapplyकाम करेंगे, लेकिन आप आर्ग के आदेश से मेल खाते हैं या उन्हें स्पष्ट रूप से नाम के लिए की जरूरत है:

mapply(testFunc, df$x, df$z)

कभी-कभी applyकाम करेगा - जब सभी आर्ग एक ही प्रकार के होते हैं तो data.frameमैट्रिक्स के साथ जोर लगाना डेटा प्रकार बदलकर समस्याएं पैदा नहीं करता है। आपका उदाहरण इस प्रकार का था।

यदि आपके फ़ंक्शन को किसी अन्य फ़ंक्शन के भीतर बुलाया जाना है जिसमें तर्क सभी पारित हो गए हैं, तो इनकी तुलना में बहुत अधिक स्लीकर विधि है। lm()यदि आप उस मार्ग पर जाना चाहते हैं तो शरीर की पहली पंक्तियों का अध्ययन करें ।


8
+10 अगर मैं कर सका। एसओ में आपका स्वागत है। महान जवाब - यह कार्यों को Vectorizemapply
वेक्टर

वाह, यह चालाक है। मेरे द्वारा उपयोग किया जाने वाला मूल फ़ंक्शन वेक्टराइज़ नहीं किया गया था (पॉवर के शीर्ष पर एक कस्टम एक्सटेंशन। सबसे अच्छा), लेकिन मुझे लगता है कि मैं इसे वेक्टर करूंगा और do.call (...) का उपयोग करूंगा। धन्यवाद!
vasek1

3
ध्यान दें कि यह उत्तर पहले ही कहता है कि लागू करें (df, 1, फ़ंक्शन (पंक्ति) ...) खराब हो सकता है क्योंकि लागू df को मैट्रिक्स में परिवर्तित करता है !!!! यह खराब हो सकता है और बहुत सारे बाल खींच सकते हैं। आवेदन करने के लिए विकल्पों की बहुत आवश्यकता है!
कॉलिन डी

वेक्टराइज्ड / नॉन-
वेक्टराइज्ड के

31

उपयोग mapply

> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
  x y z
1 1 3 5
2 2 4 6
> mapply(function(x,y) x+y, df$x, df$z)
[1] 6 8

> cbind(df,f = mapply(function(x,y) x+y, df$x, df$z) )
  x y z f
1 1 3 5 6
2 2 4 6 8

20

dplyrपैकेज के साथ नया उत्तर

यदि आप जिस फ़ंक्शन को लागू करना चाहते हैं, वह सदिश है, तो आप पैकेज mutateसे फ़ंक्शन का उपयोग कर सकते हैं dplyr:

> library(dplyr)
> myf <- function(tens, ones) { 10 * tens + ones }
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mutate(x, value = myf(tens, ones))
  hundreds tens ones value
1        7    1    4    14
2        8    2    5    25
3        9    3    6    36

plyrपैकेज के साथ पुराना उत्तर

मेरी विनम्र राय में, उपकरण सबसे अच्छा काम के लिए अनुकूल है mdplyसे plyrपैकेज।

उदाहरण:

> library(plyr)
> x <- data.frame(tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
  tens ones V1
1    1    4 14
2    2    5 25
3    3    6 36

दुर्भाग्य से, जैसा कि बर्टजन ब्रोकेसेमा ने कहा, यदि आप mdplyकॉल में डेटा फ़्रेम के सभी कॉलम का उपयोग नहीं करते हैं, तो यह दृष्टिकोण विफल हो जाता है । उदाहरण के लिए,

> library(plyr)
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
Error in (function (tens, ones)  : unused argument (hundreds = 7)

1
यह अच्छा है जब आपके पास केवल छोटे कॉलम होते हैं। मैंने कुछ ऐसा करने की कोशिश की: mdply (df, function (col1, col3) {}) और mdply की घंटी बजती है, शिकायत करते हुए col2 अप्रयुक्त है। अब, यदि आपके पास दसियों या सैकड़ों कॉलम हैं, तो भी यह दृष्टिकोण बहुत आकर्षक नहीं है।
बर्टजन ब्रोकेसेमा

1
@BertjanBroeksema स्तंभों को संशोधित करने के लिए, आप उपयोग कर सकते हैं dplyr::mutate_each। उदाहरण के लिए iris %>% mutate_each(funs(half = . / 2),-Species):।
पॉल रौजीक्स

क्या आप केवल एलिप्स, या सैकड़ों फंक्शन पास नहीं कर सकते हैं और इसका उपयोग नहीं कर सकते हैं? उस त्रुटि को ठीक करना चाहिए?
शॉन

11

दूसरों ने mapplyइस उद्देश्य के लिए सही ढंग से बताया है , लेकिन (पूर्णता के लिए) एक वैचारिक सरल तरीका सिर्फ एक forलूप का उपयोग करना है।

for (row in 1:nrow(df)) { 
    df$newvar[row] <- testFunc(df$x[row], df$z[row]) 
}

1
आप सही हे। Mapply को प्रभावी ढंग से उपयोग करने के लिए, मुझे लगता है कि आपको यह समझना होगा कि यह पर्दे के पीछे सिर्फ एक "फॉर" लूप है, खासकर यदि आप एक प्रक्रियात्मक प्रोग्रामिंग पृष्ठभूमि जैसे कि C ++ या C # से आते हैं।
कंटैंगो

10

कई फ़ंक्शन पहले से ही वैश्वीकरण हैं, और इसलिए किसी भी पुनरावृत्तियों (न ही forलूप या *pplyफ़ंक्शन) की कोई आवश्यकता नहीं है । आपका testFuncऐसा ही एक उदाहरण है। आप बस कॉल कर सकते हैं:

  testFunc(df[, "x"], df[, "z"])

सामान्य तौर पर, मैं पहले ऐसे वैश्वीकरण दृष्टिकोणों की कोशिश करने की सलाह दूंगा और देखूंगा कि क्या वे आपको आपके इच्छित परिणाम मिलते हैं।


वैकल्पिक रूप से, यदि आपको एक फ़ंक्शन के लिए कई तर्क पारित करने की आवश्यकता है, जो कि वेक्टर नहीं है, तो mapplyवह हो सकता है जो आप खोज रहे हैं:

  mapply(power.t.test, df[, "x"], df[, "z"])

ओह, बहुत अच्छे। क्या आप जानते हैं कि अगर mapply में नाम से तर्क निर्दिष्ट करने का कोई तरीका है? जैसे कुछ [pseudocode] mapply (power.t.test, delta = df [, 'delta'], power = df [, 'power'], ...)?
vasek1

1
हां, यह बिल्कुल वैसा ही है जैसा आपके पास है! ;)
रिकार्डो सपोर्टा

4

यहाँ एक वैकल्पिक दृष्टिकोण है। यह अधिक सहज है।

एक मुख्य पहलू मुझे लगता है कि कुछ जवाबों पर ध्यान नहीं दिया गया, जो कि मैं पोस्टीरिटी के लिए इंगित करता हूं, लागू है () आपको पंक्ति गणना आसानी से करने देता है, लेकिन केवल मैट्रिक्स (सभी संख्यात्मक) डेटा के लिए

डेटाफ़्रेम के लिए स्तंभों पर संचालन अभी भी संभव है:

as.data.frame(lapply(df, myFunctionForColumn()))

पंक्तियों पर काम करने के लिए, हम पहले स्थानान्तरण करते हैं।

tdf<-as.data.frame(t(df))
as.data.frame(lapply(tdf, myFunctionForRow()))

नकारात्मक पक्ष यह है कि मेरा मानना ​​है कि आर आपके डेटा तालिका की एक प्रति बनाएगा। जो एक मेमोरी इश्यू हो सकता है। (यह वास्तव में दुखद है, क्योंकि यह मूल रूप से tf के लिए प्रोग्रामिक रूप से सरल है मूल df के लिए एक पुनरावृत्ति होना, इस प्रकार मेमोरी को सहेजना है, लेकिन आर सूचक या पुनरावृत्ति संदर्भित करने की अनुमति नहीं देता है।)

इसके अलावा, एक संबंधित सवाल यह है कि डेटाफ़्रेम में प्रत्येक व्यक्ति सेल पर कैसे काम किया जाए।

newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()}))

4

मैं यहाँ tidyverse function name की तलाश में आया था - जिसे मैं जानता था कि यह अस्तित्व में है। इसे (मेरे) भविष्य के संदर्भ के लिए और tidyverseउत्साही लोगों के लिए जोड़ना : purrrlyr:invoke_rows( purrr:invoke_rowsपुराने संस्करणों में)।

मूल प्रश्न में मानक आँकड़े विधियों के संबंध में, झाड़ू पैकेज शायद मदद करेगा।


3

@ user20877984 का जवाब उत्कृष्ट है। चूंकि उन्होंने इसे मेरे पिछले उत्तर की तुलना में बेहतर बताया है, यहाँ अवधारणा के एक आवेदन पर मेरा (संभवतः अभी भी घटिया) प्रयास है:

do.callमूल फैशन में उपयोग करना :

powvalues <- list(power=0.9,delta=2)
do.call(power.t.test,powvalues)

पूर्ण डेटा सेट पर कार्य करना:

# get the example data
df <- data.frame(delta=c(1,1,2,2), power=c(.90,.85,.75,.45))

#> df
#  delta power
#1     1  0.90
#2     1  0.85
#3     2  0.75
#4     2  0.45

lapplypower.t.testनिर्दिष्ट मानों की पंक्तियों में से प्रत्येक के लिए कार्य:

result <- lapply(
  split(df,1:nrow(df)),
  function(x) do.call(power.t.test,x)
)

> str(result)
List of 4
 $ 1:List of 8
  ..$ n          : num 22
  ..$ delta      : num 1
  ..$ sd         : num 1
  ..$ sig.level  : num 0.05
  ..$ power      : num 0.9
  ..$ alternative: chr "two.sided"
  ..$ note       : chr "n is number in *each* group"
  ..$ method     : chr "Two-sample t test power calculation"
  ..- attr(*, "class")= chr "power.htest"
 $ 2:List of 8
  ..$ n          : num 19
  ..$ delta      : num 1
  ..$ sd         : num 1
  ..$ sig.level  : num 0.05
  ..$ power      : num 0.85
... ...

हाहा शायद दोषी? ;) आप टी का उपयोग क्यों कर रहे हैं () और पर लागू कर रहे हैं 2, सिर्फ लागू क्यों नहीं 1?
रिकार्डो सपोर्टा

3

data.table ऐसा करने का वास्तव में सहज तरीका है:

library(data.table)

sample_fxn = function(x,y,z){
    return((x+y)*z)
}

df = data.table(A = 1:5,B=seq(2,10,2),C = 6:10)
> df
   A  B  C
1: 1  2  6
2: 2  4  7
3: 3  6  8
4: 4  8  9
5: 5 10 10

:=ऑपरेटर एक समारोह का उपयोग कर एक नया स्तंभ जोड़ना कोष्ठक में कहा जा सकता है

df[,new_column := sample_fxn(A,B,C)]
> df
   A  B  C new_column
1: 1  2  6         18
2: 2  4  7         42
3: 3  6  8         72
4: 4  8  9        108
5: 5 10 10        150

इस विधि का उपयोग करने के साथ-साथ तर्कों को स्वीकार करना भी आसान है:

df[,new_column2 := sample_fxn(A,B,2)]

> df
   A  B  C new_column new_column2
1: 1  2  6         18           6
2: 2  4  7         42          12
3: 3  6  8         72          18
4: 4  8  9        108          24
5: 5 10 10        150          30

1

यदि data.frame कॉलम अलग-अलग प्रकार के हैं, तो apply()एक समस्या है। पंक्ति पुनरावृत्ति के बारे में एक सूक्ष्मता यह है कि apply(a.data.frame, 1, ...)स्तंभों के भिन्न प्रकार होने पर चरित्र प्रकारों में निहित प्रकार रूपांतरण कैसे होता है; जैसे। एक कारक और संख्यात्मक कॉलम। संख्यात्मक स्तंभ को संशोधित करने के लिए एक कॉलम में एक कारक का उपयोग करके यहां एक उदाहरण दिया गया है:

mean.height = list(BOY=69.5, GIRL=64.0)

subjects = data.frame(gender = factor(c("BOY", "GIRL", "GIRL", "BOY"))
         , height = c(71.0, 59.3, 62.1, 62.1))

apply(height, 1, function(x) x[2] - mean.height[[x[1]]])

घटाव विफल रहता है क्योंकि स्तंभ वर्ण प्रकारों में परिवर्तित हो जाते हैं।

एक फिक्स को दूसरे कॉलम को नंबर में वापस बदलना है:

apply(subjects, 1, function(x) as.numeric(x[2]) - mean.height[[x[1]]])

लेकिन कॉलम को अलग और उपयोग करके रूपांतरणों से बचा जा सकता है mapply():

mapply(function(x,y) y - mean.height[[x]], subjects$gender, subjects$height)

mapply()जरूरत है क्योंकि [[ ]]एक वेक्टर तर्क स्वीकार नहीं करता है। तो कॉलम पुनरावृत्ति को वेक्टर से पास करने से पहले घटाव से []थोड़ा अधिक बदसूरत कोड द्वारा किया जा सकता है :

subjects$height - unlist(mean.height[subjects$gender])

1

इस के लिए एक वास्तव में अच्छा कार्य है adplyसे plyr, खासकर यदि आप मूल dataframe लिए परिणाम संलग्न करना चाहते हैं। इस फ़ंक्शन और इसके चचेरे भाई ddplyने मुझे बहुत सारे सिरदर्द और कोड की लाइनें बचा ली हैं!

df_appended <- adply(df, 1, mutate, sum=x+z)

वैकल्पिक रूप से, आप अपनी इच्छानुसार फ़ंक्शन को कॉल कर सकते हैं।

df_appended <- adply(df, 1, mutate, sum=testFunc(x,z))

सूची या डेटाफ़्रेम लौटा सकने वाले फ़ंक्शंस से निपट सकते हैं? उदाहरण के लिए, क्या होगा अगर testFunc () एक सूची लौटाता है? क्या इसे आपके df_appened के अतिरिक्त कॉलम में म्यूट करने के लिए अनावश्यक () का उपयोग किया जाएगा?
वैल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.