SPSS Modelerの拡張モデルノードを使って、時系列予測ライブラリのfbprophetを使ってモデリングとスコアリングを行いました。
■環境
Modeler 18.2.2
python 3.7.7(Anaconda)
fbprophet 0.7.1
Windows 10
サンプルストリーム
事前作業
fbprophetのpipでの導入は、前提となるpystanのWindowsへの導入が難しく、またpystan3からWindowsのサポートがなくなりpystanの導入記事へのリンクも切れてしまっています。
ですので、今回は以下の記事を参考にSPSSの拡張ノードでAnacondaのPythonを利用できるようにしておきます。
Anacondaでconda-foregeからパッケージを導入して、options.cfgの設定まで行っておきます。私の環境ではAnaconda Public Repositoryのパッケージではうまく動きませんでした。
上の手順でつくったバッチファイルなどを使って、conda環境にPATHが入った状態でModelerを起動しておきます。
fbprophetの導入
以下のコマンドでconda-forgeからfbprophetを導入します。
#conda config --append channels conda-forge
conda activate modeler
conda install -c conda-forge fbprophet
conda list
(base) C:\Users\dsuser01>conda activate modeler
(modeler) C:\Users\dsuser01>conda install -c conda-forge fbprophet
Collecting package metadata (current_repodata.json): done
Solving environment: done
## Package Plan ##
environment location: C:\Users\dsuser01\.conda\envs\modeler
added / updated specs:
- fbprophet
The following packages will be downloaded:
package | build
---------------------------|-----------------
arviz-0.11.2 | pyhd8ed1ab_0 1.4 MB conda-forge
bzip2-1.0.8 | h8ffe710_4 149 KB conda-forge
cftime-1.4.1 | py37hda49f71_0 205 KB conda-forge
convertdate-2.3.2 | pyhd8ed1ab_0 37 KB conda-forge
curl-7.75.0 | hf1763fc_0 127 KB conda-forge
cycler-0.10.0 | py_2 9 KB conda-forge
cython-0.29.22 | py37hf2a7229_0 1.9 MB conda-forge
ephem-3.7.7.1 | py37h4ab8f01_1 715 KB conda-forge
fbprophet-0.7.1 | py37h1834ac0_0 567 KB conda-forge
freetype-2.10.4 | h546665d_1 489 KB conda-forge
hdf4-4.2.13 | h0e5069d_1004 1.1 MB conda-forge
hdf5-1.10.6 |nompi_h5268f04_1114 19.6 MB conda-forge
hijri-converter-2.1.1 | pyhd8ed1ab_0 17 KB conda-forge
holidays-0.10.5.2 | pyhd8ed1ab_0 65 KB conda-forge
jpeg-9d | h8ffe710_0 366 KB conda-forge
kiwisolver-1.3.1 | py37h8c56517_1 58 KB conda-forge
korean_lunar_calendar-0.2.1| pyh9f0ad1d_0 10 KB conda-forge
krb5-1.17.2 | hbae68bd_0 856 KB conda-forge
lcms2-2.12 | h2a16943_0 882 KB conda-forge
libcurl-7.75.0 | hf1763fc_0 292 KB conda-forge
libnetcdf-4.7.4 |nompi_h3a9aa94_107 602 KB conda-forge
libpng-1.6.37 | h1d00b33_2 724 KB conda-forge
libpython-2.0 | py37h03978a9_1 47 KB conda-forge
libssh2-1.9.0 | h680486a_6 218 KB conda-forge
libtiff-4.2.0 | hc10be44_0 1.1 MB conda-forge
lunarcalendar-0.0.9 | py_0 20 KB conda-forge
lz4-c-1.9.3 | h8ffe710_0 134 KB conda-forge
m2w64-binutils-2.25.1 | 5 44.3 MB conda-forge
m2w64-bzip2-1.0.6 | 6 102 KB conda-forge
m2w64-crt-git-5.0.0.4636.2595836| 2 3.4 MB conda-forge
m2w64-gcc-5.3.0 | 6 40.8 MB conda-forge
m2w64-gcc-ada-5.3.0 | 6 33.3 MB conda-forge
m2w64-gcc-fortran-5.3.0 | 6 10.2 MB conda-forge
m2w64-gcc-libgfortran-5.3.0| 6 342 KB conda-forge
m2w64-gcc-libs-5.3.0 | 7 520 KB conda-forge
m2w64-gcc-libs-core-5.3.0 | 7 214 KB conda-forge
m2w64-gcc-objc-5.3.0 | 6 15.2 MB conda-forge
m2w64-gmp-6.1.0 | 2 726 KB conda-forge
m2w64-headers-git-5.0.0.4636.c0ad18a| 2 5.6 MB conda-forge
m2w64-isl-0.16.1 | 2 655 KB conda-forge
m2w64-libiconv-1.14 | 6 1.5 MB conda-forge
m2w64-libmangle-git-5.0.0.4509.2e5a9a2| 2 23 KB conda-forge
m2w64-libwinpthread-git-5.0.0.4634.697f757| 2 31 KB conda-forge
m2w64-make-4.1.2351.a80a8b8| 2 117 KB conda-forge
m2w64-mpc-1.0.3 | 3 71 KB conda-forge
m2w64-mpfr-3.1.4 | 4 294 KB conda-forge
m2w64-pkg-config-0.29.1 | 2 467 KB conda-forge
m2w64-toolchain-5.3.0 | 7 3 KB conda-forge
m2w64-toolchain_win-64-2.4.0| 0 4 KB conda-forge
m2w64-tools-git-5.0.0.4592.90b8472| 2 320 KB conda-forge
m2w64-windows-default-manifest-6.4| 3 5 KB conda-forge
m2w64-winpthreads-git-5.0.0.4634.697f757| 2 47 KB conda-forge
m2w64-zlib-1.2.8 | 10 199 KB conda-forge
matplotlib-base-3.3.4 | py37h3379fd5_0 6.7 MB conda-forge
msys2-conda-epoch-20160418 | 1 3 KB conda-forge
netcdf4-1.5.6 |nompi_py37h4965ef1_100 358 KB conda-forge
olefile-0.46 | pyh9f0ad1d_1 32 KB conda-forge
packaging-20.9 | pyh44b312d_0 35 KB conda-forge
pandas-1.2.3 | py37h08fd248_0 9.9 MB conda-forge
pillow-8.1.2 | py37h96663a1_0 773 KB conda-forge
pymeeus-0.5.10 | pyhd8ed1ab_0 534 KB conda-forge
pyparsing-2.4.7 | pyh9f0ad1d_0 60 KB conda-forge
pystan-2.19.1.1 | py37h9758500_2 35.8 MB conda-forge
python-dateutil-2.8.1 | py_0 220 KB conda-forge
pytz-2021.1 | pyhd8ed1ab_0 239 KB conda-forge
scipy-1.6.1 | py37h6db1a17_0 23.1 MB conda-forge
tk-8.6.10 | h8ffe710_1 3.2 MB conda-forge
tornado-6.1 | py37hcc03f2d_1 648 KB conda-forge
xarray-0.17.0 | pyhd8ed1ab_0 561 KB conda-forge
xz-5.2.5 | h62dcd97_1 211 KB conda-forge
zlib-1.2.11 | h62dcd97_1010 126 KB conda-forge
zstd-1.4.9 | h6255e5f_0 915 KB conda-forge
------------------------------------------------------------
Total: 273.0 MB
The following NEW packages will be INSTALLED:
arviz conda-forge/noarch::arviz-0.11.2-pyhd8ed1ab_0
bzip2 conda-forge/win-64::bzip2-1.0.8-h8ffe710_4
cftime conda-forge/win-64::cftime-1.4.1-py37hda49f71_0
convertdate conda-forge/noarch::convertdate-2.3.2-pyhd8ed1ab_0
curl conda-forge/win-64::curl-7.75.0-hf1763fc_0
cycler conda-forge/noarch::cycler-0.10.0-py_2
cython conda-forge/win-64::cython-0.29.22-py37hf2a7229_0
ephem conda-forge/win-64::ephem-3.7.7.1-py37h4ab8f01_1
fbprophet conda-forge/win-64::fbprophet-0.7.1-py37h1834ac0_0
freetype conda-forge/win-64::freetype-2.10.4-h546665d_1
hdf4 conda-forge/win-64::hdf4-4.2.13-h0e5069d_1004
hdf5 conda-forge/win-64::hdf5-1.10.6-nompi_h5268f04_1114
hijri-converter conda-forge/noarch::hijri-converter-2.1.1-pyhd8ed1ab_0
holidays conda-forge/noarch::holidays-0.10.5.2-pyhd8ed1ab_0
jpeg conda-forge/win-64::jpeg-9d-h8ffe710_0
kiwisolver conda-forge/win-64::kiwisolver-1.3.1-py37h8c56517_1
korean_lunar_cale~ conda-forge/noarch::korean_lunar_calendar-0.2.1-pyh9f0ad1d_0
krb5 conda-forge/win-64::krb5-1.17.2-hbae68bd_0
lcms2 conda-forge/win-64::lcms2-2.12-h2a16943_0
libcurl conda-forge/win-64::libcurl-7.75.0-hf1763fc_0
libnetcdf conda-forge/win-64::libnetcdf-4.7.4-nompi_h3a9aa94_107
libpng conda-forge/win-64::libpng-1.6.37-h1d00b33_2
libpython conda-forge/win-64::libpython-2.0-py37h03978a9_1
libssh2 conda-forge/win-64::libssh2-1.9.0-h680486a_6
libtiff conda-forge/win-64::libtiff-4.2.0-hc10be44_0
lunarcalendar conda-forge/noarch::lunarcalendar-0.0.9-py_0
lz4-c conda-forge/win-64::lz4-c-1.9.3-h8ffe710_0
m2w64-binutils conda-forge/win-64::m2w64-binutils-2.25.1-5
m2w64-bzip2 conda-forge/win-64::m2w64-bzip2-1.0.6-6
m2w64-crt-git conda-forge/win-64::m2w64-crt-git-5.0.0.4636.2595836-2
m2w64-gcc conda-forge/win-64::m2w64-gcc-5.3.0-6
m2w64-gcc-ada conda-forge/win-64::m2w64-gcc-ada-5.3.0-6
m2w64-gcc-fortran conda-forge/win-64::m2w64-gcc-fortran-5.3.0-6
m2w64-gcc-libgfor~ conda-forge/win-64::m2w64-gcc-libgfortran-5.3.0-6
m2w64-gcc-libs conda-forge/win-64::m2w64-gcc-libs-5.3.0-7
m2w64-gcc-libs-co~ conda-forge/win-64::m2w64-gcc-libs-core-5.3.0-7
m2w64-gcc-objc conda-forge/win-64::m2w64-gcc-objc-5.3.0-6
m2w64-gmp conda-forge/win-64::m2w64-gmp-6.1.0-2
m2w64-headers-git conda-forge/win-64::m2w64-headers-git-5.0.0.4636.c0ad18a-2
m2w64-isl conda-forge/win-64::m2w64-isl-0.16.1-2
m2w64-libiconv conda-forge/win-64::m2w64-libiconv-1.14-6
m2w64-libmangle-g~ conda-forge/win-64::m2w64-libmangle-git-5.0.0.4509.2e5a9a2-2
m2w64-libwinpthre~ conda-forge/win-64::m2w64-libwinpthread-git-5.0.0.4634.697f757-2
m2w64-make conda-forge/win-64::m2w64-make-4.1.2351.a80a8b8-2
m2w64-mpc conda-forge/win-64::m2w64-mpc-1.0.3-3
m2w64-mpfr conda-forge/win-64::m2w64-mpfr-3.1.4-4
m2w64-pkg-config conda-forge/win-64::m2w64-pkg-config-0.29.1-2
m2w64-toolchain conda-forge/win-64::m2w64-toolchain-5.3.0-7
m2w64-toolchain_w~ conda-forge/win-64::m2w64-toolchain_win-64-2.4.0-0
m2w64-tools-git conda-forge/win-64::m2w64-tools-git-5.0.0.4592.90b8472-2
m2w64-windows-def~ conda-forge/win-64::m2w64-windows-default-manifest-6.4-3
m2w64-winpthreads~ conda-forge/win-64::m2w64-winpthreads-git-5.0.0.4634.697f757-2
m2w64-zlib conda-forge/win-64::m2w64-zlib-1.2.8-10
matplotlib-base conda-forge/win-64::matplotlib-base-3.3.4-py37h3379fd5_0
msys2-conda-epoch conda-forge/win-64::msys2-conda-epoch-20160418-1
netcdf4 conda-forge/win-64::netcdf4-1.5.6-nompi_py37h4965ef1_100
olefile conda-forge/noarch::olefile-0.46-pyh9f0ad1d_1
packaging conda-forge/noarch::packaging-20.9-pyh44b312d_0
pandas conda-forge/win-64::pandas-1.2.3-py37h08fd248_0
pillow conda-forge/win-64::pillow-8.1.2-py37h96663a1_0
pymeeus conda-forge/noarch::pymeeus-0.5.10-pyhd8ed1ab_0
pyparsing conda-forge/noarch::pyparsing-2.4.7-pyh9f0ad1d_0
pystan conda-forge/win-64::pystan-2.19.1.1-py37h9758500_2
python-dateutil conda-forge/noarch::python-dateutil-2.8.1-py_0
pytz conda-forge/noarch::pytz-2021.1-pyhd8ed1ab_0
scipy conda-forge/win-64::scipy-1.6.1-py37h6db1a17_0
tk conda-forge/win-64::tk-8.6.10-h8ffe710_1
tornado conda-forge/win-64::tornado-6.1-py37hcc03f2d_1
xarray conda-forge/noarch::xarray-0.17.0-pyhd8ed1ab_0
xz conda-forge/win-64::xz-5.2.5-h62dcd97_1
zlib conda-forge/win-64::zlib-1.2.11-h62dcd97_1010
zstd conda-forge/win-64::zstd-1.4.9-h6255e5f_0
Proceed ([y]/n)? y
ーーーーーーーーーーーーーーー中略ーーーーーーーーーーーーーーーーーーー
done
(modeler) C:\Users\dsuser01>conda list
# packages in environment at C:\Users\dsuser01\.conda\envs\modeler:
#
# Name Version Build Channel
arviz 0.11.2 pyhd8ed1ab_0 conda-forge
brotlipy 0.7.0 py37hcc03f2d_1001 conda-forge
bzip2 1.0.8 h8ffe710_4 conda-forge
ca-certificates 2020.12.5 h5b45459_0 conda-forge
certifi 2020.12.5 py37h03978a9_1 conda-forge
cffi 1.14.5 py37hd8e9650_0 conda-forge
cftime 1.4.1 py37hda49f71_0 conda-forge
chardet 4.0.0 py37h03978a9_1 conda-forge
conda 4.9.2 py37h03978a9_0 conda-forge
conda-package-handling 1.7.2 py37h6dbccfb_0 conda-forge
convertdate 2.3.2 pyhd8ed1ab_0 conda-forge
cryptography 3.4.6 py37h20c650d_0 conda-forge
curl 7.75.0 hf1763fc_0 conda-forge
cycler 0.10.0 py_2 conda-forge
cython 0.29.22 py37hf2a7229_0 conda-forge
ephem 3.7.7.1 py37h4ab8f01_1 conda-forge
fbprophet 0.7.1 py37h1834ac0_0 conda-forge
freetype 2.10.4 h546665d_1 conda-forge
hdf4 4.2.13 h0e5069d_1004 conda-forge
hdf5 1.10.6 nompi_h5268f04_1114 conda-forge
hijri-converter 2.1.1 pyhd8ed1ab_0 conda-forge
holidays 0.10.5.2 pyhd8ed1ab_0 conda-forge
idna 2.10 pyh9f0ad1d_0 conda-forge
intel-openmp 2020.3 h57928b3_311 conda-forge
jpeg 9d h8ffe710_0 conda-forge
kiwisolver 1.3.1 py37h8c56517_1 conda-forge
korean_lunar_calendar 0.2.1 pyh9f0ad1d_0 conda-forge
krb5 1.17.2 hbae68bd_0 conda-forge
lcms2 2.12 h2a16943_0 conda-forge
libblas 3.9.0 8_mkl conda-forge
libcblas 3.9.0 8_mkl conda-forge
libcurl 7.75.0 hf1763fc_0 conda-forge
liblapack 3.9.0 8_mkl conda-forge
libnetcdf 4.7.4 nompi_h3a9aa94_107 conda-forge
libpng 1.6.37 h1d00b33_2 conda-forge
libpython 2.0 py37h03978a9_1 conda-forge
libssh2 1.9.0 h680486a_6 conda-forge
libtiff 4.2.0 hc10be44_0 conda-forge
lunarcalendar 0.0.9 py_0 conda-forge
lz4-c 1.9.3 h8ffe710_0 conda-forge
m2w64-binutils 2.25.1 5 conda-forge
m2w64-bzip2 1.0.6 6 conda-forge
m2w64-crt-git 5.0.0.4636.2595836 2 conda-forge
m2w64-gcc 5.3.0 6 conda-forge
m2w64-gcc-ada 5.3.0 6 conda-forge
m2w64-gcc-fortran 5.3.0 6 conda-forge
m2w64-gcc-libgfortran 5.3.0 6 conda-forge
m2w64-gcc-libs 5.3.0 7 conda-forge
m2w64-gcc-libs-core 5.3.0 7 conda-forge
m2w64-gcc-objc 5.3.0 6 conda-forge
m2w64-gmp 6.1.0 2 conda-forge
m2w64-headers-git 5.0.0.4636.c0ad18a 2 conda-forge
m2w64-isl 0.16.1 2 conda-forge
m2w64-libiconv 1.14 6 conda-forge
m2w64-libmangle-git 5.0.0.4509.2e5a9a2 2 conda-forge
m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge
m2w64-make 4.1.2351.a80a8b8 2 conda-forge
m2w64-mpc 1.0.3 3 conda-forge
m2w64-mpfr 3.1.4 4 conda-forge
m2w64-pkg-config 0.29.1 2 conda-forge
m2w64-toolchain 5.3.0 7 conda-forge
m2w64-toolchain_win-64 2.4.0 0 conda-forge
m2w64-tools-git 5.0.0.4592.90b8472 2 conda-forge
m2w64-windows-default-manifest 6.4 3 conda-forge
m2w64-winpthreads-git 5.0.0.4634.697f757 2 conda-forge
m2w64-zlib 1.2.8 10 conda-forge
matplotlib-base 3.3.4 py37h3379fd5_0 conda-forge
menuinst 1.4.16 py37hc8dfbb8_1 conda-forge
mkl 2020.4 hb70f87d_311 conda-forge
msys2-conda-epoch 20160418 1 conda-forge
netcdf4 1.5.6 nompi_py37h4965ef1_100 conda-forge
numpy 1.20.1 py37hd20adf4_0 conda-forge
olefile 0.46 pyh9f0ad1d_1 conda-forge
openssl 1.1.1j h8ffe710_0 conda-forge
packaging 20.9 pyh44b312d_0 conda-forge
pandas 1.2.3 py37h08fd248_0 conda-forge
pillow 8.1.2 py37h96663a1_0 conda-forge
pip 21.0.1 pyhd8ed1ab_0 conda-forge
pycosat 0.6.3 py37hcc03f2d_1006 conda-forge
pycparser 2.20 pyh9f0ad1d_2 conda-forge
pymeeus 0.5.10 pyhd8ed1ab_0 conda-forge
pyopenssl 20.0.1 pyhd8ed1ab_0 conda-forge
pyparsing 2.4.7 pyh9f0ad1d_0 conda-forge
pysocks 1.7.1 py37h03978a9_3 conda-forge
pystan 2.19.1.1 py37h9758500_2 conda-forge
python 3.7.7 h81c818b_4
python-dateutil 2.8.1 py_0 conda-forge
python_abi 3.7 1_cp37m conda-forge
pytz 2021.1 pyhd8ed1ab_0 conda-forge
pywin32 300 py37hcc03f2d_0 conda-forge
requests 2.25.1 pyhd3deb0d_0 conda-forge
ruamel_yaml 0.15.80 py37hcc03f2d_1004 conda-forge
scipy 1.6.1 py37h6db1a17_0 conda-forge
setuptools 49.6.0 py37h03978a9_3 conda-forge
six 1.15.0 pyh9f0ad1d_0 conda-forge
sqlite 3.35.2 h8ffe710_0 conda-forge
tk 8.6.10 h8ffe710_1 conda-forge
tornado 6.1 py37hcc03f2d_1 conda-forge
tqdm 4.59.0 pyhd8ed1ab_0 conda-forge
urllib3 1.26.4 pyhd8ed1ab_0 conda-forge
vc 14.2 hb210afc_4 conda-forge
vs2015_runtime 14.28.29325 h5e1d092_4 conda-forge
wheel 0.36.2 pyhd3deb0d_0 conda-forge
win_inet_pton 1.1.0 py37h03978a9_2 conda-forge
wincertstore 0.2 py37h03978a9_1006 conda-forge
xarray 0.17.0 pyhd8ed1ab_0 conda-forge
xz 5.2.5 h62dcd97_1 conda-forge
yaml 0.2.5 he774522_0 conda-forge
zlib 1.2.11 h62dcd97_1010 conda-forge
zstd 1.4.9 h6255e5f_0 conda-forge
(modeler) C:\Users\dsuser01>
#モデルを保存するパスを作っておきます。
モデルを保存するパスを作っておきます。
#ストリームの作成
公式サイトのQuickStartにあるサンプルデータのexample_wp_log_peyton_manning.csvをダウンロードして読み込みます。
以下のようなデータが入っています。日付型のdsという列と実績値のy列があります。fbprophetはモデル作成に使う列名が決められていますので、ほかのデータを使う場合はdsとyの列名でデータを用意する必要があります。
拡張モデルノードでPython for Sparkを選びます。
以下のfbprophetのモデリングとスコアリングのスクリプトを入力します。
#モデルファイルの保存パス
modelpath='c:/temp/modelpath/'
modelfile='fbprophetModel.pkl'
# Analytics Server 対話用のライブラリのインポート
import spss.pyspark.runtime
# Analytics Serverコンテキストオブジェクト定義
ascontext = spss.pyspark.runtime.getContext()
# データ読込
df = ascontext.getSparkInputData()
#print(df.take(10))
#print(df.printSchema)
#pandas変換してdsでソート
df = df.toPandas().sort_values('ds')
#print(df.tail(10))
#dsがシリアル値でうけ渡されるのでdatetimeに変換
import pandas as pd
df['ds'] = pd.to_datetime('1970/1/1') + pd.to_timedelta(df['ds'] , unit='days')
#print(df.tail(10))
#fbprophetモデル作成
from fbprophet import Prophet
model = Prophet()
model.fit(df)
#モデルをファイルシステムに保存
import pickle
pickle.dump(model, open(modelpath+modelfile, 'wb'))
#モデルファイルの保存パス
modelpath='c:/temp/modelpath/'
modelfile='fbprophetModel.pkl'
#予測期間の設定
futureperiods=365
# Analytics Server 対話用のライブラリのインポート
import spss.pyspark.runtime
from pyspark.sql.types import DoubleType, StructField
from pyspark.sql.context import SQLContext
# Analytics Server 対話用のライブラリのインポート
import spss.pyspark.runtime
# Analytics Serverコンテキストオブジェクト定義
ascontext = spss.pyspark.runtime.getContext()
#予測値列と上側予測列、下側予測列を出力スキーマに追加
outputSchema = ascontext.getSparkInputSchema()
outputSchema.fields.append(StructField('yhat', DoubleType(), nullable=True))
outputSchema.fields.append(StructField('yhat_lower', DoubleType(), nullable=True))
outputSchema.fields.append(StructField('yhat_upper', DoubleType(), nullable=True))
ascontext.setSparkOutputSchema(outputSchema)
if not ascontext.isComputeDataModelOnly():
#ファイルシステム上のモデルを読み込む
import pickle
model = pickle.load(open(modelpath+modelfile, 'rb'))
#スコアリングデータの読み込み
indf = ascontext.getSparkInputData()
#pandas変換してdsでソート
df = indf.toPandas().sort_values('ds')
import pandas as pd
df['ds'] = pd.to_datetime('1970/1/1') + pd.to_timedelta(df['ds'] , unit='days')
#スコアリング
future = model.make_future_dataframe(periods=futureperiods)
forecast = model.predict(future)
#インプットDataframeと外部結合をして、必要列に絞る
forecast = pd.concat([forecast[['ds','yhat','yhat_lower','yhat_upper']],df[['y']]], axis=1)[['ds','y','yhat','yhat_lower','yhat_upper']]
#dsをシリアル値でModelerが受け取るので変換
import datetime
forecast['ds'] = (pd.to_datetime(forecast['ds']) - pd.to_datetime('1970/1/1'))/ datetime.timedelta(days=1)
#print(forecast.tail(10))
#Sparkコンテキストの取得
sc = ascontext.getSparkContext()
#データの出力
sqlCtx = SQLContext(sc)
outdf = sqlCtx.createDataFrame(forecast,schema=outputSchema)
# return the output DataFrame as the result
ascontext.setSparkOutputData(outdf)
実行するとモデルナゲットができるので、テーブルノードを繋ぎます。
テーブルノードを実行するとyhatに予測値、yhat_lower、yhat_upperに下側予測、上側予測が出力されます。
このデータでは2016-01-21以降は将来の予測になっています。
時系列グラフノードを接続し、以下の設定で実行します。
以下のように予実の可視化が可能です。
水色のポイントが実測値でそれ以外が予測値です。
モデリングスクリプトの解説
最初にモデルの保存パスを定義しています。
本来はascontext.setModelContentFromStringでモデルナゲット内にモデルが保存できると思うのですが、どうしても失敗するために、ファイルシステム内に保存しています。
スコアリング時にはこのファイルが必要になります。特にModeler ServerやCADSと組み合わせて実行する場合など、別のマシンでスコアリングを行う場合には、同じ場所にモデルファイルがないと動きませんので注意が必要です。
#モデルファイルの保存パス
modelpath='c:/temp/modelpath/'
modelfile='fbprophetModel.pkl'
以下でModelerとのやり取りを行うためのコンテキストオブジェクトを作っています。
# Analytics Server 対話用のライブラリのインポート
import spss.pyspark.runtime
# Analytics Serverコンテキストオブジェクト定義
ascontext = spss.pyspark.runtime.getContext()
以下でデータを読み込んでいます。
Modelerから取得されるデータフレームはSpark DataFrameなので、sdf.toPandas()
でpandasのDataFrameにして取り扱いやすくしています。
また、SparkDataFrameは順序が保証されないようなので、日付を表すds列でソートします。
# データ読込
sdf = ascontext.getSparkInputData()
#pandas変換してdsでソート
df = sdf.toPandas().sort_values('ds')
日付型のdsがシリアル値で読み込まれるので、datetime型に変換します。
#dsがシリアル値でうけ渡されるのでdatetimeに変換
import pandas as pd
df['ds'] = pd.to_datetime('1970/1/1') + pd.to_timedelta(df['ds'] , unit='days')
以下でfbprophetのモデルを作っています。
fbprophetには様々なパラメーターがありますが、ここではデフォルトで動かしています。
#fbprophetモデル作成
from fbprophet import Prophet
model = Prophet()
model.fit(df)
最後に、生成されたモデルをpickleを使ってシリアライズして、スクリプトの先頭で定義したmodelpath+modelfileに保存しています。
#モデルをファイルシステムに保存
import pickle
pickle.dump(model, open(modelpath+modelfile, 'wb'))
スコアリングスクリプトの解説
モデリングスクリプトで指定したものと同じモデルファイル保存パスを指定します。
#モデルファイルの保存パス
modelpath='c:/temp/modelpath/'
modelfile='fbprophetModel.pkl'
以下ではスコアリング時の将来予測期間を指定しています。ここでは365日を指定しています。
#予測期間の設定
futureperiods=365
モデリングスクリプトでも行ったように、Modelerとのやり取りを行うためのコンテキストオブジェクトを作っています。
モデリングスクリプトに加えて、ここでは新しいSparkDataFrameを作成するためにSQLContextを、また新しい列をつくるためにDoubleType, StructFieldもインポートしています。
# Analytics Server 対話用のライブラリのインポート
import spss.pyspark.runtime
from pyspark.sql.types import DoubleType, StructField
from pyspark.sql.context import SQLContext
# Analytics Serverコンテキストオブジェクト定義
ascontext = spss.pyspark.runtime.getContext()
以下で入力データのスキーマに予測値列と上側予測列、下側予測列を追加して、出力スキーマとしています。データ型はDoubleType()を指定しています。
#予測値列と上側予測列、下側予測列を出力スキーマに追加
outputSchema = ascontext.getSparkInputSchema()
outputSchema.fields.append(StructField('yhat', DoubleType(), nullable=True))
outputSchema.fields.append(StructField('yhat_lower', DoubleType(), nullable=True))
outputSchema.fields.append(StructField('yhat_upper', DoubleType(), nullable=True))
ascontext.setSparkOutputSchema(outputSchema)
以下は実際にスコアリングを行う場合に以降のスクリプトを実行するという条件になります。
ここは少しややこしいところです。
ここまでのスクリプトはsetSparkOutputSchemaで出力スキーマを定義するものでした。モデルナゲットの後ろにノードを追加する場合、DataModelをComputeしてスキーマを定義することは必要ですが、実際にスコアリングしたデータは必要ありません。
ですので無駄なスコアリング処理をしないために、「スコアリングデータがいる場合にのみ」(Not isComputeDataModelOnly)、スコアリングをするスクリプトを実行するという条件を入れています。
マニュアルにDataModelOnly モードという解説があります。
if not ascontext.isComputeDataModelOnly():
以下で、スクリプトの先頭で定義したmodelpath+modelfileからモデルをファイルシステムから読み込みます。
#ファイルシステム上のモデルを読み込む
import pickle
model = pickle.load(open(modelpath+modelfile, 'rb'))
以下で入力データを読み込んでPandasのDataFrameに変換しています。モデリングと同様にds列でソートをかけ、dsをシリアル値からdatetime型に変換しています。
#スコアリングデータの読み込み
indf = ascontext.getSparkInputData()
#pandas変換してdsでソート
df = indf.toPandas().sort_values('ds')
import pandas as pd
df['ds'] = pd.to_datetime('1970/1/1') + pd.to_timedelta(df['ds'] , unit='days')
以下で、スコアリングをしています。yhatは予測値を得ています。yhat_upper、yhat_lowerは上側予測列、下側予測列を得ています。
スコアリング結果のdataframeであるforecastには実績値yは入らないので、インプットデータと結合を行っています。
なお、forecastにはtrendなど別のスコアリング値もありますので、必要に応じて取り出してください。
#スコアリング
future = model.make_future_dataframe(periods=futureperiods)
forecast = model.predict(future)
#インプットDataframeと外部結合をして、必要列に絞る
forecast = pd.concat([forecast[['ds','yhat','yhat_lower','yhat_upper']],df[['y']]], axis=1)[['ds','y','yhat','yhat_lower','yhat_upper']]
Modelerに返すために日付型データのdsをシリアル変換しています。
#dsをシリアル値でModelerが受け取るので変換
import datetime
forecast['ds'] = (pd.to_datetime(forecast['ds']) - pd.to_datetime('1970/1/1'))/ datetime.timedelta(days=1)
以下で予測値列と予測列、下側予測列を追加したPandas DataFrameであるdfを、sqlCtx.createDataFrame(df,schema=outputSchema)でSparkDataFrameに変換しなおして、setSparkOutputDataでModelerに返しています。
#Sparkコンテキストの取得
sc = ascontext.getSparkContext()
#データの出力
sqlCtx = SQLContext(sc)
outdf = sqlCtx.createDataFrame(forecast,schema=outputSchema)
# return the output DataFrame as the result
ascontext.setSparkOutputData(outdf)
参考
fbprophet Quick Start
Python for Spark を使用したスクリプト