こんにちは、hokkyokunです。
VBA学習者向けに
「Pythonいいって聞いたけど実際どうなの?」
という疑問に具体的な目的を処理するコードで比べてみます。
具体的な目的の処理
具体的な目的は以下です。
このVOOの株価データから
月利(月初から月末にかけての値動きの割合)を計算して可視化する。
が大きな目的です。
これを達成するために以下の細かな目標に分けました。
それぞれ記事にしています。
- 2次元データを扱うオブジェクトを取得
- 2022年3月~2023年2月までの絞り込み
- 月末月初の日付を取得
- 月末月初の値と各月の月利(月当たりの価格変動)
を計算して表で取得する
↑今回処理するのはこれ! - 取得したデータで簡単なグラフを作成する
前回は③の処理を
PythonとVBAでそれぞれ書いてみました。
正直、Pythonの方が確実に楽です!!
今回は④月末月初の値と各月の月利(月当たりの価格変動)を計算します。
VBAでのコード
前回は月末月初の日付を取得し、
配列に格納しました。
↓前回のコードです。
Sub VBA2()
Dim ws As Worksheet
Dim table As ListObject
Dim maxRow As Long
'シートの取得
Set ws = Workbooks("VOO.csv").Worksheets(1)
'表をテーブル化して取得
'既にテーブル化している場合は、再度テーブル化するとエラーが出るので回避
Set table = ws.Cells(1, 1).ListObject
If table Is Nothing Then
Set table = ws.ListObjects.Add(xlSrcRange, ws.Cells(1, 1).CurrentRegion, , xlYes)
End If
Dim r As Range
Dim sDate, eDate As Date
Dim dateLists As Variant
Dim i As Long
For i = 1 To table.ListColumns("Date").DataBodyRange.SpecialCells(xlCellTypeVisible).Count
With table.ListColumns("Date")
'最初(2022年3月)の開始日は予め入れておく
'ここではまだ配列に入れない。
'2022年3月の終了日を取得出来てから配列に入れる
If i = 1 Then
sDate = .DataBodyRange(1)
'最後(2023年2月)の終了日は最終行の値を入れる
'2023年2月の開始日と終了日を配列に入れる
ElseIf i = .DataBodyRange.SpecialCells(xlCellTypeVisible).Count Then
eDate = .DataBodyRange(i).Value
Call add_Elm(dateLists, Array(sDate, eDate))
'日付を一つ一つ確認していき
'月が変われば(例えば2022年4月1日は一つ前が2022年3月31日。よって月が変わる)
'終了日を取得し、前回取得しておいた開始日とあわせて配列に格納する
'次の開始日をここで取得しておく
ElseIf Month(.DataBodyRange(i).Value) <> Month(.DataBodyRange(i - 1).Value) Then
eDate = .DataBodyRange(i - 1).Value
Call add_Elm(dateLists, Array(sDate, eDate))
sDate = .DataBodyRange(i).Value
End If
End With
Next
End Sub
かなりややこしいですね。
ただ、苦労して表のテーブル化と月末月初日を取得したので
ここからは結構楽です。
↓こんな感じで書いてみました。
Sub VBA2()
'=======================================================
'~
'前回と同じ。省略。
'=======================================================
Dim sVal, eVal As Double
Dim datelist As Variant
Dim j As Long
'シートに月末月初の値と月利を転記
With ThisWorkbook.Worksheets(1)
'見出しを作成
.Cells(1, 1).Value = "月初日"
.Cells(1, 2).Value = "月末日"
.Cells(1, 3).Value = "月初の値"
.Cells(1, 4).Value = "月末の値"
.Cells(1, 5).Value = "月利"
j = 2
For i = 0 To UBound(dateLists)
sDate = dateLists(i)(0)
sVal = table_Loc(table, "Date", sDate, "Adj Close")
eDate = dateLists(i)(1)
eVal = table_Loc(table, "Date", eDate, "Adj Close")
.Cells(j, 1).Value = sDate
.Cells(j, 2).Value = eDate
.Cells(j, 3).Value = sVal
.Cells(j, 4).Value = eVal
.Cells(j, 5).Value = (sVal - eVal) / eVal * 100
j = j + 1
Next
End With
End Sub
コード自体は楽なのですが、
月末月初日から値を取得するためにオリジナルの関数(table_Loc関数)を使っています。
特定の列を参照し、目的の値を取得するオリジナルの関数です。
具体的には「Date」列から特定の月末月初日を指定し、
その行の「Adj Close」列の値を取得します。
Function table_Loc(ByVal table As ListObject, ByVal index_Col As String, ByVal index_Val As Variant, ByVal col_Name As String)
Dim i As Long
For i = 1 To table.ListColumns(index_Col).DataBodyRange.Count
If table.ListColumns(index_Col).DataBodyRange(i).Value = index_Val Then
Exit For
End If
Next
table_Loc = table.ListColumns(col_Name).DataBodyRange(i).Value
End Function
詳細な使い方は下記の記事にありますので
よければ使ってみてください。
テーブルを効率的に扱うためには結構役に立ちます。
私も自分の仕事でよく使っている関数です。
話がそれましたが、
プログラムを動かすと以下のような表形式で値を取得できました。
Pythonでのコード
コードが短いので前回のも含めて「すべて」載せます。
############前回と同じ#########################################################
import pandas as pd
import datetime as dt
#ファイルを開いてデータフレームを取得
df=pd.read_csv("./VOO.csv")
#日付のデータ型を日付型に変える
df["Date"]=pd.to_datetime(df["Date"],format="%Y-%m-%d")
#日付をインデックスにして検索や絞り込みをしやすくする
df=df.set_index("Date",drop=False)
#日付で絞る
df=df.loc[dt.datetime(2022,3,8):dt.datetime(2023,2,28)]
###############################################################################
#データフレームを月毎に区分けして月初日、月末日およびそれぞれの値(Adj Close列の値)を
#新しいデータフレーム(df_result)に格納
df_result=pd.DataFrame()
df_result["月初日"]=df.resample("M").first()["Date"].tolist()
df_result["月末日"]=df.resample("M").last()["Date"].tolist()
df_result["月初の値"]=df.resample("M").first()["Adj Close"].tolist()
df_result["月末の値"]=df.resample("M").last()["Adj Close"].tolist()
df_result["月利"]=(df_result["月初の値"]-df_result["月末の値"])/df_result["月末の値"]*100
df_result
若干目的に合わせて前回のコードを少し変えましたが、
概ね前回の流れを引き継いで書いています。
すべて載せてもわずかな行でコーディングできました。
コーディングの違いは?
- 表から値取得して別の表にまとめる処理の工程数の差
VBAは23行+関数一つ(9行)で処理
Pythonは7行 - VBAは関数を自分で作らないといけないが
Pythonはすでに用意されているので作成不要
今回はそこまでコーディングの難易度に違いは感じられません
ただ、VBAは表から特定の値を取得する関数がないので
自分で作りました。
今回使用したtable_Loc関数はVBAでツールを作るときによく使っています。
もしよければ記事を確認して使用してみてください。結構便利ですよ。
こちらから確認できます。
VBAは考えて、コーディングして、デバッグして、で30分くらいはかかりましたが
Pythonは5分くらいで作りました。
それくらいPythonは楽ちんでした。
Pandasという機能が詰まったライブラリを使用していますが、
使い方に少し慣れが必要です。
が、Pythonは優秀なエディター(今回、ジュピターラボを使いました)もただで使えるので
少しずつ動きを確認しながら処理すれば初心者でも思っているよりも簡単に使えます。
VBAとPythonどちらを使えばよいか?
ケースバイケースですが
私は以下のように使い分けています。
VBAを使うケース
- エクセル、アウトルックの操作をする
- Pythonを使用できる環境構築ができていない
- 人に配布する
エクセル、アウトルックの操作をする
エクセル、アウトルックの操作は
なんだかんだVBAの方が便利な場面もたくさんあります。
例えばPythonでもエクセルの操作はできますが、
セルの幅を自動調整するオートフィットは直接操作できません。
VBAなら一行ですね。
Pythonを使用できる環境構築ができていない
また、環境構築ができていない場面ではVBAが気楽です。
なぜならエクセルが使える環境≒VBAが使える環境です(全部ではないですが…)
なので、ちょっとしたプログラムであれば、VBAでコーディングしちゃいます。
人に配布する
最後、人への配布ですが、
VBAのコードは読めるがPythonは無理って人は結構います。
なので、人に渡すものはVBAで渡した方が自分でメンテナンスしてくれる確率も高まります。
Pythonを使うケース
- 複雑なデータ処理
- Webから情報を取得する、操作をする
複雑なデータ処理
今回見たようにVBAだと処理が複雑になりますが
Pythonだと簡単にコーディングできたりします。
コーディングが楽だとメンテナンスも簡単になります。
Webから情報を取得する、操作をする
インターネット操作を自動化し、何らかの情報取得やデータ入力をする際は
Pythonの方が向いています。
VBAでもこれらの操作は可能ですが、
用意されているライブラリ(あらかじめ用意されたソースコード群)が脆弱です。
例えばグーグルクロームを操作するためには
ドライバーをインストールしなければいけないのですが、
VBAの場合はドライバーの自動更新は基本的にないので、
グーグルクロームがアップデートされるたびに
ドライバーをインストールしなおさなければいけません。
Pythonの場合はドライバーの更新を自動で行うことができるので
メンテナンスがめちゃくちゃ楽です。
結論
結論ですが、VBAはエクセル・アウトルックの自動化ツールとしては超優秀です。
が、それを超える範囲の処理(高度なデータ処理も含めます)は他言語に任せるべきです。
そして他言語の第一候補はPythonです。
理由は圧倒的に文法が理解しやすく、
また、書籍や動画解説のボリュームが他言語よりも明らかに多く、質が高いからです。
VBAをある程度学習出来たら
是非、Pythonにチャレンジしてみてください。
次回
次回は月利を簡単なグラフにしたいと思います。