www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Прогнозирующее моделирование с использованием данных о продажах спиртных напитков штата Айова

Дата публикации Apr 2, 2017

Привет друзья,

Недавно мне было поручено построить прогностическую модель с использованием большого набора данных о продажах спиртных напитков в Айове.

Вкратце, Айова - этосостояние контроля алкогольных напитковЭто означает, что государство сохраняет монополию на оптовую торговлю алкоголем по всему штату. По сути, частные ритейлеры должны покупать алкоголь у государства, прежде чем продавать его отдельным потребителям. Вы можете узнать больше об этом на странице вики дляАйова Отдел алкогольных напитков,

Величественный флаг Айовы

Для целей этого проекта я использовал подмножество данных о продажах ликера в Айове, которые можно найти на сайте Айовы.государственный портал открытых данных, Хотя портал содержит продажи спиртных напитков начиная с 2012 года, набор данных, который я использовал, включал отдельные продажи спиртных напитков за весь 2015 год и первый квартал (январь-март) 2016 года. Цель проекта заключалась в использовании данных о продажах за 2015 год для построения модель, которая может прогнозировать общий объем продаж в 2016 году на основе данных первого квартала 2016 года. Я проанализировал данные, используя библиотеку Pandas в Python.

Вкратце, набор данных содержал более 2,7 миллионов строк данных. Каждое наблюдение содержало информацию, в том числе:

  • дата покупки (мм / дд / гггг)
  • подробности о продавце, совершающем покупку (например, присвоенный номер магазина, сведения о округе и городе);
  • тип и марка приобретенного ликера (например, виски Jack Daniel's Tennessee);
  • размер бутылки (например, 750 мл) и количество приобретенных бутылок;
  • общая сумма продаж, включая государственные и розничные расходы на бутылку; а также
  • общий объем проданного ликера, в литрах и галлонах
Снимок выбранных столбцов в исходном наборе данных. Есть много дополнительных столбцов данных, не изображенных здесь.

Первичная очистка данных и исследовательский анализ

Первичная очистка данных и предварительный анализ состояли из следующих этапов.

Общая очистка данных:

  • Улучшение ясности имен столбцов, преобразование типов данных, где это необходимо.
  • Удаление повторяющихся строк
  • Замена значений строки «nan» истинными нулями
  • Для парных столбцов (category_ID и category_name), если у одного значения столбца не было соседней пары, я заполнил его, если мог, или изменил его на ноль

Обеспечение правильных расчетов:

  • Обеспечение размера бутылки (например, 750 мл) х проданных бутылок = проданных литров в объеме
  • Обеспечение розничной стоимости бутылок х проданных бутылок = продажа долларов
  • С математикой проблем не нашел, но все равно хорошо было проверить

Подготовка к моделированию

Я знал, что анализ будет значительно проще, если я смогу сгруппировать большие порции данных в более простые категории. Поэтому я создал несколько новых рубрик, которые значительно облегчили бы мою жизнь в долгосрочной перспективе.

Например, я создал столбец, в котором записи даты будут объединяться в их квартал и год появления:

(Я назвал свой датафрейм i_a для Iowa Alcohol)

#Generating column for quarter-year
i_a[“Quarter_Year”] = “-”i_a.ix[(i_a.Date > ‘2014–12–31’) & (i_a.Date < ‘2015–04–01’), “Quarter_Year”] = “Q1_2015”
i_a.ix[(i_a.Date > ‘2015–03–31’) & (i_a.Date < ‘2015–07–01’), “Quarter_Year”] = “Q2_2015”
i_a.ix[(i_a.Date > ‘2015–06–30’) & (i_a.Date < ‘2015–10–01’), “Quarter_Year”] = “Q3_2015”
i_a.ix[(i_a.Date > ‘2015–09–30’) & (i_a.Date < ‘2016–01–01’), “Quarter_Year”] = “Q4_2015”
i_a.ix[(i_a.Date > ‘2015–12–31’) & (i_a.Date < ‘2016–04–01’), “Quarter_Year”] = “Q1_2016”

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

При рассмотрении связи между определенными категориями и общим объемом продаж в 2015 году я мог видеть, что определенные позиции составляли большую долю общего объема продаж по сравнению с другими, менее популярными вариантами.

Например, было более 30 бутылок разных размеров (объем на мл). Тем не менее, подавляющее большинство проданных бутылок были либо 750 мл, 1000 мл, либо 1750 мл. Чтобы упростить дальнейший анализ и построение модели, я создал столбец, в котором указывалось бы, был ли размер бутылки приобретаемого ликера 750 мл, 1000 мл, 1750 мл или что-то еще.

i_a[“Consolidated_Bottle_ml”] = “”#After examining data, it made sense to consolidate bottle sizes into 750ml, 1000ml, 1750ml, and all others
i_a.ix[i_a.Bottle_Volume_ml == 750, “Consolidated_Bottle_ml”] = “750ml”
i_a.ix[i_a.Bottle_Volume_ml == 1000, “Consolidated_Bottle_ml”] = “1000ml”
i_a.ix[i_a.Bottle_Volume_ml == 1750, “Consolidated_Bottle_ml”] = “1750ml”
i_a.ix[i_a.Consolidated_Bottle_ml == “”, “Consolidated_Bottle_ml”] = “Other_ml”

При составлении графика относительных общих продаж, связанных с каждой из этих категорий, ясно видеть, насколько доминируют эти размеры бутылок среди всех остальных:

Точно так же я заметил широкие тенденции среди категорий спиртных напитков, указывающие, что многие покупки были или разновидностями виски или водки. Чтобы разобраться в этом, я провел текстовый поиск по категориям, чтобы определить, был ли купленный ликер чем-то вроде виски, водки или чего-то еще:

#Creating “Is_” columns as booleans for each type in the list above
#After further analysis, only whiskey and vodka seem to warrant their own categories, with everything else in “Other”i_a[“Is_WHISK”] = i_a[‘Alcohol_Category_Name’].str.contains(“WHISK”)
i_a[“Is_VODKA”] = i_a[‘Alcohol_Category_Name’].str.contains(“VODKA”)#Creating new column to identify consolidated alcohol typei_a[“Consolidated_Category”] = “”#Assigning values as either Whiskey, Vodka, or Non-Whiskey-Vodka, depending on true values in "Is_" columns. If the category is neither whiskey, nor vodka, or is null, assigning it to this other category.i_a.ix[i_a.Is_WHISK == True, “Consolidated_Category”] = “WHISKEY”
i_a.ix[i_a.Is_VODKA == True, “Consolidated_Category”] = “VODKA”

i_a.ix[((i_a.Is_WHISK == False) & (i_a.Is_VODKA == False)) | (i_a.Alcohol_Category_Name.isnull()), “Consolidated_Category”] = “NON-WHISKEY-VODKA”

Опять же, вы заметите, насколько важны две категории виски и водки по сравнению со всеми другими крепкими спиртами:

Теперь, когда я мог видеть, где сгруппирована большая часть продаж, я почувствовал, что у меня достаточно информации, чтобы приступить к созданию модели, которая могла бы

Преобразование набора данных

Чтобы построить свою модель, я знал, что мне нужно будет прогнозировать продажи за весь год, используя только данные о продажах на четверть.

Чтобы сделать это, я хотел преобразовать свой набор данных, чтобы сгруппировать данные по номеру магазина, а затем построить модель, которая будет прогнозировать общие годовые продажи каждого магазина на основе данных за один квартал.

Я знал, что хотел бы свести к минимуму количество прогнозирующих переменных, и поэтому я хотел выбрать те переменные, которые, по-видимому, учитывают наибольшую разницу в общем объеме продаж

В частности, я хотел спрогнозировать общий годовой объем продаж, исходя из долларов продаж в первом квартале 2015 года, связанных с четырьмя переменными: бутылками по 750 мл, бутылками по 1000 мл, бутылками по 1750 мл и покупками без виски / без водки.

Чтобы выполнить это в pandas, я создал серию отдельных фреймов данных, используя операторы groupby, а затем соединил эти различные фреймы данных вместе, чтобы создать мой окончательный набор моделирования.

Когда я группировал по нескольким переменным, использование функции «.unstack ()» помогло сохранить каждую интересующую переменную в виде собственного столбца, а не вкладывать результаты в иерархическом порядке. Я также создал отдельный период данных из данных за 2015 год (i_a_2015), прежде чем начать этот процесс.

(Есть несколько дополнительных переменных, которые я не включил в свою окончательную модель, которые были первоначально добавлены для любопытства.)

#Use the data from 2015 to make a linear model using as many variables as you find useful to predict the yearly sales of all stores.#We don’t want to use ALL of the data from the variables of interest — we only want about 1/4 of it (as we’ll only have a quarter’s worth of data from 2016 when generating predictions for all of 2016.)#create dataframe grouped by store for 2015 that will be used for building regression model#We DO still keep ALL of the sales data from 2015 as the target variable, as we want to predict the final 2016 totals
#for all storesstore_sales_2015 = pd.DataFrame(i_a_2015.groupby(“Store_Number”).Sale_Dollars.sum())store_sales_2015.rename(columns={‘Sale_Dollars’:’Sale_Dollars_Total_2015'}, inplace=True) #Drawing out bottles_sold; keeping Q1 data and dropping Q2:Q4add_Q1_bottle_sold = pd.DataFrame(i_a_2015.groupby([“Store_Number”, “Quarter_Year”]).Bottles_Sold.sum().unstack())#alter Q1_2015 column name for easier interpretabilityadd_Q1_bottle_sold.rename(columns={‘Q1_2015’:’Bottles_Sold_Q1_2015'}, inplace=True)add_Q1_bottle_sold.drop([‘Q2_2015’, “Q3_2015”, “Q4_2015”], axis=1, inplace=True)#Finding the “mode” of the stores’ counties, which will be the only item associated with each storeadd_store_county = pd.DataFrame(i_a_2015.groupby(“Store_Number”).County.agg(lambda x:x.value_counts().index[0]))#Creating df of only Q1_2015 to make extracting of bottle sizes and other multi-variable columns easieri_a_Q1_2015 = i_a[i_a[“Quarter_Year”] == “Q1_2015”]#Drawing out bottles_sizes sales volume for 750ml, 1000ml, 1750ml, and other_ml for Q1_2015;add_Q1_bottle_sizes = pd.DataFrame(i_a_Q1_2015.groupby([“Store_Number”, “Consolidated_Bottle_ml”]).Sale_Dollars.sum().unstack())add_Q1_bottle_sizes.rename(columns={‘1000ml’:’1000ml_Sales_Q1_2015', ‘1750ml’:’1750ml_Sales_Q1_2015',’750ml’:’750ml_Sales_Q1_2015',’Other_ml’:’Other_ml_Sales_Q1_2015'}, inplace=True)#Drawing out consolidated_category sales volume for whiskey, vodka, and others for Q1_2015;add_Q1_consolidated_category = pd.DataFrame(i_a_Q1_2015.groupby([“Store_Number”, “Consolidated_Category”]).Sale_Dollars.sum().unstack())add_Q1_consolidated_category.rename(columns={‘NON-WHISKEY-VODKA’:’NON-WHISKEY-VODKA_Sales_Q1_2015', ‘VODKA’:’VODKA_Sales_Q1_2015',’WHISKEY’:’WHISKEY_Sales_Q1_2015'}, inplace=True) #saves changes#drawing out total Q1 sales from initial i_a_2015 df, dropping Q2, Q3, and Q4add_Q1_total_sales = pd.DataFrame(i_a_2015.groupby([“Store_Number”, “Quarter_Year”]).Sale_Dollars.sum().unstack())add_Q1_total_sales.rename(columns={‘Q1_2015’:’Q1_2015_total_sales’}, inplace=True) #saves changes add_Q1_total_sales.drop([‘Q2_2015’, “Q3_2015”, “Q4_2015”], axis=1, inplace=True)#creating final dataframe for modeling by concatenating all other dfsmodel_df = pd.concat([store_sales_2015, add_Q1_bottle_sold, add_store_county, add_Q1_bottle_sizes,
add_Q1_consolidated_category, add_Q1_total_sales], axis=1)#there are some NaN values where stores didn’t buy certain bottle sizes or liquor types — convert to zerosmodel_df.fillna(value=0, inplace=True)

После всего этого у нас был наш фрейм данных об общих продажах за 2015 год и мы выбрали переменные продаж за первый квартал, которые можно использовать для прогнозирования. Вот быстрый взгляд:

Снимок нашего фрейма данных, используемого для моделирования. Крайний левый столбец индекса - это присвоенный каждому магазину номер. Не все столбцы изображены здесь.

Изучив корреляционную тепловую карту интересующих меня переменных, я вижу, что между этими переменными существуют умеренно сильные ассоциации, что неудивительно (особенно если учесть, что ликеры, не являющиеся виски / не водкой, по своей сути будут перекрываться с размерами бутылок).

Построение прогнозирующей модели

Чтобы обучить и протестировать свою модель, я в 70:30 провела разделение тестового набора модели 2015 года. Это означает, что я мог бы подогнать свою модель, используя 70% данных в моем фрейме данных моделирования, и затем проверить точность моей модели по сравнению с оставшимися 30% данных, которые не использовались при подгонке модели.

В приведенном ниже коде:

X = фрейм данных моделирования с сохранением данных только из тех переменных, которые перечислены в тепловой карте выше

y = датафрейм, содержащий только 2015 общий объем продаж по магазинам

from sklearn import linear_model
from sklearn.model_selection import train_test_split#Conducting 70:30 train-test split for purposes of building and testing model. Set random state = 1 for reproducibility.X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)#instantiate linear model for ease of coding belowlm = linear_model.LinearRegression() #generate fit of model base on training set
#fit method expects two arguments of x’s and y’smodel = lm.fit(X_train, y_train) #generate predicted values of y_test from X_test based off of training setpredictions = lm.predict(X_test)#plotting predicted ys against y values in test set plt.scatter(y_test, predictions)
plt.xlabel(“True 2015 Total Sales”)
plt.ylabel(“Predicted 2015 Total Sales”)

Получившаяся модель имела R² = 0,956, что означает, что на модель приходилось примерно 95,6% отклонения годовых продаж магазина за 2015 год, исходя из данных о продажах за первый квартал из выбранных мной переменных. Вот график прогнозируемых значений против фактических значений:

Я рассмотрел модель далее с помощью перекрестной проверки и регуляризации, но этот пост в блоге уже достаточно длинный, и они мало что добавили к модели, которой еще не было.

Генерация прогнозов на 2016 год

Последний шаг - воспроизвести аналогичный кадр данных моделирования (информация по магазинам) для первого квартала 2016 года, который у меня был доступен, а затем сгенерировать прогнозы для общих продаж каждого магазина в 2016 году. Я использовал очень похожий код для огромного блока кода выше, чтобы сгенерировать набор предикторов 2016 года.

После генерации прогнозов я добавил прогнозы (LM_Predictions) к строке каждого магазина, например так:

Окончательные результаты

Линейная модель, которую я построил, прогнозировала общий объем продаж ликероводочных изделий в 2016 году в размере 292 млн долларов, что на 3% больше по сравнению с 2015 годом (284 млн долларов).

Если прибыль государства в 2015 году составляла 33% от общего объема продаж (95 миллионов долларов США), оценочная прибыль штата в 2016 году оценивалась в 98 миллионов долларов США. Довольно круто, прочее!

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map