প্রতিটি সারি থেকে একাধিক যুক্তি সহ ডাটাফ্রেমের প্রতিটি সারিটিতে প্রয়োগ-জাতীয় ফাংশন কল করুন


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 চাই lo

আমি এটি চেষ্টা করেছি:

> 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

কিন্তু ত্রুটি পেয়েছে, কোন ধারণা?

সম্পাদনা: আমি যে প্রকৃত ফাংশনটি কল করতে চাইছি তা সাধারণ যোগফল নয়, তবে এটি পাওয়ার.টেসটেস্ট। আমি উদাহরণস্বরূপ একটি + বি ব্যবহার করেছি। শেষ লক্ষ্যটি হ'ল এর মতো কিছু করতে সক্ষম হবেন (সিউডোকোডে লিখিত):

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 এর আউটপুটগুলির একটি ভেক্টর।


পথের জন্য স্ট্যাকওভারফ্লোdplyr . com/a/24728107/946850 দেখুন ।
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]))

সম্পাদনা করুন সূচী নয় এবং নাম অনুসারে কলামগুলি অ্যাক্সেস করতে আপনি এই জাতীয় কিছু করতে পারেন:

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

ধন্যবাদ @ অগস্টুডি, যে কাজ করেছে! আপনি কি জানেন যে সূচিপত্রের পরিবর্তে নামগুলি দ্বারা আরগগুলি নির্দিষ্ট করার কোনও উপায় আছে কিনা? সুতরাং, টেস্টফুঙ্কের জন্য, প্রয়োগের মতো কিছু (ডাট [, সি ('এক্স', 'জেড')), ১, [সিউডোকোড] টেস্টফ্যাঙ্ক (a = x, b = y))? কারণটি হ'ল আমি এই পদ্ধতিতে পাওয়ার.ট.টেষ্টকে কল করছি এবং আমি বদ্বীপ, শক্তি, সিগ.লেভেল প্যারামগুলিকে পূর্বনির্ধারিত অবস্থানের সাথে একটি অ্যারেতে আটকে রাখার পরিবর্তে নাম উল্লেখ করতে সক্ষম হতে চাই এবং তারপরে আরও শক্তিশালী হওয়ার কারণে সেই অবস্থানগুলি উল্লেখ করা। যে কোনও ক্ষেত্রে অনেক ধন্যবাদ!
vasek1

পূর্ববর্তী মন্তব্যের জন্য দুঃখিত, টাইপিংয়ের আগে এন্টার টিপুন :) এটি মুছে ফেলে সম্পূর্ণ সংস্করণ পোস্ট করেছে।
vasek1

21
applyবড় ডেটাতে ব্যবহার করবেন না ra ফ্রেমগুলি এটি পুরো বস্তুকে অনুলিপি করে (ম্যাট্রিক্সে রূপান্তর করতে)। ডেটা.ফ্রেমের মধ্যে আপনার যদি বিভিন্ন শ্রেণীর অবজেক্ট থাকে তবে এটিও সমস্যা তৈরি করবে।
মণেল

105

data.frameএকটি list, তাই ...

জন্য ভেক্টরকৃত ফাংশন do.call সাধারণত একটি ভাল বাজি। তবে যুক্তিগুলির নাম কার্যকর হয়। এখানে আপনার testFunca এবং b এর স্থানে x এবং y আরগ্স দিয়ে ডাকা হবে। ...অপ্রাসঙ্গিক args একটি ত্রুটি ঘটাচ্ছে ছাড়া হস্তান্তর করা অনুমতি দেয়:

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

জন্য অ ভেক্টরকৃত ফাংশন , mapplyকাজ করবে, কিন্তু আপনি args ক্রম মেলে বা স্পষ্টভাবে এগুলির নাম দেখতে প্রয়োজন:

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

কখনও কখনও applyকাজ করবে - যখন সমস্ত আরগগুলি একই ধরণের হয় তাই data.frameম্যাট্রিক্সে জোর দেওয়া ডেটা ধরণের পরিবর্তন করে সমস্যা সৃষ্টি করে না। আপনার উদাহরণ এই ধরণের ছিল।

যদি আপনার ফাংশনটিকে অন্য কোনও ফাংশনের মধ্যে ডাকা যেতে হয় যার মধ্যে আর্গুমেন্টগুলি সমস্ত পাস হয়ে যায় তবে এর চেয়ে অনেক বেশি স্মার্ট পদ্ধতি রয়েছে। lm()আপনি যদি সেই পথে যেতে চান তবে তার শরীরের প্রথম লাইনগুলি অধ্যয়ন করুন ।


8
+10 যদি আমি পারতাম। তাই আপনাকে স্বাগতম। মহান উত্তর - এটা কহতব্য হতে পারে Vectorizeএকটি লেফাফা হিসেবে mapplyফাংশন vectorize করতে
mnel

বাহ, যে চতুর। আমি যে মূল ফাংশনটি ব্যবহার করেছি তা ভেক্টরাইজড হয়নি (পাওয়ার.টেসটেস্টের শীর্ষে একটি কাস্টম এক্সটেনশন), তবে আমি মনে করি আমি এটি ভেক্টরাইজ করব এবং ডকএল (...) ব্যবহার করব। ধন্যবাদ!
vasek1

3
এই নোটটি কেবল পুনরুক্ত করেই এই উত্তরটি ইতিমধ্যে বলেছে যে প্রয়োগ (ডিএফ, 1, ফাংশন (সারি) ...) খারাপ হতে পারে কারণ প্রয়োগটি ডিএফকে ম্যাট্রিক্সে রূপান্তর করে !!!! এটি খারাপ হতে পারে এবং প্রচুর পরিমাণে চুল টানতে পারে। প্রয়োগের বিকল্পগুলি অনেক প্রয়োজন!
কলিন ডি

ভেক্টরাইজড / অ-ভেক্টরাইজডদের মধ্যে পার্থক্য করার জন্য আপনাকে অনেক ধন্যবাদ, এটি আমি ঠিক উত্তরটি খুঁজছিলাম
ইউজার 632716

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
আপনার যখন কেবলমাত্র কয়েকটি সংখ্যক কলাম থাকবে তখনই এটি দুর্দান্ত। আমি এমন কিছু করার চেষ্টা করেছি: এমডিপিপ্লাই (ডিএফ, ফাংশন (কল 1, কল 3) {}) এবং এমডিপ্লি ব্যাইল আউট, কল 2 অভিযোগ করা অব্যবহৃত। এখন, আপনার যদি দশ বা এমনকি কয়েকশ কলাম রয়েছে তবে এই পদ্ধতির খুব আকর্ষণীয় নয়।
বার্টজান ব্রুকসেমা

1
@ বার্টজান ব্রুকসেমায় প্রচুর কলাম পরিবর্তন করতে পারেন, আপনি এটি ব্যবহার করতে পারেন 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
তুমি ঠিক বলছো. ম্যাপলি কার্যকরভাবে ব্যবহার করতে, আমি মনে করি আপনাকে বুঝতে হবে যে এটি কেবল পর্দার পিছনে "ফলের" লুপ, বিশেষত যদি আপনি প্রক্রিয়াগত প্রোগ্রামিং ব্যাকগ্রাউন্ড যেমন সি ++ বা সি # থেকে এসে থাকেন।
কনতাঙ্গো

10

অনেকগুলি ফাংশন হ'ল ইতিমধ্যে ভেক্টরাইজেশন, এবং তাই কোনও পুনরাবৃত্তির প্রয়োজন নেই (উভয় forলুপ বা *pplyফাংশন নয়)। আপনার testFuncযেমন একটি উদাহরণ। আপনি কেবল কল করতে পারেন:

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

সাধারণভাবে, আমি প্রথমে এই জাতীয় ভেক্টরাইজেশন পদ্ধতির চেষ্টা করার পরামর্শ দিচ্ছি এবং তারা আপনাকে আপনার পছন্দসই ফলাফলগুলি পেয়েছে কিনা তা দেখুন।


বিকল্পভাবে, যদি আপনাকে কোনও ফাংশন যা ভেক্টরাইজড হয় না তার কাছে একাধিক যুক্তি পাস করার প্রয়োজন হয় তবে আপনি যা mapplyখুঁজছেন তা হতে পারে:

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

আহ মিষ্টি. আপনি কী জানেন যে ম্যাপলিতে নাম দিয়ে যুক্তি নির্দিষ্ট করার কোনও উপায় আছে কিনা? উদাহরণস্বরূপ [সিউডোকোড] ম্যাপ্লি জাতীয় কিছু (পাওয়ার.টি.টেষ্ট, ডেল্টা = ডিএফ [, 'ডেল্টা'], শক্তি = ডিএফ [, 'শক্তি'], ...)?
vasek1

1
হ্যাঁ, এটি ঠিক যেমন আপনার কাছে রয়েছে! ;)
রিকার্ডো সাপোর্টা

4

এখানে একটি বিকল্প পদ্ধতি। এটি আরও স্বজ্ঞাত।

আমি অনুভব করি যে কয়েকটি মূল উত্তর আমি বিবেচনায় নিই না, যা আমি উত্তরোত্তর জন্য চিহ্নিত করেছি, প্রয়োগ করা হয় () আপনাকে সহজেই সারি গণনা করতে দেয় তবে কেবল ম্যাট্রিক্স (সমস্ত সংখ্যাসূচক) ডেটার জন্য

ডেটাফ্রেমগুলির জন্য কলামগুলিতে অপারেশনগুলি এখনও সম্ভব:

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

সারিগুলিতে পরিচালনা করতে, আমরা প্রথমে ট্রান্সপোজটি তৈরি করি।

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

ক্ষতিটি হ'ল আমি বিশ্বাস করি যে আর আপনার ডেটা টেবিলের একটি অনুলিপি তৈরি করবে। যা স্মৃতির সমস্যা হতে পারে। (এটি সত্যই দুঃখজনক, কারণ টিডিএফের পক্ষে মূল ডিএফের জন্য কেবল পুনরুক্তি হওয়াটাই প্রোগ্রামগতভাবে সহজ, এইভাবে মেমরি সঞ্চয় করে তবে আর পয়েন্টার বা পুনরুক্তি রেফারেন্সিংকে অনুমতি দেয় না))

এছাড়াও, সম্পর্কিত প্রশ্ন, কীভাবে ডেটাফ্রেমের প্রতিটি স্বতন্ত্র কক্ষে কাজ করা যায়।

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

4

আমি এখানে পরিপাটিত ফাংশনটির নাম খুঁজতে এসেছি - যা আমি জানতাম যে এর অস্তিত্ব আছে। (আমার) ভবিষ্যতে উল্লেখের জন্য এবং এই যোগ করার পদ্ধতি tidyverseউত্সাহীদের: purrrlyr:invoke_rows(purrr:invoke_rows পুরানো সংস্করণে)।

মূল প্রশ্নের মতো স্ট্যান্ডার্ড পরিসংখ্যান পদ্ধতির সাথে সংযোগের সাথে ঝাড়ু প্যাকেজটি সম্ভবত সহায়তা করবে।


3

@ ব্যবহারকারী20877984 এর উত্তরটি দুর্দান্ত। যেহেতু তারা এটি আমার পূর্ববর্তী উত্তরের চেয়ে অনেক ভাল সংক্ষিপ্তসার করেছে, তাই ধারণাটির প্রয়োগের জন্য এখানে আমার (সম্ভবত এখনও স্বল্পতর) প্রচেষ্টা:

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

যদি ডেটা.ফ্রেম কলামগুলি বিভিন্ন ধরণের হয় তবে 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 () কোনও তালিকা ফেরত দেয়? আপনার ডিএফ_অ্যাপেনের অতিরিক্ত কলামগুলিতে রূপান্তর করতে অযাচিত () ব্যবহার করা হবে?
Val
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.