Используя панд, вычислим матрицу коэффициентов Крамера

У меня есть датафрейм в 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 Тейла).

Другие вопросы по тегам