четверг, 19 мая 2022 г.

Корреляционная матрица с Python

Корреляционная матрица (correlation matrix) : таблица, в которой строки и столбцы — это переменные, а значения ячеек —корреляции между этими переменными. Рассмотрим, как можно получить эту матрицу с помощью Python. 

Для примера используем следующий набор данных :

df = pd.read_excel("shop06.xlsx", index_col="date")
df.head()


wdPlanRevVisitConvLCHPrice
date
2021-01-062120018791838.744.1328
2021-02-063120014311879.093.2426
2021-03-06412005472195.482.3326
2021-04-065120010511938.812.8227
2021-05-066220022473098.093.2028


Данные представляют собой дневные показатели работы розничного магазина одежды.


Расшифруем показатели :
  • date -  дата
  • wd - день недели 1-понедельник, 7-воскресенье.
  • Plan- план в у.е.
  • Rev - выручка в у.е.
  • Visit - количество посетителей
  • Conv - конверсия = количество чеков / количество посетителей
  • LCH - длина чека = количество покупок / количество чеков
  • Price - средняя цена покупки

Добавим еще одну колонку - выполнение плана


df['PlanPct']=df.apply(lambda r: round(r.Rev/r.Plan,2),axis=1)
df.head()

wdPlanRevVisitConvLCHPricePlanPct
date
2021-01-062120018791838.744.13281.57
2021-02-063120014311879.093.24261.19
2021-03-06412005472195.482.33260.46
2021-04-065120010511938.812.82270.88
2021-05-066220022473098.093.20281.02


Корреляционную матрицу можно получить с помощью метода DataFrame.corr

corr_mat = round(df.corr(),2)
corr_mat

wdPlanRevVisitConvLCHPricePlanPct
wd1.000.840.720.84-0.03-0.100.310.13
Plan0.841.000.730.89-0.09-0.070.16-0.02
Rev0.720.731.000.730.230.230.430.64
Visit0.840.890.731.00-0.330.000.160.09
Conv-0.03-0.090.23-0.331.00-0.050.190.48
LCH-0.10-0.070.230.00-0.051.00-0.260.44
Price0.310.160.430.160.19-0.261.000.41
PlanPct0.13-0.020.640.090.480.440.411.00

Чтобы можно было отобрать максимальные корреляции, уберем единицы с диагонали


for x in range(corr_mat.shape[0]):
    corr_mat.iloc[x,x] = 0.0
    
corr_mat


     wdPlanRevVisitConvLCHPricePlanPct
wd0.000.840.720.84-0.03-0.100.310.13
Plan0.840.000.730.89-0.09-0.070.16-0.02
Rev0.720.730.000.730.230.230.430.64
Visit0.840.890.730.00-0.330.000.160.09
Conv-0.03-0.090.23-0.330.00-0.050.190.48
LCH-0.10-0.070.230.00-0.050.00-0.260.44
Price0.310.160.430.160.19-0.260.000.41
PlanPct0.13-0.020.640.090.480.440.410.00

А теперь выведем пары с максимальными корреляциями c их значениями

pc=corr_mat.abs().idxmax()
cormax=corr_mat.loc[pc.index,pc.values]
df_cor=pd.DataFrame({'colname ': pc,'cor': np.diagonal(cormax)})
df_cor.sort_values(by=['cor'])

colnamecor
PriceRev0.43
LCHPlanPct0.44
ConvPlanPct0.48
PlanPctRev0.64
RevPlan0.73
wdPlan0.84
PlanVisit0.89
VisitPlan0.89

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

С помощью метода corr() получаем корреляционную матрицу

df_cor=df.corr()

wdPlanRevVisitConvLCHPricePlanPct
wd1.000.840.720.84-0.03-0.100.310.13
Plan0.841.000.730.89-0.09-0.070.16-0.02
Rev0.720.731.000.730.230.230.430.64
Visit0.840.890.731.00-0.330.000.160.09
Conv-0.03-0.090.23-0.331.00-0.050.190.48
LCH-0.10-0.070.230.00-0.051.00-0.260.44
Price0.310.160.430.160.19-0.261.000.41
PlanPct0.13-0.020.640.090.480.440.411.00

С  помощью функции nympy tril обнуляем значения выше диагонали

df_cor=pd.DataFrame(np.tril(df_cor, k=-1),columns=df_cor.columns,
index=df_cor.columns)

wdPlanRevVisitConvLCHPricePlanPct
wd0.000.000.000.000.000.000.000.0
Plan0.840.000.000.000.000.000.000.0
Rev0.720.730.000.000.000.000.000.0
Visit0.840.890.730.000.000.000.000.0
Conv-0.03-0.090.23-0.330.000.000.000.0
LCH-0.10-0.070.230.00-0.050.000.000.0
Price0.310.160.430.160.19-0.260.000.0
PlanPct0.13-0.020.640.090.480.440.410.0

Метод stack() поворачивает уровень меток столбцов, превращая его в индекс строк
помещаем столбцы в еще один уровень индекса строк
Результатом становится объект Series

df_cor=df_cor.stack()

wd       wd         0.00
         Plan       0.00
         Rev        0.00
         Visit      0.00
         Conv       0.00
                    ... 
PlanPct  Visit      0.09
         Conv       0.48
         LCH        0.44
         Price      0.41
         PlanPct    0.00
Length: 64, dtype: float64

Убираем нулевые значения

df_cor=df_cor[df_cor.abs()>0]

Plan     wd       0.84
Rev      wd       0.72
         Plan     0.73
Visit    wd       0.84
         Plan     0.89
         Rev      0.73
Conv     wd      -0.03
         Plan    -0.09
         Rev      0.23
         Visit   -0.33
LCH      wd      -0.10
         Plan    -0.07
         Rev      0.23
         Conv    -0.05
Price    wd       0.31
         Plan     0.16
         Rev      0.43
         Visit    0.16
         Conv     0.19
         LCH     -0.26
PlanPct  wd       0.13
         Plan    -0.02
         Rev      0.64
         Visit    0.09
         Conv     0.48
         LCH      0.44
         Price    0.41
dtype: float64

Обозначаем столбец с корреляцией и сбрасываем индекс

df_cor=df_cor.rename("pearson")
df_cor=df_cor.reset_index()

level_0level_1pearson
0Planwd0.84
1Revwd0.72
2RevPlan0.73
3Visitwd0.84
4VisitPlan0.89
5VisitRev0.73
6Convwd-0.03
7ConvPlan-0.09
8ConvRev0.23
9ConvVisit-0.33
10LCHwd-0.10
11LCHPlan-0.07
12LCHRev0.23
13LCHConv-0.05
14Pricewd0.31
15PricePlan0.16
16PriceRev0.43
17PriceVisit0.16
18PriceConv0.19
19PriceLCH-0.26
20PlanPctwd0.13
21PlanPctPlan-0.02
22PlanPctRev0.64
23PlanPctVisit0.09
24PlanPctConv0.48
25PlanPctLCH0.44
26PlanPctPrice0.41

Добавляем колонку с p-значением

df_cor['p']=df_cor.apply(lambda r: 
        round(stats.pearsonr(df[r.level_0],df[r.level_1])[1],4),axis=1)

level_0level_1pearsonp
0Planwd0.840.0000
1Revwd0.720.0000
2RevPlan0.730.0000
3Visitwd0.840.0000
4VisitPlan0.890.0000
5VisitRev0.730.0000
6Convwd-0.030.8848
7ConvPlan-0.090.6231
8ConvRev0.230.2256
9ConvVisit-0.330.0756
10LCHwd-0.100.5979
11LCHPlan-0.070.7181
12LCHRev0.230.2167
13LCHConv-0.050.7775
14Pricewd0.310.0973
15PricePlan0.160.3897
16PriceRev0.430.0175
17PriceVisit0.160.3941
18PriceConv0.190.3096
19PriceLCH-0.260.1587
20PlanPctwd0.130.4793
21PlanPctPlan-0.020.9210
22PlanPctRev0.640.0001
23PlanPctVisit0.090.6522
24PlanPctConv0.480.0068
25PlanPctLCH0.440.0143
26PlanPctPrice0.410.0256

Убираем значения с p-уровнем значимости больше 0.05

df_cor=df_cor.query('p<=0.05')

level_0level_1pearsonp
0Planwd0.8430350.0000
1Revwd0.7154710.0000
2RevPlan0.7260030.0000
3Visitwd0.8354150.0000
4VisitPlan0.8905890.0000
5VisitRev0.7329990.0000
11PriceRev0.4306560.0175
15PlanPctRev0.6423990.0001
17PlanPctConv0.4835070.0068
18PlanPctLCH0.4425280.0143
19PlanPctPrice0.4069540.0256

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

Эту последовательность шагов поместим в функцию 

def cor_mat_p_val(df):
    df_cor=df.corr()
    df_cor=pd.DataFrame(np.tril(df_cor, k=-1),columns=df_cor.columns,
        index=df_cor.columns)
    df_cor=df_cor.stack()
    df_cor=df_cor[df_cor.abs()>0]
    df_cor=df_cor.rename("pearson")
    df_cor=df_cor.reset_index()
    df_cor['p']=df_cor.apply(lambda r:
        round(stats.pearsonr(df[r.level_0],df[r.level_1])[1],4),axis=1)
    return df_cor.query('p<=0.05')


Выведем корреляционную матрицу в виде "тепловой карты"

sns.heatmap(corr_mat, annot=True, fmt='.2f', linewidths=2)



Уберем вывод верхней половины матрицы

sns.heatmap(df.corr(),vmin=-0.3,vmax=0.6,center=0,annot=True,fmt='.2f',
mask=~np.tri(df.corr().shape[1], k=-1, dtype=bool),
linewidth=2,cbar=False)










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

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