note.nkmk.me

pandasのagg(), aggregate()の使い方

Posted: 2020-06-18 / Tags: Python, pandas

pandas.DataFrame, Seriesagg(), aggregate()メソッドを使うと、一度に複数の処理を適用できる。agg()aggregate()のエイリアスで、どちらも同じもの。

ここでは以下の内容について説明する。

  • agg()aggregate()は同一
  • agg()の基本的な使い方
    • pandas.DataFrameの場合
    • pandas.Seriesの場合
  • agg()に指定できる処理(関数・メソッド)
    • 文字列
    • 呼び出し可能オブジェクト
      • 組み込み関数など
      • 自作の関数、ラムダ式(無名関数)
  • 対応していないデータ型dtypeに対する処理

なお、各列の主要な要約統計量(平均や標準偏差など)を一度に取得したい場合はdescribe()メソッドがある。いちいちagg()でリストを指定するより簡単。

また、agg()は、groupby(), resample(), rolling()などが返すオブジェクトのメソッドとしても提供されている。基本的な使い方・考え方は本記事の説明と同じ。具体例は以下の記事を参照。

スポンサーリンク

agg()とaggregate()は同一

冒頭に書いたように、agg()aggregate()のエイリアス。どちらを使ってもよい。

import pandas as pd
import numpy as np

print(pd.__version__)
# 1.0.0

print(pd.DataFrame.agg is pd.DataFrame.aggregate)
# True

以降のサンプルコードではagg()を使う。

agg()の基本的な使い方

pandas.DataFrameの場合

以下のpandas.DataFrameを例とする。

df = pd.DataFrame({'A': [0, 1, 2], 'B': [3, 4, 5]})
print(df)
#    A  B
# 0  0  3
# 1  1  4
# 2  2  5

agg()の引数に、適用したい処理を表す文字列や呼び出し可能オブジェクトのリストを指定する。ここでは文字列を使う。詳細は後述。

各列に各処理が適用された結果がpandas.DataFrameで返される。

print(df.agg(['sum', 'mean', 'min', 'max']))
#         A     B
# sum   3.0  12.0
# mean  1.0   4.0
# min   0.0   3.0
# max   2.0   5.0

print(type(df.agg(['sum', 'mean', 'min', 'max'])))
# <class 'pandas.core.frame.DataFrame'>

要素数が1個でもリストの場合はpandas.DataFrameが返される。リストではなく、文字列や呼び出し可能オブジェクトを単独で指定することも可能。そのときはpandas.Seriesが返される。

print(df.agg(['sum']))
#      A   B
# sum  3  12

print(type(df.agg(['sum'])))
# <class 'pandas.core.frame.DataFrame'>

print(df.agg('sum'))
# A     3
# B    12
# dtype: int64

print(type(df.agg('sum')))
# <class 'pandas.core.series.Series'>

キーkeyを列名、値valueを適用する処理とした辞書(dict)を指定すると、各列に異なる処理を適用できる。

print(df.agg({'A': ['sum', 'min', 'max'],
              'B': ['mean', 'min', 'max']}))
#         A    B
# max   2.0  5.0
# mean  NaN  4.0
# min   0.0  3.0
# sum   3.0  NaN

適用する処理をリストではなく単独で指定する場合はpandas.Seriesが返される。いずれかの列にリストを指定するとpandas.DataFrame

print(df.agg({'A': 'sum', 'B': 'mean'}))
# A    3.0
# B    4.0
# dtype: float64

print(df.agg({'A': ['sum'], 'B': ['mean']}))
#         A    B
# mean  NaN  4.0
# sum   3.0  NaN

print(df.agg({'A': ['min', 'max'], 'B': 'mean'}))
#         A    B
# max   2.0  NaN
# mean  NaN  4.0
# min   0.0  NaN

デフォルトは列ごとだが、引数axis1とすると行ごとに処理される。

print(df.agg(['sum', 'mean', 'min', 'max'], axis=1))
#    sum  mean  min  max
# 0  3.0   1.5  0.0  3.0
# 1  5.0   2.5  1.0  4.0
# 2  7.0   3.5  2.0  5.0

pandas.Seriesの場合

以下のpandas.Seriesを例とする。

s = df['A']
print(s)
# 0    0
# 1    1
# 2    2
# Name: A, dtype: int64

引数にリストを指定するとpandas.Series、単独で指定するとスカラー値が返される。

print(s.agg(['sum', 'mean', 'min', 'max']))
# sum     3.0
# mean    1.0
# min     0.0
# max     2.0
# Name: A, dtype: float64

print(type(s.agg(['sum', 'mean', 'min', 'max'])))
# <class 'pandas.core.series.Series'>

print(s.agg(['sum']))
# sum    3
# Name: A, dtype: int64

print(type(s.agg(['sum'])))
# <class 'pandas.core.series.Series'>

print(s.agg('sum'))
# 3

print(type(s.agg('sum')))
# <class 'numpy.int64'>

リストで指定した場合は結果のラベル名は処理の名前になるが、キーkeyを新たなラベル名とする辞書を指定すると任意のラベル名を設定できる。

print(s.agg({'Total': 'sum', 'Average': 'mean', 'Min': 'min', 'Max': 'max'}))
# Total      3.0
# Average    1.0
# Min        0.0
# Max        2.0
# Name: A, dtype: float64

辞書で指定する場合、値valueにリストを指定することはできない。

# print(s.agg({'NewLabel_1': ['sum', 'max'], 'NewLabel_2': ['mean', 'min']}))
# SpecificationError: nested renamer is not supported

このように、辞書を引数に指定したときの挙動はpandas.DataFramepandas.Seriesで異なるので注意。groupby(), resample(), rolling()などが返すオブジェクトからagg()を実行する場合も、元のオブジェクトがpandas.DataFramepandas.Seriesかによって異なる挙動となる。

agg()に指定できる処理(関数・メソッド)

これまでの例と同じpandas.DataFrameを例とする。

df = pd.DataFrame({'A': [0, 1, 2], 'B': [3, 4, 5]})
print(df)
#    A  B
# 0  0  3
# 1  1  4
# 2  2  5

文字列

これまでの例のように、文字列で処理を指定することが可能。

文字列は_try_aggregate_string_function()という関数でチェックされている。

ソースコードをざっと読む限り、pandas1.0.4時点では、自分自身(ourselves: この場合は列ごとに処理されるのでpandas.Series)のメソッド・属性、および、NumPyの関数と一致する文字列が有効になっている。

例えば'mad'pandas.SeriesのメソッドでNumPyの関数には存在せず、'amax'はNumPyの関数でpandas.Seriesのメソッドには存在しないが、どちらも文字列で指定可能。

あまり使い所はないかもしれないが、pandas.Seriesの属性、例えばdtypeなども指定できる。

print(df.agg(['mad', 'amax', 'dtype']))
#               A         B
# mad    0.666667  0.666667
# amax          2         5
# dtype     int64     int64

print(df['A'].mad())
# 0.6666666666666666

print(np.amax(df['A']))
# 2

print(df['A'].dtype)
# int64

関数やメソッドの引数を指定したい場合は後述のラムダ式を使う。

自分自身のメソッド・属性が先にチェックされるので、同名の処理の場合は、NumPyの関数ではなく自分自身のメソッド・属性として扱われる。

当然ながら、どちらにも当てはまらない文字列はエラーとなる。

# print(df.agg(['xxx']))
# AttributeError: 'xxx' is not a valid function for 'Series' object

# print(df.agg('xxx'))
# AttributeError: 'xxx' is not a valid function for 'DataFrame' object

自分自身の型の名前はエラーメッセージで確認できる。

AttributeError: '<文字列>' is not a valid function for '<自分自身の型の名前>' object

pandas.DataFrameからagg()を呼んだからといって「自分自身 = pandas.DataFrame」とは限らない。上の例のように、引数をリストで指定するか文字列単独で指定するかによって異なる場合もある。

また、上記の_try_aggregate_string_function()のソースコードを見ると分かるように、NumPyの関数が有効になるのは自分自身が__array__属性を持っている場合のみ。

pandas.DataFrame, Series__array__属性を持つが、groupby(), resample(), rolling()などが返すオブジェクトは__array__属性を持たない。

print(hasattr(pd.DataFrame, '__array__'))
# True

print(hasattr(pd.core.groupby.GroupBy, '__array__'))
# False

したがって、groupby(), resample(), rolling()などが返すオブジェクトのagg()メソッドでは、NumPy関数の名前の文字列は認識されない。次に示すように、呼び出し可能オブジェクト(np.xxx)の形で指定することは可能。

なお、これはpandas1.0.4での仕様。バージョンが異なると変わる場合もあるので注意。

呼び出し可能オブジェクト

組み込み関数など

組み込み関数やNumPyなどサードパーティライブラリの関数などの呼び出し可能オブジェクトも指定できる。上述の通り、pandas.DataFrame, SeriesではNumPyの関数は文字列でも指定可能。

print(df.agg([np.sum, max]))
#      A   B
# sum  3  12
# max  2   5

print(np.sum(df['A']))
# 3

print(max(df['A']))
# 2

スカラー値以外を返す関数も指定できるが、複数指定する場合はそれぞれの処理の返り値の数が揃っていないとエラーになる。

print(np.abs(df['A']))
# 0    0
# 1    1
# 2    2
# Name: A, dtype: int64

print(df.agg([np.abs]))
#          A        B
#   absolute absolute
# 0        0        3
# 1        1        4
# 2        2        5

# print(df.agg([np.abs, max]))
# ValueError: cannot combine transform and aggregation operations

自作の関数、ラムダ式(無名関数)

defで定義した自作の関数、ラムダ式(無名関数)もOK。柔軟な処理を適用できる。

def my_func(x):
    return min(x) / max(x)

print(df.agg([my_func, lambda x: min(x) / max(x)]))
#             A    B
# my_func   0.0  0.6
# <lambda>  0.0  0.6

関数やメソッドの引数を指定したい場合は以下のようにラムダ式を利用する。

print(df['A'].std())
# 1.0

print(df['A'].std(ddof=0))
# 0.816496580927726

print(df.agg(['std', lambda x: x.std(ddof=0)]))
#                  A         B
# std       1.000000  1.000000
# <lambda>  0.816497  0.816497

agg()の引数に指定した場合、処理を単独で指定した場合は適用されるが、リストで指定した場合は適用されない(何か指定方法があるのかもしれないが分からない)。

print(df.agg('std', ddof=0))
# A    0.816497
# B    0.816497
# dtype: float64

print(df.agg(['std'], ddof=0))
#        A    B
# std  1.0  1.0

対応していないデータ型dtypeに対する処理

文字列を要素とする列を追加して例とする。

df_str = df.assign(C=['X', 'Y', 'Z'])
print(df_str)
#    A  B  C
# 0  0  3  X
# 1  1  4  Y
# 2  2  5  Z

例えば、文字列を要素とするpandas.Seriesからmean()を呼ぶとエラーになるが、agg()の場合は欠損値NaNとなる。エラーにはならない。

# df_str['C'].mean()
# TypeError: Could not convert XYZ to numeric

print(df_str.agg(['sum', 'mean']))
#         A     B    C
# sum   3.0  12.0  XYZ
# mean  1.0   4.0  NaN

指定したすべての処理に対応していない場合、その列は結果から除外される。

print(df_str.agg(['mean', 'std']))
#         A    B
# mean  1.0  4.0
# std   1.0  1.0

なお、sum()min(), max()などは文字列に対しても値を返す。sum()は文字列の結合となり、min(), max()は文字コードの大小から算出される。

print(df_str.agg(['sum', 'min', 'max']))
#      A   B    C
# sum  3  12  XYZ
# min  0   3    X
# max  2   5    Z

数値の列のみを対象としたい場合は、select_dtypes()のあとでagg()を呼ぶという方法がある。

print(df_str.select_dtypes(include='number').agg(['sum', 'mean']))
#         A     B
# sum   3.0  12.0
# mean  1.0   4.0
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事