!pip install --upgrade --force-reinstall --no-cache-dir numpy==1.23.5 pmdarima sktime tensorflow==2.12.0rc0 keras-tuner
import itertools
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose, STL
from statsmodels.stats.diagnostic import acorr_ljungbox
import statsmodels.graphics.api as smgraphics
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.statespace.sarimax import SARIMAX
from scipy.stats import boxcox
from scipy.special import inv_boxcox
from pmdarima import auto_arima
import warnings
from sktime.forecasting.compose import (TransformedTargetForecaster, make_reduction)
from sktime.forecasting.model_selection import (ExpandingWindowSplitter, ForecastingGridSearchCV)
from sktime.performance_metrics.forecasting import MeanAbsolutePercentageError
from sktime.forecasting.trend import PolynomialTrendForecaster
from sktime.transformations.series.detrend import Deseasonalizer, Detrender
from sktime.forecasting.base import ForecastingHorizon
from xgboost import XGBRegressor
import numpy as np
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense,LSTM, Dropout, InputLayer
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_absolute_percentage_error
from sklearn.preprocessing import MinMaxScaler
import keras_tuner as kt
from keras.regularizers import l1_l2
from keras.optimizers import Adam, SGD, RMSprop
from keras.callbacks import EarlyStopping
# Suppress specific warnings.
warnings.filterwarnings("ignore", category=FutureWarning)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
isbn = pd.read_excel("/content/ISBN List.xlsx", sheet_name=None)
uk_weekly = pd.read_excel("/content/UK Weekly Trended Timeline from 200101_202429.xlsx", sheet_name=None)
isbn_Educational = pd.DataFrame(isbn["Y Childrens, YA & Educational"])
isbn_Trade = pd.DataFrame(isbn['T Adult Non-Fiction Trade'])
isbn_Specialist = pd.DataFrame(isbn['S Adult Non-Fiction Specialist'])
isbn_Fiction = pd.DataFrame(isbn['F - Adult Fiction'])
uk_weekly_Educational = pd.DataFrame(uk_weekly["Y Children's, YA & Educational"])
uk_weekly_Fiction = pd.DataFrame(uk_weekly['F Adult Fiction'])
uk_weekly_Trade = pd.DataFrame(uk_weekly['T Adult Non-Fiction Trade'])
uk_weekly_Specialist = pd.DataFrame(uk_weekly['S Adult Non-Fiction Specialist'])
isbn_names = {"isbn_Educational": isbn_Educational, "isbn_Trade": isbn_Trade, "isbn_Specialist": isbn_Specialist,
"isbn_Fiction": isbn_Fiction}
uk_weekly_name = {"uk_weekly_Educational":uk_weekly_Educational, "uk_weekly_Trade": uk_weekly_Trade,
"uk_weekly_Specialist": uk_weekly_Specialist,"uk_weekly_Fiction": uk_weekly_Fiction}
def explore(dataframe_name):
for i, data in dataframe_name.items():
print(f"{' '.join(i.split('_'))} have the follwing properties")
print("------------------------------------------------------------------------------------------")
print(f"Shape \n {data.shape}")
print("------------------------------------------------------------------------------------------")
print(data.info())
print("------------------------------------------------------------------------------------------")
print(data.describe())
print("******************************************************************************************")
explore(uk_weekly_name)
uk weekly Educational have the follwing properties
------------------------------------------------------------------------------------------
Shape
(55286, 13)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 55286 entries, 0 to 55285
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ISBN 55286 non-null int64
1 Title 55286 non-null object
2 Author 50113 non-null object
3 Interval 55286 non-null int64
4 End Date 55286 non-null datetime64[ns]
5 Volume 55286 non-null int64
6 Value 55286 non-null float64
7 ASP 55193 non-null float64
8 RRP 54856 non-null float64
9 Binding 55286 non-null object
10 Imprint 55286 non-null object
11 Publisher Group 55286 non-null object
12 Product Class 55286 non-null object
dtypes: datetime64[ns](1), float64(3), int64(3), object(6)
memory usage: 5.5+ MB
None
------------------------------------------------------------------------------------------
ISBN Interval End Date \
count 5.528600e+04 55286.000000 55286
mean 9.780811e+12 200744.123720 2007-09-04 13:22:46.523170304
min 9.780002e+12 200101.000000 2001-01-06 00:00:00
25% 9.780441e+12 200318.000000 2003-05-03 00:00:00
50% 9.780721e+12 200601.000000 2006-01-07 00:00:00
75% 9.780753e+12 201023.000000 2010-06-05 00:00:00
max 9.781904e+12 202429.000000 2024-07-20 00:00:00
std 5.779893e+08 561.901387 NaN
Volume Value ASP RRP
count 55286.000000 55286.000000 55193.000000 54856.000000
mean 530.412564 2743.474619 5.556607 6.921442
min -269.000000 -1348.460000 -11.010000 0.100000
25% 18.000000 90.835000 4.406400 4.990000
50% 151.000000 784.520000 4.962700 6.500000
75% 493.000000 2435.630000 5.990000 7.990000
max 193645.000000 483767.750000 40.000000 25.000000
std 2314.946914 10668.029749 2.709133 3.219413
******************************************************************************************
uk weekly Trade have the follwing properties
------------------------------------------------------------------------------------------
Shape
(65344, 13)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 65344 entries, 0 to 65343
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ISBN 65344 non-null int64
1 Title 65344 non-null object
2 Author 60655 non-null object
3 Interval 65344 non-null int64
4 End Date 65344 non-null datetime64[ns]
5 Volume 65344 non-null int64
6 Value 65344 non-null float64
7 ASP 65274 non-null float64
8 RRP 65210 non-null float64
9 Binding 65344 non-null object
10 Imprint 65344 non-null object
11 Publisher Group 65344 non-null object
12 Product Class 65344 non-null object
dtypes: datetime64[ns](1), float64(3), int64(3), object(6)
memory usage: 6.5+ MB
None
------------------------------------------------------------------------------------------
ISBN Interval End Date \
count 6.534400e+04 65344.000000 65344
mean 9.780467e+12 200884.679603 2009-01-30 21:32:22.360430848
min 9.780003e+12 200101.000000 2001-01-06 00:00:00
25% 9.780140e+12 200347.000000 2003-11-22 00:00:00
50% 9.780416e+12 200731.000000 2007-08-04 00:00:00
75% 9.780718e+12 201317.000000 2013-04-27 00:00:00
max 9.781904e+12 202429.000000 2024-07-20 00:00:00
std 3.994394e+08 611.839318 NaN
Volume Value ASP RRP
count 65344.000000 6.534400e+04 65274.000000 65210.000000
mean 376.208849 3.081678e+03 9.206646 12.781906
min -74.000000 -4.372600e+02 -77.460000 1.000000
25% 7.000000 5.921750e+01 6.767075 8.990000
50% 38.000000 3.285200e+02 7.986300 10.990000
75% 239.000000 1.902920e+03 10.470900 15.990000
max 80620.000000 1.056257e+06 32.010000 30.000000
std 1591.597425 1.664461e+04 4.683351 6.206623
******************************************************************************************
uk weekly Specialist have the follwing properties
------------------------------------------------------------------------------------------
Shape
(32827, 13)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32827 entries, 0 to 32826
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ISBN 32827 non-null int64
1 Title 32827 non-null object
2 Author 28077 non-null object
3 Interval 32827 non-null int64
4 End Date 32827 non-null datetime64[ns]
5 Volume 32827 non-null int64
6 Value 32827 non-null float64
7 ASP 32731 non-null float64
8 RRP 27429 non-null float64
9 Binding 32827 non-null object
10 Imprint 32827 non-null object
11 Publisher Group 32827 non-null object
12 Product Class 32827 non-null object
dtypes: datetime64[ns](1), float64(3), int64(3), object(6)
memory usage: 3.3+ MB
None
------------------------------------------------------------------------------------------
ISBN Interval End Date \
count 3.282700e+04 32827.000000 32827
mean 9.780743e+12 200450.247479 2004-09-24 01:00:03.180308864
min 9.780003e+12 200101.000000 2001-01-06 00:00:00
25% 9.780341e+12 200218.000000 2002-05-04 00:00:00
50% 9.780672e+12 200340.000000 2003-10-04 00:00:00
75% 9.780765e+12 200602.000000 2006-01-14 00:00:00
max 9.781904e+12 202429.000000 2024-07-20 00:00:00
std 5.613454e+08 342.780391 NaN
Volume Value ASP RRP
count 32827.000000 32827.000000 32731.000000 27429.000000
mean 87.351296 1176.246431 13.479478 16.302991
min -86.000000 -863.620000 -5.765000 2.950000
25% 5.000000 49.545000 8.429300 9.990000
50% 41.000000 456.540000 12.020600 12.990000
75% 114.000000 1451.050000 17.132050 19.950000
max 4378.000000 39125.560000 46.690000 69.990000
std 143.989186 2137.410382 7.445967 10.155029
******************************************************************************************
uk weekly Fiction have the follwing properties
------------------------------------------------------------------------------------------
Shape
(73767, 13)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 73767 entries, 0 to 73766
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ISBN 73767 non-null int64
1 Title 73767 non-null object
2 Author 73500 non-null object
3 Interval 73767 non-null int64
4 End Date 73767 non-null datetime64[ns]
5 Volume 73767 non-null int64
6 Value 73767 non-null float64
7 ASP 73683 non-null float64
8 RRP 73767 non-null float64
9 Binding 73767 non-null object
10 Imprint 73767 non-null object
11 Publisher Group 73767 non-null object
12 Product Class 73767 non-null object
dtypes: datetime64[ns](1), float64(3), int64(3), object(6)
memory usage: 7.3+ MB
None
------------------------------------------------------------------------------------------
ISBN Interval End Date \
count 7.376700e+04 73767.000000 73767
mean 9.780393e+12 200877.213347 2009-01-03 22:50:01.049249536
min 9.780002e+12 200101.000000 2001-01-06 00:00:00
25% 9.780140e+12 200410.000000 2004-03-06 00:00:00
50% 9.780341e+12 200735.000000 2007-09-01 00:00:00
75% 9.780554e+12 201229.000000 2012-07-21 00:00:00
max 9.781860e+12 202429.000000 2024-07-20 00:00:00
std 3.179225e+08 592.264956 NaN
Volume Value ASP RRP
count 73767.000000 73767.000000 73683.000000 73767.000000
mean 381.227473 2448.109140 7.157384 9.566535
min -20.000000 -96.420000 -4.010000 2.500000
25% 9.000000 62.270000 6.200400 7.990000
50% 55.000000 383.900000 6.892600 8.990000
75% 192.000000 1331.300000 7.730400 9.990000
max 51175.000000 385543.990000 60.000000 25.000000
std 1541.015144 9746.887969 2.323545 3.012778
******************************************************************************************
def initial_processing(dataframe_name):
for i, data in dataframe_name.items():
data.set_index("End Date", inplace = True)
data.sort_index(inplace = True)
data["ISBN"] = data["ISBN"].astype("str")
data.resample("W").sum().fillna(0, inplace = True)
initial_processing(uk_weekly_name)
def plot_isbn(dataframe_name):
data2024_07_01 = {}
unique_after2024_07_01 = []
cutoff = pd.Timestamp('2024-07-01')
for i, data in dataframe_name.items():
for isbncutoff in data[data.index > cutoff]["ISBN"].unique():
unique_after2024_07_01.append(isbncutoff)
plt.figure(figsize=(12,8))
plt.plot(data[data["ISBN"] == isbncutoff]["Volume"])
plt.title(f"Sales Volume Over Time for ISBN {isbncutoff}")
plt.xlabel("Sales Date")
plt.ylabel("Volume")
plt.show()
print(unique_after2024_07_01)
plot_isbn(uk_weekly_name)
['9781841462400', '9780006647553', '9780440864141', '9780241003008', '9781841460406', '9780744523232', '9781841462301', '9780440864554', '9781841461502', '9780752844299', '9781841460307', '9781841462509', '9780752846576', '9780099422587', '9780340696767', '9780099285823', '9780552145954', '9780552997034', '9780593048153', '9780140275421', '9780091816971', '9781841150437', '9780006531203', '9780140281293', '9780091867775', '9780749395698', '9780140259506', '9780719559792', '9780140276619', '9780340766057', '9780099286578', '9780099428558', '9780140294231', '9780224060875', '9780330355667', '9780340786055', '9780099286387', '9780552998482', '9780261103252', '9780099771517', '9780349114033', '9780552998727', '9780552997348', '9780006514213', '9780140276336', '9780140285215', '9780552998000', '9780747268161', '9780140295962', '9780552998444', '9780349113609', '9780349112763', '9780099244721', '9780749397548', '9780006512134', '9780722532935', '9780006514091', '9780007101887', '9780552145060', '9780006550433', '9780552145053']
books = ["very hungry caterpillar", "alchemist"]
def filter_books(isbn_books, weekly_sales, books):
lookedup_isbn = {}
for i in books:
for (key1, data1), (key2, data2) in zip(isbn_books.items(), weekly_sales.items()):
data1["ISBN"] = data1["ISBN"].astype("str")
result = data1[data1["Title"].str.lower().str.contains(i)]["ISBN"]
if not result.empty:
data2_result = data2[data2["ISBN"].isin(list(result))]
data2_result.index = data2_result.index + pd.Timedelta(days=1)
lookedup_isbn[i.split()[-1]] = data2_result.groupby(data2_result.index).sum().asfreq('W').fillna(0)["Volume"]
return lookedup_isbn
selected_books = filter_books(isbn_names, uk_weekly_name, books)
| Category | Rows | Columns |
|---|---|---|
| Educational | 55,286 | 13 |
| Trade | 65,344 | 13 |
| Specialist | 32,827 | 13 |
| Fiction | 73,767 | 13 |
| Column | Educational | Trade | Specialist | Fiction |
|---|---|---|---|---|
| Author | 5,173 | 4,689 | 4,750 | 267 |
| ASP | 93 | 70 | 96 | 84 |
| RRP | 430 | 134 | 5,398 | 0 |
| Metric | Educational | Trade | Specialist | Fiction |
|---|---|---|---|---|
| Volume | 530 | 376 | 87 | 381 |
| Value (£) | 2,743 | 3,082 | 1,176 | 2,448 |
| ASP (£) | 5.56 | 9.21 | 13.48 | 7.16 |
| RRP (£) | 6.92 | 12.78 | 16.30 | 9.57 |
2001-01-06 to 2024-07-20Volume and End Date, used as the time series index for analysis.End Date has been set as the index and resampled to weekly intervals.alchemist = pd.DataFrame(selected_books['alchemist'])
caterpillar = pd.DataFrame(selected_books['caterpillar'])
print(type(alchemist["Volume"].index))
print(alchemist.index.freq)
print(alchemist.index.inferred_freq)
<class 'pandas.core.indexes.datetimes.DatetimeIndex'> <Week: weekday=6> W-SUN
cutoff = pd.Timestamp('2012-01-01')
alchemist_cutoff = alchemist[alchemist.index > cutoff]
caterpillar_cutoff = caterpillar[caterpillar.index > cutoff]
two_books = {"The Alchemist": alchemist_cutoff, "The Very Hungry Caterpillar": caterpillar_cutoff}
explore(two_books)
The Alchemist have the follwing properties
------------------------------------------------------------------------------------------
Shape
(655, 1)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 655 entries, 2012-01-08 to 2024-07-21
Freq: W-SUN
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Volume 655 non-null float64
dtypes: float64(1)
memory usage: 10.2 KB
None
------------------------------------------------------------------------------------------
Volume
count 655.000000
mean 528.102290
std 227.965588
min 0.000000
25% 415.500000
50% 508.000000
75% 606.000000
max 2201.000000
******************************************************************************************
The Very Hungry Caterpillar have the follwing properties
------------------------------------------------------------------------------------------
Shape
(655, 1)
------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 655 entries, 2012-01-08 to 2024-07-21
Freq: W-SUN
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Volume 655 non-null float64
dtypes: float64(1)
memory usage: 10.2 KB
None
------------------------------------------------------------------------------------------
Volume
count 655.000000
mean 1348.909924
std 710.689315
min 0.000000
25% 723.000000
50% 1324.000000
75% 1758.500000
max 3905.000000
******************************************************************************************
plt.figure(figsize=(15,8))
alchemist_cutoff["Volume"].plot(label='Alchemist')
caterpillar_cutoff["Volume"].plot(label='Caterpillar')
plt.title('Sales Volume for both Alchemist and Caterpillar')
plt.xlabel('Date')
plt.ylabel('Volume')
plt.legend()
plt.show()
def decomposition(data, pd):
data_stl = STL(data , period=pd)
data_fit = data_stl.fit()
residual = data_fit.resid
#data_fit.plot();
print('Ljung-Box test output\n', acorr_ljungbox(residual), '...\n')
return residual
print("Decomposition for The Very Hungry Caterpillar")
residual_cat = decomposition(caterpillar_cutoff, 52)
print("\n Decomposition for The Alchemist")
residual_alch = decomposition(alchemist_cutoff, 52)
Decomposition for The Very Hungry Caterpillar
Ljung-Box test output
lb_stat lb_pvalue
1 370.471831 1.477278e-82
2 563.416272 4.525964e-123
3 655.510867 9.300685e-142
4 703.722530 5.447512e-151
5 735.165914 1.221569e-156
6 745.231614 1.044323e-157
7 746.925241 5.238363e-157
8 747.070784 5.223687e-156
9 747.322418 4.603664e-155
10 752.384656 3.530257e-155 ...
Decomposition for The Alchemist
Ljung-Box test output
lb_stat lb_pvalue
1 322.397402 4.352123e-72
2 493.431912 7.122324e-108
3 598.685446 1.942542e-129
4 669.332329 1.521385e-143
5 712.116376 1.178602e-151
6 735.396404 1.390001e-155
7 743.813088 2.457366e-156
8 745.613803 1.076019e-155
9 748.310139 2.822442e-155
10 763.421832 1.500879e-157 ...
def acf_pacf_plot(data):
smgraphics.tsa.plot_acf(data, lags=52);
smgraphics.tsa.plot_pacf(data, lags=52);
print("ACF and PACF Plot for The Very Hungry Caterpillar using Decomposition Residual")
acf_pacf_plot(residual_cat)
plt.show()
print("\n ACF and PACF Plot for The Alchemist using Decomposition Residual")
acf_pacf_plot(residual_alch)
plt.show()
ACF and PACF Plot for The Very Hungry Caterpillar using Decomposition Residual
ACF and PACF Plot for The Alchemist using Decomposition Residual
print("ACF and PACF Plot for The Very Hungry Caterpillar using Volume")
acf_pacf_plot(caterpillar_cutoff)
plt.show()
print("\n ACF and PACF Plot for The Alchemist using Volume")
acf_pacf_plot(alchemist_cutoff)
plt.show()
ACF and PACF Plot for The Very Hungry Caterpillar using Volume