Python Pandas(データフレーム

スポンサーリンク

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
インデックス(index)については別項目で詳しく説明します。
登録するデータがリスト型になっているのも注意点ですね。

データフレームのコピー(複製、代入)

データフレームはイコール(=)でコピー(複製、代入)すると、参照渡しになります。
参照渡しとは、コピーしたデータフレームを変更すると、元のデータフレームも変更されるということです。

ついつい、忘れてバグりますので注意しましょう。

参照渡しではなく、普通の変数のようにコピー(複製、代入)する場合は、以下のように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の表示
intint64
floatfloat64
strobject
boolbool
datetimedatetime64

データ型の変更

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)に変換

Pandasでは無さそうです。datetime.timestamp()を使って変換するのが最良かと思います。
詳しくはこちらを確認してください。
Pythonの日時

タイプスタンプ(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
タイプスタンプ(UNIX TIME STAMP)型は、数値ではなく文字列である必要があります。
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は行番号、列番号を指定する。

atiatは行ラベルと列番号、行番号と列ラベルの組み合わせはできません。
ilocでも行ラベルと列番号の組み合わせができません。
ラベルのみ、番号のみで指定の場合、locilocでも取得できますが、atiatの方が驚くほど早いです。

行毎に値を取得

この場合は、atiatは使えず、locilocとなります。

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
FlaseをTrueにすればNanの個数が分かります。
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
この例では2と8の値の中間値の5で補間されました。パラメーターはいろいろありますが、投資ではあまり使わないと思います。興味がある方は、ドキュメントを参照してください。
pandas.DataFrame.interpolate — pandas 2.0.1 documentation

日時の加算/減算

単純に日時を加算・減算

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
タイトルとURLをコピーしました