Прогнозиране: как да открием остатъците?

Статията по-долу е откъс от моята книга Data Science for Supply Chain прогноза, достъпен тук. Можете да намерите другите ми статии тук:

  • Как да открием остатъците
  • Машинно обучение за прогноза на веригата за доставки
  • Croston прогноза за периодична прогноза
  • Прогноза KPI: RMSE, MAE, MAPE & Bias
„Днес няма да се опитвам да дефинирам по-нататък този материал (…) и може би никога не бих могъл да постигна разбираемо това. Но го знам, когато го видя. "
Потър Стюарт

През 1964 г. Потър Стюарт е върховен съд на Съединените щати. Той не е обсъждал пришълци, но дали филмът Любителите е бил или не е бил неприличен.

Докато работите върху прогнозата, ще забележите, че вашият набор от данни ще има остатъци. И макар да знам, че когато виждам, че може да е единственото практическо определение, тези хора са истинска заплаха за веригите за доставки. Тези високи (или ниски) точки ще доведат до свръх реакции във вашата прогноза или в запасите ви за безопасност, в крайна сметка ще доведат до (в най-добрия случай) ръчни корекции или (в най-лошия случай) мъртви запаси, загуби и гаден ефект на бичи. Всъщност, когато гледате блогове, книги, статии или софтуер при прогноза, въпросът за откриването на външни страни често се избягва. Това е жалко. Откриването на външни работи е сериозен бизнес.

Тези хора се появяват непрекъснато в съвременните вериги за доставки. Те се дължат най-вече на две основни причини:

Грешки и грешки Това са очевидни хора. Ако забележите подобен тип грешки или кодиране на грешки, той изисква подобряване на процеса, за да се предотврати повторното им извършване.
Изключително търсене Въпреки че някои наблюдения на търсенето са реални, това не означава, че не са изключителни и не трябва да се почистват или заглаждат. Този вид изключителни продажби всъщност не са толкова рядко срещани във веригите за доставки. Помислете за промоции, маркетинг, странно поведение на клиентите или преустановяване. Обикновено може да не искате да вземете предвид за прогнозата си изключителните продажби от -80%, които направихте миналата година, за да се отървете от стар почти остарял инвентар.

Ако успеете да забележите, че са останали хора и да ги изгладите, ще направите по-добра прогноза. Виждах многобройни примери, при които грешката при прогнозата беше намалена с няколко процента, само благодарение на външното почистване. Всъщност, колкото по-голям е наборът от данни, толкова по-важно е да се автоматизира това откриване и почистване. Нека да видим как можем да направим това.

В тази статия ще обсъдим три идеи и половина, за да забележим тези хора и да ги върнем на разумно ниво.

Идея №1 - Уинсоризация

Както казахме, външността е изключително висока или ниска стойност. Въз основа на тази проста дефиниция, първата идея за откриване на остатъците би била просто да се намали най-горната x най-висока и най-ниска точка от набора от данни. Нека да видим как ще работи това на двата набора от макети на таблиците по-долу.

Тази първа техника просто ще намали стойностите отгоре / надолу x% от историческото ни търсене до границата на x-ия перцентил.

x-ият перцентил е стойност, под която x% от наблюденията в групата ще паднат. Например, 99% от наблюденията за търсенето на даден продукт ще бъдат по-ниски от 99-те процентила.

Тази техника на просто свиване на търсенето до определен процентил се нарича winorization. Името идва от Чарлз П. Уинсор, статистик от първата половина на XX век.

Ако погледнем 1-ви и 99-и перцентил на нашите два набора от манекени по-горе, ето какво получаваме:

На тази таблица виждаме, че и в двата набора от данни всички ниски стойности ще бъдат увеличени до 4.4. На фигурата по-долу можете да видите, че това отрязва част от нашата база данни. Високите стойности ще бъдат намалени до 16.6 за набора от данни без външни източници (виж фигура 10.1) и до 70.9 за набора от данни с външен вид (виж фигура 10.2).

Може би сте забелязали, че победителността не ни дава кръгли резултати като 4 или 5, но вместо това получихме това 4.4. Всъщност, тъй като нямаме точна стойност, която съкращава набора от данни на 99%, правим линейно приближение въз основа на двете най-близки стойности. Ето как получихме тези числа вместо кръгли числа.

И така, доволни ли сме от тази техника?
Не, не сме.
- Забелязахме фалшиви атрибути в база данни без външни.
- В набора от данни с остатъците не сме намалили достатъчно външните (той отиде от 100 на 70,9).

Разбира се, може просто да се предложи понижаване на по-високия лимит на winorization от 99% на 95%, за да се намали допълнително външният номер на базата данни № 2, но за съжаление това би имало ефект и върху набор от данни № 1. Това не е добро решение. Човек би могъл също така да предложи да се премахне тази долна граница, така че да не увеличаваме търсенето си до 4,4. Но какво ще стане, ако имаме периоди с липсващо търсене? Не трябва ли да почистваме и тях, ако има такива?

Направи го сам
Excel можете лесно да получите различните процентили от диапазон от клетки в Excel, като използвате формулата = PERCENTILE.INC (обхват, граница). Разбира се, ще трябва да използвате тази формула веднъж за горната граница (със стойност около 0,95–0,99) и веднъж за долната граница (със стойност около 0,01–0,05).
Python Лесно можем да спечелим нашия набор от данни в Python благодарение на NumPy. Можем да изчислим различните процентили на масива благодарение на функцията np.percentile (масив, процентил).

импортиране numpy като np
по-висок_лимит = np.percentile (масив, 99)
lower_limit = np.percentile (масив, 1)

Обърнете внимание, че процентната функция приема процентил, изразен като стойност между 0 и 100, а не съотношение (т.е. стойност между 0 и 1), като в Excel.

След това можем просто да намалим масива до тези долни и по-високи граници, благодарение на функцията np.clip (масив, min, max):

масив = np.clip (масив, a_min = долен_лимит, a_max = по-висок_лимит)

Идея №2 Стандартно отклонение

Както току-що видяхме, winorization не беше идеалният начин за изключване на остатъците, тъй като щеше да извади високи и ниски стойности на набора от данни, дори и да не бяха изключителни за всеки вид.

Друг подход би бил да се разгледа изменението на търсенето около историческата средна стойност и да се изключат стойностите, които са изключително далечни от тази средна стойност.
Нека определим стандартното отклонение на търсенето като:

където n е количеството наблюдения на търсенето, което имаме.

Ако приемем, че нашите данни обикновено се разпределят около историческата средна стойност, можем да изчислим вероятността търсенето да бъде между два прага. Точната математика, включена тук, е извън обхвата на статията и за съжаление по-често не се спазва стриктно предположението за нормалност. Тези два прага ще бъдат съсредоточени върху средната стойност на търсенето (μ) с разпръскване x пъти на стандартното отклонение (σ) в двете посоки. Колкото по-хаотично е търсенето (т.е. голямо е), толкова по-широки са праговете.

Например имаме 98% вероятност да бъдем в диапазона: средно търсене +/- 2.33 пъти повече от стандартното отклонение (както е на фигурата по-горе). Така че, ако искахме да премахнем първите 1% от високите и ниските стойности, бихме ограничили търсенето до μ +/- 2.33 σ.

Имайте предвид, че това означава, че имаме 99% вероятност да бъде по-ниска от μ + 2.33 σ. И 99% вероятност да бъде по-висока от μ - 2.33 σ.

Ако приложихме това към нашите примерни набори от данни (вижте първите две таблици), ще получим тези ограничения:

Нека да видим как се държат тези нови нормални граници в сравнение с лимитите за победители.

Това вече е много по-добро от резултатите, които получихме при winorization:
- В набора от данни без остатъци (вижте фигура 10.4), ние не променяме никакво наблюдение на търсенето (перфектно! - точно както искаме).
- В набора от данни с външно устройство не променяме точките с ниско търсене, а само действителните външни (вижте фигура 10.5).

Все пак, макар да намалим външния размер до по-лесно управляема сума (47,9), отколкото при winorization (70,9), това може да не е достатъчно.

И така, щастливи ли сме сега?
Все още не съвсем.

Както може би си спомняте, ние предположихме, че грешката е около историческата средна стойност. Това е добре за продукт с плоско търсене, но действителното ограничение ще възникне, когато имате продукт с тенденция или сезонност. Например, на сезонната таблица по-долу, най-високите (или най-ниските) точки вече не са тези, които искате да премахнете.

Можете да видите как викторизацията и нормализирането работят на това сезонно търсене на фигурата по-долу.

Това просто няма смисъл: и двете техники отбелязват сезона като върхови и те прескачат истинския външен човек, който е Y2 M11.

Ще решим това със следващата си техника.

Направи го сам
Excel Можете да изчислите стандартното отклонение на диапазон от клетки, благодарение на формулата = STDEV.P (диапазон). Както винаги, можете да изчислите средната стойност благодарение на = AVERAGE (обхват). След като имате тези две, можете да изчислите по-високите и долните граници благодарение на = NORM.INV (процентил, среден, stdev). Обикновено ще искате високият процентил да бъде около 0,99, а ниският около 0,01.
Python Можете да изчислите стандартното отклонение чрез np.std (масив) за подобен на масив (например списък, DataFrame и т.н.) или за DataFrame директно чрез метода .std (). Така че, ако имате df DataFrame, можете просто да напишете:

m = df.mean ()
s = df.std ()

След това отново ще използваме библиотеката SciPy, за да изчислим нормалните вероятности. След това ще използваме метода .clip в нашата DataFrame, за да го ограничим до нашите граници.

от нормата за внос на scipy.stats
# Отпечатайте вероятностите на всяко наблюдение на търсенето
печат (norm.cdf (df.values, m, s) .round (2))
limit_high = norm.ppf (0.99, m, s)
limit_low = norm.ppf (0.01, m, s)
df = df.clip (долна = граница_ долна, горна = граница_висока)

Идея № 3 Грешка стандартно отклонение

Втората идея, на която трябваше да подадем флаг, беше да сравним всяко наблюдение със средната стойност на търсенето. Видяхме, че няма смисъл, ако имаме тенденция или сезонност, тъй като разликата между наблюдение и историческа средна стойност не беше релевантна.

Е, да се върнем към дефиницията на външен човек: външността е стойност, която не сте очаквали. Точно както показва испанската инквизиция в Monty Python. Това означава, че външният човек е стойност далеч от прогнозата ви (т.е. прогнозата ви). Следователно, за да забележим остатъците, ще анализираме прогнозната грешка и ще видим кои периоди са изключително грешни. За целта ще използваме стандартния подход за отклонение, който използвахме преди.

Нека се върнем на сезонен пример, който направихме по-горе. Ще сравним историческото търсене с проста (но сезонна) прогноза, която имаме за него.

Ако изчислим грешката, която имаме за такава прогноза (която е просто средна стойност от историческото търсене), бихме получили средна грешка от 0,4 и стандартно отклонение 3,2 (това разбира се е силно повлияно от грешката, която имаме за Y2 M11). Ако използвахме 99% доверителен интервал около това средно ниво, бихме свили прогнозните грешки в -0.4 +/- 2.33 x 3.2 = -8,7. На фигурата по-долу можете да видите как тези граници около прогнозата перфектно отговарят на сезонното търсене.

Вече можем да коригираме нашия външен от Y2 M11. Търсенето беше 19, но прогнозата беше 5 за този период. Максималната приемлива стойност е тогава 5 + 7 = 12. Това означава, че можем да заменим външната част на Y2 M11 (19) с тази нова стойност (12).

заключение
С този по-интелигентен метод за откриване - анализирайки отклонението от прогнозата за грешка, а не просто изменението на търсенето около средната стойност - ще можем да маркираме много по-прецизно и да ги намалим до правдоподобна сума. Както можете да видите на фигурата по-горе, нормализирането и winorization не биха могли да постигнат значими резултати за това сезонно търсене.

Фината настройка на този метод колко стандартни отклонения трябва да вземете като ограничение? е, разбира се, оставено на вас да експериментирате ...

Направи го сам
Python Ако имате панда DataFrame с една колона като прогноза и друга като търсене (типичният изход от нашите експоненциални модели изглаждане), можем да използваме този код:

df [“Грешка”] = df [“Прогноза”] - df [“Търсене”]
m = df ["Грешка"]. означава ()
s = df [„Грешка“]. std ()
от нормата за внос на scipy.stats
limit_high = norm.ppf (0.99, m, s) + df [“Прогноза”]
limit_low = norm.ppf (0.01, m, s) + df [“Прогноза”]
df [“Актуализирано”] = df [“Търсене”]. клип (долен = ограничение_ нисък, горен = граница_висока)
печат (DF)

Изминете допълнителната миля!

Ако се замислите върху идеята ни да анализираме прогнозната грешка и да направим праг на приемливи грешки, всъщност все още имаме малък проблем. Прагът, който изчисляваме, се основава на набора от данни, включително външните. Този външен елемент води до промяна на грешката нагоре, така че приемливият праг е пристрастен и надценен. За да се коригира това, човек може в действителност да свие външния размер не до прага, изчислен въз основа на оригиналния набор от данни за търсенето, а до ограничение, изчислено за набор от данни, без този конкретен външен размер. Ето и рецептата:

  1. Населете първа прогноза спрямо историческото търсене.
  2. Изчислете грешката, средната грешка и стандартното отклонение за грешка
  3. Изчислете долните и горните приемливи прагове (въз основа на средната грешка и стандартното отклонение).
  4. Идентифицирайте остатъците, както е обяснено по-горе.
  5. Преизчислете средното ниво на грешката и стандартното отклонение, но без да се изключва разходът.
  6. Актуализирайте долните и горните приемливи прагове въз основа на тези нови стойности.
  7. Актуализирайте стойностите на външните стойности въз основа на новия праг.

Ако вземем отново нашия сезонен пример, първоначално имахме прогнозна грешка средно 0,4 и стандартно отклонение 3,22. Ако премахнем точката Y2 M11, получаваме средна грешка от -0.1 и стандартно отклонение от 2.3. Това означава, че сега праговете са -5.3,5.2 около прогнозата. След това нашият външен от Y2 M11 ще бъде актуализиран до 10 (вместо 12 с предишната ни техника).

Направи го сам
Ще върнем кода от предишната си идея и ще добавим нова стъпка за актуализиране на средните стойности на грешките и стойностите на стандартното отклонение.

df [“Грешка”] = df [“Прогноза”] - df [“Търсене”]
m = df ["Грешка"]. означава ()
s = df [„Грешка“]. std ()
от нормата за внос на scipy.stats
prob = norm.cdf (df [„Грешка“], m, s)
outliers = (prob> 0.99) | (проблем <0,01)
m2 = df [“Грешка”] [~ outliers] .mean ()
s2 = df [“Грешка”] [~ outliers] .std ()
limit_high = norm.ppf (0.99, m2, s2) + df [“Прогноза”]
limit_low = norm.ppf (0.01, m2, s2) + df [“Прогноза”]
df [“Актуализирано”] = df [“Търсене”]. клип (долен = ограничение_ нисък, горен = граница_висока)
печат (DF)

За автора

Nicolas Vandeput е учен по данни от веригата на доставки, специализиран в прогнозиране на търсенето и оптимизация на запасите.
През 2016 г. той основава SupChains (www.supchains.com), своята консултантска компания; две години по-късно той е съосновател на SKU Science (www.skuscience.com), интелигентна онлайн платформа за управление на веригата за доставки.
Ако се интересувате от прогноза и машинно обучение, можете да си купите книгата му Data Science for Supply Chain Forecast