やましなぶろぐ

Pandas Tips

Pandasでデータ分析に関連するデータ加工Tips

🕒 Last mod: 2020-09-25


pandasでのデータ加工についてのTipsをご紹介します。

1. Cardinalityを調べる : 要素をユニークカウントすることで各変数のCardinalityを調べる

変数の情報で要素数(Cardinality)は調べて置くことは大事。

調べる方法については別記事にて詳細を公開しています。

記事へのリンク
Example.py
data = pd.read_csv(
  'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv'
)
print(data.head())
data.nunique()

#  output
# sepal_length    35
# sepal_width     23
# petal_length    43
# petal_width     22
# species          3
# dtype: int64

2. 列選択:pandas.DataFrame.filter

使用する機会が多い列選択。選択肢が多い処理だけどfilterを使用する方法です。

別記事にて詳細を公開しています。

記事へのリンク
Example.py
import pandas as pd


df = pd.DataFrame(
    dict(
        value_mean=range(1, 10),
        value_min=range(11, 20),
        value_max=range(21, 30),
        value_sum=range(31, 40),
    )
)
df.filter(['value_min', 'value_max'])
df.filter(like='value_m')
df.filter(regex='n$')

3. 値の置換、 dplyr::recodeと同等の処理を実現したい

頻繁に使用する値の置換もpandasでは直感的には難しい。

dplyr::recodeと同等の処理を実現したい。

用途に応じてreplaceとmapを使い分けることが必要です。

3.1. mapとreplaceの使い方

様々な使い方がありますが、値の置換だけなら第一引数に辞書型を指定することで置換できる。

3.2. replaceとmapの違い

replaceは辞書型で指定された置換ルールに該当しない場合は元々の値がそのまま。

一方mapはルールに該当しない場合は欠損値(NaN)となる。

値の置換では特定の値は別の値に置換してその他の値はその他としてまとめたいことがある。dplr::recodeでは.default引数で実現できる。pandasでは.defaultのような処理をする方法が見つからなかったのでmapとfillnaを使用して実現する。

replaceとmapの使い分けは直感的には理解できないし面倒。mapというメソッド名も実行したい処理と乖離している。この辺りの処理の設計はやはりdplrが優れていると感じる。

3.3. 実例

Example.py
import pandas as pd


df = pd.DataFrame(
    dict(
        value=range(1, 10),
    )
)

after = df.assign(
    by_map=lambda x: (
        x.value.map(
            {1: 1111}
        ).fillna(0)
    ),
    by_replace=lambda x: (
        x.value.replace(
            {1: 1111}
        )
    ),
)
処理結果:after

value

by_map

by_replace

1

1111.0

1111

2

0.0

2

3

0.0

3

4

0.0

4

5

0.0

5

6

0.0

6

7

0.0

7

8

0.0

8

9

0.0

9

4. 条件に応じて値を置換:case 文のような処理を実現する。

この処理も地味に実現方法に悩みます。検索してもなかなか簡便な方法がみつかりません。

4.1. lambda を使用する方法:コードが複雑なので非推奨

いろいろ試した結果、下記のapplyとif else を使用する方法に行き着きました。

whereやmaskを使用する方法もあるのですがif elseを複数重ねて使用するには下記の方法がシンプルです。

Example.py
import pandas as pd


df = pd.DataFrame(
    dict(
        value=range(1, 10),
    )
)
elseif_after = df.assign(
    after=lambda x: (
        x.value.apply(
            lambda x:
            1 if x > 8 or x < 3 else
            2 if x > 6 else
            3 if x > 4 else 4
        )
    ),
)
処理結果:elseif_after

value

after

1

1

2

1

3

4

4

4

5

3

6

3

7

2

8

2

9

1

4.2. リスト内包表記を使用する方法

lambda関数を使用する方法が後から見返してみると複雑で可読性が低い。 また、条件として2変数を使用する場合の対応が難しい。

ですので新たな方法を模索しました。

結果、リスト内包表記を使用する方法を思いつきました。

Example.py
import pandas as pd


df = pd.DataFrame(
    dict(
        value1=range(1, 10),
        value2=range(11, 20),
    )
)
after = df.assign(
    after=[
        1 if v1 > 8 or v2 < 13 else
        2 if v1 > 5 or v2 < 14 else
        3
        for v1, v2 in zip(
            df.value1,
            df.value2,
        )
    ]
)
print(after)
output
   value1  value2  after
   0       1      11      1
   1       2      12      1
   2       3      13      2
   3       4      14      3
   4       5      15      3
   5       6      16      2
   6       7      17      2
   7       8      18      2
   8       9      19      1

リスト内包表記を使用したほうが簡単なのでおすすめです。

5. 欠損値の件数と比率を確認する

データ・セットの初見では欠損値の状況が気になります。

欠損値の件数と比率を確認します。

5.1. コード

データ・セットに含まれる変数全部を確認するためにapplyを使用します。

欠損値件数を確認するために.isna().sum()を使用します。

比率を確認するために.isna().mean()を使用します。

Example.py
## count missing values
missing_count=X.apply(
    lambda x: (
        x.isna().sum()
    )
)

## proportion missing values
missing_proportion = X.apply(
    lambda x: (
        x.isna().mean()
    )
)

5.2. 例:notebook

実行例