Python Pandasの集計と分析、相関係数

スポンサーリンク
スポンサーリンク

集計の種類

主な集計は以下です。
()内にaxis=1と指定すれば行毎に集計することができますが、このページでは基本的に列の集計とします。

合計sum()
平均mean()
中央値median()
最大値max()
最小値min()
データ数count()
標準偏差std()
パーセンタイルquantile(0.9)*
最頻値mode()、またはvalue_counts()*

*別途、以下で説明します。

パーセンタイル

ディープラーニングはもちろん投資でも、異常値を外すためにパーセンタイルを使う場合があります。
パーセンタイルは統計学になるので説明は難しいですが、超簡単に言えば、最小値から数えてX%に位置する値ということです。なので例えば90%パーセンタイルを超える(簡単に言えば全体の90%を超える)データを異常値とみなして外しましょうということです。

パーセンタイルで得た値をもとに、条件式で抽出すれば異常値を切ることができます。

以下のように、全ての列、指定した列、パーセントを複数指定することができます。

df = pd.DataFrame([[90,80,70],[95,85,75],[99,89,79]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成

print(df)
#    aaa  bbb  ccc
# 0   90   80   70
# 1   95   85   75
# 2   99   89   79

# 全ての列
print(df.quantile(0.9))
# aaa    98.2
# bbb    88.2
# ccc    78.2
# Name: 0.9, dtype: float64

# 指定した列
print(df.aaa.quantile(0.9))
# 98.2

# パーセントを複数指定
print(df.quantile([0.9, 0.5]))
#       aaa   bbb   ccc
# 0.9  98.2  88.2  78.2
# 0.5  95.0  85.0  75.0

最頻値

mode()で最頻値を取得できます。

df = pd.DataFrame([["2020/05/01 12:34:56",1,100],["2020/05/01 16:34:56",4,98],["2020/05/02 16:34:56",4,90]], 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  100
# 1 2020-05-01 16:34:56    4   98
# 2 2020-05-02 16:34:56    4   90


aaa = df['bbb'].mode()

print(aaa[0])
# 0    4

print(aaa[0])
# 4

value_counts()を利用すると出現回数を含めてデータフレームの形式で取得できます。

df = pd.DataFrame([["2020/05/01 12:34:56",1,100],["2020/05/01 16:34:56",4,98],["2020/05/02 16:34:56",4,90]], 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  100
# 1 2020-05-01 16:34:56    4   98
# 2 2020-05-02 16:34:56    4   90


aaa = df['bbb'].value_counts()

print(aaa)
# 4    2
# 1    1
# Name: bbb, dtype: int64

print(aaa[0:1])
# 4    2
# Name: bbb, dtype: int64

最大値と最小値のインデックスを取得

集計ではないですが、最大値と最小値のインデックスを取得する方法です。

  • idxmax():最大値のインデックスを取得
  • idxmin():最小値のインデックスを取得

rolling()には使用できないので注意しましょう。

import pandas as pd

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成
print(df)
#    aaa  bbb  ccc
# 0    1    2    3
# 1    4    5    6
# 2    7    8    9

print(df["ccc"].max())
# 9
print(df["ccc"].idxmax())
# 2

print(df["ccc"].min())
# 3
print(df["ccc"].idxmin())
# 0

行の集計

ページの冒頭で説明した通り、axis=1を指定することにより列での集計が可能です。

import pandas as pd

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成
print(df)
#    aaa  bbb  ccc
# 0    1    2    3
# 1    4    5    6
# 2    7    8    9

df["ddd"] = df.max(axis=1)
print(df)
#    aaa  bbb  ccc  ddd
# 0    1    2    3    3
# 1    4    5    6    6
# 2    7    8    9    9

行で集計する列(カラム)を指定

  • 対象となるカラムをdf[["aaa","bbb"]]のように指定します。
  • []が入れ子になっていますので注意してください。
  • 範囲での指定はできないようです。
import pandas as pd

df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成
print(df)
#    aaa  bbb  ccc
# 0    1    2    3
# 1    4    5    6
# 2    7    8    9

df["ddd"] = df[["aaa","bbb"]].max(axis=1)
print(df)
#    aaa  bbb  ccc  ddd
# 0    1    2    3    2
# 1    4    5    6    5
# 2    7    8    9    8

集計の絞り込み

全てのデータ

df.カラム名.集計方法()またはdf["カラム名"].集計方法()で集計します。

df = pd.DataFrame([["2020/04/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'], utc=True), name='Date Time')
del df['Date Time']

print(df)
#                            bbb    ccc
# Date Time                            
# 2020-04-01 12:34:56+00:00    1   True
# 2020-05-01 16:34:56+00:00    4  False
                     
    
print(df.bbb.sum())
# 5

条件付け

df[条件式].カラム名.集計方法()またはdf[条件式]["カラム名"].集計方法()で集計します。

df = pd.DataFrame([["2020/04/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'], utc=True), name='Date Time')
del df['Date Time']

print(df)
#                            bbb    ccc
# Date Time                            
# 2020-04-01 12:34:56+00:00    1   True
# 2020-05-01 16:34:56+00:00    4  False
                     
    
print(df[df.index >= "2020/05/01"].bbb.sum())
# 4

複数カラム

以下のようにすれば、まとめて複数の集計が可能です。

df = pd.DataFrame([[90,80,70],[95,85,75],[99,89,79]], columns = ["aaa", "bbb", "ccc"]) # リスト型のデータで作成

print(df)
#    aaa  bbb  ccc
# 0   90   80   70
# 1   95   85   75
# 2   99   89   79


print(df.agg(['min', 'max', 'sum']))
#      aaa  bbb  ccc
# min   90   80   70
# max   99   89   79
# sum  284  254  224

グループ化(グルーピング

df.groupby('カラム名').集計方法()としてグループ化した集計ができます。

投資においては、年や月、曜日などでグループ化して集計するといいですね。

df = pd.DataFrame([["2020/05/01 12:34:56",1,True],["2020/05/01 16:34:56",4,False],["2020/05/02 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
# 2 2020-05-02 16:34:56    4  False

df['week'] = df['Date Time'].dt.strftime('%a')

print(df)
#             Date Time  bbb    ccc week
# 0 2020-05-01 12:34:56    1   True  Fri
# 1 2020-05-01 16:34:56    4  False  Fri
# 2 2020-05-02 16:34:56    4  False  Sat

# 全ての列を取得
print(df.groupby('week').mean())
#       bbb  ccc
# week          
# Fri   2.5  0.5
# Sat   4.0  0.0

# 指定の列のみ取得
print(df.groupby('week').bbb.mean())
# week
# Fri    2.5
# Sat    4.0
# Name: bbb, dtype: float64

リサンプル

グルーピングと似ていますが、datetimeのインデックスから直接期間で集計することができます。

df.resample(rule="集計単位", label="値", closed="値").カラム名.mean()

集計単位の主な設定は以下です。集計単位は、例えば5分間隔の場合、5Tと指定できます。

D
W
M
Q四半期
A
ASYear start
H
T, min
S
BBusiness day
BABusiness year

labelclosedでインデックスの日時を開始とするか終了とするかを指定します。

label="left":開始日時、label="right":終了日時
closed="left":含めず、label="right":含める

例えば、
df.resample(rule="3D", label="right", closed="right")
とした場合、「インデックスの日を終了日とし、その日も含め、3日前」という意味になります。

ちょっと難しいですね。投資のチャートの5分足なども上記のようになっていることが多いと思います。
(OandaやIB証券ではそうでした。

ruleによりlabelclosedデフォルト値が異なるので指定した方が無難です。

以下の例では、日次の平均を出力しています。

df = pd.DataFrame([["2020/05/01 12:34:56",1,100],["2020/05/01 16:34:56",4,98],["2020/05/02 16:34:56",4,90],["2020/05/02 16:34:56",5,90]], 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  100
# 2020-05-01 16:34:56    4   98
# 2020-05-02 16:34:56    4   90
# 2020-05-02 16:34:56    5   90


print(df.resample(rule="D", label="right", closed="right").ccc.mean())
# Date Time
# 2020-05-02    99
# 2020-05-03    90
# Freq: D, Name: ccc, dtype: int64
インデックスがdatetimeとなっている必要があります。

ダウンサイジング(リサンプル)1分足を5分足になど

リサンプルの応用編になるが、以下のような1分足を5分足にダウンサイジングすることが可能です。

df.resample(rule="集計単位", label="値", closed="値").agg(集計方法の連想配列)

  • df.resample:期間で集計する。
    • 期間はリサンプルと同様です。日足を週足にする場合は、“W-Mon”とすれば週間の月曜日スタートになります。
    • その他の、closedとlabelは上記のリサンプルに記載していますので確認してください。
  • agg:カラムの集計方法を連想配列で設定する。
#                               Open     High      Low    Close  Volume
# Date Time                                                            
# 2019-01-01 22:00:00+00:00  109.590  109.590  109.590  109.590     1.0
# 2019-01-01 22:01:00+00:00  109.673  109.673  109.673  109.673     1.0
# 2019-01-01 22:02:00+00:00  109.673  109.673  109.673  109.673     1.0
# 2019-01-01 22:03:00+00:00  109.673  109.673  109.673  109.673     1.0
# 2019-01-01 22:04:00+00:00  109.673  109.673  109.673  109.673     1.0
            
agg_columns = {
    'Open': 'first',
    'High': 'max',
    'Low': 'min',
    'Close': 'last',
    'Volume': 'sum'
}

df_resample = df.resample('5T', closed='left', label='left').agg(agg_columns)
print(df_resample.head())
#                               Open     High      Low    Close  Volume
# Date Time                                                            
# 2019-01-01 22:00:00+00:00  109.590  109.673  109.590  109.673     5.0
# 2019-01-01 22:05:00+00:00  109.673  109.673  109.669  109.669     5.0
# 2019-01-01 22:10:00+00:00  109.669  109.669  109.650  109.650    11.0
# 2019-01-01 22:15:00+00:00  109.650  109.674  109.635  109.635    26.0
# 2019-01-01 22:20:00+00:00  109.635  109.654  109.616  109.651    16.0


# 実際のデータ
#                    Date Time     Open     High      Low    Close  Volume
# 0  2019-01-01 22:00:00+00:00  109.590  109.673  109.590  109.673       3
# 1  2019-01-01 22:05:00+00:00  109.669  109.669  109.669  109.669       1
# 2  2019-01-01 22:10:00+00:00  109.666  109.669  109.650  109.650       6
# 3  2019-01-01 22:15:00+00:00  109.650  109.674  109.635  109.635      14
# 4  2019-01-01 22:20:00+00:00  109.630  109.654  109.616  109.651      13
しかし、実際にダウンロードした5分足のデータと比べてもらうとわかるのですが、少し異なります
集計が間違っているのではなく、ダウンロードした会社の集計方法が少し異なっているのだと思います。
投資の解析を行う場合は、特にシストレの場合は、実際に取得したデータで解析した方がいいと思います。
理由は、シストレの場合、リアルタイムでデータを取得することになりますが、その際、足データもその会社から取得することになります。誤差も含めて指標となる数値の解析をしてもデータが異なっては意味がありません。ですから、シストレで取得するデータで分析した方がいいですね。

四本値(始値、高値、安値、終値)OHLC

リサンプルでohlcを使用すると簡単に四本値を求めることができます。

df.resample(rule="集計単位", label="値", closed="値").カラム名.ohlc()

以下の例は、あまりよくないですが、1分の価格だけある場合、それを5分足の四本値に変換しています。
実際はティックデータを1分足にするなどに利用できるかと思います。

出来高はsumで集計して追加するしか方法はないです。

これも同様に週足や月足などで集計が可能です。

#                              Price  Volume
# Date Time                                 
# 2019-01-01 22:00:00+00:00  109.590     1.0
# 2019-01-01 22:01:00+00:00  109.673     1.0
# 2019-01-01 22:02:00+00:00  109.673     1.0
# 2019-01-01 22:03:00+00:00  109.673     1.0
# 2019-01-01 22:04:00+00:00  109.673     1.0

df_ohlc = df.resample('5T', closed='left', label='left').Price.ohlc()
print(df_ohlc.head())

#                               open     high      low    close
# Date Time                                                    
# 2019-01-01 22:00:00+00:00  109.590  109.673  109.590  109.673
# 2019-01-01 22:05:00+00:00  109.673  109.673  109.669  109.669
# 2019-01-01 22:10:00+00:00  109.669  109.669  109.664  109.664
# 2019-01-01 22:15:00+00:00  109.650  109.670  109.635  109.635
# 2019-01-01 22:20:00+00:00  109.635  109.651  109.630  109.651

df_ohlc["volume"] = df.resample('5T', closed='left', label='left').Volume.sum()
print(df_ohlc.head())

#                               open     high      low    close  volume
# Date Time                                                            
# 2019-01-01 22:00:00+00:00  109.590  109.673  109.590  109.673     5.0
# 2019-01-01 22:05:00+00:00  109.673  109.673  109.669  109.669     5.0
# 2019-01-01 22:10:00+00:00  109.669  109.669  109.664  109.664    11.0
# 2019-01-01 22:15:00+00:00  109.650  109.670  109.635  109.635    26.0
# 2019-01-01 22:20:00+00:00  109.635  109.651  109.630  109.651    16.0

集計の範囲

全体ではなく、一部分のデータを対象に集計します。条件式ではなく、その集計データを新たな列(カラム)として利用します。例えば、投資では移動平均線なんかに利用できます。

df.カラム名.rolling(データ数).集計方法()

データ数は対象行を含むデータ数となります。

df = pd.DataFrame([["2020/05/01 12:34:56",1,100],["2020/05/01 16:34:56",4,98],["2020/05/02 16:34:56",4,90],["2020/05/02 16:34:56",5,90]], 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  100
# 1 2020-05-01 16:34:56    4   98
# 2 2020-05-02 16:34:56    4   90
# 3 2020-05-02 16:34:56    5   90


df["mean"] = df.ccc.rolling(2).mean()

print(df)
#             Date Time  bbb  ccc  mean
# 0 2020-05-01 12:34:56    1  100   NaN
# 1 2020-05-01 16:34:56    4   98  99.0
# 2 2020-05-02 16:34:56    4   90  94.0
# 3 2020-05-02 16:34:56    5   90  90.0

ピポットテーブル

pd.pivot_table(df, index="ラベル名", columns="ラベル名", values="ラベル名", aggfunc="集計方法")

  • index:行(集計のインデックスのグルーピング
  • columns:列(集計のカラムのグルーピング
  • values:集計対象
  • aggfunc:集計方法
df = pd.DataFrame(
    [["2020/05/01 12:34:56",1,"x",100],
     ["2020/05/01 16:34:56",4,"y",98],
     ["2020/05/02 16:34:56",4,"x",90],
     ["2020/05/02 16:34:56",5,"y",90]], columns = ["Date Time", "aaa", "bbb", "ccc"]
)
df.index = pd.DatetimeIndex(pd.to_datetime(df['Date Time']), name='Date Time')
del df['Date Time']

print(df)
#                      aaa bbb  ccc
# Date Time                        
# 2020-05-01 12:34:56    1   x  100
# 2020-05-01 16:34:56    4   y   98
# 2020-05-02 16:34:56    4   x   90
# 2020-05-02 16:34:56    5   y   9


print(pd.pivot_table(df, index="aaa", columns="bbb", values="ccc", aggfunc="mean"))
# bbb      x     y
# aaa             
# 1    100.0   NaN
# 4     90.0  98.0
# 5      NaN  90.0
pd.pivot_tabledf.pivot_tableではなく、( )内に対象のデータフレームとなるdfを指定します。

分析

ユニークな値

ユニークな値の一覧

これは投資でも使えますね。例えば1分足のデータから営業日を出すときに、日時データを日付のみを取り出して、その日付のユニークな一覧を出せば、営業日の一覧となります。

df = pd.DataFrame([[1,2,3],[4,5,6],[4,5,6]], columns = ["aaa", "bbb", "ccc"])
print(data)
# [[1 2 3]
#  [4 5 6]]

print(df['aaa'].unique())
# [1 4]

ユニークな値の出現回数

df = pd.DataFrame([[1,2,3],[4,5,6],[4,5,6]], columns = ["aaa", "bbb", "ccc"])
print(data)
# [[1 2 3]
#  [4 5 6]]

print(df['aaa'].value_counts())
# 4    2
# 1    1

ユニークな値の個数

これは投資でも使いますね。例えば1分足のデータから営業日数を出すときに、日時データを日付のみを取り出して、その日付のユニークな個数を出せば、営業日数がわかります。

df = pd.DataFrame([[1,2,3],[4,5,6],[4,5,6]], columns = ["aaa", "bbb", "ccc"])
print(data)
# [[1 2 3]
#  [4 5 6]]

print(df['aaa'].nunique())
# 2

最大値・最小値のインデックスとカラム名を取得

投資では案外利用します。最大値や最小値の日時(インデックス)を取得するのに便利です。カラム名の取得はあまり使うことはないかも知れませんが。

  • 最大値:df.カラム名.idxmax()idxmax(axis=1)とすればカラム名を取得
  • 最小値:df.カラム名.idxmin()idxin(axis=1)とすればカラム名を取得
df = pd.DataFrame([["2020/05/01 12:34:56",1,2,3],["2020/05/02 12:34:56",4,5,6],["2020/05/03 12:34:56",7,8,9]], columns = ["DateTime", "aaa", "bbb", "ccc"])
df.index = pd.DatetimeIndex(pd.to_datetime(df['DateTime']), name='DateTime')
del df['DateTime']

print(df)
#                      aaa  bbb  ccc
# DateTime                          
# 2020-05-01 12:34:56    1    2    3
# 2020-05-02 12:34:56    4    5    6
# 2020-05-03 12:34:56    7    8    9

print(df.aaa.idxmax())
# 2020-05-03 12:34:56

print(df.aaa.idxmin())
# 2020-05-01 12:34:56

print(df.idxmax(axis=1))
# 2020-05-03 12:34:56
# DateTime
# 2020-05-01 12:34:56    ccc
# 2020-05-02 12:34:56    ccc
# 2020-05-03 12:34:56    ccc
# dtype: object

print(df.idxmin(axis=1))
# 2020-05-01 12:34:56
# DateTime
# 2020-05-01 12:34:56    aaa
# 2020-05-02 12:34:56    aaa
# 2020-05-03 12:34:56    aaa
# dtype: object

応用

一定の期間の最大値(または最小値)のインデックスを取得する場合は以下のとおりです。
ループしているので処理は遅くなります。

for index, row in df.iterrows():
    df[index-10:index]['aaa'].idxmax()

相関係数(相関性

投資ではよく相関性が議論されます。その相関性を簡単にチェックすることができます。
以下のように相関性はNumpyですが、Pandasと組み合わせて使うので本ページで紹介します。

np.corrcoef(列1, 列2)[0, 1]とするだけです。[0, 1]としているのはおまじないだと思ってください。

df = pd.DataFrame([["2020/05/01 12:34:56",1,100],["2020/05/01 16:34:56",4,98],["2020/05/02 16:34:56",4,90]], 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  100
# 1 2020-05-01 16:34:56    4   98
# 2 2020-05-02 16:34:56    4   90


aaa = np.corrcoef(df.bbb, df.ccc)[0, 1]

print(aaa)
# -0.6546536707079772
今回は一つのデータフレーム内で相関係数を算出しましたが、別のデータフレームでももちろん可能です。

 

 

コメント

タイトルとURLをコピーしました