I had a whole Python month on my blog right here. I establish a pattern, which I call the gateway class, which is a COM-enabled Python class, it will register itself if run from the command line and once registered is instantiated with CreateObject("foo.bar").
Here is a good example of VBA calling a Python class that uses some SciPy functions:
import numpy as np
import pandas as pd
from scipy.stats import skewnorm
class PythonSkewedNormal(object):
_reg_clsid_ = "{1583241D-27EA-4A01-ACFB-4905810F6B98}"
_reg_progid_ = 'SciPyInVBA.PythonSkewedNormal'
_public_methods_ = ['GeneratePopulation', 'BinnedSkewedNormal']
def GeneratePopulation(self, a, sz):
# <https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
np.random.seed(10)
# <https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html>
return skewnorm.rvs(a, size=sz).tolist()
def BinnedSkewedNormal(self, a, sz, bins):
# <https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html>
np.random.seed(10)
# <https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html>
pop = skewnorm.rvs(a, size=sz)
bins2 = np.array(bins)
bins3 = pd.cut(pop, bins2)
table = pd.value_counts(bins3, sort=False)
table.index = table.index.astype(str)
return table.reset_index().values.tolist()
if __name__ == '__main__':
print("Registering COM server...")
import win32com.server.register
win32com.server.register.UseCommandLine(PythonSkewedNormal)
And the calling VBA code
Option Explicit
Sub TestPythonSkewedNormal()
Dim skewedNormal As Object
Set skewedNormal = CreateObject("SciPyInVBA.PythonSkewedNormal")
Dim lSize As Long
lSize = 100
Dim shtData As Excel.Worksheet
Set shtData = ThisWorkbook.Worksheets.Item("Sheet3") '<--- Change sheet to your circumstances
shtData.Cells.Clear
Dim vBins
vBins = Array(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5)
' Stop
Dim vBinnedData
vBinnedData = skewedNormal.BinnedSkewedNormal(-5, lSize, vBins)
Dim rngData As Excel.Range
Set rngData = shtData.Cells(2, 1).Resize(UBound(vBins) - LBound(vBins), 2)
rngData.Value2 = vBinnedData
'Stop
End Sub
Full commentary can be found at the original blog entry here.
The advantage here is that there is no shelling. When the code it returns, you know it has finished. With shelling, one has to check if the shelled process has ended, etc. This gateway class is much better, in my humble opinion.