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

Modified: | Tags: Python, pandas

pandas.DataFrame, Seriesagg()およびaggregate()メソッドを使うと、行・列に一度に複数の処理を適用して集約できる。agg()aggregate()のエイリアスで、どちらを使っても同じ。

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

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

本記事のサンプルコードのpandasおよびNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

import pandas as pd
import numpy as np

print(pd.__version__)
# 2.1.2

print(np.__version__)
# 1.26.1

agg()とaggregate()は同一

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

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

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

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

agg()の基本的な使い方

pandas.DataFrameの場合

以下の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()の第一引数に、適用したい関数・メソッド名の文字列や呼び出し可能オブジェクト、またはそのリストを指定する。ここでは文字列を使う。詳細は後述。

リストを指定するとDataFrame、文字列や呼び出し可能オブジェクトを単独で指定するとSeriesが返される。要素数が1個でもリストの場合は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'>

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
# sum   3.0  NaN
# min   0.0  3.0
# max   2.0  5.0
# mean  NaN  4.0

適用する処理をリストではなく単独で指定するとSeriesが返される。いずれかの列にリストを指定すると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
# sum   3.0  NaN
# mean  NaN  4.0

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

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

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の場合

以下のSeriesを例とする。

s = pd.Series([0, 1, 2])
print(s)
# 0    0
# 1    1
# 2    2
# dtype: int64

agg()の第一引数にリストを指定するとSeries、単独で指定するとスカラー値が返される。要素数1個でもリストならSeries

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

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

print(s.agg(['sum']))
# sum    3
# 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
# dtype: float64

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

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

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

関数・メソッド名の文字列

agg()の第一引数に指定された文字列は_apply_str()という関数でチェックされる。以前は_try_aggregate_string_function()という名前だった。

    def _apply_str(self, obj, func: str, *args, **kwargs):
        """
        if arg is a string, then try to operate on it:
        - try to find a function (or attribute) on obj
        - try to find a numpy function
        - raise
        """
source: apply.py

obj(ここではSeriesまたはDataFrame)のメソッド・属性、および、NumPyの関数と一致する文字列が有効となる。

例えば'count'SeriesおよびDataFrameのメソッドでNumPyの関数には存在せず、'amax'はNumPyの関数でSeriesおよび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

print(df.agg(['count', 'amax']))
#        A  B
# count  3  3
# amax   2  5

print(df['A'].count())
# 3

# print(np.count(df['A']))
# AttributeError: module 'numpy' has no attribute 'count'

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

# print(df['A'].amax())
# AttributeError: 'Series' object has no attribute 'amax'

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

# 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

上のエラーメッセージから分かるように、リストで指定した場合はSeries、文字列単独で指定した場合はDataFrameのメソッド・属性が使われる。

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

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)の形で指定することは可能。

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

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

agg()の第一引数には、defで定義した関数やラムダ式(無名関数)などの呼び出し可能オブジェクトも指定できる。

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

def my_func(x):
    return x.min() + x.max()

print(df.agg([my_func, lambda x: x.min() - x.max()]))
#           A  B
# my_func   2  8
# <lambda> -2 -2

関数やメソッドに引数を指定

agg()に指定したキーワード引数は適用する関数やメソッドに渡される。

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

print(df.agg('std'))
# A    1.0
# B    1.0
# dtype: float64

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

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

複数の関数やメソッドを指定した場合は、そのすべてにキーワード引数が渡される。受け取れない場合はエラーとなる。

# print(df.agg(['max', 'std'], ddof=0))
# TypeError: max() got an unexpected keyword argument 'ddof'

個別に引数を指定したい場合はラムダ式を利用する。

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

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

文字列を要素とする列を含むDataFrameを例とする。

df_str = pd.DataFrame({'A': [0, 1, 2], 'B': [3, 4, 5], 'C': ['X', 'Y', 'Z']})
print(df_str)
#    A  B  C
# 0  0  3  X
# 1  1  4  Y
# 2  2  5  Z

例えば、文字列を要素とするSeriesからmean()を呼ぶとエラーになるため、agg()に指定してもエラーになる。

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

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

なお、これはpandas2.1.2での仕様。pandas1.0.4ではエラーにならずにNaNになっていた。

DataFramemean()では引数numeric_onlyが実装されているが、Seriesmean()ではnumeric_onlyが実装されていないので、リストで指定した場合(Seriesとして処理される場合)は、numeric_onlyは使えない。

print(df_str.mean(numeric_only=True))
# A    1.0
# B    4.0
# dtype: float64

print(df_str.agg('mean', numeric_only=True))
# A    1.0
# B    4.0
# dtype: float64

# df_str['C'].mean(numeric_only=True)
# TypeError: Series.mean does not allow numeric_only=True with non-numeric dtypes.

# print(df_str.agg(['mean'], numeric_only=True))
# TypeError: Series.mean does not allow numeric_only=True with non-numeric dtypes.

数値の列のみを対象としたい場合は、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

関連カテゴリー

関連記事