Using supplychainpy with Pandas, Jupyter and Matplotlib

The following is taken from the jupyter notebook title ‘0.0.4-Using-Supplychainpy-and-Pandas-v1’ found here . For a more interactive experience please retrieve this notebook and run with jupyter.

To use supplychainpy with Pandas, we first read a csv file to a Pandas DataFrame.

%matplotlib inline

import matplotlib
import pandas as pd

from supplychainpy.model_inventory import analyse
from supplychainpy.model_demand import simple_exponential_smoothing_forecast
from supplychainpy.sample_data.config import ABS_FILE_PATH

raw_df =pd.read_csv(ABS_FILE_PATH['COMPLETE_CSV_SM'])
print(raw_df)
          Sku   jan   feb   mar   apr   may   jun   jul   aug   sep   oct  0   KR202-209  1509  1855  2665  1841  1231  2598  1988  1988  2927  2707
1   KR202-210  1006   206  2588   670  2768  2809  1475  1537   919  2525
2   KR202-211  1840  2284   850   983  2737  1264  2002  1980   235  1489
3   KR202-212   104  2262   350   528  2570  1216  1101  2755  2856  2381
4   KR202-213   489   954  1112   199   919   330   561  2372   921  1587
5   KR202-214  2416  2010  2527  1409  1059   890  2837   276   987  2228
6   KR202-215   403  1737   753  1982  2775   380  1561  1230  1262  2249
7   KR202-216  2908   929   684  2618  1477  1508   765    43  2550  2157
8   KR202-217  2799  2197  1647  2263   224  2987  2366   588  1140   869
9   KR202-218  1333   402   804   318  1408   830  1028   534  1871  2730
10  KR202-219   813   969   745  1001  2732  1987   717   599  2722   171
11  KR202-220  1481   905  1067  2513   861  1670   650  2630  1245   997
12  KR202-221   771  2941  1360  2714  1801  1744  1428  1660   436   578
13  KR202-222  2349     4   345   524   340  2698  2137  1164   498  1583
14  KR202-223  2045  2055   552    81  2780   176  2316  1475  2566  1678
15  KR202-224  2482  1887  1911  1446  2939  1241  1281   692   119   627
16  KR202-225  2744  2770  2697  1726  1776  2264   332  2420  2722  1161
17  KR202-226  2509   914   903   877  1859  2263   383   593   236   189
18  KR202-227   368  2502  2955  2994  1270  2884  2208   699   854   877
19  KR202-228  1468  1109  2464  2799   948   589  2858  1140   501  2691
20  KR202-229  2114   198  1479  1249  1475   744   407  2280   226  2285
21  KR202-230  1023  1150  1672  2026  1590   441  2484  2300  2928  1082
22  KR202-231   482   546   299  2304  2953  1029  1863  2809   454   927
23  KR202-232   614  2138   962  2017  2398  2963  2189  1804   414  2016
24  KR202-233  2395  2521  2157   728  1028    43   138   826   570  2825
25  KR202-234  1336  1478   865   533  1562   422  2287  1302  1230  1059
26  KR202-235  2565  2762  2721  1431   845  2163  2413  2227  1753   740
27  KR202-236  1912  1726  1569   316    71  2082   108   174  1974   609
28  KR202-237  2153  1112    16   130   590  2619  2576  2390  2567  1531
29  KR202-238  1417  2044  1981  1936  2377   780  1544  1521    51  1056
30  KR202-239  2717  2186  2300   677  2157  2328  1917  2519   561   281
31  KR202-240  1015   741  2754  2925  2302   695  2869   440   406  1083
32  KR202-241  3050  1507  3637  1112  1963  1675   898  1986  2262  3895
33  KR202-242  1875  2368   830   823   868  1409  1845  3095  3247  1894
34  KR202-243  1717   593  3006  2935  3139  2753  3247  3845  1720  3413
35  KR202-244  2383  2046  2487  3827  1674  3118  2849  2233  3888  2566
36  KR202-245  1115  2694  3038  3366  1058  2724  2863  1930  1787   838
37  KR202-246  3108  1197  2472  1264  3179  3638  1268  1581  3456  1630
38  KR202-247  3439  1854   652  1827  1645  2257  2733  1337  2034  2106

     nov   dec  unit cost  lead-time  retail_price  quantity_on_hand  backlog
0    731  2598       1001          2          5000              1003       10
1    440  2691        394          2          1300              3224       10
2    218   525        434          4          1200               390       10
3   1867  2743        474          3            10               390       10
4   1532  1512        514          1          2000              2095       10
5   1095  1396        554          2          1800                55       10
6    824   743        594          1          2500              4308       10
7    937  1201        634          3          3033                34       10
8   1707  1180        674          3          5433               390       10
9   2022    94        714          2          3034              3535       10
10   639  2108        754          3          5000               334       10
11  1936  2780        794          3          7500              3434       10
12  1956  1101        834          2          4938              4433       10
13  1241  2965        874          2          4922              3435       10
14  1553  2745        914          1          4894             34533       10
15  1941  1383        954          2          2942                33       10
16  1986  2587        994          6          8999              2000       10
17   920  1686       1034          3          4342              4344       10
18  2320   160       1074          3          4920               489       10
19    93  1060       1114          2         15000              9439       10
20   796  1948       1154          2         13000              8939       10
21  2064  2412       1194          2         10000               349       10
22  2488  2341       1234          4          9999              3434       10
23  1350  2464       1274          2          7500               234       10
24   181   787       1314          4          6000               349       10
25  1153   399       1354          2         20000               324       10
26  1139  2300       1394          3         59500               850       10
27  2896   566       1434          3          2300              4930       10
28   842   242       1474          2          4500              9483       10
29  1876  1356       1514          3          8000               839       10
30  1162  1146       1554          2         39000               433       10
31  2334  1015       1594          3          3943               390       10
32  1229  2904        769          5          8007              2125       10
33  2558  3048       1819          1         13225              1253       10
34  3399  2799       1120          3         14682              1128       10
35  2216  3817       1067          5         11997              1191       10
36  3087  1565       1623          2         12876               611       10
37  1788  2288        608          2          6548              2192       10
38   877  2409       1578          2         10463              1017       10

Passing a Pandas DataFrame as a keyword parameter (df=) returns a DataFrame with the inventory profile analysed. Excluding the import statements this can be achieved in 3 lines of code. There are several columns, so the print statement has been limited to a few.

orders_df = raw_df[['Sku','jan','feb','mar','apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']]
#orders_df.set_index('Sku')
analysis_df = analyse(df=raw_df, start=1, interval_length=12, interval_type='months')
print(analysis_df[['sku','quantity_on_hand', 'excess_stock', 'shortages', 'ABC_XYZ_Classification']])
          sku quantity_on_hand excess_stock shortages ABC_XYZ_Classification
0   KR202-209             1003            0      5969                     BY
1   KR202-210             3224            0         0                     CY
2   KR202-211              390            0      7099                     CY
3   KR202-212              390            0      7759                     CY
4   KR202-213             2095            0         0                     CY
5   KR202-214               55            0      5824                     CY
6   KR202-215             4308          732         0                     CY
7   KR202-216               34            0      6999                     CY
8   KR202-217              390            0      7245                     BY
9   KR202-218             3535            0         0                     CZ
10  KR202-219              334            0      5917                     CZ
11  KR202-220             3434            0         0                     BY
12  KR202-221             4433            0         0                     BY
13  KR202-222             3435            0         0                     CZ
14  KR202-223            34533        30030         0                     BY
15  KR202-224               33            0      5580                     CY
16  KR202-225             2000            0     10542                     AY
17  KR202-226             4344            0         0                     CZ
18  KR202-227              489            0      7587                     BZ
19  KR202-228             9439         3572         0                     AZ
20  KR202-229             8939         3994         0                     AY
21  KR202-230              349            0      5913                     AY
22  KR202-231             3434            0         0                     AZ
23  KR202-232              234            0      6150                     AY
24  KR202-233              349            0      6856                     CZ
25  KR202-234              324            0      3822                     AY
26  KR202-235              850            0      7339                     AY
27  KR202-236             4930            0         0                     CZ
28  KR202-237             9483         3742         0                     CZ
29  KR202-238              839            0      5693                     BY
30  KR202-239              433            0      5737                     AY
31  KR202-240              390            0      7094                     CZ
32  KR202-241             2125            0     10328                     AY
33  KR202-242             1253            0         0                     AY
34  KR202-243             1128            0     10227                     AY
35  KR202-244             1191            0     13200                     AY
36  KR202-245              611            0      7081                     AY
37  KR202-246             2192            0         0                     AY
38  KR202-247             1017            0      5776                     AY

Before we can make a forecast we need to select a SKU from the analysis_df variable, slice the row to retrive only orders data and convert to a Series.

row_ds = raw_df[raw_df['Sku']=='KR202-212'].squeeze()
print(row_ds[1:12])
jan     104
feb    2262
mar     350
apr     528
may    2570
jun    1216
jul    1101
aug    2755
sep    2856
oct    2381
nov    1867
Name: 3, dtype: object

Now that we have a series of orders data fro the SKU KR202-212, we can now perform a forecast using the model_demand module. We can perform a simple_exponential_smoothing_forecast by passing the forecasting function the orders data using the keyword parameter ds=.

ses_df = simple_exponential_smoothing_forecast(ds=row_ds[1:12], length=12, smoothing_level_constant=0.5)
print(ses_df)
{'statistics': {'pvalue': 0.0047852515832242743, 'test_statistic': 3.8634855288615153, 'std_residuals': 4793.7283216530095, 'intercept': 377.59999999999991, 'trend': True, 'slope': 224.4909090909091, 'slope_standard_error': 58.105797838218294}, 'alpha': 0.5, 'forecast_breakdown': [{'squared_error': 2345353.024793389, 'alpha': 0.5, 'demand': 104, 'one_step_forecast': 1635.4545454545455, 't': 1, 'level_estimates': 869.72727272727275, 'forecast_error': -1531.4545454545455}, {'squared_error': 1938423.3471074379, 'alpha': 0.5, 'demand': 2262, 'one_step_forecast': 869.72727272727275, 't': 2, 'level_estimates': 1565.8636363636365, 'forecast_error': 1392.2727272727273}, {'squared_error': 1478324.3822314052, 'alpha': 0.5, 'demand': 350, 'one_step_forecast': 1565.8636363636365, 't': 3, 'level_estimates': 957.93181818181824, 'forecast_error': -1215.8636363636365}, {'squared_error': 184841.36828512402, 'alpha': 0.5, 'demand': 528, 'one_step_forecast': 957.93181818181824, 't': 4, 'level_estimates': 742.96590909090912, 'forecast_error': -429.93181818181824}, {'squared_error': 3338053.5693440083, 'alpha': 0.5, 'demand': 2570, 'one_step_forecast': 742.96590909090912, 't': 5, 'level_estimates': 1656.4829545454545, 'forecast_error': 1827.034090909091}, {'squared_error': 194025.23324509294, 'alpha': 0.5, 'demand': 1216, 'one_step_forecast': 1656.4829545454545, 't': 6, 'level_estimates': 1436.2414772727273, 'forecast_error': -440.4829545454545}, {'squared_error': 112386.84808400051, 'alpha': 0.5, 'demand': 1101, 'one_step_forecast': 1436.2414772727273, 't': 7, 'level_estimates': 1268.6207386363635, 'forecast_error': -335.24147727272725}, {'squared_error': 2209323.3086119094, 'alpha': 0.5, 'demand': 2755, 'one_step_forecast': 1268.6207386363635, 't': 8, 'level_estimates': 2011.8103693181818, 'forecast_error': 1486.3792613636365}, {'squared_error': 712656.13255070464, 'alpha': 0.5, 'demand': 2856, 'one_step_forecast': 2011.8103693181818, 't': 9, 'level_estimates': 2433.905184659091, 'forecast_error': 844.18963068181824}, {'squared_error': 2798.9585638125168, 'alpha': 0.5, 'demand': 2381, 'one_step_forecast': 2433.905184659091, 't': 10, 'level_estimates': 2407.4525923295455, 'forecast_error': -52.905184659090992}, {'squared_error': 292089.0045557259, 'alpha': 0.5, 'demand': 1867, 'one_step_forecast': 2407.4525923295455, 't': 11, 'level_estimates': 2137.226296164773, 'forecast_error': -540.4525923295455}], 'mape': 100.69830747447692, 'forecast': [2137.226296164773, 2137.226296164773, 2137.226296164773, 2137.226296164773, 2137.226296164773]}
print(ses_df.get('forecast', 'UNKNOWN'))
[2137.226296164773, 2137.226296164773, 2137.226296164773, 2137.226296164773, 2137.226296164773]

If we check the statistcs for the forecast we can see whether there is a linear trend and subsequently if the forecast is useful.

print(ses_df.get('statistics', 'UNKNOWN'),'\n mape: {}'.format(ses_df.get('mape', 'UNKNOWN')))
{'pvalue': 0.0047852515832242743, 'test_statistic': 3.8634855288615153, 'std_residuals': 4793.7283216530095, 'intercept': 377.59999999999991, 'trend': True, 'slope': 224.4909090909091, 'slope_standard_error': 58.105797838218294}
 mape: 100.69830747447692

The breakdown of the forecast is also returned with the forecast and statistics.

print(ses_df.get('forecast_breakdown', 'UNKNOWN'))
[{'squared_error': 2345353.024793389, 'alpha': 0.5, 'demand': 104, 'one_step_forecast': 1635.4545454545455, 't': 1, 'level_estimates': 869.72727272727275, 'forecast_error': -1531.4545454545455}, {'squared_error': 1938423.3471074379, 'alpha': 0.5, 'demand': 2262, 'one_step_forecast': 869.72727272727275, 't': 2, 'level_estimates': 1565.8636363636365, 'forecast_error': 1392.2727272727273}, {'squared_error': 1478324.3822314052, 'alpha': 0.5, 'demand': 350, 'one_step_forecast': 1565.8636363636365, 't': 3, 'level_estimates': 957.93181818181824, 'forecast_error': -1215.8636363636365}, {'squared_error': 184841.36828512402, 'alpha': 0.5, 'demand': 528, 'one_step_forecast': 957.93181818181824, 't': 4, 'level_estimates': 742.96590909090912, 'forecast_error': -429.93181818181824}, {'squared_error': 3338053.5693440083, 'alpha': 0.5, 'demand': 2570, 'one_step_forecast': 742.96590909090912, 't': 5, 'level_estimates': 1656.4829545454545, 'forecast_error': 1827.034090909091}, {'squared_error': 194025.23324509294, 'alpha': 0.5, 'demand': 1216, 'one_step_forecast': 1656.4829545454545, 't': 6, 'level_estimates': 1436.2414772727273, 'forecast_error': -440.4829545454545}, {'squared_error': 112386.84808400051, 'alpha': 0.5, 'demand': 1101, 'one_step_forecast': 1436.2414772727273, 't': 7, 'level_estimates': 1268.6207386363635, 'forecast_error': -335.24147727272725}, {'squared_error': 2209323.3086119094, 'alpha': 0.5, 'demand': 2755, 'one_step_forecast': 1268.6207386363635, 't': 8, 'level_estimates': 2011.8103693181818, 'forecast_error': 1486.3792613636365}, {'squared_error': 712656.13255070464, 'alpha': 0.5, 'demand': 2856, 'one_step_forecast': 2011.8103693181818, 't': 9, 'level_estimates': 2433.905184659091, 'forecast_error': 844.18963068181824}, {'squared_error': 2798.9585638125168, 'alpha': 0.5, 'demand': 2381, 'one_step_forecast': 2433.905184659091, 't': 10, 'level_estimates': 2407.4525923295455, 'forecast_error': -52.905184659090992}, {'squared_error': 292089.0045557259, 'alpha': 0.5, 'demand': 1867, 'one_step_forecast': 2407.4525923295455, 't': 11, 'level_estimates': 2137.226296164773, 'forecast_error': -540.4525923295455}]

We can convert the forecast_breakdown back into a DataFrame.

forecast_breakdown_df = pd.DataFrame(ses_df.get('forecast_breakdown', 'UNKNOWN'))
print(forecast_breakdown_df)
    alpha  demand  forecast_error  level_estimates  one_step_forecast  0     0.5     104    -1531.454545       869.727273        1635.454545
1     0.5    2262     1392.272727      1565.863636         869.727273
2     0.5     350    -1215.863636       957.931818        1565.863636
3     0.5     528     -429.931818       742.965909         957.931818
4     0.5    2570     1827.034091      1656.482955         742.965909
5     0.5    1216     -440.482955      1436.241477        1656.482955
6     0.5    1101     -335.241477      1268.620739        1436.241477
7     0.5    2755     1486.379261      2011.810369        1268.620739
8     0.5    2856      844.189631      2433.905185        2011.810369
9     0.5    2381      -52.905185      2407.452592        2433.905185
10    0.5    1867     -540.452592      2137.226296        2407.452592

    squared_error   t
0    2.345353e+06   1
1    1.938423e+06   2
2    1.478324e+06   3
3    1.848414e+05   4
4    3.338054e+06   5
5    1.940252e+05   6
6    1.123868e+05   7
7    2.209323e+06   8
8    7.126561e+05   9
9    2.798959e+03  10
10   2.920890e+05  11

Let’s look at the demand and the one_step_forecast in a chart.

forecast_breakdown_df.plot(x='t', y=['one_step_forecast','demand'])
<matplotlib.axes._subplots.AxesSubplot at 0x10e1be400>
_images/image1.png

Using y = mx + c we can also create the data points for the regression line.

regression = {'regression': [(ses_df.get('statistics')['slope']* i ) + ses_df.get('statistics')['intercept'] for i in range(1,12)]}
print(regression)
{'regression': [602.09090909090901, 826.58181818181811, 1051.0727272727272, 1275.5636363636363, 1500.0545454545454, 1724.5454545454545, 1949.0363636363636, 2173.5272727272727, 2398.0181818181818, 2622.5090909090909, 2847.0]}

We can add the regression data points to the forecast breakdwn DataFrame.

forecast_breakdown_df['regression'] = regression.get('regression')
print(forecast_breakdown_df)
    alpha  demand  forecast_error  level_estimates  one_step_forecast  0     0.5     104    -1531.454545       869.727273        1635.454545
1     0.5    2262     1392.272727      1565.863636         869.727273
2     0.5     350    -1215.863636       957.931818        1565.863636
3     0.5     528     -429.931818       742.965909         957.931818
4     0.5    2570     1827.034091      1656.482955         742.965909
5     0.5    1216     -440.482955      1436.241477        1656.482955
6     0.5    1101     -335.241477      1268.620739        1436.241477
7     0.5    2755     1486.379261      2011.810369        1268.620739
8     0.5    2856      844.189631      2433.905185        2011.810369
9     0.5    2381      -52.905185      2407.452592        2433.905185
10    0.5    1867     -540.452592      2137.226296        2407.452592

    squared_error   t   regression
0    2.345353e+06   1   602.090909
1    1.938423e+06   2   826.581818
2    1.478324e+06   3  1051.072727
3    1.848414e+05   4  1275.563636
4    3.338054e+06   5  1500.054545
5    1.940252e+05   6  1724.545455
6    1.123868e+05   7  1949.036364
7    2.209323e+06   8  2173.527273
8    7.126561e+05   9  2398.018182
9    2.798959e+03  10  2622.509091
10   2.920890e+05  11  2847.000000
forecast_breakdown_df.plot(x='t', y=['one_step_forecast','demand', 'regression'])
<matplotlib.axes._subplots.AxesSubplot at 0x110a83b38>
_images/image2.png

We have a choice now, we can use another alpha and repeat the analysis to reduce the Standard Error or use supplychainpy’s optimise=True parameter to use an evolutionary algorithm and get closer to an optimal solution.

opt_ses_df = simple_exponential_smoothing_forecast(ds=row_ds[1:12], length=12, smoothing_level_constant=0.4,optimise=True)
print(opt_ses_df)
{'statistics': {'pvalue': 0.0047852515832242743, 'test_statistic': 3.8634855288615153, 'std_residuals': 4793.7283216530095, 'intercept': 377.59999999999991, 'trend': True, 'slope': 224.4909090909091, 'slope_standard_error': 58.105797838218294}, 'optimal_alpha': 0.006889829296806371, 'mape': 209.37388042679993, 'standard_error': 1097.3575476759161, 'forecast_breakdown': [{'squared_error': 2345353.024793389, 'alpha': 0.006889829296806371, 'demand': 104, 'one_step_forecast': 1635.4545454545455, 't': 1, 'level_estimates': 1624.9030850605454, 'forecast_error': -1531.4545454545455}, {'squared_error': 405892.47902537062, 'alpha': 0.006889829296806371, 'demand': 2262, 'one_step_forecast': 1624.9030850605454, 't': 2, 'level_estimates': 1629.2925740500002, 'forecast_error': 637.09691493945456}, {'squared_error': 1636589.4900194753, 'alpha': 0.006889829296806371, 'demand': 350, 'one_step_forecast': 1629.2925740500002, 't': 3, 'level_estimates': 1620.4784665941236, 'forecast_error': -1279.2925740500002}, {'squared_error': 1193509.1999718475, 'alpha': 0.006889829296806371, 'demand': 528, 'one_step_forecast': 1620.4784665941236, 't': 4, 'level_estimates': 1612.9514764488533, 'forecast_error': -1092.4784665941236}, {'squared_error': 915941.87643142976, 'alpha': 0.006889829296806371, 'demand': 2570, 'one_step_forecast': 1612.9514764488533, 't': 5, 'level_estimates': 1619.5453774048813, 'forecast_error': 957.04852355114667}, {'squared_error': 162848.87162484805, 'alpha': 0.006889829296806371, 'demand': 1216, 'one_step_forecast': 1619.5453774048813, 't': 6, 'level_estimates': 1616.7650186410463, 'forecast_error': -403.54537740488126}, {'squared_error': 266013.5544537988, 'alpha': 0.006889829296806371, 'demand': 1101, 'one_step_forecast': 1616.7650186410463, 't': 7, 'level_estimates': 1613.2114857053452, 'forecast_error': -515.76501864104625}, {'squared_error': 1303681.0113751951, 'alpha': 0.006889829296806371, 'demand': 2755, 'one_step_forecast': 1613.2114857053452, 't': 8, 'level_estimates': 1621.0782136618895, 'forecast_error': 1141.7885142946548}, {'squared_error': 1525031.8183725097, 'alpha': 0.006889829296806371, 'demand': 2856, 'one_step_forecast': 1621.0782136618895, 't': 9, 'level_estimates': 1629.5866139646664, 'forecast_error': 1234.9217863381105}, {'squared_error': 564622.07671308529, 'alpha': 0.006889829296806371, 'demand': 2381, 'one_step_forecast': 1629.5866139646664, 't': 10, 'level_estimates': 1634.7637239257851, 'forecast_error': 751.41338603533359}, {'squared_error': 53933.687924818943, 'alpha': 0.006889829296806371, 'demand': 1867, 'one_step_forecast': 1634.7637239257851, 't': 11, 'level_estimates': 1636.3637922244625, 'forecast_error': 232.23627607421486}], 'forecast': [1636.3637922244625, 1636.3637922244625, 1636.3637922244625, 1636.3637922244625, 1636.3637922244625]}
print(opt_ses_df.get('statistics', 'UNKNOWN'),'\n mape: {}'.format(opt_ses_df.get('mape', 'UNKNOWN')))
{'pvalue': 0.0047852515832242743, 'test_statistic': 3.8634855288615153, 'std_residuals': 4793.7283216530095, 'intercept': 377.59999999999991, 'trend': True, 'slope': 224.4909090909091, 'slope_standard_error': 58.105797838218294}
 mape: 209.37388042679993
print(opt_ses_df.get('forecast', 'UNKNOWN'))
[1636.3637922244625, 1636.3637922244625, 1636.3637922244625, 1636.3637922244625, 1636.3637922244625]
optimised_regression = {'regression': [(opt_ses_df.get('statistics')['slope']* i ) + opt_ses_df.get('statistics')['intercept'] for i in range(1,12)]}
print(optimised_regression)
{'regression': [602.09090909090901, 826.58181818181811, 1051.0727272727272, 1275.5636363636363, 1500.0545454545454, 1724.5454545454545, 1949.0363636363636, 2173.5272727272727, 2398.0181818181818, 2622.5090909090909, 2847.0]}
opt_forecast_breakdown_df = pd.DataFrame(opt_ses_df.get('forecast_breakdown', 'UNKNOWN'))

We can compare the MAPE of our previous forecast with the optimised simple exponential smoothing forecast to see which is a better forecast.

opt_forecast_breakdown_df['regression'] = optimised_regression.get('regression')
print(opt_forecast_breakdown_df)
      alpha  demand  forecast_error  level_estimates  one_step_forecast  0   0.00689     104    -1531.454545      1624.903085        1635.454545
1   0.00689    2262      637.096915      1629.292574        1624.903085
2   0.00689     350    -1279.292574      1620.478467        1629.292574
3   0.00689     528    -1092.478467      1612.951476        1620.478467
4   0.00689    2570      957.048524      1619.545377        1612.951476
5   0.00689    1216     -403.545377      1616.765019        1619.545377
6   0.00689    1101     -515.765019      1613.211486        1616.765019
7   0.00689    2755     1141.788514      1621.078214        1613.211486
8   0.00689    2856     1234.921786      1629.586614        1621.078214
9   0.00689    2381      751.413386      1634.763724        1629.586614
10  0.00689    1867      232.236276      1636.363792        1634.763724

    squared_error   t   regression
0    2.345353e+06   1   602.090909
1    4.058925e+05   2   826.581818
2    1.636589e+06   3  1051.072727
3    1.193509e+06   4  1275.563636
4    9.159419e+05   5  1500.054545
5    1.628489e+05   6  1724.545455
6    2.660136e+05   7  1949.036364
7    1.303681e+06   8  2173.527273
8    1.525032e+06   9  2398.018182
9    5.646221e+05  10  2622.509091
10   5.393369e+04  11  2847.000000
opt_forecast_breakdown_df.plot(x='t', y=['one_step_forecast','demand', 'regression'])
<matplotlib.axes._subplots.AxesSubplot at 0x110a98f98>
_images/image3.png