2017-10-12 8 views
4

私は、文字列に基づいてデータフレームを操作する関数を作成しています。私は問題が何であるかを理解しなぜquoureはgroup_by()では動作しますが、filter()では動作しませんか?

library(dplyr) 

orig_df <- data_frame(
    id = 1:3 
    , amt = c(100, 200, 300) 
    , anyA = c(T,F,T) 
    , othercol = c(F,F,T) 
) 


summarize_my_df_broken <- function(df, my_string) { 

    my_column <- quo(paste0("any", my_string)) 

    df %>% 
    filter(!!my_column) %>% 
    group_by(othercol) %>% 
    summarize(
     n = n() 
     , total = sum(amt) 
    ) %>% 
    # I need the original string as new column which is why I can't 
    # pass in just the column name 
    mutate(stringid = my_string) 


} 


summarize_my_df_works <- function(df, my_string) { 

    my_column <- quo(paste0("any", my_string)) 

    df %>% 
    group_by(!!my_column, othercol) %>% 
    summarize(
     n = n() 
     , total = sum(amt) 
    ) %>% 
    mutate(stringid = my_string) 

} 

# throws an error: 
# Argument 2 filter condition does not evaluate to a logical vector 
summarize_my_df_broken(orig_df, "A") 

# works just fine 
summarize_my_df_works(orig_df, "A") 

:関数内で、私は、文字列からと列名を構築し、データフレームを操作するためにそれを使用して、このようなものでしょうfilter()への引数としてquosureをunquoting破損したバージョンでは、実際の列anyAを参照していません。

summarize()で動作する理由はわかりませんが、filter()では何の違いがありますか?

答えて

4

今はシンボル名ではなく文字列のクォアを作成しています。それはそれらがどのように使用されるべきではありません。 quo("hello")quo(hello)の間には大きな違いがあります。文字列から適切なシンボル名を作成する場合は、rlang::symを使用する必要があります。だから、クイックフィックスは、あなたがより密接に見れば、私はあなたがgroup_by/summarizeが実際に(あなただけの同じエラーメッセージを得ることはありませんが)あなたはどちらか期待どおりに動作していない参照してくださいよと思う

summarize_my_df_broken <- function(df, my_string) { 

    my_column <- rlang::sym(paste0("any", my_string)) 
    ... 
} 

だろう。これらの2つは同じ結果を生成しません。

summarize_my_df_works(orig_df, "A") 
# `paste0("any", my_string)` othercol  n total 
#      <chr> <lgl> <int> <dbl> 
# 1      anyA FALSE  2 300 
# 2      anyA  TRUE  1 300 

orig_df %>% 
    group_by(anyA, othercol) %>% 
    summarize(
    n = n() 
    , total = sum(amt) 
) %>% 
    mutate(stringid = "A") 
# anyA othercol  n total stringid 
# <lgl> <lgl> <int> <dbl> <chr> 
# 1 FALSE FALSE  1 200  A 
# 2 TRUE FALSE  1 100  A 
# 3 TRUE  TRUE  1 300  A 

ここでも問題は記号の代わりに文字列を使用していることです。

+0

ああ、なるほど! 'quo()'はシンボルをquosureに変換し、 'enquo()'は関数の引数の値をquosureに変換し、sym()は文字列をquosureに変換します。だから私は文字列を渡していたが、記号のように扱っていた。 'summarize_my_df_works()'で動作するように見えたのは、関数に基づいて要約することができたからです。 – crazybilly

0

'broken'関数にはfilter()の条件がありません。カラム名を指定するだけです。

さらに、より大きな式にクォーザを挿入できるかどうかはわかりません。たとえば、次のようなものを試してみてください:

df %>% filter((!!my_column) == TRUE) 

しかし、私はそれがうまくいくとは思いません。

代わりに、条件付き関数filter_at()を使用して適切な列をターゲットにすることをお勧めします。その場合、あなたはフィルタ条件からquosureを分離:

summarize_my_df_broken <- function(df, my_string) { 

    my_column <- quo(paste0("any", my_string)) 

    df %>% 
    filter_at(vars(!!my_column), all_vars(. == TRUE)) %>% 
    group_by(othercol) %>% 
    summarize(
     n = n() 
     , total = sum(amt) 
    ) %>% 
mutate(stringid = my_string) 

}

+0

これは正しくありません。 'any_A'がブール値の列であるため、' orig_df%>%filter(anyA) 'のようなフィルタを完全に正常に動作させることができます。さらに、 'vars()'を使うことを望むなら、関数は文字列をうまく受け入れるので、実際にはquosuresは必要ありません: 'orig_df%>%filter_at(vars(paste)(" any "、" A ")) all_vars(。= TRUE)) ' – MrFlick

+0

良い点、MrFlick! –

+0

filter_at()を使用することは良い考えであり、手元の問題に確実に対処してくれます.MhFlickのソリューションは、問題の例にとっては良い方法です。しかし、それは私の主な質問、つまりなぜquartzがsummary()ではなくfilter()で動作しないのでしょうか?私は、NSEに関して欠けているという根本的な理解があると思う。 – crazybilly

関連する問題