Используя панд, вычислим матрицу коэффициентов Крамера
У меня есть датафрейм в pandas
который содержит метрики, рассчитанные по статьям Википедии. Две категориальные переменные nation
о какой стране идет речь в статье, и lang
с какого языка википедия это была взята. Для одной метрики я хотел бы увидеть, насколько тесно коррелируют национальная и языковая переменные. Я полагаю, что это делается с помощью статистики Крамера.
index qid subj nation lang metric value
5 Q3488399 economy cdi fr informativeness 0.787117
6 Q3488399 economy cdi fr referencerate 0.000945
7 Q3488399 economy cdi fr completeness 43.200000
8 Q3488399 economy cdi fr numheadings 11.000000
9 Q3488399 economy cdi fr articlelength 3176.000000
10 Q7195441 economy cdi en informativeness 0.626570
11 Q7195441 economy cdi en referencerate 0.008610
12 Q7195441 economy cdi en completeness 6.400000
13 Q7195441 economy cdi en numheadings 7.000000
14 Q7195441 economy cdi en articlelength 2323.000000
Я хотел бы создать матрицу, которая отображает коэффициент Крамера между всеми комбинациями нации (Франция, США, Кот-д'Ивори и Уганда) ['fra','usa','uga']
и три языка ['fr','en','sw']
, Таким образом, будет получена матрица 4 на 3, например:
en fr sw
usa Cramer11 Cramer12 ...
fra Cramer21 Cramer22 ...
cdi ...
uga ...
В конце концов, я сделаю это по всем отслеживаемым метрикам.
for subject in list_of_subjects:
for metric in list_of_metrics:
cramer_matrix(metric, df)
Затем я могу проверить свою гипотезу о том, что показатели будут выше для статей, язык которых является языком Википедии. Спасибо
6 ответов
Cramers V кажется довольно оптимистичным в нескольких тестах, которые я сделал. Википедия рекомендует исправленную версию.
def cramers_corrected_stat(confusion_matrix):
""" calculate Cramers V statistic for categorial-categorial association.
uses correction from Bergsma and Wicher,
Journal of the Korean Statistical Society 42 (2013): 323-328
"""
chi2 = ss.chi2_contingency(confusion_matrix)[0]
n = confusion_matrix.sum()
phi2 = chi2/n
r,k = confusion_matrix.shape
phi2corr = max(0, phi2 - ((k-1)*(r-1))/(n-1))
rcorr = r - ((r-1)**2)/(n-1)
kcorr = k - ((k-1)**2)/(n-1)
return np.sqrt(phi2corr / min( (kcorr-1), (rcorr-1)))
Также обратите внимание, что матрица путаницы может быть рассчитана с помощью встроенного метода панд для категориальных столбцов с помощью:
import pandas as pd
confusion_matrix = pd.crosstab(df[column1], df[column2])
Немного модифицированная функция от ответа Зигги Юнисьена. Добавлено 2 модификации: 1) проверка, что одна переменная постоянна 2) исправление в ss.chi2_contingency(conf_matrix, коррекция = правильно) - FALSE, если матрица путаницы 2x2
import scipy.stats as ss
import pandas as pd
import numpy as np
def cramers_corrected_stat(x,y):
""" calculate Cramers V statistic for categorial-categorial association.
uses correction from Bergsma and Wicher,
Journal of the Korean Statistical Society 42 (2013): 323-328
"""
result=-1
if len(x.value_counts())==1 :
print("First variable is constant")
elif len(y.value_counts())==1:
print("Second variable is constant")
else:
conf_matrix=pd.crosstab(x, y)
if conf_matrix.shape[0]==2:
correct=False
else:
correct=True
chi2 = ss.chi2_contingency(conf_matrix, correction=correct)[0]
n = sum(conf_matrix.sum())
phi2 = chi2/n
r,k = conf_matrix.shape
phi2corr = max(0, phi2 - ((k-1)*(r-1))/(n-1))
rcorr = r - ((r-1)**2)/(n-1)
kcorr = k - ((k-1)**2)/(n-1)
result=np.sqrt(phi2corr / min( (kcorr-1), (rcorr-1)))
return round(result,6)
V статистика Крамера позволяет понять корреляцию между двумя категориальными признаками в одном наборе данных. Итак, это ваш случай.
Чтобы вычислить статистику Cramers V, вам нужно вычислить матрицу путаницы. Итак, шаги решения:
1. Фильтр данных для одной метрики
2. Рассчитать матрицу путаницы
3. Рассчитать статистику Cramers V
Конечно, вы можете сделать эти шаги в цикле гнезд, представленных в вашем посте. Но в вашем начальном абзаце вы упоминаете только метрики в качестве внешнего параметра, поэтому я не уверен, что вам нужны оба цикла. Теперь я предоставлю код для шагов 2-3, потому что фильтрация проста, и, как я уже говорил, я не уверен, что вам определенно нужно.
Шаг 2. В коде ниже data
это pandas.dataFrame
отфильтровано по желанию на шаге 1.
import numpy as np
confusions = []
for nation in list_of_nations:
for language in list_of_languges:
cond = data['nation'] == nation and data['lang'] == language
confusions.append(cond.sum())
confusion_matrix = np.array(confusions).reshape(len(list_of_nations), len(list_of_languges))
Шаг 3. В приведенном ниже коде confusion_matrix
это numpy.ndarray
полученный на шаге 2.
import numpy as np
import scipy.stats as ss
def cramers_stat(confusion_matrix):
chi2 = ss.chi2_contingency(confusion_matrix)[0]
n = confusion_matrix.sum()
return np.sqrt(chi2 / (n*(min(confusion_matrix.shape)-1)))
result = cramers_stat(confusion_matrix)
Этот код был протестирован на моем наборе данных, но я надеюсь, что это нормально использовать без изменений в вашем случае.
Используя пакет python ассоциативных метрик для вычисления матрицы коэффициентов Крамера из объекта pandas.DataFrame, это довольно просто, позвольте мне показать вам:
Сначала установите association_metrics, используя:
pip install association-metrics
Затем вы можете использовать следующий псевдокод
# Import association_metrics
import association_metrics as am
# Convert you str columns to Category columns
df = df.apply(
lambda x: x.astype("category") if x.dtype == "O" else x)
# Initialize a CamresV object using you pandas.DataFrame
cramersv = am.CramersV(df)
# will return a pairwise matrix filled with Cramer's V, where columns and index are
# the categorical variables of the passed pandas.DataFrame
cramersv.fit()
Давайте не будем изобретать велосипед! В Scipy уже есть функция.
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.contingency.association.html
import numpy as np
from scipy.stats.contingency import association
obs4x2 = np.array([[100, 150], [203, 322], [420, 700], [320, 210]])
association(obs4x2, method="cramer")
0.18617813077483678
Есть гораздо более простой ответ. Итак, вопрос о V Крамера, и я постараюсь ответить на него.
Для вашего фрейма данных pandas:
data
, если вас интересуют только столбцы языка и нации, вы можете легко получить тепловую карту V Крамера, используя несколько простых строк ниже:
# first chose your category columns of interest
df = data[['nation', 'lang']]
# now change this to dummy variables, one-hot encoded:
DataMatrix = pd.get_dummies(df)
# plot as simply as:
plt.figure(figsize=(15,12)) # for large datasets
plt.title('Cramer\'s V comparing nation and language')
sns.heatmap(DataMatrix.corr('pearson'), cmap='coolwarm', center=0)
Я могу порекомендовать следующие альтернативы: критерий хи-квадрат пропорций 2 на 2 или асимметричная нормализованная взаимная информация (NMI или U Тейла).