如何在具有负值的数据集中使用卡方检验

数据挖掘 机器学习 Python 优化
2022-03-13 20:35:57

我无法完全解释标题。为了在我的数据集中使用卡方检验,我找到了最小值并将每个单元格与该值相加。(例如,这里的数据范围是 [-8,11] 所以我在每个单元格中添加了 +8 并且范围变成了 [0,19])。

dataValues 变量是 DataFrame 类型,它包含我的所有数据并包含 ~2000 个特征,~1000 行,dataTargetEncoded 变量是包含结果为 0 和 1 的 Array 类型。

for i in range(len(dataValues.index)):
    for j in range(len(dataValues.columns)):
        dataValues.iat[i, j] += 8

#fit chi2 and get results
bestfeatures = SelectKBest(score_func=chi2, k="all")
fit = bestfeatures.fit(dataValues, dataTargetEncoded)
feat_importances = pd.Series(fit.scores_, index=dataValues.columns)

#print top 10 feature
print(feat_importances.nlargest(10).index.values)

# back to normal
for i in range(len(dataValues.index)):
    for j in range(len(dataValues.columns)):
        dataValues.iat[i, j] -= 8

但这会导致性能问题。我正在考虑的另一个解决方案是将其标准化。我写了一个看起来像这样的函数:

def normalization(df):
    from sklearn import preprocessing

    x = df.values  # returns a numpy array
    min_max_scaler = preprocessing.MinMaxScaler()
    x_scaled = min_max_scaler.fit_transform(x)
    df = pd.DataFrame(x_scaled, columns=df.columns)

return df

我的程序加速了很多,但这次我的准确性降低了。我用第一种方法完成的特征选择过程产生了 0.85 的准确度结果,这次我产生了 0.70 的准确度。

我想摆脱这种原始方法,但我也希望准确性保持不变。我该如何进行?先感谢您

2个回答

首先,对于卡方检验,您的数据是正数、负数、字符串还是任何其他类型都无关紧要,只要它是离散的(或很好地分箱)即可。这是因为卡方检验计算基于列联表不是您的原始数据。sklearn.feature_selection.chi2的文档和相关的使用示例根本不清楚。不仅如此,两者在输入数据的类型上也不一致(文档中说的是布尔值或频率,而该示例使用的是原始 iris 数据集,其数量以厘米为单位),因此这会导致更多的混乱。sklearn 的卡方仅期望非负特征的原因很可能是实现:作者依赖于逐行求和,这意味着允许负值会产生错误的结果。一些难以理解的优化也在内部发生,所以为了简单的特性选择,我个人会选择scipy 的 implementation

由于您的数据不是离散的,因此您必须将每个特征分类到一定数量的名义类别中才能执行卡方检验。请注意,无论您采用何种技术,在此步骤中都会发生信息丢失;您的目标是通过找到最适合您的数据的方法来最小化它。您还必须了解,结果不能被视为绝对真理,因为测试不是为连续性质的数据设计的。另一个肯定会影响你的特征选择过程的大问题是特征的数量大于观察的数量。我肯定会推荐看看sklearn 的分解方法比如 PCA 来减少特征的数量,如果你的特征是分组的,你可以尝试多因素分析(Python 实现可通过Prince获得)。

现在已经不碍事了,让我们看一个使用 iris 数据集进行简单特征选择的示例。我们将在构造的数据框中添加一个无用的正态分布变量进行比较。

import numpy as np
import scipy as sp
import pandas as pd

from sklearn import datasets, preprocessing as prep

iris = datasets.load_iris()

X, y = iris['data'], iris['target']
df = pd.DataFrame(X, columns= iris['feature_names'])
df['useless_feature'] = np.random.normal(0, 5, len(df))

现在我们必须对数据进行分类。对于基于值和基于分位数的分箱,您可以分别使用pd.cutpd.qcut(这个很好的答案解释了两者之间的区别),但 sklearn 的KBinsDiscretizer提供了更多选项。在这里,我将它用于一维k 均值聚类以创建 bin(对每个特征进行单独计算):

def bin_by_kmeans(pd_series, n_bins):
    binner = prep.KBinsDiscretizer(n_bins= n_bins, encode= 'ordinal', strategy= 'kmeans')
    binner.fit(pd_series.values.reshape(-1, 1))
    bin_edges = [
        '({:.2f} .. {:.2f})'.format(left_edge, right_edge)
        for left_edge, right_edge in zip(
            binner.bin_edges_[0][:-1],
            binner.bin_edges_[0][1:]
        )
    ]
    return list(map(lambda b: bin_edges[int(b)], binner.transform(pd_series.values.reshape(-1, 1))))

df_binned = df.copy()
for f in df.columns:
    df_binned[f] = bin_by_kmeans(df_binned[f], 5)

调查各个特征的分箱情况的一个好方法是计算每个分箱 ( ) 中的数据点数,df_binned['feature_name_here'].value_counts()并打印出给定特征和标签列的pd.crosstab(列联表)。

该计算的有效性经常被引用的准则是,只有在每个单元中观察到的和预期的频率至少为 5 时才应使用该测试。

因此,您在列联表中看到的零越多,卡方结果的准确性就越低。这将需要一些手动调整。

接下来是对两个变量的独立性执行卡方检验的函数(本教程有非常有用的解释,强烈推荐阅读,代码从那里提取):

def get_chi_squared_results(series_A, series_B):
    contingency_table = pd.crosstab(series_A, series_B)
    chi2_stat, p_value, dof, expected_table = sp.stats.chi2_contingency(contingency_table)
    threshold = sp.stats.chi2.ppf(0.95, dof)
    return chi2_stat, threshold, p_value

要关注的值是统计数据本身、阈值及其 p 值。阈值是从分位数函数中获得的。您可以使用这三个来对单个特征标签测试进行最终评估:

print('{:<20} {:>12} {:>12}\t{:<10} {:<3}'.format('Feature', 'Chi2', 'Threshold', 'P-value', 'Is dependent?'))
for f in df.columns:
    chi2_stat, threshold, p_value = get_chi_squared_results(df[f], y)
    is_over_threshold = chi2_stat >= threshold
    is_result_significant = p_value <= 0.05
    print('{:<20} {:>12.2f} {:>12.2f}\t{:<10.2f} {}'.format(
        f, chi2_stat, threshold, p_value, (is_over_threshold and is_result_significant)
    ))

就我而言,输出如下所示:

Feature                      Chi2    Threshold  P-value    Is dependent?
sepal length (cm)          156.27        88.25  0.00       True
sepal width (cm)            89.55        60.48  0.00       True
petal length (cm)          271.80       106.39  0.00       True
petal width (cm)           271.75        58.12  0.00       True
useless_feature            300.00       339.26  0.46       False

为了声明两个变量之间的依赖关系,结果统计量应大于阈值p 值低于 0.05。您可以选择较小的 p 值以获得更高的置信度(您必须sp.stats.chi2.ppf据此计算阈值),但 0.05 是您的结果被视为显着所需的“最大”值。就有用特征的排序而言,请考虑查看计算的统计量与每个特征的阈值之间差异的相对幅度。

Chi-Sqaured 统计量是 Z 统计量的平方,所以我不明白为什么如果你有负值会很重要。