Help us understand the problem. What is going on with this article?

Kaggleで使える!Pandasテクニック集

More than 1 year has passed since last update.

はじめに

Kaggleで使えるPandasの使い方を備忘録として残します。
随時更新していく予定です。

更新:2019年1月29日15時 様々なコンペで使える便利な関数を追記しました。

Pandas Basics Cheat Sheet(基本的な使い方)

pandas_cheat_sheet.png

Pandas_Cheat_Sheeta.png

[引用]https://becominghuman.ai/cheat-sheets-for-ai-neural-networks-machine-learning-deep-learning-big-data-678c51b4b463

プログレスバーを表示する

df.apply()やdf.map()の進捗を見たい時に、
tqdmというライブラリを使うことでプログレスバーを表示することができます。

実装例
import pandas as pd
import numpy as np
from tqdm._tqdm_notebook import tqdm_notebook

df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))

# プログレスバーのラベル設定
tqdm_notebook.pandas(desc="hoge progress: ")

# progress_apply/progress_mapはpandasのapply/mapメソッドと同じ動きをする
df['hoge'] = df[0].progress_apply(lambda x: x**2)

df

実行結果

2019-01-16_113242.png

実装例(その2ラベル設定なし)
import pandas as pd
import numpy as np
from tqdm import tqdm
tqdm.pandas()

df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))

# progress_apply/progress_mapはpandasのapply/mapメソッドと同じ動きをする
df['hoge'] = df[0].progress_apply(lambda x: x**2)

df

実行結果

2019-01-16_113410.png

様々なコンペで使える便利な関数

int型、float型のカラムに対し、最大値・最小値の幅などを判定し、最適なデータ型に変換してくれる関数です。

最初に書かれたのはいつかわかりませんが、2019年1月29日現在Kaggleで開催されている、
「Elo Merchant Category Recommendation」や「Microsoft Malware Prediction」コンペのKernelで使われています。

実装例
import pandas as pd
import numpy as np

def reduce_mem_usage(df, verbose=True):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    start_mem = df.memory_usage(deep=True).sum() / 1024**2    
    for col in df.columns:
        col_type = df[col].dtypes
        if col_type in numerics:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)    
    end_mem = df.memory_usage(deep=True).sum() / 1024**2
    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
    return df

#使用する時は、以下のようにtrain/testデータフレームを引数に渡すだけ
train = reduce_mem_usage(train)
# 出力結果 →  Mem. usage decreased to 1923.79 Mb (0.0% reduction)

CSVにJson形式のカラムがある場合の読み込み方

Kaggleの「Google Analytics Customer Revenue Prediction」というコンペのデータセットには
Json形式のデータが含まれています。
そのようなデータへの対応方法はいくつかありますが、そのうちの一つを紹介します。

参考にしたKernelは以下です。元からコメントで細かく説明があるため、それを日本語訳しています。
https://www.kaggle.com/kabure/simple-eda-exploring-the-consumer-patterns

実装例
import os # it's a operational system library, to set some informations
import random # random is to generate random values

import pandas as pd # to manipulate data frames 
import numpy as np # to work with matrix

import json # to convert json in df
from pandas.io.json import json_normalize # to normalize the json file


columns = ['device', 'geoNetwork', 'totals', 'trafficSource'] # json形式のカラムを設定

dir_path = "../input/" # CSVファイルの格納されているパスを指定

# ファイルの先頭スキップする行数のセット 
p = 0.1 # *** ここではデータセットの50%を使う *** #

# Json形式をテーブルに変換する関数
def json_read(df):
    data_frame = dir_path + df

    #CSVを読みこむ
    df = pd.read_csv(data_frame, 
                     converters={column: json.loads for column in columns}, # Json形式のカラムを正しく読み込む
                     dtype={'fullVisitorId': 'str'}, # IDのカラムは0落ちを防ぐため、stringで読みこむ
                     skiprows=lambda i: i>0 and random.random() > p)# ランダムにインポートされる行数

    for column in columns: #データフレームに変換
        # Jsonからテーブルに標準化する
        column_as_df = json_normalize(df[column]) 
        # Jsonカラムのカテゴリとサブカテゴリを設定する
        column_as_df.columns = [f"{column}.{subcolumn}" for subcolumn in column_as_df.columns] 
        # 値を抽出後、オリジナルのカラムを削除
        df = df.drop(column, axis=1).merge(column_as_df, right_index=True, left_index=True)

    # データの行数と列数をプリント     
    print(f"Loaded {os.path.basename(data_frame)}. Shape: {df.shape}")
    return df # 完成したデータフレームを返却

%%time 
# %%time はコードの実行タイミングの計算をします。

df_train = json_read("train.csv") 

結果

Loaded train.csv. Shape: (90396, 54)
CPU times: user 19.3 s, sys: 1.2 s, total: 20.5 s
Wall time: 21.1 s

大量データを読み込む場合の対処法

Kaggleのコンペのデータセット(CSVの場合)にはかなりの容量がある場合があります。
このような膨大データはデータタイプを指定すると問題なく読み込めます。

実装例
# 一部のみを読み込み、各データ型を調べます。
train_df.info()

#調べたデータ型を定義します。
#実際は下記よりもカラムがありますが、省略しています。
dtypes = {
        'MachineIdentifier':                                    'category',
        'ProductName':                                          'category',
        'EngineVersion':                                        'category',
        'AppVersion':                                           'category',
        'AvSigVersion':                                         'category',
        'IsBeta':                                               'int8',
        'RtpStateBitfield':                                     'float16'
    }

#その後データ型を指定し読み込む
train_df = pd.read_csv('../input/train.csv', dtype=dtypes)

時系列データ操作

formatの主な例は以下の通りです。

書式 説明
%Y 4桁の年
%y 2桁の年
%m 2桁の月[01,02,...,12]
%d 日[2桁]
%H 時間[24時間]
%I 時間[12時間]
%M
%S
%z タイムゾーン UTCのずれの分[+HHMM]
%F %Y-%m-%d のalias
%f sub-second [0.000 sec]
実装例
train.date = pd.to_datetime(train.date, format="%Y%m%d")

to_datetimeのオプションについて

unit='s'

秒まで出力します

実装例
train.date = pd.to_datetime(train.date, unit='s')

日付け変換

Series.dt()で年月日を抽出する

抽出の種類

Datetime Properties 説明
Series.dt.date datetime.dateオブジェクトのnumpy配列を返します
Series.dt.time datetime.timeのnumpy配列を返します
Series.dt.year 日時の年
Series.dt.month 月は1月= 1、12月= 12
Series.dt.day 日時の日数
Series.dt.hour 日時の時間
Series.dt.minute 日時の分
Series.dt.second 日時の秒数
Series.dt.microsecond マイクロ秒
Series.dt.nanosecond ナノ秒
Series.dt.week 年の序数
Series.dt.weekofyear 今年の序数
Series.dt.dayofweek 月曜日= 0、日曜日= 6の曜日
Series.dt.weekday Monday = 0、Sunday = 6の曜日
Series.dt.weekday_name 週の名前(例:金曜日)
Series.dt.dayofyear 年の順番の日
Series.dt.quarter 日付の四半期
Series.dt.is_month_start 月の最初の日かどうかを示す論理値
Series.dt.is_month_end 月の最終日
Series.dt.is_quarter_start 四半期の初めの日かどうかを示す論理値
Series.dt.is_quarter_end 四半期の最終日かどうかを示す論理値。
Series.dt.is_year_start 年の最初の日かどうかを示す論理値。
Series.dt.is_year_end 最後の年かどうかを示す論理値。
Series.dt.is_leap_year 日付がうるう年に属するかどうかを示す論理的
Series.dt.daysinmonth 月の日数
Series.dt.days_in_month 今月の日数

公式ドキュメントpandas.pydata.org

実装例
print(df)
#       date       value
# 0 2016/5/1 0:03    45
# 1 2017/9/1 0:04    4
# 2 2016/3/1 0:06    23
# 3 2018/4/1 0:06    12
# 4 2017/7/12 0:06   78
# 5 2018/1/12 0:07   33
# 6 2018/2/1 0:10    55
# 7 2018/3/1 0:12    32
# 8 2018/4/1 0:13    76
日にちまで抽出する場合
df['date'] = df['date'].dt.date
print(df)

#       date    value
# 0 2016/5/1     45
# 1 2017/9/1     4
# 2 2016/3/1     23
# 3 2018/4/1     12
# 4 2017/7/12    78
# 5 2018/1/12    33
# 6 2018/2/1     55
# 7 2018/3/1     32
# 8 2018/4/1     76

Series.dt.dayofweekを使うと、月曜日= 0、日曜日= 6にして返す。

df['date'] = df['date'].dt.dayofweek
print(df)

#     date  value
# 0   6.0    45
# 1   4.0    4
# 2   1.0    23
# 3   6.0    12
# 4   2.0    78
# 5   4.0    33
# 6   3.0    55
# 7   3.0    32
# 8   6.0    76

重複データの調べ方

実装例
ids_train = set(train.fullVisitorId.unique())
ids_test = set(test.fullVisitorId.unique())
print("Unique visitor ids in train:", len(ids_train))
print("Unique visitor ids in test:", len(ids_test))
print("Common visitors in train and test:", len(ids_train & ids_test))
結果
Unique visitor ids in train: 714167
Unique visitor ids in test: 617242
Common visitors in train and test: 7679

カラムの違いの調べ方

実装例
print(train_df.columns.difference(test_df.columns))
print(test_df.columns.difference(train_df.columns))
結果
Index(['trafficSource.campaignCode'], dtype='object')
Index([], dtype='object')

データ変換

melt()

  • id_vars= 基本軸となるカラムを指定
  • var_name= 変数(Variable)となるカラムに名前を付ける

※値(Value)となるカラムに名前を付けることも可能(value_nameを引数に指定)

実装例
>>> import pandas as pd
>>> df = pd.DataFrame({'A': {0: 'a', 1: 'b', 2: 'c'},
...                    'B': {0: 1, 1: 3, 2: 5},
...                    'C': {0: 2, 1: 4, 2: 6}})
>>> df
   A  B  C
0  a  1  2
1  b  3  4
2  c  5  6
>>> pd.melt(df, id_vars=['A'], value_vars=['B'])
   A variable  value
0  a        B      1
1  b        B      3
2  c        B      5
>>> pd.melt(df, id_vars=['A'], value_vars=['B', 'C'])
   A variable  value
0  a        B      1
1  b        B      3
2  c        B      5
3  a        C      2
4  b        C      4
5  c        C      6

Seriesの各要素に適用: map()

実装例
>>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
>>> x
one      1
two      2
three    3
dtype: int64
>>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
>>> y
1    foo
2    bar
3    baz
>>> x.map(y)
one   foo
two   bar
three baz

map()の引数に辞書dict({key: value})を指定すると、keyと一致する要素がvalueに置き換えられる。

map()では置換されない値がNaNとなる。

DataFrameの各要素に適用: applymap()

map()と同じく、Pythonの組み込み関数、無名関数(lambda)やdefで定義した関数をapplymap()の引数に渡す。

実装例
df = pd.DataFrame({'a':[11, 21, 31],
                  'b':[12, 22, 32],
                  'c':[13, 23, 33],
                  'd':[14, 24, 34]})

f_oddeven = lambda x: 'odd' if x % 2 == 1 else 'even'
print(df.applymap(f_oddeven))
     a     b    c     d
0  odd  even  odd  even
1  odd  even  odd  even
2  odd  even  odd  even

DataFrame, Seriesの各行・各列に適用: apply()

一次元配列に適用可能な関数をapply()の引数に渡す。
デフォルトでは各列に対して適用され、引数axis=1とすると各行に対して適用される。

実装例
f_maxmin = lambda x: max(x) - min(x)
print(df.apply(f_maxmin))

print(df.apply(f_maxmin, axis=1))
a    20
b    20
c    20
d    20
dtype: int64
0    3
1    3
2    3
dtype: int64

実装例(応用)

文字列がたのデータに対し、「%」をなくし、float型に変換する。

python
df["Bounce Rate"] = df["Bounce Rate"].astype(str).apply(lambda x: x.replace('%', '')).astype(float)

データを行・列(縦・横)方向にずらす: shift()

実装例
import pandas as pd

df = pd.DataFrame({'a': range(1, 6),
                   'b': [x**2 for x in range(1, 6)],
                   'c': [x**3 for x in range(1, 6)]})

print(df)
#    a   b    c
# 0  1   1    1
# 1  2   4    8
# 2  3   9   27
# 3  4  16   64
# 4  5  25  125

デフォルトでは下方向に1行ずれる。

Python
print(df.shift())
#      a     b     c
# 0  NaN   NaN   NaN
# 1  1.0   1.0   1.0
# 2  2.0   4.0   8.0
# 3  3.0   9.0  27.0
# 4  4.0  16.0  64.0

負の値の場合は上方向にずれる

Python
print(df.shift(-1))
#      a     b      c
# 0  2.0   4.0    8.0
# 1  3.0   9.0   27.0
# 2  4.0  16.0   64.0
# 3  5.0  25.0  125.0
# 4  NaN   NaN    NaN

データサイズの測り方

shape()

shapeでデータのサイズを測ることができる。
引数なしではデータセット全体の行数と列数を、
引数に0を指定すると行数、1を指定すると列数を数える。

実装例
import pandas as pd

sample = pd.DataFrame({'A':[1,2],
                       'B':[3,4],
                       'C':[5,6]})

# 以下のようなデータフレーム
#   A B C
# 0 1 3 5
# 1 2 4 6

sample.shape     # 全ての行と列の数を数える → (2,3)
sample.shape[0]  # 0番目の行数(インデックスの数)を数える → (2)
sample.shape[1]  # 列数を数える → (3)
R1ck29
AIベンチャーで機械学習エンジニアやってます。 Kaggle関連の投稿が多いです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away