xlwingsでVBAからPythonの関数に配列を引数として渡したりnumpy配列を戻り値として受け取るときの挙動をまとめる
VBAからPythonへの引数の渡し方
xlwingsを使ってVBAのコード内でpython内の関数と引数や戻り値をやり取りするためには,ユーザー定義関数(UDF)を使う必要がある
関数の引数および戻り値
RunPython - xlwings Documentation
技術的には、RunPython
が呼び出す関数に引数を含めることは可能ですが、使い勝手は悪いです。また、RunPython
には戻り値がありません。これらを解決するには、UDFを使いましょう。詳細については ユーザー定義関数 (UDFs) をご覧ください。ただし、UDFを利用できるのは現在Windowsのみとなっています。
というわけでいろいろな組み合わせを試してみる
単一の引数を渡す/戻り値を受け取る
次のようなマクロ有効ブック(.xlsm)を用意する
Pythonのテストプログラム(test.py)
#!python3.6
import os
import numpy as np
import xlwings as xw
@xw.func
def py_type(arr):
msg = str(type(arr))
return msg
@xw.func
def np_shape(arr):
msg = str(np.array(arr).shape)
return msg
@xw.func
def np_arr(arr):
np_arr = type(arr)
return np_arr
if __name__ == "__main__":
wb = xw.Book("myproject.xlsm")
macro = wb.macro('SampleCall')
macro()
VBAのサンプルプログラム
Sub SampleCall()
Dim arr1 As Variant
Dim arr2 As Variant
Dim msg1 As Variant
Dim msg2 As Variant
Dim arr_shape As Variant
arr1 = Range("A1:A10")
msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
End Sub
引数はVariant型で渡し,戻り値もVariant型で受け取る
それではいってみよう
1行1列のセル
1行1列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではfloat型の変数になる
numpy配列には変換できない
float型の変数を戻り値としてVBAで受け取ると,VBA側ではDouble型の変数になる
UDF
マクロ
'マクロ
arr1 = Range("A1")
msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
実行結果
n行1列のセル
n行1列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(n,)になる
大きさ(n,)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(n,)の1次元配列になる
UDF
マクロ
'マクロ
arr1 = Range("A1:A10")
msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
実行結果
1行n列のセル
1行n列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(n,)になる
大きさ(n,)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(n,)の1次元配列になる
UDF
マクロ
'マクロ
arr1 = Range("A1:A10")
msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
実行結果
m行n列のセル
m行n列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(m,n)になる
大きさ(m,n)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(m,n)の2次元配列になる
UDF
マクロ
'マクロ
arr1 = Range("A1:C10")
msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
実行結果
複数の引数を渡す/戻り値をもらう
複数の引数を渡す場合はカンマで区切る
複数の戻り値をもらう場合は1つの配列で受け取る
複数の配列を戻り値に指定した場合は,戻り値に指定した配列をそれぞれ要素に持つ配列(多次元配列)になる
UDF
#!python3.6
import os
import numpy as np
import xlwings as xw
@xw.func
def py_types(arr1,arr2):
msg = str(type(arr1))+str(type(arr2))
return msg
@xw.func
def np_shapes(arr1,arr2):
msg = str(np.array(arr1).shape)+str(np.array(arr2).shape)
return msg
@xw.func
def np_arrs(arr1,arr2):
np_arr1 = np.array(arr1)
np_arr2 = np.array(arr2)
return np_arr1, np_arr2
if __name__ == "__main__":
wb = xw.Book("myproject.xlsm")
macro = wb.macro('SampleCall')
macro()
マクロ
Sub SampleCall()
Dim arr1 As Variant
Dim arr2 As Variant
Dim arr3 As Variant
Dim msg1 As Variant
Dim msg2 As Variant
Dim arr_shape As Variant
arr1 = Range("A1:C10")
arr2 = Range("A1:G5")
msg1 = xlwings_udfs.py_types(arr1, arr2)
msg2 = xlwings_udfs.np_shape(arr1, arr2)
arr3 = xlwings_udfs.np_arr(arr1, arr2)
arr_shape = arr_size(arr3)
MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
End Sub
実行結果
ローカルウィンドウを見てみるとこんな感じ
まとめ
xlwingsでVBAからPythonの関数に引数を渡すときとnumpy配列を戻り値として返すときの挙動をまとめると次のようになる
引数として与える配列 | Pythonでの型 | numpyに変換したときの配列 | 戻り値として受け取る配列 |
(1,1) | float | - | Double |
(n,1) | list | (n,) | (n,) |
(1,n) | list | (n,) | (n,) |
(m,n) | list | (m,n) | (m,n) |
(m1,n1), ..., (mi,ni) | list, ..., list | (m1,n1), ..., (mi,ni) | ((m1,n1), ..., (mi,ni)) |
VBAのVariant型はPythonのlistと同じくらい融通が利く
↓Pythonのインストールについてはこちら
コメント