понедельник, 24 апреля 2023 г.

Статистика с Python в розничной торговле : оценки центрального положения

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

Загружаем необходимые библиотеки

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import weightedstats as ws
%matplotlib inline

В качестве примера разберем анализ месячных продаж ста розничных магазинов одежды. 
df = pd.read_excel(.\data\shops100.xlsx")
df.head()


shoprevvisitchsaleconvpricelch
0sh12981706962864518060.06716512.8
1sh2258237267662849370.04227563.3
2sh31751464862244011440.05115312.6
3sh44817280913664019200.07025093.0
4sh5604554610186103925980.10223272.5

Обозначения :

  • shop - индекс магазина
  • rev - выручка
  • visit - количество посетителей
  • ch - количество чеков
  • sale - количество покупок
  • lch - длина чека = количество покупок / количество чеков
  • price - средняя цена покупки = выручка / количество покупок


Среднее (mean) 

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

Анализируем длину чека. Рассчитаем среднее значения длины чека по 100 магазинам двумя методами : с помощью формулы и с помощью метода pandas data frame mean()

#Просуммировали значения в колонке 'lch' и разделили на количество значений
print(f'Среднее lch по формуле = {df.lch.sum()/df.shape[0]:.2f}')

#Используем метод pandas data frame mean()
print(f'Среднее lch по методу = {df.lch.mean():.2f}')

Среднее lch по формуле = 2.95

Среднее lch по методу  = 2.95

Среднее усеченное (trimmed mean) 


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

#Для усеченного среднего значения требуется функция trim_mean из библиотеки #scipy.stats: (trim=0.1 отбрасывает по 10% с каждого конца)

print(f'Усеченное среднее lch  = {stats.trim_mean(df.lch, 0.1):.2f}')

Усеченное среднее lch  = 2.94

Однако при расчете усеченного среднего иногда хотелось бы иметь несимметричное
усечение. Например в случае длины чека хотелось бы например откинуть 25% худших
и только 5% лучших значений. Для этого напишем специальную функцию с
использованием метода quantile

def qtrim_mean(x,trim_left,trim_right):
    x_l=x.quantile(trim_left)
    x_r=x.quantile(trim_right)
    return x[x.between(x_l, x_r)].mean()

print(f'Asymmetric mean lch  = {qtrim_mean(df.lch,0.25,0.95):.2f}')

Asymmetric mean lch  = 3.08

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

Среднее взвешенное (weighted mean) 

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

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

планом и фактом, больший вес мы отдаем факту, но при этом плановую долютоже учитываем, только с меньшим весом.


♦ Осредняемые данные не одинаковы по своей "массе", поэтому например, рассчитав
среднюю длину чека по колонке lch и умножив но суммарное количество чеков мы не
получим фактическое количество покупок. Для этого надо рассчитать взвешенное 
среднее и в качестве весов использовать количество чеков.
Взвешенное среднее значение доступно с помощью пакета NumPy


Проверим на наших данных

#Средневзвешенная длина чека, вес - количество чеков

lch_wm=np.average(df['lch'], weights=df['ch'])
print(f'средневзвешенная lch  = {lch_wm :.2f}')

#Количество покупок = средневзвешенная длина чека * количество чеков
print(f'количество покупок формула  = {lch_wm*df.ch.sum():.0f}')

#Количество покупок = метод sum()
print(f'количество покупок метод  = {df.sale.sum():.0f}')

средневзвешенная lch  = 2.94
количество покупок формула  = 202946
количество покупок метод = 202946

Если же мы рассчитаем количество покупок по среднему арифметическому, то получим неверный результат

#Средняя длина чека
lch_m=df.lch.mean()
print(f'среднее арифметическое lch  = {lch_m :.2f}')

#Количество покупок = средняя длина чека * количество чеков
print(f'количество покупок формула = {lch_m*df.ch.sum():.0f}')

среднее арифметическое lch = 2.95
количество покупок формула = 203194


Рассмотрим еще два вида средних 

Среднее геометрическое (geometric mean)


Корень n-й степени из произведения n-показателей
Несколько примеров, где работает среднее геометрическое:

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

s=np.array([100,110, 90, 105])

Тогда темпы роста по годам будут следующие : первый год к базовому 110/100, второй
год к первому 90/110 и третий год к второму 105/90.  

t=s[1:]/s[:3] print("темпы роста {0:0.0%} {1:0.0%} {2:0.0%}".format(t[0],t[1],t[2]))

темпы роста 110% 82% 117%

Среднегодовой темп роста как среднее арифметическое

t_m=t.mean() print("средний темп роста {0:0.0%}".format(t_m))

средний темп роста 103%


Среднегодовой темп роста как среднее геометрическое
t_g=stats.gmean(t) print("средний геометрический темп роста {0:0.0%}".format(t_g))

средний геометрический темп роста 102%

Продажи последнего года, как мы помним, 105 единиц, однако если в качестве
среднегодового темпа роста принять среднее арифметическое, то получим
print("продажи среднее арифметическое {0:0.0f}".format(100*t_m*t_m*t_m))

продажи среднее арифметическое 109


Продажи последнего года, если в качестве среднегодового темпа роста принять
среднее геометрическое рассчитаются верно
print("продажи среднее геометрическое {0:0.0f}".format(100*t_g*t_g*t_g))

продажи среднее геометрическое 105

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

Оценки образцов по трем характеристикам :
mod1=np.array([5,5,2])
mod2=np.array([4,4,4])

Итоговые оценки как среднее арифметическое

rating_1_m=mod1.mean()
rating_2_m=mod2.mean()

Итоговые оценки как среднее геометрическое

rating_1_gm=stats.gmean(mod1)
rating_2_gm=stats.gmean(mod2)

print(f'Итоговые оценки как среднее арифметическое : 
образец 1 = {rating_1_m:.2f} образец 2 = {rating_2_m:.2f}')

print(f'Итоговые оценки как среднее геометрическое : 
образец 1 = {rating_1_gm:.2f} образец 2 = {rating_2_gm:.2f}')

Итоговая оценки как среднее арифметическое : образец 1 = 4.00 образец 2 = 4.00
Итоговая оценки как среднее геометрическое : образец 1 = 3.68 образец 2 = 4.00

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

Среднее гармоническая (harmonic mean)


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

Среднемесячная выручка с квадратного метра 
rs=np.array([20000,25000,30000,35000,40000])

Средняя арифметическая дневная выручка с квадратного метра
rs_m=rs.mean()

Средняя гармоническая дневная выручка с квадратного метра
rs_gm=statistics.harmonic_mean(rs)

Плановая общая дневная выручка 
2 000 000*5=10 000 000

Требуемая площадь для плановой выручке

Через среднее арифметическое
s_m=10000000/rs_m

Через среднее геометрическое
s_gm=10000000/rs_gm

print(f'Cреднее арифметическое : {rs_m:.2f}')
print(f'Cреднее гармоническое : {rs_gm:.2f}')
print(f'Площадь через среднее арифметическое : {s_m:.2f}') 
print(f'Площадь через среднее гармоническое : {s_gm:.2f}')
print(f'Площадь прямым счетом : 
{2000000/20000+2000000/25000+2000000/30000+2000000/35000+2000000/40000:.2f}')

Cреднее арифметическое : 30000.00
Cреднее гармоническое : 28263.80
Площадь через среднее арифметическое : 333.33
Площадь через среднее гармоническое : 353.81
Площадь прямым счетом : 353.81


Как видно, правильный результат получен через среднее гармоническое.

По величине средние располагаются следующим образом :
гармоническое <= геометрическое <= арифметическое
Равенство возможно, если все значения равны. Проверим на наших данных
по цене продажи

print(f'Cреднее арифметическое : {df.price.mean():.0f}')
print(f'Cреднее геометрическое : {stats.gmean(df.price):.0f}')
print(f'Cреднее гармоническое : {statistics.harmonic_mean(df.price):.0f}')

Cреднее арифметическое : 2259
Cреднее геометрическое : 2220
Cреднее гармоническое : 2181


Медиана (median) 


Медиана - такое значение, что половина сортированных данных находится выше и ниже данного значения. Если имеется четное число данных, то срединным значением
является то, которое не находится в наборе данных фактически, а является средним
арифметическим двух значений, которые делят сортированные данные на верхнюю и
нижнюю половины. Таким образом медианой может быть величина, которой нет в
наборе данных. Если это вас не устраивает и если вам необходимо вычислить
медианное количество сотрудников в магазинах и вы не хотите иметь дело с нецелым
числом, то в python в библиотеки statistics есть несколько функций для расчета
медианы :  median - обычная медиана,median_low - возвращает нижнюю медиану
числовых данных, median_high - возвращает верхнюю медиану числовых данных.


print(f'Медиана = {statistics.median([2, 3, 6, 9]):.1f} ') print(f'Медиана нижняя = {statistics.median_low([2, 3, 6, 9]):.1f} ') print(f'Медиана верхняя = {statistics.median_high([2, 3, 6, 9]):.1f} ')

Медиана = 4.5 
Медиана нижняя = 3.0 
Медиана верхняя = 6.0 


Медиана взвешенная (weighted median) 

По тем же самым причинам, по которым используется среднее взвешенное, можно

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

Для взвешенной
медианы мы можем использовать специализированный пакет weightedstats:

print(f'Медиана = {np.median(df.price):.0f}')
print(f'Медиана взвешенная = {ws.weighted_median(df.price, weights=df.sale):.0f}')

Медиана = 2280
Медиана взвешенная = 2249

В заключении выведем рассмотренные величины на гистограмме средней цены
продажи

price_m=df.price.mean()
price_gm=stats.gmean(df.price)
price_grm=statistics.harmonic_mean(df.price)
price_wm=np.average(df.price, weights=df.sale)
price_md=df.price.median()
price_wmd=ws.weighted_median(df.price,weights=df.sale)

print(f'Cреднее арифметическое : {price_m:.0f}')
print(f'Cреднее арифметическое взвешенное  = {price_wm :.0f}')
print(f'Cреднее геометрическое : {price_gm:.0f}')
print(f'Cреднее гармоническое : {price_grm:.0f}')
print(f'Медиана = {price_md:.0f}')
print(f'Медиана взвешенная = {price_wmd:.0f}')

Cреднее арифметическое : 2259
Cреднее арифметическое взвешенное  = 2256
Cреднее геометрическое : 2220
Cреднее гармоническое : 2181
Медиана = 2280
Медиана взвешенная = 2249








Комментариев нет:

Отправить комментарий