Python

【VBAとPythonどっちがいい?③】表から値を取得するコードで違いを解説

記事内に商品プロモーションを含む場合があります

こんにちは、hokkyokunです。

VBA学習者向けに
「Pythonいいって聞いたけど実際どうなの?」
という疑問に具体的な目的を処理するコードで比べてみます

具体的な目的の処理

具体的な目的は以下です。

上記は米国のインデックス(S&P500)連動ETFのVOOの株価です。

このVOOの株価データから
月利(月初から月末にかけての値動きの割合)を計算して可視化する

が大きな目的です。

これを達成するために以下の細かな目標に分けました。
それぞれ記事にしています。

  1. 2次元データを扱うオブジェクトを取得
  2. 2022年3月~2023年2月までの絞り込み
  3. 月末月初の日付を取得
  4. 月末月初の値と各月の月利(月当たりの価格変動)
    を計算して表で取得する
    ↑今回処理するのはこれ!
  5. 取得したデータで簡単なグラフを作成する

前回は③の処理を
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

詳細な使い方は下記の記事にありますので
よければ使ってみてください。
テーブルを効率的に扱うためには結構役に立ちます。
私も自分の仕事でよく使っている関数です。

【VBA】超簡単!テーブルから直感的に値を取得する関数(番号を使わない) 概要 テーブルから値を取得する関数を公開します。 横軸方向への検索→列名を指定(列番号特定)縦軸方向への検索→特定の列の特定の...

話がそれましたが、
プログラムを動かすと以下のような表形式で値を取得できました。

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にチャレンジしてみてください。

次回

次回は月利を簡単なグラフにしたいと思います。