各行からの複数の引数を使用して、データフレームの各行でapply-like関数を呼び出します


168

複数の列を持つデータフレームがあります。データフレームの各行について、その行で関数を呼び出したいのですが、関数の入力はその行の複数の列を使用しています。たとえば、このデータと、2つの引数を受け入れるこのtestFuncがあるとします。

> 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

このtestFuncを列xおよびzに適用するとします。したがって、行1には1 + 5、行2には2 + 6が必要です。forループを記述せずにこれを行う方法はありますか。

私はこれを試しました:

> 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 2015

回答:


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']))

@agstudy、ありがとうございます!インデックスではなく名前で引数を指定する方法があるかどうか知っていますか?したがって、testFuncの場合、apply(dat [、c( 'x'、 'z')]、1、[pseudocode] testFunc(a = x、b = y))のようなものですか?その理由は、私がこの方法でpower.t.testを呼び出しているためです。また、delta、power、sig.level paramsを、事前に指定された位置を持つ配列に貼り付けずに、名前で参照できるようにしたいと考えています。より堅牢であるという理由で、それらの位置を参照します。とにかく本当にありがとう!
vasek1 2013

以前のコメントについて申し訳ありません。入力が完了する前にEnterキーを押してください:)それを削除して完全版を投稿しました。
vasek1 2013

21
apply大きなdata.framesでは使用しないでください。オブジェクト全体がコピーされます(マトリックスに変換するため)。これは、data.frame内に異なるクラスオブジェクトがある場合にも問題を引き起こします。
mnel 2013

105

A data.framelistなので、...

以下のためにベクトル化機能 do.call通常は良い賭けです。しかし、議論の名前が関係してくる。ここtestFuncでは、aとbの代わりに引数xとyを使用して呼び出されます。は...、エラーを引き起こさずに無関係な引数を渡すことができます。

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

以下のために非ベクトル化機能mapply動作していますが、引数の順序を一致させるか、明示的に名前を付ける必要があります。

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

時々apply動作します-すべての引数が同じ型であるときのように強制するように、data.frame行列にデータ型を変更することで問題は発生しません。あなたの例はこの種のものでした。

関数が引数がすべて渡される別の関数内で呼び出される場合、これらよりもはるかに洗練されたメソッドがあります。lm()あなたがそのルートに行きたいなら体の最初の行を調べてください。


8
できれば+10。SOへようこそ。素晴らしい答え- 関数をベクトル化Vectorizeするmapplyためのラッパーとして言及する価値があるかもしれません
mnel

うわー、それは滑らかです。私が使用した元の関数はベクトル化されていませんでしたが(power.t.testのカスタム拡張)、ベクトル化してdo.call(...)を使用すると思います。ありがとう!
vasek1 2013

3
applyがdfを行列に変換するため、apply(df、1、function(row)...)は不適切である可能性があることをこの回答がすでに言っているという注記を繰り返します。これは悪いことであり、多くの髪を引っ張ることになります。適用する代替案が大いに必要です!
コリンD

ベクトル化/非ベクトル化を区別していただきありがとうございます。これは私が探していた答えです
User632716

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パッケージの新しい答え

適用する関数がベクトル化されている場合はmutatedplyrパッケージの関数を使用できます。

> 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パッケージの古い答え

私の控えめな意見では、タスクに最適なツールmdplyplyrパッケージからのものです。

例:

> 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

残念ながら、Bertjan Broeksemaが指摘した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が未使用であると文句を言いました。現在、数十または数百の列がある場合、このアプローチはあまり魅力的ではありません。
Bertjan Broeksema 2015

1
@BertjanBroeksemaを使用して多くの列を変更するには、を使用できますdplyr::mutate_each。例:iris %>% mutate_each(funs(half = . / 2),-Species)
ポールルージュ

楕円または何百もの関数を関数に渡して、それを使用しないのではないでしょうか?それはそのエラーを修正する必要がありますか?
Shawn

11

他の人はmapplyこの目的のために作成されたことを正しく指摘しましたが、(完全を期すために)概念的に単純な方法はforループを使用することです。

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

1
あなたが正しい。mapplyを効果的に使用するには、特にC ++やC#などの手続き型プログラミングのバックグラウンドを使用している場合は、それが舞台裏の "for"ループであることを理解する必要があると思います。
Contango 2014年

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 2013

1
うん、それはあなたがそれを持っているとおりです!;)
リカルドサポルタ2013

4

ここに別のアプローチがあります。より直感的です。

いくつかの回答が考慮に入れられていないと感じている重要な側面の1つは、後世のために指摘しましたが、apply()を使用すると、行(行列(すべて数値)データのみ)を簡単に計算できます

列に対する操作は、データフレームに対しても可能です。

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

行を操作するには、最初に転置を行います。

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

欠点は、Rがあなたのデータテーブルのコピーを作成すると信じていることです。これはメモリの問題である可能性があります。(これは本当に悲しいことです。tdfがプログラムで単純に元のdfのイテレータになるだけなので、メモリを節約できますが、Rはポインタやイテレータの参照を許可していません。)

また、関連する質問は、データフレーム内の個々のセルを操作する方法です。

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

4

私はここにきちんとした関数名を探しに来ました-私はそれが存在することを知っていました。(私の)将来の参照用とのためにこれを追加するtidyverse愛好家:purrrlyr:invoke_rowspurrr: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
... ...

ははは、たぶん複雑ですか?;)なぜt()を使用してオーバー2に適用するの1ですか?
Ricardo Saporta 2013

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, ...)、列が異なる型である場合に、暗黙的に型を文字型に変換する方法です。例えば。因子と数値列。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]]])

列が文字型に変換されるため、減算は失敗します。

1つの修正は、2番目の列を数値に逆変換することです。

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、あなたは、元のデータフレームに結果を追加したい場合は特に、。この関数とそのいとこddplyによって、頭痛の種やコード行を大幅に節約できました。

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

または、必要な関数を呼び出すこともできます。

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

adply()はリストまたはデータフレームを返す関数を処理できますか?たとえば、testFunc()がリストを返すとどうなりますか?unnest()を使用して、df_appenedの追加の列に変更しますか?
ヴァル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.