web-dev-qa-db-ja.com

列が存在する場合にのみdplyr操作を実行します

条件付きdplyr評価 に関する議論に基づいて、渡されたデータフレームに参照列が存在するかどうかに応じて、パイプラインでステップを条件付きで実行したいと思います。

_1)__2)_で生成された結果は同じである必要があります。

既存の列

_# 1)
mtcars %>% 
  filter(am == 1) %>%
  filter(cyl == 4)

# 2)
mtcars %>%
  filter(am == 1) %>%
  {
    if("cyl" %in% names(.)) filter(cyl == 4) else .
  }
_

使用できない列

_# 1)
mtcars %>% 
  filter(am == 1)

# 2)    
mtcars %>%
  filter(am == 1) %>%
  {
    if("absent_column" %in% names(.)) filter(absent_column == 4) else .
  }
_

問題

使用可能な列の場合、渡されたオブジェクトは初期データフレームに対応していません。元のコードはエラーメッセージを返します。

filter(cyl == 4)のエラー:オブジェクト_'cyl'_が見つかりません

私は別の構文を試しました(運が悪い):

_>> mtcars %>%
...   filter(am == 1) %>%
...   {
...     if("cyl" %in% names(.)) filter(.$cyl == 4) else .
...   }
 Show Traceback

 Rerun with Debug
 Error in UseMethod("filter_") : 
  no applicable method for 'filter_' applied to an object of class "logical" 
_

ファローアップ

filter呼び出しの_==_の右側の評価を説明するこの質問を拡張したいと思いました。たとえば、以下の構文は、最初の使用可能な値でフィルタリングを試みます。 mtcars%>%

_filter({
    if ("does_not_ex" %in% names(.))
      does_not_ex
    else
      NULL
  } == {
    if ("does_not_ex" %in% names(.))
      unique(.[['does_not_ex']])
    else
      NULL
  })
_

予想通り、呼び出しはエラーメッセージに評価されます。

filter_impl(.data, quo)のエラー:結果の長さは0ではなく32でなければなりません

既存の列に適用した場合:

_mtcars %>%
  filter({
    if ("mpg" %in% names(.))
      mpg
    else
      NULL
  } == {
    if ("mpg" %in% names(.))
      unique(.[['mpg']])
    else
      NULL
  })
_

警告メッセージが表示されます:

_  mpg cyl disp  hp drat   wt  qsec vs am gear carb
1  21   6  160 110  3.9 2.62 16.46  0  1    4    4
_

警告メッセージ:_{_の場合:長いオブジェクトの長さは短いオブジェクトの長さの倍数ではありません

フォローアップ質問

filter呼び出しの右側で条件付き評価を取得するために、既存の構文を拡張して、理想的にはdplyrワークフロー内にとどまる、きちんとした方法はありますか?

12
Konrad

ここでスコープが機能するため、ifステートメント内からデータフレームにアクセスすることはできません。幸いなことに、その必要はありません。

試してください:

mtcars %>%
  filter(am == 1) %>%
  filter({if("cyl" %in% names(.)) cyl else NULL} == 4)

ここでは、条件内で '.'オブジェクトを使用できるため、列が存在するかどうかを確認でき、存在する場合は、filter関数に列を返すことができます。

編集:質問に対するdocendo discimusのコメントに従って、暗黙的にではなくデータフレームにアクセスできます-つまり、.で具体的に参照する必要があります

14
Eumenedies

私はパーティーに遅れていることを知っていますが、ここでは、あなたが当初考えていたものに少し沿った答えを示します。

mtcars %>%
  filter(am == 1) %>%
  {
    if("cyl" %in% names(.)) filter(., cyl == 4) else .
  }

基本的に、filter.がありませんでした。これは、.で囲まれた式内にあるため、{}filter(expr)に追加しないためです。

2
Felipe Gerard

このコードはトリックを実行し、かなり柔軟です。 ^と$は、完全一致を実行するために使用される正規表現です。

mtcars %>% 
  set_names(names(.) %>% 
              str_replace("am","1") %>% 
              str_replace("^cyl$","2") %>% 
              str_replace("Doesn't Exist","3")
              )
0
Jonathan

編集:残念ながら、これは本当であるには余りにも良かった

パーティーには少し遅れるかもしれません。しかし

mtcars %>% 
 filter(am == 1) %>%
 try(filter(absent_column== 4))

解決策?

0
Johannes