20

ベクトルの操作について理解したと思うたびに、単純な問題のように見えるものが頭をひっくり返してしまいます。今回は、さまざまな例を読んで試してみましたが、役に立ちませんでした。ここでスプーンで食べさせてください...

データフレームの各行に 2 つのカスタム関数を適用し、結果を 2 つの新しい列として追加したいと考えています。ここに私のサンプルコードがあります:

# Required packages:
library(plyr)

FindMFE <- function(x) {
    MFE <- max(x, na.rm = TRUE) 
    MFE <- ifelse(is.infinite(MFE ) | (MFE  < 0), 0, MFE)
    return(MFE)
}

FindMAE <- function(x) {
    MAE <- min(x, na.rm = TRUE) 
    MAE <- ifelse(is.infinite(MAE) | (MAE> 0), 0, MAE)
    return(MAE)
}

FindMAEandMFE <- function(x){
        # I know this next line is wrong...
    z <- apply(x, 1, FindMFE, FindMFE)
        return(z)
}

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))

df1 = transform(df1, 
    FindMAEandMFE(df1)  
)

#DF1 should end up with the following data...
#Bar1   Bar2    MFE MAE
#1      3       3   0
#2      1       2   0
#3      3       3   0
#-3     -2      0   -3
#-2     -3      0   -3
#-1     -1      0   -1

plyr ライブラリと、より基本的なアプローチを使用して回答を得ることは素晴らしいことです。どちらも私の理解に役立ちます。もちろん、明らかに間違っているところがあれば指摘してください。;-)

ヘルプ ファイルに戻りましょう。

編集:列名が変更され、時間の経過とともに拡張される可能性があるため、多変量ソリューションが必要です。また、将来的にコードを再利用することもできます。

4

4 に答える 4

19

私は3つの代替ワンライナーを示します:

  • each機能を使用してplyr
  • plyr eachベースRで関数を使用する
  • pminベクトル化された関数とpmax関数を使用する

解決策1:プライヤーとそれぞれ

plyrパッケージは、必要なことを実行する関数を定義しますeach。From ?each複数の関数を1つの関数に集約します。 これは、ワンライナーを使用して問題を解決できることを意味します。

library(plyr)
adply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

解決策2:それぞれとベースR

もちろん、each基本関数で使用することもできます。これを使用する方法は次applyのとおりです。元のdata.frameに追加する前に、結果を転置する必要があることに注意してください。

library(plyr)
data.frame(df1, 
  t(apply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

解決策3:ベクトル化された関数を使用する

ベクトル化された関数pminとを使用するとpmax、次のワンライナーを使用できます。

transform(df1, MFE=pmax(0, Bar1, Bar2), MAE=pmin(0, Bar1, Bar2))

  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
于 2011-08-24T10:54:14.433 に答える
19

ここであなたは複雑に考えすぎていると思います。apply()2 つの別々の呼び出しの何が問題になっていますか? ただし、ループ/適用呼び出しを含まない、ここで行っていることを実行するためのはるかに優れた方法があります。これらを個別に扱いますが、真にベクトル化されているため、2 番目のソリューションをお勧めします。

2 つの適用呼び出しバージョン

すべてベースの R 関数を使用した最初の 2 つの個別の適用呼び出し:

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))
df1 <- transform(df1, MFE = apply(df1, 1, FindMFE), MAE = apply(df1, 1, FindMAE))
df1

これにより、次のことが得られます。

> df1
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

わかりました、行を 2 回ループするのdf1はおそらく少し非効率的ですが、大きな問題であっても、1 回のパスでこれを賢く行うことを考えるのに、その方法で節約できるよりも多くの時間を費やしています。

ベクトル化された関数の使用pmax()pmin()

したがって、これを行うためのより良い方法は、関数と関数に注意し、pmax()それぞれpmin()apply(df1, 1, FindFOO()呼び出しが行っていたことを実行できることを理解することです。例えば:

> (tmp <- with(df1, pmax(0, Bar1, Bar2, na.rm = TRUE)))
[1] 3 2 3 0 0 0

あなたの質問からMFEになります。Bar12 つの列があり、それらがおよびBar2または の最初の 2 列である場合、これは非常に簡単に操作できますdf1。しかし、それはあまり一般的ではありません。これを計算したい列が複数ある場合などはどうなりますか?pmax(df1[, 1:2], na.rm = TRUE)私たちが望むことをしません:

> pmax(df1[, 1:2], na.rm = TRUE)
  Bar1 Bar2
1    1    3
2    2    1
3    3    3
4   -3   -2
5   -2   -3
6   -1   -1

pmax()とを使用して一般的な解決策を得る秘訣pmin()は、 を使用do.call()してこれら 2 つの関数の呼び出しを調整することです。私たちが持っているこのアイデアを使用するように関数を更新します。

FindMFE2 <- function(x) {
   MFE <- do.call(pmax, c(as.list(x), 0, na.rm = TRUE))
   MFE[is.infinite(MFE)] <- 0
   MFE
}

FindMAE2 <- function(x) {
   MAE <- do.call(pmin, c(as.list(x), 0, na.rm = TRUE))
   MAE[is.infinite(MAE)] <- 0
   MAE
}

与える:

> transform(df1, MFE = FindMFE2(df1), MAE = FindMAE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

そして見えないapply()。これを 1 つのステップで実行したい場合は、ラップするのがはるかに簡単になりました。

FindMAEandMFE2 <- function(x){
    cbind(MFE = FindMFE2(x), MAE = FindMAE2(x))
}

次のように使用できます。

> cbind(df1, FindMAEandMFE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
于 2011-08-24T10:30:36.610 に答える
6

ここにはたくさんの良い答えがあります。Gavin Simpson が編集している間にこれを始めたので、似たような内容を取り上げます。並列の min と max が行うこと (pmin と pmax) は、関数を作成する目的とほとんど同じです。0 が pmax(0, Bar1, Bar2) で何をするかは少し不透明かもしれませんが、本質的に 0 はリサイクルされるので、それはやっているようなものです

pmax(c(0,0,0,0,0,0), Bar1, Bar2)

これは、渡された 3 つの項目の各項目を取得し、それらの最大値を見つけます。したがって、最大値が負の場合は 0 になり、ifelse ステートメントが行ったことの多くを達成します。書き直して、ベクトルを取得し、行っていたことと同様の関数と組み合わせることができます。これにより、もう少し透過的になる可能性があります。この場合、データフレームを新しい並列で高速な findMFE 関数に渡すだけです。この関数は、任意の数値データフレームで動作し、ベクトルを取得します。

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
}

MFE <- findMFE(df1)

この関数が行うことは、渡されたデータ フレームに 0 の余分な列を追加し、pmax を呼び出して、df1 の各列をリストのように渡します (データフレームはリストなので簡単です)。

ここで、例にないデータの Inf 値を実際に修正したいことに注意してください...関数に余分な行を追加できます...

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MFE), 0, MFE)
}

これで、ベクトルに対する ifelse() 関数の適切な使用法です。私はあなたの例としてそのようにしましたが、Gavin Simpson の MFE[is.infinite(MFE)] <- 0 の使用はより効率的です。この findMFE 関数はループでは使用されず、データ フレーム全体が渡されるだけであることに注意してください。

同等のfindMAEは...

findMAE <- function(dataf){
    MAE <- do.call( pmin, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MAE), 0, MAE)
}

組み合わせた機能は単純です...

findMFEandMAE <- function(dataf){
    MFE <- findMFE(dataf)
    MAE <- findMAE(dataf)
    return(data.frame(MFE, MAE))
}

MFEandMAE <- findMFEandMAE(df1) df1 <- cbind(df1, MFEandMAE)

いくつかのヒント

ifelse() を使用しないスカラー if ステートメントがある場合は、if() else を使用します。スカラー状況でははるかに高速です。また、関数はスカラーであり、それらをベクトル化しようとしています。ifelse() はすでにベクトル化されており、そのように使用すると非常に高速に実行されますが、スカラーを使用すると if() else よりもはるかに遅くなります。

また、ループや apply ステートメントに何かを入れる場合は、可能な限り小さくしてください。たとえば、あなたの場合、実際には ifelse() をループから取り出し、後で MFE 結果全体に適用する必要がありました。

于 2011-08-24T11:39:08.330 に答える
1

あなたが本当に、本当にそれを望んでいるなら、あなたは次のことができます:

FindMAEandMFE <- function(x){
    t(apply(x, 1, function(currow){c(MAE=FindMAE(currow), MFE=FindMFE(currow))}))
}

(テストされていません-2つの(名前が付けられていると思います)列とdata.frameと同じ数の行を持つ配列を返す必要があります)。今、あなたはすることができます:

df1<-cbind(df1, FindMAEandMFE(df1))

非常に厄介です。ギャビンのアドバイスに注意してください。

于 2011-08-24T10:49:24.280 に答える