pandasのagg(), aggregate()の使い方
pandas.DataFrame
, Series
のagg()
およびaggregate()
メソッドを使うと、行・列に一度に複数の処理を適用して集約できる。agg()
はaggregate()
のエイリアスで、どちらを使っても同じ。
各列の主要な要約統計量(平均や標準偏差など)を一度に取得したい場合はdescribe()
メソッドがある。いちいちagg()
でリストを指定するより簡単。
また、agg()
は、groupby()
, resample()
, rolling()
などが返すオブジェクトのメソッドとしても提供されている。基本的な使い方・考え方は本記事の説明と同じ。具体例は以下の記事を参照。
- 関連記事: pandasのgroupby()でグルーピングし統計量を算出
- 関連記事: pandasで時系列データをリサンプリングするresample, asfreq
- 関連記事: pandasで窓関数を適用する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
デフォルトは列ごとだが、引数axis
を1
または'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
"""
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
になっていた。
DataFrame
のmean()
では引数numeric_only
が実装されているが、Series
のmean()
では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