使用pandas,计算Cramér的系数矩阵

时间:2014-01-02 22:06:09

标签: python pandas statistics

我在pandas中有一个数据框,其中包含根据维基百科文章计算的指标。文章所涉及的两个分类变量nation,以及lang哪个语言来自维基百科。对于单个指标,我想看看国家和语言变量的相关程度,我相信这是使用Cramer的统计数据完成的。

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)

然后我可以检验我的假设,即语言为维基百科语言的文章的指标会更高。感谢

5 个答案:

答案 0 :(得分:17)

在我做过的一些测试中,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)))

另请注意,混淆矩阵可以通过内置的pandas方法计算分类列:

import pandas as pd
confusion_matrix = pd.crosstab(df[column1], df[column2])

答案 1 :(得分:6)

Cramer的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是在第2步获得的numpy.ndarray

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)

此代码已在我的数据集上进行了测试,但我希望可以在不更改的情况下使用它。

答案 2 :(得分:2)

使用association-metrics python包从pandas.DataFrame对象计算Cramér的系数矩阵非常简单,让我告诉你:

首先使用以下方法安装 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()

Package info

答案 3 :(得分:0)

Ziggy Eunicien回答中的功能有所改进。 添加了2个修改 1)检查一个变量是常量 2)校正ss.chi2_contingency(conf_matrix,Correction = correct)-如果混淆矩阵为2x2,则为FALSE

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)

答案 4 :(得分:0)

有一个简单得多的答案。 所以问题是关于克莱默的V,我将坚持回答这个问题。

对于您的熊猫数据框:data,如果您仅对语言和国家/地区列感兴趣,则可以使用下面的几行代码轻松获得Cramer 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或Theil's U)。