Is there a way to call a Python code in Excel-VBA?

前端 未结 4 1844
暖寄归人
暖寄归人 2020-12-13 10:51

I have an Excel file (Main.xlsm) containing macros. I have a Python file (python.py) to generate a subsidiary Excel file (sub.xlsx) which I would further call in the macros

4条回答
  •  情书的邮戳
    2020-12-13 11:10

    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 once has to check if the shelled process has ended etc. This gateway class is much better IMHO.

提交回复
热议问题