ユーザー定義関数 (UDFs)#
このチュートリアルではユーザー定義関数(User Defined Functions, UDFs)の作成方法を学びます。
注釈
UDFが使えるのは、現在Windowsのみです。
引数と戻り値の制御方法の詳細については コンバーターおよびオプション をご覧ください。
デコレーターとそのオプションについての包括的な概要は、対応するAPIドキュメントをご覧ください: UDF デコレーター 。
Excelの準備(1回だけ)#
ファイル > オプション > トラスト センター > トラスト センターの設定 > マクロの設定
にあるVBA プロジェクト オブジェクト モデルへのアクセスを信頼する
を有効化します。この作業は一度だけです。また、関数のインポートでのみ必要になります。つまり、エンド ユーザー(訳注: 開発者が作成したUDFを利用するユーザー)には関係ありません。コマンド プロンプトからアドインをインストールします:
xlwings addin install
(see アドインおよび設定)。
ワークブックの準備#
コマンドプロンプトから xlwings quickstart myproject
を実行すれば、簡単に新しいプロジェクトを始められます (see コマンド ライン クライアント(CLI))。この方法で作成したワークブックは、xlwingsへの参照が設定されています。
シンプルなUDF#
quickstart
で作成した場合、デフォルトの設定では、Pythonのソース ファイルは次のようになっていなければなりません:
Excel ファイルと同じディレクトリ
Excelファイルと同じ名前で、拡張子が
.xlsm
ではなく.py
デフォルトの設定以外では、xlwingsのリボンの UDF Modules
で特定のモジュールを指定することもできます。
myproject.xlsm
というワークブックを例に説明します。 myproject.py
というファイルに次のコードを書きます:
import xlwings as xw
@xw.func
def double_sum(x, y):
"""Returns twice the sum of the two arguments"""
return 2 * (x + y)
次に、xlwingsタブの
Import Python UDFs
をクリックして、myproject.py
への変更を取り込みます。セルに数式
=double_sum(1, 2)
を入力すれば、正しい結果を取得できます:(トリプル クォートで囲われた)docstringは、Excelでは関数の説明に表示されます。
注釈
再度インポートする必要があるのは、関数の引数や名前を変えた時のみです。
コードの変更は自動的(例えば、
Ctrl-Alt-F9
でトリガーされる、数式の次回計算時)に取り込まれますが、コード内でインポートしたモジュールの変更は取り込まれません。これはPythonのインポートの挙動によるものです。全てをフレッシュな状態にするには、Restart UDF Server
をクリックします。xlwingsが
@xw.func
デコレーターが使用するのは、関数をExcelに取り込むときにのみです。デコレーターは、どの関数に対してxlwingsがVBAラッパー関数を作るかを教えますが、関数のPythonでの挙動に影響を与えることはありません。
配列数式: 効率的に計算する#
多数のセルから個別に数式を呼び出すよりも、1つの大きな配列数式を呼び出す方が効率的です。一般的に配列数式の使用は良い方法であり、パフォーマンスに問題があるケースでは特に効果があります。
単独セル以外にも、ExcelのRangeを関数の引数にできます。その際、RangeはPythonではリストのリストになります。
例えば、Rangeに含まれる全てのセルに1を加える関数は次のようになります:
@xw.func
def add_one(data):
return [[cell + 1 for cell in row] for row in data]
この数式を使うには
Import Python UDFs
ボタンをもう一度押しますA1:B2
に値を入力しますD1:E2
を選択します数式
=add_one(A1:B2)
を入力しますCtrl+Shift+Enter
を押し、配列数式を作ります。全てを正しく行えば、次のスクリーンショットのように、数式が波括弧で囲われます。

配列の次元数: ndim#
上記の数式の問題点は、例えば [[1, 2], [3, 4]]
のような入れ子状のリストのような"2次元の"インプットを前提としていることです。したがって、この数式を単独セルで使うと、次のエラーが出ます: TypeError: 'float' object is not iterable
。
引数が単独セル、列/行、2次元のRangeのいずれの場合でも、Excelから常に2次元の配列を取得するには、上記の数式を次のように拡張します:
@xw.func
@xw.arg('data', ndim=2)
def add_one(data):
return [[cell + 1 for cell in row] for row in data]
NumpyおよびPandasでの配列数式#
科学計算分野のPythonのエコシステムを最大限に活用するために、Numpyの配列やPandasのDataFrameをUDFで使用する方法を紹介します。
Numpyの配列を使った行列の掛け算の数式を定義するには、次のように記述します:
import xlwings as xw
import numpy as np
@xw.func
@xw.arg('x', np.array, ndim=2)
@xw.arg('y', np.array, ndim=2)
def matrix_mult(x, y):
return x @ y
注釈
Python 3.5以上、Numpy 1.10以上ではない場合、 x @ y
の代わりに x.dot(y)
を使ってください。
Pandasの活用方法の良い例は、配列ベースの CORREL
関数です。例えば、Excelの CORREL
は、2つのデータセット間の相関係数は計算できますが、複数の時系列データの相関行列は手軽には計算できません。Pandasを使えば、配列ベースの CORREL2
関数をたった一行で書けます。
import xlwings as xw
import pandas as pd
@xw.func
@xw.arg('x', pd.DataFrame, index=False, header=False)
@xw.ret(index=False, header=False)
def CORREL2(x):
"""Like CORREL, but as array formula for more than 2 data sets"""
return x.corr()
デコレーター@xw.argと@xw.ret#
これらのデコレーターはUDFに対するもので、Range
オブジェクトに対してどのような options
メソッドを適用するかを指定します。デコレーターにより、関数の引数(@xw.arg
)や戻り値(@xw.ret
)に対してコンバーターとそのオプションを適用できます。例えば、引数 x
をPandasのDataFrameに変換し、戻り値のインデックスを省略するには、次のようにします:
@xw.func
@xw.arg('x', pd.DataFrame)
@xw.ret(index=False)
def myfunction(x):
# x is a DataFrame, do something with it
return x
詳細は コンバーターおよびオプション のドキュメントをご覧ください
動的配列数式#
注釈
Excelが新しいネイティブ動的配列をサポートしているなら、特別な事は何も必要ありませんし、 expand
デコレーターを使用すべきではありません! =UNIQUE()
式が利用可能なら、Excelはネイティブ動的配列をサポートしています。ネイティブ動的配列は2018年9月末にOffice 365のInsider Fastに導入されました。
先述のとおり、Excelで配列数式を使うには、事前に特定した次元に応じて、結果配列を選択し、数式を入力し、 最後に Ctrl-Shift-Enter
を押す必要があります。実際、時系列データのような動的配列を扱うときには特に、煩わしいプロセスです。v0.10から、xlwingsでは動的UDF拡張が利用可能になりました:
単純な例でシンタックスとUDF拡張の効果を説明してみます。以下をご覧ください。
import numpy as np
@xw.func
@xw.ret(expand='table')
def dynamic_array(r, c):
return np.random.randn(int(r), int(c))


注釈
配列数式の拡張はプロンプトせずにセルを上書きします。
v0.15.0より前では、Volatile関数、(例えば
=TODAY
のような関数)は引数には使えませんでした。v0.15から、Volatile関数を引数に使えるようになりましたが、UDFは1回以上呼び出されます。動的配列はv0.15.0からリファクタリングされ、適切なレガシー配列になりました:xlwings >= v0.15.0で動的配列を編集するには、左上セルで
Ctrl-Shift-Enter
を入力する必要があります。最初に数式を入力するときには、そのような必要はありません。
Docstrings#
関数および引数x、yのdocstringを書いて、Excelの関数ウィザードに表示させる方法を以下の例で示します。
import xlwings as xw
@xw.func
@xw.arg('x', doc='This is x.')
@xw.arg('y', doc='This is y.')
def double_sum(x, y):
"""Returns twice the sum of the two arguments"""
return 2 * (x + y)
"caller" 引数#
UDF呼び出し元セルが必要なこともあります。 xlwingsには予約済み引数 caller
があり、UDFの呼び出し元セルを表すxlwingsのrangeオブジェクトとして利用できます:
@xw.func
def get_caller_address(caller):
# caller will not be exposed in Excel, so use it like so:
# =get_caller_address()
return caller.address
注意: caller
はExcelからは見えませんが、xlwingsが自動的に生成します。
"vba" キーワード#
vba
キーワードを使えば、pywin32オブジェクトの形のあらゆるExcel VBAオブジェクトにアクセスすることができます。例えば、シート オブジェクトを CodeName
で参照するには次のようにします:
@xw.func
@xw.arg('sheet1', vba='Sheet1')
def get_name(sheet1):
# call this function in Excel with:
# =get_name()
return sheet1.Name
注意: vba
引数はExcelからは見えませんが、xlwingsが自動的に生成します。
マクロ#
Windowsでは、 RunPython でマクロを呼び出す代わりに、 @xw.sub
デコレーターを使うこともできます。
import xlwings as xw
@xw.sub
def my_macro():
"""Writes the name of the Workbook into Range("A1") of Sheet 1"""
wb = xw.Book.caller()
wb.sheets[0].range('A1').value = wb.name
Import Python UDFs
をクリックすれば、 Alt + F8
か、登録したボタン等からこのマクロを実行できます。後者の方法を説明します。まずは ファイル > オプション > リボンのユーザー設定
で、 開発
タブが選択されている事を確認してください。 開発
タブ内の 挿入 > フォーム コントロール
から、ボタンを挿入します。ボタン描画後、マクロを割り当てるプロンプトが表示されるので、 my_macro
を選択してください。
VBAからUDFを呼び出す#
インポートした関数はVBAからも利用できます。2次元配列を返す関数の例を以下に示します:
Sub MySub()
Dim arr() As Variant
Dim i As Long, j As Long
arr = my_imported_function(...)
For j = LBound(arr, 2) To UBound(arr, 2)
For i = LBound(arr, 1) To UBound(arr, 1)
Debug.Print "(" & i & "," & j & ")", arr(i, j)
Next i
Next j
End Sub
非同期UDF#
注釈
これは実験的な機能です
バージョン v0.14.0 で追加.
xlwingsには、Excelの非同期関数を簡単に書く方法もあります。 非期関数は、即時に #N/A waiting...
を返します。関数が戻り値を待つ間、Excelを使って他のことができます。まが、戻り値が取得でき次第、セルの値はアップデートされます。
現在利用可能なモードは async_mode='threading'
のみで、例えば、ウェブからAPIでデータを利用する場合のようなI/Oバウンドなタスクに有用です。
関数のデコレーターに対応する引数を入れれば、簡単に非同期関数を作れます。次の例では、時間がかかるI/Oバウンドなタスクを、 time.sleep
でシミュレートしています:
import xlwings as xw
import time
@xw.func(async_mode='threading')
def myfunction(a):
time.sleep(5) # long running tasks
return a
他のxlwings関数と同様に、セルに =myfunction("abcd")
を入力すればこの関数を利用できます(もちろん、関数をインポートした後でですが)
なお、xlwingsはExcel 2010で導入されたネイティブな非同期関数を使用していないので、xlwingsの非同期関数は全バージョンのExcelで使えます。