pandasで行・列ごとの最頻値を取得するmode
pandasでDataFrameやSeriesの最頻値を取得するにはmode()メソッドを使う。
ユニークな要素の数や頻度(出現回数)などを取得したい場合は以下の記事を参照。
最頻値を含む要約統計量をまとめて算出したい場合はdescribe()メソッドが便利。
本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import pandas as pd
print(pd.__version__)
# 2.1.4
pandas.Seriesのmode()
Seriesのmode()はSeriesを返す。最頻値が一つだけでもSeriesなので注意。
s = pd.Series(['X', 'X', 'X', 'Y'])
print(s)
# 0 X
# 1 X
# 2 X
# 3 Y
# dtype: object
print(s.mode())
# 0 X
# dtype: object
print(type(s.mode()))
# <class 'pandas.core.series.Series'>
print(s.mode()[0])
# X
print(type(s.mode()[0]))
# <class 'str'>
最頻値が複数ある場合は以下の通り。Seriesはtolist()メソッドでリストに変換可能。
s_multi = pd.Series(['X', 'X', 'Y', 'Y'])
print(s_multi)
# 0 X
# 1 X
# 2 Y
# 3 Y
# dtype: object
print(s_multi.mode())
# 0 X
# 1 Y
# dtype: object
print(s_multi.mode()[0])
# X
print(s_multi.mode().tolist())
# ['X', 'Y']
print(type(s_multi.mode().tolist()))
# <class 'list'>
デフォルトでは欠損値NaNは除外される。引数dropnaをFalseとするとNaNも含めて処理される。
s_nan = pd.Series(['X', float('nan'), float('nan'), float('nan')])
print(s_nan)
# 0 X
# 1 NaN
# 2 NaN
# 3 NaN
# dtype: object
print(s_nan.mode())
# 0 X
# dtype: object
print(s_nan.mode(dropna=False))
# 0 NaN
# dtype: object
pandasにおける欠損値については以下の記事を参照。
pandas.DataFrameのmode()
以下のDataFrameを例とする。
df = pd.DataFrame({'col1': ['X', 'X', 'X', 'Y'],
'col2': ['X', 'X', 'Y', 'Y']},
index=['row1', 'row2', 'row3', 'row4'])
print(df)
# col1 col2
# row1 X X
# row2 X X
# row3 X Y
# row4 Y Y
列ごとの最頻値を取得
デフォルトではDataFrameのmode()メソッドは列ごとの最頻値を要素とするDataFrameを返す。最頻値が一つだけでも一行のDataFrameが返される。
列によって最頻値の個数が異なる場合、空き部分は欠損値NaNとなる。
print(df.mode())
# col1 col2
# 0 X X
# 1 NaN Y
print(type(df.mode()))
# <class 'pandas.core.frame.DataFrame'>
各列の最頻値の個数は欠損値NaNではない要素の個数をカウントするcount()メソッドで取得できる。
print(df.mode().count())
# col1 1
# col2 2
# dtype: int64
結果のDataFrameの一行目が各列の最頻値(複数ある場合はその中の一つ)になる。一行目はiloc[0]で取得可能。
print(df.mode().iloc[0])
# col1 X
# col2 X
# Name: 0, dtype: object
DataFrameからmode()を呼んで列を選択すると欠損値NaNを含む場合があるが、先に列を選択してからSeriesとしてmode()を呼ぶと欠損値NaNは含まれない。
print(df.mode()['col1'])
# 0 X
# 1 NaN
# Name: col1, dtype: object
print(df['col1'].mode())
# 0 X
# Name: col1, dtype: object
apply()メソッドで各列からmode()を呼んでtolist()でリスト化すると、最頻値のリストlistを要素とするSeriesを取得できる。
- 関連記事: pandasで要素・行・列に関数を適用するmap, apply, applymap
- 関連記事: Pythonのlambda(ラムダ式、無名関数)の使い方
- 関連記事: pandasで任意の位置の値を取得・変更するat, iat, loc, iloc
s_list = df.apply(lambda x: x.mode().tolist())
print(s_list)
# col1 [X]
# col2 [X, Y]
# dtype: object
print(s_list.at['col2'])
# ['X', 'Y']
print(type(s_list.at['col2']))
# <class 'list'>
行ごとの最頻値を取得: 引数axis
引数axisを1または'columns'とすると行ごとの最頻値が取得できる。欠損値NaNではない要素の個数をカウントするcount()メソッドにも引数axisがある。
print(df.mode(axis=1))
# 0 1
# row1 X NaN
# row2 X NaN
# row3 X Y
# row4 Y NaN
print(df.mode(axis=1).count(axis=1))
# row1 1
# row2 1
# row3 2
# row4 1
# dtype: int64
なお、pandasでは列ごとにデータ型dtypeを持ち、基本的には列ごとに同種のデータが並んでいることを前提としている。行ごとに同種のデータが並んでいるのであれば転置したほうがいいかもしれない。
print(df.T)
# row1 row2 row3 row4
# col1 X X X Y
# col2 X X Y Y
print(df.T.mode())
# row1 row2 row3 row4
# 0 X X X Y
# 1 NaN NaN Y NaN
欠損値NaNを含めるか指定: 引数dropna
デフォルトでは欠損値NaNは除外される。引数dropnaをFalseとするとNaNも含めて処理される。
df_nan = df.copy()
df_nan.iloc[1:, 1] = float('nan')
print(df_nan)
# col1 col2
# row1 X X
# row2 X NaN
# row3 X NaN
# row4 Y NaN
print(df_nan.mode())
# col1 col2
# 0 X X
print(df_nan.mode(dropna=False))
# col1 col2
# 0 X NaN
数値列のみを対象とするか指定: 引数numeric_only
デフォルトでは数値列もその他の型の列も処理の対象となる。引数numeric_onlyをTrueとすると数値列のみが対象となる。
df_num = df.copy()
df_num['col3'] = [1, 1, 1, 0]
print(df_num)
# col1 col2 col3
# row1 X X 1
# row2 X X 1
# row3 X Y 1
# row4 Y Y 0
print(df_num.mode())
# col1 col2 col3
# 0 X X 1.0
# 1 NaN Y NaN
print(df_num.mode(numeric_only=True))
# col3
# 0 1
数値列以外のみを対象としたい場合はselect_dtypes()を使う。
print(df_num.select_dtypes(exclude='number').mode())
# col1 col2
# 0 X X
# 1 NaN Y
最頻値の頻度(出現回数)を取得
最頻値の頻度(出現回数)はSeriesのvalue_counts()メソッドで取得できる。
value_counts()は、ユニークな要素の値をインデックス(ラベル)、その個数を要素とするSeriesを返す。デフォルトでは出現回数が多い順にソートされるので、返り値のSeriesの先頭の値が最頻値の頻度となる。
df = pd.DataFrame({'col1': ['X', 'X', 'X', 'Y'],
'col2': ['X', 'X', 'Y', 'Y']},
index=['row1', 'row2', 'row3', 'row4'])
print(df)
# col1 col2
# row1 X X
# row2 X X
# row3 X Y
# row4 Y Y
print(df['col1'].value_counts())
# col1
# X 3
# Y 1
# Name: count, dtype: int64
print(df['col1'].value_counts().iat[0])
# 3
元のSeriesの要素が結果のSeriesのindexとなる。数値がindexの場合は[番号]で値を指定するとエラーになるためiat[番号]を使って厳密に指定している。上の例は文字列なので[番号]でも問題はない。
- 関連記事: pandasのインデックス指定で行・列を抽出
各列の要約統計量を算出するdescribe()メソッドでも最頻値とその頻度が求められる。
print(df.describe())
# col1 col2
# count 4 4
# unique 2 2
# top X X
# freq 3 2
項目topが最頻値でfreqがその頻度。最頻値が複数ある場合はその中の一つだけが返される。結果はDataFrameなので、locやatなどで行や要素を取得可能。
print(df.describe().loc['freq'])
# col1 3
# col2 2
# Name: freq, dtype: object
print(df.describe().at['freq', 'col2'])
# 2
describe()には引数axisはないので、行に対して適用したい場合は転置してから呼ぶ。
print(df.T.describe())
# row1 row2 row3 row4
# count 2 2 2 2
# unique 1 1 2 1
# top X X X Y
# freq 2 2 1 2
describe()についての詳細は以下の記事を参照。