PandasはDataFrame(データフレーム)で二次元の表形式データが基本となっています。
単なる表データではなく、データベースに近い仕組みを持っていると考えた方がいいです。
ということはとても便利だということです。
まずは、データベースに近いという認識を持って対応すると勉強がはかどります。
(データベースを使ったことがない方には申し訳ないですが。。。
特徴
- 異なるデータ型の要素を格納可能
- 長さ(要素数)が可変
- 要素の書き換えが可能
- インデックス(番号)で要素にアクセスが可能
- スライスが可能
- 二次元、多次元配列が可能
- 標準ライブラリのインポートが必要
データフレーム(データ)の作成
pandas
という標準ライブラリを使用します。標準ですのでインストールは不要です。
import pandas as pd # インポートする必要がある df = pd.DataFrame([[1,2,3],[4,5,6]]) # リスト型のデータで作成 print(df.iat[0, 0]) # 1と表示される print(df) # 0 1 2 # 0 1 2 3 # 1 4 5 6 # 列名をつけることが可能 df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"]) print(df) # 0 1 2 # 0 1 2 3 # 1 4 5 6 # 列名をつけることが可能 df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1 2 3 # 1 4 5 6 # 行名(index)をつけることも可能 df = pd.DataFrame([[1,2,3],[4,5,6]], index = ["aaa", "bbb"]) print(df) # 0 1 2 # aaa 1 2 3 # bbb 4 5 6 # 列名と行名(index)をつけることも可能 df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["aaa", "bbb"]) print(df) # aaa bbb ccc # aaa 1 2 3 # bbb 4 5 6
データフレームのコピー(複製、代入)
データフレームはイコール(=)でコピー(複製、代入)すると、参照渡しになります。
参照渡しとは、コピーしたデータフレームを変更すると、元のデータフレームも変更されるということです。
ついつい、忘れてバグりますので注意しましょう。
参照渡しではなく、普通の変数のようにコピー(複製、代入)する場合は、以下のようにcopy()
を付けます。
df_temp = df.copy()
このようにすれば、df_tempを変更しても、dfに影響はありません。
データ型
データ型の確認
# 一括で確認 df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"]) print(df.dtypes) # aaa int64 # bbb int64 # ccc int64 # dtype: object # 列毎に確認 print(df["aaa"].dtypes) # int64となる print(df.aaa.dtypes) # int64となる
df[“aaa”]とdf.aaaはどちらでも大丈夫です。
主なデータ型
Pythonの型 | dtypeの表示 |
---|---|
int | int64 |
float | float64 |
str | object |
bool | bool |
datetime | datetime64 |
データ型の変更
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"]) # 整数から文字列 df['aaa'] = df['aaa'].astype(str) print(df.aaa.dtypes) # object # 文字列から整数 df['aaa'] = df['aaa'].astype(int) print(df.aaa.dtypes) # int64 # 整数から浮動小数点 df['aaa'] = df['aaa'].astype(float) print(df.aaa.dtypes) # float64 # 浮動小数点から文字列 df['aaa'] = df['aaa'].astype(str) print(df.aaa.dtypes) # object # 文字列から浮動小数点 df['aaa'] = df['aaa'].astype(float) print(df.aaa.dtypes) # float64 # 文字列から日時(または日付 df = pd.DataFrame([["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False]], columns = ["Date Time", "bbb", "ccc"]) df['Date Time'] = pd.to_datetime(df['Date Time']) print(df['Date Time'].dtypes) # datetime64[ns]
np.floatなどとnp=Numpyですので、Numpyのインポートを忘れないようにしてください。
タイムゾーンを設定と変更
- タイムゾーンの設定
df.index.tz_localize('Asia/Tokyo')
のように設定します。- この時、日時データはindexである必要があります。
- タイムゾーンが設定されているindexに対して
tz_localize
は使用できません。
- タイムゾーンの変更
df.index.tz_convert('UTC')
のように設定します。- この時、日時データはindexである必要があります。
- タイムゾーンが設定されていないindexに対して
tz_convert
は使用できません。
df = pd.DataFrame([["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False]], columns = ["Date Time", "bbb", "ccc"]) # リスト型のデータで作成 df.index = pd.DatetimeIndex(pd.to_datetime(df['Date Time']), name='Date Time') del df['Date Time'] print(df) # bbb ccc # Date Time # 2020-05-01 12:34:56 1 True # 2020-05-01 16:34:56 4 False df.index = df.index.tz_localize('Asia/Tokyo') print(df) # bbb ccc # Date Time # 2020-05-01 12:34:56+09:00 1 True # 2020-05-01 16:34:56+09:00 4 False df.index = df.index.tz_convert('UTC') print(df) # bbb ccc # Date Time # 2020-05-01 03:34:56+00:00 1 True # 2020-05-01 07:34:56+00:00 4 False
タイムスタンプの変換
日時(DITETIME)をタイプスタンプ(UNIX TIME STAMP)に変換
タイプスタンプ(UNIX TIME STAMP)を日時(DITETIME)に変換
df = pd.DataFrame([["1493530261",1,True],["1588304096",4,False]], columns = ["timestamp", "bbb", "ccc"]) print(df) # timestamp bbb ccc # 0 1493530261 1 True # 1 1588304096 4 False df["datetime"] = pd.to_datetime(df.timestamp,unit='s') print(df) # timestamp bbb ccc datetime # 0 1493530261 1 True 2017-04-30 05:31:01 # 1 1588304096 4 False 2020-05-01 03:34:56
10桁の場合は
unit='s'
、13桁の場合はunit='ms'
とします。それぞれ桁数と指定があっていないとエラーになります。フォーマット(形式)の変換・変更
カンマ区切りに変換
データフレーム全体を変換する際と、個別のカラム(列)を変換する場合は、同じようなやり方ですが、関数がことなりますので注意してください。
- データフレーム全体(DataFrame):
applymap
- 個別のカラム(列)(Series):
map
import pandas as pd # データフレーム全体 df = pd.DataFrame([[1000,1100,1200],[2000,2100,2200]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1000 1100 1200 # 1 2000 2100 2200 df = df.applymap('{:,}'.format) print(df) # aaa bbb ccc # 0 1,000 1,100 1,200 # 1 2,000 2,100 2,200 # 個別のカラム(列) df = pd.DataFrame([[1000,1100,1200],[2000,2100,2200]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1000 1100 1200 # 1 2000 2100 2200 df["aaa"] = df["aaa"].map('{:,}'.format) print(df) # aaa bbb ccc # 0 1,000 1100 1200 # 1 2,000 2100 2200
小数点の桁数を揃える
こちらも以下を使用します。
- データフレーム全体(DataFrame):
applymap
- 個別のカラム(列)(Series):
map
小数点以下の桁数は以下のように変換されます。
- 小数点以下の桁数が指定より多い場合は、四捨五入ではなく偶数への丸めになる。
- 小数点以下の桁数が指定より少ない場合は、ゼロパディング(ゼロ埋め)になる。
import pandas as pd # データフレーム全体 df = pd.DataFrame([[1.234,2.345,3.456],[4.567,7.89,8.9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1.234 2.345 3.456 # 1 4.567 7.890 8.900 df = df.applymap('{:.2f}'.format) print(df) # aaa bbb ccc # 0 1.23 2.35 3.46 # 1 4.57 7.89 8.90 # 個別のカラム(列) df = pd.DataFrame([[1.234,2.345,3.456],[4.567,7.89,8.9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1.234 2.345 3.456 # 1 4.567 7.890 8.900 df["aaa"] = df["aaa"].map('{:.2f}'.format) print(df) # aaa bbb ccc # 0 1.23 2.345 3.456 # 1 4.57 7.890 8.900
カンマ区切りと小数点の桁を揃える
それぞれのやり方をあわせます。
'{:,}'
+ '{:.2f}'
= '{:,.2f}'
# 個別のカラム(列) df = pd.DataFrame([[1000.234,2000.345,3.456],[4000.567,7000.89,8000.9]], columns = ["aaa", "bbb", "ccc"]) df["aaa"] = df["aaa"].map('{:,.2f}'.format) print(df) # aaa bbb ccc # 0 1,000.23 2000.345 3.456 # 1 4,000.57 7000.890 8000.900
パーセントに変換
こちらも以下を使用します。
- データフレーム全体(DataFrame):
applymap
- 個別のカラム(列)(Series):
map
import pandas as pd # データフレーム全体 df = pd.DataFrame([[0.234,0.345,0.456],[0.567,0.89,0.9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 0.234 0.345 0.456 # 1 0.567 0.890 0.900 df = df.applymap('{:.1%}'.format) print(df) # aaa bbb ccc # 0 23.4% 34.5% 45.6% # 1 56.7% 89.0% 90.0% # 個別のカラム(列) df = pd.DataFrame([[0.234,0.345,0.456],[0.567,0.89,0.9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 0.234 0.345 0.456 # 1 0.567 0.890 0.900 df["aaa"] = df["aaa"].map('{:.1%}'.format) print(df) # aaa bbb ccc # 0 23.4% 0.345 0.456 # 1 56.7% 0.890 0.900
日時データの形式(フォーマット)を変更
日時型のデータを任意のフォーマットに変換することができます。変換後は文字型になります。
df = pd.DataFrame([["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False]], columns = ["Date Time", "bbb", "ccc"]) df['Date Time'] = pd.to_datetime(df['Date Time']) print(df) # Date Time bbb ccc # 0 2020-05-01 12:34:56 1 True # 1 2020-05-01 16:34:56 4 False df['Date Time'] = df['Date Time'].dt.strftime('%Y年%m月%d日') print(df) # Date Time bbb ccc # 0 2020年05月01日 1 True # 1 2020年05月01日 4 False
dt
はPandasのdtです。datetime
ではありません。値を取得
スライスを用いて値を取得できますが、それ以外の方法となります。
任意の位置の値を取得
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2"]) # ラベルで指定 print(df.at['val1', 'aaa']) # 1 # 行番で指定 print(df.iat[1, 0]) # 4 # 行番とラベルで指定 print(df.iloc[0]['aaa']) # 1
at
は行ラベル、列ラベルを指定する。iat
は行番号、列番号を指定する。
at
とiat
は行ラベルと列番号、行番号と列ラベルの組み合わせはできません。iloc
でも行ラベルと列番号の組み合わせができません。ラベルのみ、番号のみで指定の場合、
loc
、iloc
でも取得できますが、at
やiat
の方が驚くほど早いです。行毎に値を取得
この場合は、at
やiat
は使えず、loc
とiloc
となります。
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2"]) # ラベルで指定 print(df.loc['val1']) # aaa 1 # bbb 2 # ccc 3 # Name: val1, dtype: int64 # 行番で指定 print(df.iloc[1]) # aaa 4 # bbb 5 # ccc 6 # Name: val2, dtype: int64
出力形式が少し難解に見えますが、以下のように取得したデータを利用できます。
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2"]) val1 = df.loc['val1'] print(val1[1]) # 2 print(val1["bbb"]) # 2
個数(有無
全ての個数
df = pd.DataFrame([[1,2,3],[4,None,6],[None,None,None]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2", "val3"]) # すべての列の個数(空を除く print(df.count()) # aaa 2 # bbb 1 # ccc 2 # dtype: int64 # 指定した列の個数(空を除く print(df.aaa.count()) # 2 # すべての行数と列数 print(df.shape) # (3, 3) # すべての行数 print(len(df)) # 3 # すべての要素数 print(df.size) # 9
count
を使用すると空は含まれませんので注意しましょう。条件を満たす行の個数
df = pd.DataFrame([[1,2,3],[4,None,6],[None,None,None]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2", "val3"]) print(len(df[df.aaa >= 2])) # 1
len
で個数を出力します。これが一番簡単だと思います。Nan以外の個数
df = pd.DataFrame([[1,2,3],[4,np.nan,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 NaN 6 # 2 7 8.0 9 print(df[df['bbb'].isna() == False].count()) # aaa 1 # bbb 0 # ccc 1 # dtype: int64 print(df[df['bbb'].isna() == False].bbb.count()) # 2
Pandasでは、inf(無限大)という考え方は基本的にないです。ディープラーニングなどで必要な場合はNumpyに変換して使った方がいいと思います。
値の変更
スライスを用いて値を変更できますが、それ以外の方法となります。
任意の位置の値を変更
要素のアクセスと同じです。
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2"]) # ラベルで指定 df.at['val1', 'aaa'] = 7 print(df.at['val1', 'aaa']) # 7 # 行番で指定 df.iat[1, 0] = 8 print(df.iat[1, 0]) # 8
行毎に値を変更
at
を用いて既存の行ラベル(インデックス)のみを指定し、リスト型またはタプル型の値を代入します。
df = pd.DataFrame([[1,2,3],[4,5,6]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2"]) # ラベルで指定 df.at['val1'] = [7, 8, 9] print(df) # aaa bbb ccc # val1 7 8 9 # val2 4 5 6
df.iat[1]
という指定はできません。条件で値を変更
df = pd.DataFrame([[1,2,3],[4,None,6],[None,None,None]], columns = ["aaa", "bbb", "ccc"], index = ["val1", "val2", "val3"]) print(df) # aaa bbb ccc # val1 1.0 2.0 3.0 # val2 4.0 NaN 6.0 # val3 NaN NaN NaN # 条件での値の代入 df.loc[df['aaa'] < 2, 'aaa'] = 100 print(df) # aaa bbb ccc # val1 100.0 2.0 3.0 # val2 4.0 NaN 6.0 # val3 NaN NaN Na # 条件(除く)での値の代入 df.loc[~(df['ccc'] < 4), 'ccc'] = 100 print(df) # aaa bbb ccc # val1 100.0 2.0 3.0 # val2 4.0 NaN 100.0 # val3 NaN NaN 100.0
特定の文字を置換(削除
df = pd.DataFrame([["1a",2,3],["4a",5,6]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1a 2 3 # 1 4a 5 6 df['aaa'] = df['aaa'].str.replace('a', 'b') print(df) # aaa bbb ccc # 0 1b 2 3 # 1 4b 5 6 df['aaa'] = df['aaa'].str.replace('b', '') print(df) # aaa bbb ccc # 0 1 2 3 # 1 4 5 6
df['aaa'] = df['aaa'].str.replace(',', '').astype(int)
Nanの穴埋め
特定の値で
df.fillna(値)
でNaN
を置換することができます。
df = pd.DataFrame([[1,2,3],[4,None,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 NaN 6 # 2 7 8.0 9 df = df.fillna(0) print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 0.0 6 # 2 7 8.0 9
前後の値で
methodをffill
にすると前の値、bfill
にすると後の値でデータを埋めます。
df = pd.DataFrame([[1,2,3],[4,None,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成 print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 NaN 6 # 2 7 8.0 9 df = df.fillna(method='ffill') # 前の値 print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 2.0 6 # 2 7 8.0 9
前後の値より計算して
df = pd.DataFrame([[1,2,3],[4,None,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成 print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 NaN 6 # 2 7 8.0 9 df = df.interpolate() print(df) # aaa bbb ccc # 0 1 2.0 3 # 1 4 5.0 6 # 2 7 8.0 9
日時の加算/減算
単純に日時を加算・減算
timedelta(days=1)
などで加算・減算します。他にも方法はありますが一番簡単かと思います。
from datetime import timedelta df = pd.DataFrame( [["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False]], columns = ["Date Time", "bbb", "ccc"] ) df['Date Time'] = pd.to_datetime(df['Date Time']) print(df) # Date Time bbb ccc # 0 2020-05-01 12:34:56 1 True # 1 2020-05-01 16:34:56 4 False df['Date Time'] = df['Date Time'] + timedelta(days=1) print(df) # Date Time bbb ccc # 0 2020-05-01 12:34:56 1 True # 1 2020-05-01 16:34:56 4 False # Date Time bbb ccc # 0 2020-05-02 12:34:56 1 True # 1 2020-05-02 16:34:56 4 False
営業日を考慮した加算・減算
今回はアメリカのビジネス日を使用しています。
日本のビジネス日は以下のページを参考にしてください。
Pythonの日時
from pandas.tseries.offsets import BusinessDay df = pd.DataFrame( [["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False]], columns = ["Date Time", "bbb", "ccc"] ) df['Date Time'] = pd.to_datetime(df['Date Time']) df['week'] = df['Date Time'].dt.weekday_name print(df) # Date Time bbb ccc week # 0 2020-05-01 12:34:56 1 True Friday # 1 2020-05-01 16:34:56 4 False Friday df['Date Time'] = df['Date Time'] + BusinessDay(2) df['week'] = df['Date Time'].dt.weekday_name print(df) # Date Time bbb ccc week # 0 2020-05-01 12:34:56 1 True Friday # 1 2020-05-01 16:34:56 4 False Friday # Date Time bbb ccc week # 0 2020-05-05 12:34:56 1 True Tuesday # 1 2020-05-05 16:34:56 4 False Tuesday
登録するデータがリスト型になっているのも注意点ですね。