Przykład tworzenia zamówień automatycznych w piekarni. Model regresji liniowej

Dzisiaj omówimy przykład tworzenia zamówień automatycznych w piekarni. Użyjemy do tego modelu regresji logistycznej. Nie da się ukryć, że jest to nieco przestarzały sposób w środowisku machine learning. Niemniej model regresji liniowej jest klasyczną i często skuteczną metodą prognozowania wyników.  

Problem zamówień w sklepach piekarni, przykład tworzenia zamówień automatycznych

Nasza przykładowa sieć sprzedaży składa się z 16 sklepów sprzedających pieczywo. Sieć sklepów działa w małych miastach pod Warszawą.  Sklepy zlokalizowane są blisko dworców kolejowych skąd wielu mieszkańców dojeżdża do pracy do Warszawy. Idąc i wracając z pracy mieszkańcy kupują pieczywo. Na pierwszej zmianie piekarnia dostarcza do swoich sklepów maksymalną ilość produktów gdyż wie, że pieczywo zostanie w większości sprzedane w ciągu dnia . Problem pojawia się gdy trzeba uzupełnić zasoby sklepu przed szczytem powrotów z pracy, pomiędzy godzinami 14 a 18. Jeżeli piekarnia dostarczy zbyt wielu produktów na drugiej zmianie, nie zostaną one sprzedane i zostaną wyrzucone. Jeżeli na drugiej zmianie produktów będzie zbyt mało, z czasem mieszkańcy zniechęcą się do wstępowania do sklepu i spadnie sprzedaż. Teraz omówimy przykład tworzenia zamówień automatycznych opartych na starej, dobrej regresji liniowej.

Model regresji liniowej przewidującej dzienne zapotrzebowanie na produkty

Model zasilany będzie rocznym rejestrem sprzedaży. Zbiór danych zawiera datę sprzedaży, rodzaj asortymentu oraz informacje pogodowe w postaci: temperatury, ciśnienia atmosferycznego oraz opadów. W ocenie prowadzącego badania, dane meteorologiczne mogły mieć bezpośredni wpływ na wielkość zakupów w sklepach piekarni. Ściągamy bazę danych oraz potrzebne biblioteki. W tym ćwiczeniu wyjątkowo baza danych nie będzie dostępna.    

import pandas as pd
import numpy as np
import itertools
from itertools import chain, combinations
import statsmodels.formula.api as smf
import scipy.stats as scipystats
import statsmodels.api as sm
import statsmodels.stats.stattools as stools
import statsmodels.stats as stats 
from statsmodels.graphics.regressionplots import *
import matplotlib.pyplot as plt
import seaborn as sns
import copy
#from sklearn.cross_validation import train_test_split
import math


df = pd.read_csv('c:/1/BazaPiekarni.csv', parse_dates=['Time'])
df.sample(5)

Sprawdzamy jak wielki jest zbiór danych. Zbiór zawiera 1 mln 48 tysięcy transakcji. Duża liczba rekordów dobrze rokuje przy tworzeniu modelu.

df.shape

(1048575, 9)

Sprawdźmy jaki jest format naszych danych. Dzięki zastosowaniu parametru  parse_dates=[‘Time’] ) udało się zachować format daty w kolumnie ‘Time’.

df.dtypes

Teraz zmienimy nazwy kolumn oraz kasujemy kolumny ‘index’ i ‘Number’

df.columns

 

df.columns = ['Number', 'index', 'Date', 'Assortment_code', 'Assortment_name', 'Location', 'Temperature', 'Weather', 'Pressure']

del df['index']
del df['Number']
df.sample(5)

Uporządkowane dane mają postać:

Dodajemy nowe kolumny porządkujące datę i czas.

df['Hour'] = df.Date.dt.hour
df['Weekday'] = df.Date.dt.weekday_name
df['Date2'] = df.Date.dt.date
df.sample(4)

Problematyczna druga połowa dnia, druga zmiana, zaczyna się o godzinie 14. Naszym celem jest określenie jaka powinna być wielkość dostaw poszczególnych asortymentów. Powinnyśmy wybrać jedną z lokalizacji następnie wybrać dane sprzedaży tylko po godzinie 14. Należy również wybrać jeden produkt.

Sprawdzamy do jakiej godziny sprzedawane jest pieczywo.

df.Hour.max()

18

Wydzielamy dane tylko dla sklepu nr 7 i tylko po godzinie 14.

df7 = df[(df['Hour']>=14)&(df['Hour']<=18)&(df['Location']==7)&(df['Assortment_code']==1)]
df7.sample(5)

Z danych została wyodrębniona tabela danych zawierająca sprzedaż chleba: ‘Primary Bread’ w sklepie numer 7 i tylko po godzinie 14.

Teraz zliczamy ile sztuk asortymentów pieczywa zostało sprzedanych w tej lokalizacji każdego dnia. Baza zbudowana jest tak, że jeden rekord reprezentuje jedną sprzedaż. Nasz model ma pokazywać ile sztuk pieczywa zostanie  sprzedanych w określonym czasie. Aby zsumować sprzedaż tworzymy tabelę przestawną.

kot = df7.pivot_table(index=['Assortment_name','Assortment_code','Date2','Weather','Temperature','Pressure','Location', 'Weekday'], values='Date',aggfunc='count')

kot.head()

Teraz przerabiam tabelę przestawną na zwykłą tabele danych dataframe, sortujemy dane.

df7 = kot.reset_index()
df7.sort_values('Date2', ascending = True, inplace=True )
df7.head(5)

Kolumnę ze zmiennymi wynikowymi nazwiemy ‘Quantity sold’.

df7.rename(columns={'Date':'Quantity_sold'},inplace=True)
df7.sample(3)

Regresja jednoczynnikowa

Pierwszymi modelami stosowanymi do prognozowania były proste jednoczynnikowe modele regresji liniowej.

Aby zbudować przykładowy jednoczynnikowy model regresji liniowej trzeba znaleźć niezależną zmienną, która posiada największy związek liniowy ze zmienną zależną: ‘Quantity sold’.

Żeby to jednak zrobić, trzeba najpierw zakodować wszystkie kolumny, które obecnie są wyrażone w formie tekstu. W naszym przykładzie takimi kolumnami są ‘Weather’ i ‘Weekday’

 

Przy okazji okazało się, że straciliśmy format daty w kolumnie ‘date2’. Naprawiamy to.

df7['Date2']= pd.to_datetime(df7.Date2)
df7.dtypes

Teraz możemy utworzyć nowe kolumny z cyfrową wersją tekstu.

df7['Weather_code'] = pd.Categorical(df7['Weather']).codes
df7['Weekday_code'] = df7['Date2'].dt.weekday

Teraz tworzę wektor korelacji między zmiennymi niezależnymi a zmienną zależną.

CORREL = df7.corr().sort_values('Quantity_sold')
CORREL['Quantity_sold']

Podstawowym argumentem do zastosowania modelu regresji liniowej jest istnienie wyraźnego związku liniowego pomiędzy zmienną zależną i zmienną niezależną.

W naszym przypadku zmienna niezależna ‘Weekday_code’ ma wyraźną korelację ze zmienną zależną.

Sprawdzimy teraz korelację wszystkich zmiennych.

sns.heatmap (df7.corr (), cmap="coolwarm")

Jednoczynnikowy model regresji liniowej

Wprowadzamy równanie regresji liniowej oraz kod umożliwiający wizualizację przebiegu linii regresji.

lm = smf.ols(formula = 'Quantity_sold ~ Weekday_code', data = df7).fit()
print(lm.summary())


### wykres--------------------------------------###
plt.figure()
plt.scatter(df7.Weekday_code, df7.Quantity_sold, c = 'grey')
plt.plot(df7.Weekday_code, lm.params[0] + lm.params[1] * df7.Weekday_code, c = 'r')
plt.xlabel('Weekday_code')
plt.ylabel('Quantity_sold')
plt.title("Linear Regression Plot")

Model regresji liniowej okazał się względnie słaby z uwagi na niski poziom wskaźnika R2 (R-squared) wynoszący 0.424%.

 

Wieloczynnikowy model regresji liniowej

lm = smf.ols(formula = 'Quantity_sold ~ Weekday_code + Pressure + Weather_code + Temperature', data = df7).fit()
print (lm.summary())

Model wielorakiej regresji liniowej nie okazał się lepszy. Jego wskaźnik wyniósł 0,432%.

Mimo istnienia związku liniowego między zmienną niezależną i zmienną zależną nie udało się stworzyć modelu pozwalającego wiarygodnie szacować przyszły poziom zakupów w sklepach piekarni. Aby model był efektywny należy rozejrzeć się za innym zestawem zmiennych niezależnych, które w leszy sposób opiszą rzeczywistość.

 

Praktyczne użycie modelu regresji wielorakiej jako przykład tworzenia zamówień automatycznych 

Praca nad modelem, którego nie można wykorzystać jest pracą jałową. Nie udało nam się stworzyć dobrego modelu ale nic nie stoi na przeszkodzie aby pokazać jak nasz model jest zły.

Na początek możemy ręcznie przekleić parametry modelu tworząc nową kolumnę.

df7['Model_Reg'] = (87.6337) +(-5.8891*df7['Weekday_code']) +(-0.0478*df7['Pressure']) +(0.6384*df7['Weather_code'])+(0.0236*df7['Temperature'])
df7['Model_Reg']= np.round(df7['Model_Reg'], decimals=0)
df7[['Quantity_sold','Model_Reg']].head()

 Widać, że model nie jest zbyt dokładny.

 

Uruchomienie modelu regresji liniowej

Na przyszłość trudno oczekiwać, że za każdym razem będziemy przeklejali parametry modelu. Dlatego warto tą czynność zautomatyzować.

Tworzymy słownik współczynników.

coef = lm.params.to_dict()

Teraz możemy sobie przywołać dowolny współczynnik przy x.

coef['Weekday_code']

-5.889075106403816

coef['Intercept']

87.6336717107532

Teraz w oddzielnej kolumnie utworzymy kod tworzący równanie regresji.

df7['Model_Reg2'] = (87.6337) +(coef['Weekday_code']*df7['Weekday_code']) +(coef['Pressure']*df7['Pressure']) +(coef['Weather_code']*df7['Weather_code'])+(coef['Temperature']*df7['Temperature'])
df7['Model_Reg2']= np.round(df7['Model_Reg2'], decimals=0)
df7[['Quantity_sold','Model_Reg','Model_Reg2']].head()

Jak widać model nie jest w stanie przewidzieć właściwej ilością produktów.

Teraz sprawdzamy o ile różnią się estymacje modelu.