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)
# Generate your trend
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.