Plyrの理解をdplyrに移そうとしていますが、複数の列でグループ化する方法がわかりません。
# make data with weird column names that can't be hard coded
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
# plyr - works
ddply(data, columns, summarize, value=mean(value))
# dplyr - raises error
data %.%
group_by(columns) %.%
summarise(Value = mean(value))
#> Error in eval(expr, envir, enclos) : index out of bounds
Plyrの例をdplyr風の構文に変換するには何が欠けていますか?
編集2017:Dplyrが更新されたため、よりシンプルなソリューションが利用可能になりました。現在選択されている回答を参照してください。
この質問が投稿されてから、dplyrはgroup_by
のスコープバージョンを追加しました( documentation here )。これにより、次のようにselect
で使用するのと同じ関数を使用できます。
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
library(dplyr)
df1 <- data %>%
group_by_at(vars(one_of(columns))) %>%
summarize(Value = mean(value))
#compare plyr for reference
df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value))
table(df1 == df2, useNA = 'ifany')
## TRUE
## 27
サンプルの質問からの出力は期待どおりです(上記のplyrと下の出力の比較を参照)。
# A tibble: 9 x 3
# Groups: asihckhdoydkhxiydfgfTgdsx [?]
asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value
<fctr> <fctr> <dbl>
1 A A 0.04095002
2 A B 0.24943935
3 A C -0.25783892
4 B A 0.15161805
5 B B 0.27189974
6 B C 0.20858897
7 C A 0.19502221
8 C B 0.56837548
9 C C -0.22682998
dplyr::summarize
は一度に1層のグループ化だけを除去するため、結果のティブルでまだいくつかのグループ化が行われていることに注意してください(後でサプライズによって人々をキャッチすることができます)。予期しないグループ化動作から完全に安全にしたい場合は、要約した後、いつでも%>% ungroup
をパイプラインに追加できます。
コードを完全に記述するために、新しい構文を使用したHadleyの回答の更新を以下に示します。
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# Columns you want to group by
grp_cols <- names(df)[-3]
# Convert character vector to list of symbols
dots <- lapply(grp_cols, as.symbol)
# Perform frequency counts
df %>%
group_by_(.dots=dots) %>%
summarise(n = n())
出力:
Source: local data frame [9 x 3]
Groups: asihckhdoydk
asihckhdoydk a30mvxigxkgh n
1 A A 10
2 A B 10
3 A C 13
4 B A 14
5 B B 10
6 B C 12
7 C A 9
8 C B 12
9 C C 10
Dplyrでのこのサポートは現在かなり弱く、最終的には構文は次のようになると思います。
df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))
しかし、それはおそらくしばらくは存在しないでしょう(私はすべての結果を熟考する必要があるため)。
それまでの間、シンボルのリストを取得するregroup()
を使用できます。
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
df %.%
regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.%
summarise(n = n())
列名の文字ベクトルがある場合は、lapply()
およびas.symbol()
を使用して適切な構造に変換できます。
vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)
df %.% regroup(vars2) %.% summarise(n = n())
dplyr
の列の文字列指定は、名前がアンダースコアで終わるdplyr
関数のバリアントを通じてサポートされるようになりました。たとえば、group_by
関数に対応して、文字列引数をとることができるgroup_by_
関数があります。 このビネット は、これらの関数の構文を詳細に説明しています。
次のスニペットは、@ sharozが最初に引き起こした問題を完全に解決します(.dots
引数を書き出す必要があることに注意してください)。
# Given data and columns from the OP
data %>%
group_by_(.dots = columns) %>%
summarise(Value = mean(value))
(dplyrは%>%
演算子を使用するようになり、%.%
は廃止されることに注意してください)。
Dplyrが文字列引数を完全にサポートするまで、おそらくこのGistは便利です。
https://Gist.github.com/skranz/9681509
文字列引数を使用するs_group_by、s_mutate、s_filterなどのラッパー関数が多数含まれています。これらを通常のdplyr関数と混合できます。例えば
cols = c("cyl","gear")
mtcars %.%
s_group_by(cols) %.%
s_summarise("avdisp=mean(disp), max(disp)") %.%
arrange(avdisp)
文字ベクトルとしてではなく、オブジェクトを渡した場合に機能します(そうではありませんが...)。
df %.%
group_by(asdfgfTgdsx, asdfk30v0ja) %.%
summarise(Value = mean(value))
> df %.%
+ group_by(asdfgfTgdsx, asdfk30v0ja) %.%
+ summarise(Value = mean(value))
Source: local data frame [9 x 3]
Groups: asdfgfTgdsx
asdfgfTgdsx asdfk30v0ja Value
1 A C 0.046538002
2 C B -0.286359899
3 B A -0.305159419
4 C A -0.004741504
5 B B 0.520126476
6 C C 0.086805492
7 B C -0.052613078
8 A A 0.368410146
9 A B 0.088462212
df
はdata
でした。
?group_by
言う:
...: variables to group by. All tbls accept variable names, some
will also accept functons of variables. Duplicated groups
will be silently dropped.
名前の文字バージョンではなく、foo$bar
でどのように参照するかを意味すると解釈します。 bar
はここでは引用されていません。または、式で変数を参照する方法:foo ~ bar
。
@Arunは、あなたができることにも言及しています:
df %.%
group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
summarise(Value = mean(value))
ただし、nvaluatedがデータオブジェクトの変数の名前ではないものを渡すことはできません。
これは、Hadleyが...
引数を介して渡したものを検索するために使用している内部メソッドによるものと推測されます。
data = data.frame(
my.a = sample(LETTERS[1:3], 100, replace=TRUE),
my.b = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value))
ここでの回答から欠落している(小さな)ケースの1つは、グループ化する変数がパイプラインの中間で動的に生成される場合です。
library(wakefield)
df_foo = r_series(rnorm, 10, 1000)
df_foo %>%
# 1. create quantized versions of base variables
mutate_each(
funs(Quantized = . > 0)
) %>%
# 2. group_by the indicator variables
group_by_(
.dots = grep("Quantized", names(.), value = TRUE)
) %>%
# 3. summarize the base variables
summarize_each(
funs(sum(., na.rm = TRUE)), contains("X_")
)
これは基本的に、grep
をgroup_by_(.dots = ...)
と組み合わせて使用してこれを実現する方法を示しています。
.dots
関数への文字ベクトル入力としてdplyr::group_by
引数を使用する一般的な例:
iris %>%
group_by(.dots ="Species") %>%
summarise(meanpetallength = mean(Petal.Length))
または、グループ化変数のハードコーディングされた名前なし(OPからの要求に応じて):
iris %>%
group_by(.dots = names(iris)[5]) %>%
summarise_at("Petal.Length", mean)
OPの例では:
data %>%
group_by(.dots =names(data)[-3]) %>%
summarise_at("value", mean)
dplyr vignette on programming も参照してください。これは、代名詞、準引用、引用、およびtidyevalを説明しています。