# How to approach forecasting time-series data

I'm statistics newbie and any help in picking a good method to analyze the data that I have would be very welcome:

We have a customer that has an active Facebook page, that gets posted on regularity. I have data on their last, say, 200 posts (from the last month) - how many likes the post got and time it was posted. If we assume that all the content is received equally well, I'm trying to alter their posting times so that the content will get more views and consequently more likes/comments/etc. What I'd like is to figure the probability that posting in 11am on tuesday will gain X more likes than posting on 10 am tues. It would be good if this can be easily retrained as new data from posts come in.

I've been looking on some methods to this and K-nearest neighbors or Neural networks seem most likely to work fine (maybe SVM can be adapted for this too).

PS: I'm attaching a graph of the sample data. Since I only have data for a month, I figured I'd separate the week into 168 1-hour segments. X-axis is hour of the week (from 0 to 168) and y-axis is the engagement the post got:

UPDATE: The real data is very unevenly spaced, e.g. 4 posts made in the morning, and no posts until the late evening. How would you recommend to proceed with that? I think resampling will lead to data loss.

A simple approach is to post at the hour slot you expect to receive the most likes.

Your description suggests that the only expected component of your time series is seasonal by hours of the day.

To be more precise, suppose that influence is the multiplicative. A parametrized realization of that model for 30 days is given below.

If we normalize and overlay each day, we can perform regression on it.

As if by cheating, we've recovered our seasonal component.

The code.

import numpy as np
import pandas
from matplotlib import pyplot as plt
from sklearn.neighbors import KNeighborsRegressor

def generate_ts(hours=24, days=30):
np.random.seed(123)
# Generate some iid like data
x = np.random.binomial(10, .5, hours * days)
slice = np.linspace(-np.pi, np.pi, hours)
hourly_trend = np.round(np.cos(slice) * 5)
hourly_trend -= hourly_trend.min()
rep_hourly_trend = np.tile(hourly_trend, days)
data = x * rep_hourly_trend
# Generate a index
ind = pandas.DatetimeIndex(freq='h',
start='2013-09-29 00:00:00',
periods=days * hours)
return pandas.Series(data, index=ind), hourly_trend

def recover_trend(ts, hours=24, days=30):
obs_trend = ts.values.reshape(-1, hours)
obs_trend = (obs_trend.T - obs_trend.mean(axis=1)) / obs_trend.std(axis=1)
y = obs_trend.ravel()
x = (np.repeat(np.arange(hours), days)).reshape(-1, 1)
model = KNeighborsRegressor()
model.fit(x, y)
rec_trend = model.predict(np.arange(hours).reshape(-1, 1))
return x, y, rec_trend

def main():
hours, days = 24, 30
ts, true_trend = generate_ts(hours=hours, days=days)
true_trend = (true_trend - true_trend.mean()) / true_trend.std()
ts.plot()
plt.title("Run Sequence Plot of Likes")
plt.ylabel("Likes")
plt.xlabel("Time")
plt.show()
x, y, rec_trend = recover_trend(ts, hours=hours, days=days)
plt.scatter(x.ravel(), y, c='k', label='Observed Trend')
plt.plot(np.arange(hours), rec_trend, 'g', label='Recovered Trend', linewidth=5)
plt.plot(np.arange(hours), true_trend, 'r', label='True Trend', linewidth=5)
plt.grid()
plt.title("Trend Regression")
plt.ylabel("Normalized Like Influence")
plt.xlabel("Hours")
plt.legend()
plt.show()
season_comp = pandas.Series(np.tile(rec_trend, days), index=ts.index)
season_comp.plot()
plt.title("Run Sequence Plot of Seasonal Component of Likes")
plt.ylabel("Likes")
plt.xlabel("Time")
plt.show()

if __name__ == '__main__':
main()


Before using this, I must caution that there are several issues.

• If there is a trend component, it must be dealt with first. Low order polynomial regression or the lag operator are popular options.

• Careful inspection of the autocorrelation and partial autocorrelation plots may reveal additional components of the time series to consider.

• After detrending your time series, you should inspect the residuals for stationarity.

• No information is given on the distributions of times that the posts were made in the collected data.

• Though it may seem obvious that the optimal posting time is prior to the maximum of the recovered seasonal trend, this may not be the case.

• Changing the posting time, may change the seasonality of the likes.

• Clumping all the posts on the hour that receives the most likes, will likely change the user behaviour.

• This problem is better suited to reinforcement learning. The principled approach is to perform sequential optimization of post time by contextual bandits.

• Thank you for the detailed and helpful answer! The real data is very unevenly spaced, e.g. 4 posts made in the morning, and no posts until the late evening. How would you recommend to proceed with that? I think resampling will lead to data loss. – Slav Oct 2 '13 at 15:53
• You can smooth the data with the exponentially weight moving average. It sounds like you only have a little bit of data though. The seasonal trend will be clearer the longer you collect it. – Jessica Mick Oct 4 '13 at 2:22

It sounds like you only care about what day of week and what hour of that day will likely garner the most attention. You can format your data into hour of week, and treat each week as a set of observations, like you have done. From here you can calculate the data-derived expected likes by hour of week. If you normalize the data, then the likes for each hour over the total likes for a week will give you the probability for that hour of the week.

You can regress on that data, but to utilize a clustering algorithm like k-NN, or a neural network to predict based on latent features, will require that you have more than an x and y. Adding features like the general topic, perhaps some term frequency or semantic analysis, maybe the format of the post (images or not, links or not, question or not, etc.), will offer you data to cluster by. You will likely need to adjust by overall activity, and will need many more weeks to gain any kind of confidence in the output.

However, if you get a good set of features and can remove general unrelated trends in activity, you might be best served by generating a self organizing map (a type of neural network) in which the hour of the week is a node whose response correlates best with a specific combined set of features. A good, simple implementation in Python is here. Then, when you get a specific post and decompose it into the feature set, you can see which node responds well and post on that hour of the week. Afterward, add the true response back into your training data and retrain the map to include the new data.