python §04:これでわかったpandasの「loc」を使った条件抽出

Python基礎

この記事は、pythonのpandasについて、以下のようなことを知りたい人のために書きました。

  1. pandasライブラリーのlocを使い方
  2. データフレームのある列について、条件による抽出をしたい
  3. データフレームのある列について、複数の条件で抽出したい
  4. pandasのloc機能を使わずに条件抽出する方法を知りたい(力技)
  5. pandasで株価解析をやってみたい

 

題材は、日本の株式市場の株価データを使います。無料で株価データをダウンロードする方法は、こちらで詳しく紹介しています。

pythonカスタム関数§07:download_mujinzo() 無尽蔵さんのサイトから指定日の全株価データをダウンロードする

 

また、pandasの基本的なことについては、以下の記事でも紹介していますので、参考にしてください。

python§03:データ分析ライブラリーpandasを徹底解説します

 

ライブラリーのインポート関連

今回使用するライブラリーをインポートします。

import datetime
import pandas as pd
from pandas import DataFrame,Series
import requests
import os
import sys
import numpy as np

 

下準備(株価データのダウンロード)

以下は、ダウンロードしてくるファイルをエクセルで開いた状態をキャプチャーしたものです。

左から 日付、銘柄コード、市場コード、銘柄名(code+名前)、始値、高値、安値、終値、出来高、市場名

の順番に並んでいます。列名は、date,code,no,name,open,high,low,close,volume,marketとします。

 

if __name__ == '__main__':
    #スクリプトを実行するバッチファイルを生成
    fname=os.path.splitext(os.path.basename(sys.argv[0]))[0]+'.bat'
    with open(fname,'w') as f:f.write(f'python "{sys.argv[0]}" \n pause \n')

 

デバッグのときに楽なので、私はいつもこれをメインコードの先頭で入れるようにしています。バッチファイルを起動すれば、pythonが実行され、表示をいったん停止してくれます。このバッチファイルについては、この記事でも紹介しています。→

pythonカスタム関数§05:CreateBatfile() Pythonファイルを実行できるバッチファイルを自動生成

 

    #print()で出力させるファイルを設定
    f = open('memo.txt','w')

 

次にファイルをオープンしています。今回このmemo.txtには、一部、print()関数でファイル出力させようとしています。(print()関数についてはこちらへ

 

    #日付と保存フォルダを指定して、株価データをダウンロード
    date_str='2020-08-21'   #ここを変更して利用する
    year, month, day = date_str.split('-')
    fileName = 'T{0}{1}{2}.zip'.format(year[-2:], month, day)

無尽蔵さんのサイトから株価データをいただくわけですが、ファイル名が「T200821.zip」という形式になっていますので、format()関数を使って、ファイル名を生成しています。(format()関数に関してはこちらへ

 

    #ダウンロードファイルの存在を確認。なければ、ダウンロードを実行
    if not os.path.exists(fileName):
        fileName=download_mujinzo(date_str=date,folder='./')
    else:
        print(f'ファイル:{fileName}はすでにダウンロード済みです')
    print('*'*30,file=f)
    print(f'Download Filename:{fileName}',file=f)
    print('*'*30,file=f)

 

ダウンロードを自動で行う関数 download_mujinzo() というカスタム関数(ユーザー定義関数)を使います。日付とダウンロードファイルの保存場所を指定するシンプルな使い方です。

この株価ダウンロード関数いついてはこちらで詳しく紹介しています。

pythonカスタム関数§07:download_mujinzo() 無尽蔵さんのサイトから指定日の全株価データをダウンロードする

 

今回、少し工夫したところは、if not os.path.exists(fileName) の判定です。

もし、ダウンロードファイルがまだ保存されていなければ実行するという文です。つまり、すでにダウンロードしていた場合は、実行しないということですね。サーバーの負荷を少しでも減らす配慮です。

ここまでを実行すると、ファイルがダウンロードされて、準備OKです。

pandasでzip(csv)ファイルを読み込む pandas.read_csv()

 

    #zipファイルを読み込み、DataFrameに変換する → その後列名をセットする
    df=pd.read_csv(fileName,encoding="shift_jis",header=None)    
    df.columns=['date','code','no','name','open','high','low','close','volume','market']#列名をセット

    df=df.sort_values('code')

 

pandasには、read_csv関数という便利な機能が搭載されています。csvという名前ですが、実はzipのような圧縮ファイルも中身を自動で判断して、読み込んでくれます。すごい!!!

引数には ファイル名、エンコード、ヘッダーを設定しています。ファイル名だけでも読み込める場合がありますが、そのときはエンコードはUTF-8、ヘッダーは1行目をヘッダーとして使うことになります。

ここでは、さきほどのエクセル表を見て、ヘッダーがないことがわかりますので、header=Noneとします。(もし省略すると1行目のデータを消失してしまいますので、ご注意を)

そして、後先逆になりましたが、エンコードです。デフォルトではUTF-8という文字コードが選ばれるので、実はエラーで止まってしまいます。

UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0x93 in position 5: invalid start byte

こんな表示が出れば、UTF-8で変換しようとして失敗したというメッセージです。

ここでは、shift-JISを設定しておけば大丈夫です。

 

df.columns=[‘date’,’code’,’no’,’name’,’open’,’high’,’low’,’close’,’volume’,’market’]

で、列名をセットしています。columnsにリストを渡せば設定できます。ここで、注意すべきは、列数を一致させないといけないことです。たとえば、列名が不足していると

ValueError: Length mismatch: Expected axis has 10 elements, new values have 9 elements

という具合に要素数が10あるのに設定しようとしているのは9個だからダメよんと言っています。

逆に多すぎる場合もエラーが出てしまいます。CSVをエクセルで確かめるか、df.info()などを使って、データ列数を下調べしておくとよいですね。

次の df=df.sort_values(‘code’) は、銘柄コード番号でソートするという意味です。順番を並び替えて、コードが若い順にすることができます。df変数に入れなおしていますが、別の変数を新たに作ってもいいです。

    #データフレームの内容を表示    year, month, day = date_str.split('-')

    print('*'*10,'DataFrame information','*'*10)
    print(df.info())    #各列の情報
    
    print('*'*10,'DataFrame first 10 data ','*'*10)
    print(df.head(50))  #先頭10個のデータ表示
    
    print('*'*30)

 

********** DataFrame information **********
<class ‘pandas.core.frame.DataFrame’>
Int64Index: 4054 entries, 0 to 4053
Data columns (total 11 columns):
# Column Non-Null Count Dtype
— —— ————– —–
0 date 4054 non-null object
1 code 4054 non-null int64
2 no 4054 non-null int64
3 name 4054 non-null object
4 open 4054 non-null float64
5 high 4054 non-null float64
6 low 4054 non-null float64
7 close 4054 non-null float64
8 volume 4054 non-null int64
9 market 4054 non-null object
10 name2 4054 non-null object
dtypes: float64(4), int64(3), object(4)
memory usage: 380.1+ KB

 

********** DataFrame first 10 data  **********
         date  code  no              name     open     high      low    close     volume market                 name2
0   2020/8/21  1001  11        1001 日経225  23022.0  23135.0  22920.0  22920.0  910280000   東証1部       日経225
1   2020/8/21  1002  11        1002 TOPIX   1608.0   1615.0   1602.0   1604.0  910280000   東証1部       TOPIX
2   2020/8/21  1301  11           1301 極洋   2672.0   2699.0   2672.0   2699.0       7000   東証1部    極洋
3   2020/8/21  1305  11       1305 ダイワTPX   1673.0   1683.0   1670.0   1671.0      89200   東証1部        ダイワTPX
4   2020/8/21  1306  11       1306 TOPIX投   1655.0   1663.0   1649.0   1653.0    1201420   東証1部        TOPIX投
5   2020/8/21  1308  11        1308 上場TPX   1636.0   1646.0   1633.0   1633.0     138300   東証1部       上場TPX
6   2020/8/21  1309  11       1309 上海上証50  37750.0  38150.0  37750.0  37900.0        216   東証1部        上海上証50
7   2020/8/21  1311  11       1311 T30連動投    729.0    733.0    725.0    725.0       5540   東証1部        T30連動投
8   2020/8/21  1312  11       1312 野村小型コア  18480.0  18490.0  18440.0  18440.0        135   東証1部        野村小型コア
9   2020/8/21  1313  11     1313 KODEX200   2780.0   2780.0   2770.0   2770.0        120   東証1部  KODEX200
10  2020/8/21  1319  12       1319 日経300F      0.0      0.0      0.0      0.0          0   東証2部    日経300F
11  2020/8/21  1320  12       1320 ダイワ225  23520.0  23640.0  23410.0  23450.0      52790   東証2部        ダイワ225
12  2020/8/21  1321  12       1321 日経225投  23570.0  23690.0  23460.0  23510.0     187607   東証2部        日経225投
13  2020/8/21  1322  11        1322 上場パンダ   6850.0   6860.0   6830.0   6830.0       1290   東証1部       上場パンダ
14  2020/8/21  1323  11      1323 南アフリカ投信    308.0    308.0    308.0    308.0        400   東証1部         南アフリカ投信
15  2020/8/21  1324  11       1324 ロシア株投信    135.0    135.0    134.0    135.0      23700   東証1部        ロシア株投信
16  2020/8/21  1325  11       1325 NFブラジル    156.0    159.0    156.0    159.0      13400   東証1部      NFブラジル
17  2020/8/21  1326  11     1326 SPDRゴールド  19400.0  19420.0  19260.0  19310.0       9212   東証1部      SPDRゴールド
18  2020/8/21  1327  11       1327 EASY商品   2720.0   2764.0   2719.0   2719.0         28   東証1部        EASY商品
19  2020/8/21  1328  11        1328 金連動投信   5350.0   5360.0   5340.0   5340.0       5990   東証1部       金連動投信
20  2020/8/21  1329  11       1329 iシェ225  23670.0  23780.0  23560.0  23600.0       9458   東証1部        iシェ225
21  2020/8/21  1330  11        1330 上場225  23610.0  23720.0  23510.0  23550.0      32940   東証1部       上場225
22  2020/8/21  1332  11           1332 日水    483.0    488.0    477.0    477.0     752700   東証1部    日水
23  2020/8/21  1333  11       1333 マルハニチロ   2270.0   2291.0   2255.0   2255.0      86600   東証1部        マルハニチロ
24  2020/8/21  1343  11      1343 NF東REIT   1814.0   1855.0   1813.0   1852.0     341560   東証1部         NF東REIT
25  2020/8/21  1344  11         1344 MXコア    714.0    714.0    705.0    705.0       5390   東証1部      MXコア
26  2020/8/21  1345  11       1345 上場Jリート   1725.0   1761.0   1724.0   1761.0     633500   東証1部        上場Jリート
27  2020/8/21  1346  11      1346 MX225投信  23680.0  23790.0  23560.0  23610.0       8728   東証1部    MX225投信
28  2020/8/21  1348  11     1348 MAXトピックス   1654.0   1664.0   1650.0   1653.0       7950   東証1部       MAXトピックス
29  2020/8/21  1349  11       1349 ABFアジア  12710.0  12770.0  12710.0  12710.0          6   東証1部     ABFアジア
30  2020/8/21  1352  11         1352 ホウスイ    881.0    886.0    878.0    878.0        500   東証1部      ホウスイ
31  2020/8/21  1356  11       1356 TOPXベア   1884.0   1898.0   1863.0   1891.0     250000   東証1部    TOPXベア
32  2020/8/21  1357  11        1357 日経ダブル    736.0    744.0    730.0    742.0   43814121   東証1部       日経ダブル
33  2020/8/21  1358  11       1358 上場日経2倍  18870.0  19060.0  18710.0  18730.0      10682   東証1部        上場日経2倍
34  2020/8/21  1360  11      1360 日経ベア2倍投   1791.0   1809.0   1775.0   1804.0    2162080   東証1部         日経ベア2倍投
35  2020/8/21  1364  11  1364 JPX日経400ETF  14740.0  14740.0  14740.0  14740.0          2   東証1部    JPX日経400ETF
36  2020/8/21  1365  11     1365 大和225ダブル  15500.0  15650.0  15370.0  15400.0      44509   東証1部       大和225ダブル
37  2020/8/21  1366  11     1366 大和225Wベア   1920.0   1939.0   1903.0   1934.0     334177   東証1部       大和225Wベア
38  2020/8/21  1367  11     1367 大和TPXダブル  12490.0  12640.0  12420.0  12470.0       1764   東証1部       大和TPXダブル
39  2020/8/21  1368  11     1368 大和TPXWベア   2739.0   2760.0   2710.0   2748.0      65138   東証1部      大和TPXWベア
40  2020/8/21  1369  11   1369 DIAM 日経225  23090.0  23110.0  22940.0  22940.0       2239   東証1部  DIAM
41  2020/8/21  1376  11         1376 カネコ種   1419.0   1419.0   1405.0   1405.0        700   東証1部      カネコ種
42  2020/8/21  1377  11        1377 サカタタネ   3485.0   3535.0   3445.0   3480.0      41000   東証1部       サカタタネ
43  2020/8/21  1379  11          1379 ホクト   2160.0   2168.0   2152.0   2162.0      71800   東証1部     ホクト
44  2020/8/21  1380  91         1380 秋川牧園   1110.0   1110.0   1091.0   1102.0      12100    JAQ      秋川牧園
45  2020/8/21  1381  91        1381 アクシーズ   2811.0   2830.0   2811.0   2814.0        700    JAQ       アクシーズ
46  2020/8/21  1382  91          1382 ホープ    975.0    975.0    962.0    969.0       2000    JAQ     ホープ
47  2020/8/21  1383  91       1383 ベルグアース   2111.0   2139.0   2111.0   2130.0        600    JAQ        ベルグアース
48  2020/8/21  1384  11        1384 ホクリョウ    638.0    648.0    638.0    648.0       5400   東証1部       ホクリョウ
49  2020/8/21  1385  11    1385 ETFユーロ大型株   4215.0   4215.0   4115.0   4125.0         49   東証1部        ETFユーロ大型株
******************************

このように df.info()やdf.head()、df.tail()などで先頭や最後の少しのデータを表示させる機能も使いましょう。df.head(10)とすれば、10行分だけ表示させることができます。

head()の表示結果は、桁がそろっていなくて、見にくいですね。これは、あとで改善することにしましょう。

銘柄リスト出力 (DataFrameをndarrayに変換して加工する)

 

最初の課題「銘柄コード」をCSVに出力させることをやってみましょう。

素直にdf.to_csv()というCSV書き出し機能を使ったもできるのですが、エクセルの図を見ると、株価データの中の銘柄名が「銘柄コード」+「 」+「銘柄名」という形なので、銘柄名だけを取り出したいと思いました。

    #csvファイルに書き出す 銘柄リスト出力
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df)
    name2=[]
    for i in range(len(df)):
        try:
            name2.append(name[i].split(' ')[1].strip())
        except:
            name2.append(name[i])
            
    df['name2']=name2
    savefile='./code_list.csv'
    df[['code','name2']].to_csv(savefile,encoding='utf_8_sig') #UTF-8 BOM付ファイル書き込み エクセルで文字化け防止

 

ここで、先頭行に

kabu_df2ndarray(df)

という怪しげな関数が登場しました。これもユーザー定義関数で、以下のようなコードとなっています。

def kabu_df2ndarray(df):
    code   = df['code'].values
    no     = df['no'].values
    name   = df['name'].values
    open   = np.array(df['open'].values, dtype=np.int64)    #float→intに変換
    high   = np.array(df['high'].values, dtype=np.int64)    #float→intに変換
    low    = np.array(df['low'].values, dtype=np.int64)     #float→intに変換
    close  = np.array(df['close'].values, dtype=np.int64)   #float→intに変換
    volume = df['volume'].values
    market = df['market'].values
    date   = df['date'].values
    return code,no,name,open,high,low,close,volume,market,date

 

この関数の中では、基本的には、numpyのndarrayという多次元リストに変換しているだけです。ここでは、多次元だけではなく、列のみのデータなので1次元リストです。

np.arrayによって、変数の型の変換を行うことができます。open,high,low,closeは元のデータはfloatですが、整数にしたほうが見やすいと考え、int変換しています。

そして、最後のreturn文で変換したndarrayをすべて戻しています。

for文以降で、銘柄名だけを取り出す加工をしています。

name2.append(name[i].split(‘ ‘)[1].strip())

name[i]をスペースで分離して、右側の文字列だけを取り出して、空白文字を除去しています(strip())

ここで、try文を使っているのは、1~2個の銘柄のみ、スペースがなかったり、スペースが2つあったりしたので、エラー処理をしています。ほとんどの銘柄名はうまく処理できています。

市場の種類をリスト化 unique()

 

pandasのSeriesの機能ですが、ユニークな要素を取得する機能です。つまり、重複している要素はすべて1種類とします。やったほうが早いですね。

    #市場の種類をリスト化
    market_list=df['market'].unique()
    print(market_list,file=f)  
    #['東証1部' '東証2部' 'JAQ' '東証マ' '名古セ' '名証1部' '外国' '名証2部' '名証セ' '東証M']

matket_listという新しい変数にdf[‘market’]というSeriesデータにunique()を使って、要素を取り出します。

結果、東証1部、以下10個のマーケットの種類が抽出できました。

(おそらく東証マと東証Mは同じかな?・・)

さらに、この要素には何個のデータがあるのか調べることができます。

 

    print(df['market'].value_counts(),file=f)
    #東証1部        2462
    #JAQ          696
    #東証2部        488
    #東証マ          335
    #名証2部        51
    #名証セ          12
    #名証1部        6
    #外国            2
    #名古セ          1
    #東証M          1

 

東証1部の銘柄が2462、JAQが696、東証2部が488という具合です。

locにより条件抽出する

 

やっとlocにたどりつきました。

    print('データを整形して桁をそろえて表示させる【東証1部】',file=f)
    df1=df.loc[df['market']=='東証1部' ]
    kabu_seikei(df1,1,30)
    kabu_seikei2(df1,1,30,f=f)

 

表示をきれいに整形することにもチャレンジしますが、まずはlocですね。

df1=df.loc[df[‘market’]==’東証1部’ ]

これを翻訳すると、df[‘market’]のSeriesデータが’東証1部’と同じものを取り出す命令となっています。==は、if文のときと同じで、イコール二つに注意してください。

この演算を行えば、df1というサブセットが出来上がります。dfに入れてしまうと、東証一部以外のデータを取り出すことができなくなるので、サブセットとしました。

kabu_seikei()とkabu_seikei2()という二つのユーザー定義関数は、さきほどの表示の不具合、桁がそろわない問題を解決するものです。kabu_seikei2は、ファイルに出力する以外は、kabu_seikeiと同じなので、kabu_seikei()について説明します。

 

def kabu_seikei(df,start=1,end=30):
    #DataFrameをndarrayに変換してリストのように扱えるようにする
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df)

    start=start-1
    end=end-1
    
    #データを整形して桁をそろえて表示させる
    print('{:5} {:4} {:2} {:^20} {:>10} {:>10} {:>10} {:>10} {:>10} {:<10} {:}'.format ('No','code','no','name','open','high','low','close','volume','market','date')) print('-'*120) for i in range(len(df)): if i>=start and i<=end:
            print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:}'.format
            (i+1,code[i],no[i],haba_cal(name[i].split(' ')[1].strip(),haba=20,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i],haba=10,embedded='',hoko='<'),date[i]))
        else:
            break

 

引数は、データフレーム、開始番号、終了番号です。ひとつめのデータから表示したい場合は1とします。終了番号も5番目のデータまで表示したい場合は5とします。

さきほどのkabu_df2ndarray(df) を使って、ndarrayに変換します。

そして、泥臭いですが、for文で最初から最後まで1行ずつ処理していきます。

fomart()関数を使って、文字幅を決めたり、右寄せや左寄せなども設定しています。半角と全角が混在している部分をhaba_cal()というユーザー定義関数を使って、全角は2文字分、半角は1文字分として計算します。(※おそらくdf.head()などで桁がそろわないのは、全角も1文字分として計算してしまうからだと思います)

このhaba_cal()については、別の記事で紹介しています。→

pythonカスタム関数§01:haba_cal() 半角1文字、全角2文字として正確な文字幅を決める

そして、結果が以下のとおりです。HTMLの関係か、普通に貼り付けると桁が不揃いなので、画面をキャプチャーしました。

 

以上のように、locを使って1条件での抽出は簡単にできました。

その他、東証2部、東証マザーズ、JAQなどのコード例は以下のとおりです。

    #データを整形して桁をそろえて表示させる【東証2部】
    print('データを整形して桁をそろえて表示させる【東証2部】',file=f)
    df2=df.loc[df['market']=='東証2部' ]
    kabu_seikei(df2,1,30)
    kabu_seikei2(df2,1,30,f=f)

    #データを整形して桁をそろえて表示させる【東証マ】
    print('データを整形して桁をそろえて表示させる【東証マ】',file=f)
    dfm=df.loc[df['market']=='東証マ' ]
    kabu_seikei(dfm,1,30)
    kabu_seikei2(dfm,1,30,f=f)

    #データを整形して桁をそろえて表示させる【JAQ】
    print('データを整形して桁をそろえて表示させる【JAQ】',file=f)
    dfJ=df.loc[df['market']=='JAQ' ]
    kabu_seikei(dfJ,1,30)
    kabu_seikei2(dfJ,1,30,f=f)

 

locを使って、複数条件で抽出するには

 

    #複数の市場のデータを抽出する  【東証1部】or【東証2部】
    print('複数の市場のデータを抽出する  【東証1部】or【東証2部】',file=f)
    df1_2=df.loc[(df['market']=='東証1部')| (df['market']=='東証2部')]

わかってみれば、簡単なもので、ORなら|、ANDなら&で接続すればいいだけです。

ここで、注意点は、複数条件の場合は、それぞれの単独の条件部分を()でくくる必要がある点です。 df.loc[(df[‘market’]==’東証1部’)| (df[‘market’]==’東証2部’)]

 

locを使って、演算結果から条件抽出するには

 

今回の例題は、「売買代金」が〇〇億円以上の銘柄を抽出したい とします。

売買代金=終値×出来高 と定義します。

売買代金が大きい銘柄は「流動性が高い」と言われており、株式売買において、売りたいときに売りやすく、買いたいとき買いやすいという意味です。つまり売買代金の大きな銘柄を選定して売買することは、利益を出しやすい銘柄選定のひとつの戦略だと言えます。

 

    #全データから売買代金が20億円以上の銘柄を抽出する
    print('全データから売買代金が20億円以上の銘柄を抽出する',file=f)
    df['baibai'] = df['close']*df['volume']/100000000
    df_baibai=df.loc[df['baibai']>=20]
    df_baibai=df_baibai.sort_values('baibai',ascending=False)

 

このコードでは、売買代金が20億円以上の銘柄を抽出するためのコードです。

まず df[‘close’]×df[‘volume’]で売買代金を計算し、100000000で割って単位を億にしています。

そしてその結果を新しい列「baibai」を作って、代入しています。

df_baibai=df.loc[df[‘baibai’]>=20]によって、20億円以上の銘柄を抽出して、df_baibaiというサブセットを作っています。

df_baibai=df_baibai.sort_values(‘baibai’,ascending=False) では、baibai列でソートして、降順に並べ替えています。デフォルトでは昇順なので、ascending=Falseとして、降順設定としています。

 

始値→高値 の割合が高い銘柄を抽出

 

ここからは、コード例ですが、株価解析のヒントになるかもしれません。始値から高値までの上昇率が高い銘柄を抽出します。ソートで上昇率の高い銘柄順にしています。さらに東証1部の銘柄に絞っています。

 

   #始値→高値の割合が高い順
    
    df['oh_ratio'] = (df['high']-df['open'])/df['open']*100
    df_oh=df.sort_values('oh_ratio',ascending=False)
    df_oh=df_oh.loc[df_oh['market']=='東証1部' ]

 

locの使い方まとめ

以上のようにpandasのlocを使えば、条件による抽出が簡単に行えます。

しかし、条件を記述する文が難しい、あるいはできない場合もあるので、スピードが遅くなることが我慢できるのであれば、ndarrayにして、for文で抽出するという手があります。

    #ndarrayでfor文を使った抽出例
    
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df)
    oh_ratio=[]
    for i in range(len(df)):
        oh_ratio.append(((high[i]-open[i])/open[i])*100)
        
    df['oh_ratio2']=oh_ratio
    
    df_oh=df.sort_values('oh_ratio2',ascending=False)
    df_oh=df_oh.loc[df_oh['market']=='東証1部' ]

    #表示整形するために再度、ndarray化する
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df_oh)
    oh_ratio = df_oh['oh_ratio'].values
    

    #データを整形して桁をそろえて表示させる【始値→高値の上昇率ソートで抽出】
    print('{:5} {:4} {:2} {:^30} {:>10} {:>10} {:>10} {:>10} {:>10} {:<15} {:>10} {:<20}'.format ('No','code','no','name','open','high','low','close','volume','market','date','high-open ratio'),file=f) print('-'*120,file=f) start=1 end=50 start=start-1 end=end-1 for i in range(len(df_oh)): if i>=start and i<=end: print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:>10} {:>4.1f}'.format
            (i+1,code[i],no[i],haba_cal(name[i],haba=30,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i].strip(),haba=15,embedded='',
            hoko='<'),date[i],oh_ratio[i]),file=f)
        else:
            break
    print('*'*120,file=f)

 

このようにndarrayに変換してからfor文で処理を行う方法が理解できると、どんな処理(加工)もできるので、鬼に金棒かもしれません。ただし、速度の犠牲やコードの見やすさは犠牲にしていますので、どうしてもpandasのlocで条件が書ききれないと思ったら、あまり悩まずにこの手を使うのもありだと思います。

今回の全ソースコード

 

import datetime
import pandas as pd
from pandas import DataFrame,Series
import requests
import os
import sys
import numpy as np

pd.set_option('display.width', 250)         #表示幅を変更
pd.set_option("display.max_columns", 25)    #最大表示列数
pd.set_option("display.max_colwidth", 80)   #列の最大文字数
pd.set_option("display.max_rows", 200)      #最大表示行数


def get_east_asian_width_count(text):
    import unicodedata 
    #半角と全角の幅を求める(半角:1 全角:2)
    count = 0
    for c in text:
        if unicodedata.east_asian_width(c) in 'FWA':
            count += 2
        else:
            count += 1
    return count

def haba_cal(s,haba=20,embedded='',hoko='<'):
    #半角と全角混在でも幅を揃える文字を返す
    #s:文字列 haba:揃えたい文字数 
    #embeged:埋め込みい文字 
    #hoko:左寄せ< 中央揃え^ 右寄せ>
    n=get_east_asian_width_count(s)
    new_haba=haba+len(s)-n
    ss='{:'+embedded+hoko+str(new_haba)+'}'
    final_s=ss.format(s)
    return final_s

    #F - East Asian Full-width
    #H - East Asian Half-width
    #W - East Asian Wide
    #Na - East Asian Narrow (Na)
    #A - East Asian Ambiguous (A)
    #N - Not East Asian

def download_mujinzo(date_str='2020-08-21',folder='./'):
    #無尽蔵さんから指定日付の株価データをダウンロード
    year, month, day = date_str.split('-')
    fileName = 'T{0}{1}{2}.zip'.format(year[-2:], month, day)
    url= 'http://mujinzou.com/d_data/{0}d/{1}_{2}d/{3}'.format(year, year[-2:], month, fileName)  #2020
    filename = url.split('/')[-1]
    r = requests.get(url, stream=True)

    if  "" in str(r):
        filepath=os.path.join(folder, filename)
        with open(filepath, 'wb') as f:
            for chunk in r.iter_content(chunk_size=1024):
                if chunk:
                    f.write(chunk)
                    f.flush()
        print(f'Download Success:{filename}')
    else:
        print('Open Error')
    return filename

def kabu_df2ndarray(df):
    code   = df['code'].values
    no     = df['no'].values
    name   = df['name'].values
    open   = np.array(df['open'].values, dtype=np.int64)    #float→intに変換
    high   = np.array(df['high'].values, dtype=np.int64)    #float→intに変換
    low    = np.array(df['low'].values, dtype=np.int64)     #float→intに変換
    close  = np.array(df['close'].values, dtype=np.int64)   #float→intに変換
    volume = df['volume'].values
    market = df['market'].values
    date   = df['date'].values
    return code,no,name,open,high,low,close,volume,market,date

def kabu_seikei(df,start=1,end=30):
    #DataFrameをndarrayに変換してリストのように扱えるようにする
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df)

    start=start-1
    end=end-1
    
    #データを整形して桁をそろえて表示させる
    print('{:5} {:4} {:2} {:^20} {:>10} {:>10} {:>10} {:>10} {:>10} {:<10} {:}'.format ('No','code','no','name','open','high','low','close','volume','market','date')) print('-'*120) for i in range(len(df)): if i>=start and i<=end:
            print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:}'.format
            (i+1,code[i],no[i],haba_cal(name[i].split(' ')[1].strip(),haba=20,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i],haba=10,embedded='',hoko='<'),date[i])) else: break def kabu_seikei2(df,start=1,end=30,f=''):#fはファイルオブジェクトを渡すこと #DataFrameをndarrayに変換してリストのように扱えるようにする(出力はファイルへ) code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df) start=start-1 end=end-1 #データを整形して桁をそろえて表示させる print('{:5} {:4} {:2} {:^20} {:>10} {:>10} {:>10} {:>10} {:>10} {:<15} {:>10}'.format
        ('No','code','no','name','open','high','low','close','volume','market','date'),file=f)
    print('-'*100,file=f)
    for i in range(len(df)):
        if i>=start and i<=end: print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:>10}'.format
            (i+1,code[i],no[i],haba_cal(name[i].split(' ')[1].strip(),haba=20,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i].strip(),haba=15,embedded='',
            hoko='<'),date[i]),file=f)
        else:
            break
    print('*'*120,file=f)
if __name__ == '__main__':
    #スクリプトを実行するバッチファイルを生成
    fname=os.path.splitext(os.path.basename(sys.argv[0]))[0]+'.bat'
    with open(fname,'w') as f:f.write(f'python "{sys.argv[0]}" \n pause \n')

    #print()で出力させるファイルを設定
    f = open('memo.txt','w')

    #日付と保存フォルダを指定して、株価データをダウンロード
    date_str='2020-08-21'   #ここを変更して利用する
    year, month, day = date_str.split('-')
    fileName = 'T{0}{1}{2}.zip'.format(year[-2:], month, day)
    
    #ダウンロードファイルの存在を確認。なければ、ダウンロードを実行
    if not os.path.exists(fileName):
        fileName=download_mujinzo(date_str=date,folder='./')
    else:
        print(f'ファイル:{fileName}はすでにダウンロード済みです')
    print('*'*30,file=f)
    print(f'Download Filename:{fileName}',file=f)
    print('*'*30,file=f)
    
    
    #zipファイルを読み込み、DataFrameに変換する → その後列名をセットする
    df=pd.read_csv(fileName,encoding="shift_jis",header=None)    
    df.columns=['date','code','no','name','open','high','low','close','volume','market']#列名をセット

    df=df.sort_values('code')

    #データフレームの内容を表示    year, month, day = date_str.split('-')

    print('*'*10,'DataFrame information','*'*10)
    print(df.info())    #各列の情報
    
    print('*'*10,'DataFrame first 10 data ','*'*10)
    print(df.head(50))  #先頭10個のデータ表示
    
    print('*'*30)

    #csvファイルに書き出す 銘柄リスト出力
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df)
    name2=[]
    for i in range(len(df)):
        try:
            name2.append(name[i].split(' ')[1].strip())
            #name2.append(haba_cal(name[i].split(' ')[1].strip(),haba=20,embedded='',hoko='<')) except: name2.append(name[i]) df['name2']=name2 savefile='./code_list.csv' df[['code','name2']].to_csv(savefile,encoding='utf_8_sig') #UTF-8 BOM付ファイル書き込み エクセルで文字化け防止 #市場の種類をリスト化 market_list=df['market'].unique() print(market_list,file=f) #['東証1部' '東証2部' 'JAQ' '東証マ' '名古セ' '名証1部' '外国' '名証2部' '名証セ' '東証M'] print(df['market'].value_counts(),file=f) #東証1部 2462 #JAQ 696 #東証2部 488 #東証マ 335 #名証2部 51 #名証セ 12 #名証1部 6 #外国 2 #名古セ 1 #東証M 1 #データを整形して桁をそろえて表示させる【東証2部】 print('データを整形して桁をそろえて表示させる【東証1部】',file=f) df1=df.loc[df['market']=='東証1部' ] kabu_seikei(df1,1,30) kabu_seikei2(df1,1,30,f=f) #データを整形して桁をそろえて表示させる【東証2部】 print('データを整形して桁をそろえて表示させる【東証2部】',file=f) df2=df.loc[df['market']=='東証2部' ] kabu_seikei(df2,1,30) kabu_seikei2(df2,1,30,f=f) #データを整形して桁をそろえて表示させる【東証マ】 print('データを整形して桁をそろえて表示させる【東証マ】',file=f) dfm=df.loc[df['market']=='東証マ' ] kabu_seikei(dfm,1,30) kabu_seikei2(dfm,1,30,f=f) #データを整形して桁をそろえて表示させる【JAQ】 print('データを整形して桁をそろえて表示させる【JAQ】',file=f) dfJ=df.loc[df['market']=='JAQ' ] kabu_seikei(dfJ,1,30) kabu_seikei2(dfJ,1,30,f=f) #複数の市場のデータを抽出する 【東証1部】or【東証2部】 print('複数の市場のデータを抽出する 【東証1部】or【東証2部】',file=f) df1_2=df.loc[(df['market']=='東証1部')| (df['market']=='東証2部')] kabu_seikei(df1_2,1,100) kabu_seikei2(df1_2,1,100,f=f) #全データから売買代金が20億円以上の銘柄を抽出する print('全データから売買代金が20億円以上の銘柄を抽出する',file=f) df['baibai'] = df['close']*df['volume']/100000000 df_baibai=df.loc[df['baibai']>=20]
    df_baibai=df_baibai.sort_values('baibai',ascending=False)
    
    #print(df_baibai)
    
    code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df_baibai)
    baibai = df_baibai['baibai'].values


    start=1
    end=50

    start=start-1
    end=end-1
    
    #データを整形して桁をそろえて表示させる【売買代金条件で抽出】
    print('{:5} {:4} {:2} {:^20} {:>10} {:>10} {:>10} {:>10} {:>10} {:<15} {:>10} {:<20}'.format ('No','code','no','name','open','high','low','close','volume','market','date','売買代金[億]'),file=f) print('-'*120,file=f) for i in range(len(df_baibai)): if i>=start and i<=end: print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:>10} {:>10.1f}'.format
            (i+1,code[i],no[i],haba_cal(name[i].split(' ')[1].strip(),haba=20,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i].strip(),haba=15,embedded='',
            hoko='<'),date[i],baibai[i]),file=f) else: break print('*'*120,file=f) #始値→高値の割合が高い順 df['oh_ratio'] = (df['high']-df['open'])/df['open']*100 df_oh=df.sort_values('oh_ratio',ascending=False) df_oh=df_oh.loc[df_oh['market']=='東証1部' ] code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df_oh) oh_ratio = df_oh['oh_ratio'].values #データを整形して桁をそろえて表示させる【始値→高値の上昇率ソートで抽出】 print('{:5} {:4} {:2} {:^30} {:>10} {:>10} {:>10} {:>10} {:>10} {:<15} {:>10} {:<20}'.format ('No','code','no','name','open','high','low','close','volume','market','date','high-open ratio'),file=f) print('-'*120,file=f) start=1 end=50 start=start-1 end=end-1 for i in range(len(df_oh)): if i>=start and i<=end: print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:>10} {:>4.1f}'.format
            (i+1,code[i],no[i],haba_cal(name[i],haba=30,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i].strip(),haba=15,embedded='',
            hoko='<'),date[i],oh_ratio[i]),file=f) else: break print('*'*120,file=f) #ndarrayでfor文を使った抽出例 code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df) oh_ratio=[] for i in range(len(df)): oh_ratio.append(((high[i]-open[i])/open[i])*100) df['oh_ratio2']=oh_ratio df_oh=df.sort_values('oh_ratio2',ascending=False) df_oh=df_oh.loc[df_oh['market']=='東証1部' ] #表示整形するために再度、ndarray化する code,no,name,open,high,low,close,volume,market,date = kabu_df2ndarray(df_oh) oh_ratio = df_oh['oh_ratio'].values #データを整形して桁をそろえて表示させる【始値→高値の上昇率ソートで抽出】 print('{:5} {:4} {:2} {:^30} {:>10} {:>10} {:>10} {:>10} {:>10} {:<15} {:>10} {:<20}'.format ('No','code','no','name','open','high','low','close','volume','market','date','high-open ratio'),file=f) print('-'*120,file=f) start=1 end=50 start=start-1 end=end-1 for i in range(len(df_oh)): if i>=start and i<=end: print('{:5} {} {} {} {:10} {:10} {:10} {:10} {:10} {:10} {:>10} {:>4.1f}'.format
            (i+1,code[i],no[i],haba_cal(name[i],haba=30,embedded='',hoko='<')
            ,open[i],high[i],low[i],close[i],volume[i],haba_cal(market[i].strip(),haba=15,embedded='',
            hoko='<'),date[i],oh_ratio[i]),file=f)
        else:
            break
    print('*'*120,file=f)

    f.close()   #print()用ファイルをクローズ
    
    #一時停止のための入力待ち
    a=input('Program finished!')
    

 

pandasはいいね~

今回、locにフォーカスを当てて記事を書きましたが、奥が深いので、さまざまなテクニックも紹介したいと思います。基礎的な以下の記事もぜひぜひ参考にしてくださいね。

 

python§03:データ分析ライブラリーpandasを徹底解説します

コメント

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