Uncategorized
How to Generate Time Series Forecasts in TM1 with TM1py and Prophet

How to Generate Time Series Forecasts in TM1 with TM1py and Prophet

It’s a Friday afternoon. You’ve been slaving over this forecast all week….and it’s still not right. “Seriously…can’t we just copy last year?!” If only there was a way to speed this up a little…something…anything! There’s no time to start again. You need something now.

Your prayers have been heard and answered.

TM1 just got a whole lot sexier thanks to (a not so secret weapon called) TM1py.

If you haven’t heard about it before. Listen closely, you’re going to want to hear this.

Tm1py was a Python package developed by the wizards at Cubewise. It allows you to seamlessly code in Python and interact with your TM1 application. This is mind blowing because it opens up TM1 to a whole new world of possibilities. One of those possibilities comes in the form of faster forecasting. This plug and play example goes through how to use TM1py and the Prophet timeseries package (that was built by the Facebook data team) to forecast forward using data from TM1 AND how to automatically send that forecast back into your TM1 cube so you can use it for analysis.

Before actually stepping through this code it’s a good idea to install all of the required dependencies. Assuming you already have Anaconda installed on your pc, you’ll only need to install TM1py and Prophet. To install TM1py, first run the following command:
pip install TM1py

If you’re working from a Windows machine you’ll also need to install a Visual C compiler, this sounds harder than it actually is. Grab it from here and run the install by clicking the executable.

Next install Prophet and Pystan (a Prophet dependency). To do this run the following commands:

pip install pystan
pip install fbprophet

Want to try this out yourself? Download the code here TM1py Time Series Forecasting

After you’ve downloaded the Jupyter Notebook above. Start a new Jupyter session by navigating to your terminal and running the command Jupyter Notebook. Hold down Shift + Enter to run each code cell.

The rest of this post goes through how it all works!

Step 1: Import the required modules

Before stepping through the process there are a number of modules that need to be imported. This model relies heavily on Pandas for data manipulation and transformation and TM1py for interacting with the TM1 API. These modules are imported below:

#import tm1 service module
from TM1py.Services import TM1Service
#import tm1 utils module
from TM1py.Utils import Utils
#MDX view
from TM1py.Objects import MDXView
#import pandas
import pandas as pd
#import matplotlib
import matplotlib.pyplot as plt
#inline plotting for matplotlib
%matplotlib inline
#import statsmodels package
from fbprophet import Prophet

Step 2: Setup your TM1 parameters

TM1 Server Parameters

Populate your TM1 server parameters below. In this example the script is connecting to the local server and 8882 for the HTTP port.

#Server address
address = 'localhost'
#HTTP port number - this can be found in your config file
port = '8882'
#username
user = 'admin'
#password
password = ''
#SSL parameter - this can be found in your config file
ssl = True

Step 3: Get TM1 Data

The following code obtains a data set from TM1 based on the cube and view specified above. The resulting dataset is transformed into a pandas dataframe for statistical analysis.

For more on pandas and data frames check out this pandas tutorial.

Before importing any data, create the view you would like to import into the data frame. In this case, the cube name is Retail and the view name is Time Series. You can update the fields below with your desired cube and view.

#specify the cube
cube_name = 'Retail'
#specify the view
view_name = 'Time Series'
with TM1Service(address= address, port=port, user=user, password=password, ssl=ssl) as tm1:
    
    #specify cube view that you want to analyse
    cube_name = cube_name
    view_name = view_name

    # Extract MDX from CubeView
    mdx = tm1.cubes.views.get_native_view(cube_name, view_name, private=False).MDX
    #print(mdx)
    
    # Get data from P&L cube through MDX
    rawdata = tm1.cubes.cells.execute_mdx(mdx)

    # Build pandas DataFrame fram raw cellset data
    df = Utils.build_pandas_dataframe_from_cellset(rawdata)

 

Step 4: Time Series Forecast with Prophet

ts = df
ts = ts.reset_index()
ts.dtypes
ts['Date'] = ts['Year'] + '-' + ts['Period']
ts['Date'] = pd.DatetimeIndex(ts['Date'])
ts = ts.rename(columns={'Date': 'ds',
                        'Values': 'y'})

Rather than looping through each region and product within the data set, the following cell creates a subset of data to forecast. If you’d like to forecast for an alternate region/product combination this is where you’re able to make the change by updating the region and product variables below (i.e. update Region from 13 to something else).

region = '13'
product = '353'

sub = ts[ts['Region']==region]
sub = sub[sub['Product']==product]

sub_model = Prophet(interval_width=0.95)
sub_model.fit(sub)

In this case we’re forecasting this data set.

The next step is to specify how many periods that you would like to forecast forward. This next code block creates a future data frame with those periods available.
(Side Note: If you would like to automatically load this data back into TM1 you will need to make sure these dates/periods are already created as elements)

#specify the number of future periods
future_dates=sub_model.make_future_dataframe(periods=24,freq='MS')
forecast = sub_model.predict(future_dates)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
row ds yhat yhat_lower yhat_upper
79 2019-08-01 310.801879 307.477384 314.040854
80 2019-09-01 295.945280 292.474399 299.419966
81 2019-10-01 272.046733 268.285466 275.718460
82 2019-11-01 253.014918 249.002047 256.901590
83 2019-12-01 273.283099 269.021261 277.363902
sub_model.plot(forecast, uncertainty=True)

sub_model.plot_components(forecast)

Step 5: Send the data back into TM1

This next block reformats the existing data frame and sends the result back into the Forecast Version.

#Combine the forecast dataframe and our original subset
sub = sub.set_index('ds')
forecast = forecast.set_index('ds')
result = pd.concat([sub, forecast], axis = 1)
#fill out other dimensions
result['Version'] = result.Version.fillna('3')
result['Currency'] = result.Currency.fillna('Local')
result['Region'] = result.Region.fillna(region)
result['Product'] = result.Product.fillna(product)
result['Retail Measure'] = result['Retail Measure'].fillna('Sales Amount')

#make ds accessible
result = result.reset_index()
result['Year'] = pd.DatetimeIndex(result['ds']).year
result['Period'] = pd.DatetimeIndex(result['ds']).month
with TM1Service(address= address, port=port, user=user, password=password, ssl=ssl) as tm1:
    
    # cellset to store the new data
    cellset = {}
    # Populate cellset with coordinates and value pairs
    for index, row in result.iterrows():
        cellset[('3', row['Year'], row['Period'], row['Currency'], row['Region'], row['Product'],row['Retail Measure'])] = row['yhat']        
        tm1.cubes.cells.write_values('Retail', cellset)

And presto…the result now in TM1.

Want to try this out yourself? Download the Jupyter Notebook:  TM1py Time Series Forecasting – Blog Post.

Load More Related Articles

Facebook Comments