pandasで行・列ごとの最頻値を取得するmode

Modified: | Tags: Python, pandas

pandasでDataFrameSeriesの最頻値を取得するにはmode()メソッドを使う。

ユニークな要素の数や頻度(出現回数)などを取得したい場合は以下の記事を参照。

最頻値を含む要約統計量をまとめて算出したい場合はdescribe()メソッドが便利。

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

import pandas as pd

print(pd.__version__)
# 2.1.4

pandas.Seriesのmode()

Seriesmode()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'>

最頻値が複数ある場合は以下の通り。Seriestolist()メソッドでリストに変換可能。

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は除外される。引数dropnaFalseとすると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

列ごとの最頻値を取得

デフォルトではDataFramemode()メソッドは列ごとの最頻値を要素とする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を取得できる。

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

引数axis1または'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は除外される。引数dropnaFalseとすると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_onlyTrueとすると数値列のみが対象となる。

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

最頻値の頻度(出現回数)を取得

最頻値の頻度(出現回数)はSeriesvalue_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の要素が結果のSeriesindexとなる。数値がindexの場合は[番号]で値を指定するとエラーになるためiat[番号]を使って厳密に指定している。上の例は文字列なので[番号]でも問題はない。

各列の要約統計量を算出するdescribe()メソッドでも最頻値とその頻度が求められる。

print(df.describe())
#        col1 col2
# count     4    4
# unique    2    2
# top       X    X
# freq      3    2

項目topが最頻値でfreqがその頻度。最頻値が複数ある場合はその中の一つだけが返される。結果はDataFrameなので、locatなどで行や要素を取得可能。

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()についての詳細は以下の記事を参照。

関連カテゴリー

関連記事