statsmodels is one of the complex modules for PyInstaller and that is because it depends on some other modules that they would mess the PyInstaller import graph. My solution might not look optimized but would do the job.
hidden-imports would only tell PyInstaller to look for that module but sometimes it can't trace module dependencies (like DLLs, external py files, etc.). so for statsmodels it won't help.
The overall procedure is to first tell PyInstaller to don't trace statsmodels with exclude-module and feed the module manually to the final executable.
Also, we need to bundle some modules like (numpy, pandas, etc) with Tree class.
I'm using Python 3.7.4 with latest PyInstaller 3.5.
Suppose below example taken from official docs.
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf
dat = sm.datasets.get_rdataset("Guerry", "HistData").data
results = smf.ols('Lottery ~ Literacy + np.log(Pop1831)', data=dat).fit()
print(results.summary())
Use below spec file (I'm using Python's venv called env next to the script)
# -*- mode: python -*-
block_cipher = None
a = Analysis(['script.py'],
pathex=['<root_project_path>'],
binaries=[],
datas=[],
hiddenimports=['six', 'fractions', 'csv', 'pytz', 'timeit'],
hookspath=[],
runtime_hooks=[],
excludes=['statsmodels'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
a.datas += Tree("./env/Lib/site-packages/statsmodels", prefix="statsmodels")
a.datas += Tree("./env/Lib/site-packages/numpy", prefix="numpy")
a.datas += Tree("./env/Lib/site-packages/pandas", prefix="pandas")
a.datas += Tree("./env/Lib/site-packages/scipy", prefix="scipy")
a.datas += Tree("./env/Lib/site-packages/dateutil", prefix="dateutil")
a.datas += Tree("./env/Lib/site-packages/patsy", prefix="patsy")
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='script',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
runtime_tmpdir=None,
console=True )
And finally generate your executable with:
pyinstaller script.spec