【问题标题】:In python, how to discretize continuous variable using accuracy as a criterion taking class into consideration在python中,如何使用精度作为考虑类的标准来离散化连续变量
【发布时间】:2021-11-06 01:41:48
【问题描述】:

对于一组受试者,我有一个范围为 0-100 的连续变量,表示受试者状态 cont_attribute 的量化。对于每个主题,我还有一个序数变量,将主题状态的读者注释表示为四种状态之一(例如 1、2、3、4)class_labelcont_attribute 的值在类之间重叠。我的目标是离散化cont_attribute,以便优化与类的一致性。

在离散化cont_attribute时,可以将任意阈值x1x2x3直接应用于连续变量,以产生四个序数类别的bin,并且可以评估与读者注释类的一致性:

cohen_kappa_score((pd.cut(df['cont_attribute'],bins=[0, x1, x2, x3, 100], labels=['1','2','3','4']).astype('int'))
, df['class_label'].astype('int'))

我发现了几个连续变量离散化的选项,例如 Jenks natural breaksklearn Kmeans,尽管这些选项没有考虑到类。

我尝试了什么:

我尝试使用 scipy.optimize.minimize 优化上面的函数以产生最大值。这里对于两个类之间的每个阈值,我使用较大类的最小值和较小类的最大值作为找到这些类之间各自最佳截止点的范围。使用这种方法我遇到了一个问题,提示:

ValueError: bins 必须单调增加。

def objfunc(grid):
    x1, x2, x3 = grid
    return (cohen_kappa_score((pd.cut(df.cont_attribute,bins=[0, x1, x2, x3, 100],labels=['1','2','3','4'], duplicates='drop').astype('int'))
, df['class_label'].astype('int'))) * (-1);

grid = (slice(df[(df['class_label'] == 2)]['cont_attribute'].min(), df[(df['class_label'] == 1)]['cont_attribute'].max(), 0.5), (slice(df[(df['class_label'] == 3)]['cont_attribute'].min(), df[(df['class_label'] == 2)]['cont_attribute'].max(), 0.5), (slice(df[(df['class_label'] == 4)]['cont_attribute'].min(), df[(df['class_label'] == 3)]['cont_attribute'].max(), 0.5))
solution = brute(objfunc, grid, finish=None,full_output = True)
solution

在 python 中,是否有一种直接的方法来优化阈值x1x2x3 并考虑与类的一致性(监督离散化)?或者,如何使用 scipy.optimize.minimize 重写上述函数以产生最大值?

【问题讨论】:

  • 我相信你是正确的,大多数截止确定代码是在假设你不知道你想要的分类的情况下设计的,而是试图确定它。那么,我认为你是在正确的轨道上。您只需要确定要优化的混淆矩阵的度量。例如,假阳性等同于假阴性。不过,我可以直接回答最后一个问题。 f(x) 的最小值是 -f(x) 的最大值。
  • 谢谢,我想我能够按照编辑的方式重写函数,尽管我在对连续变量应用剪切时遇到了问题。
  • 我可以将上述方法应用于其他函数,以根据先决条件优化精度或其他相关系数。分箱问题仍然存在。

标签: python scipy binning scipy-optimize-minimize discretization


【解决方案1】:

错误信息不是太难。 pandas cut 方法要求切割向量 [0,x1,x2,x3,100] 是严格单调的。通过有一些机制来确保没有无效值被传递给cut 函数,我们是安全的。这就是我在下面实现的。为了表示invalid 设置,习惯上使用np.inf,因为所有其他值都较低。因此,每个 minizmier 都会说这样的无效作为解决方案是不可取的。实现见下文。我还包括了所有的导入和一些数据生成,因此使用代码很简单。请在以后的问题中这样做。

您可能希望在蛮力搜索中每个维度使用 10 个以上的 bin。

另外 - 代码效率很低。由于它对 x1、x2、x3 的所有组合进行暴力破解,但其中很多是无效的(例如 x2

最后,brute 方法是一个最小化器,所以你应该从目标返回 -cohen_kappa

#%%
import numpy as np
from sklearn.metrics import cohen_kappa_score, confusion_matrix
from scipy.stats import truncnorm
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.optimize import brute

#
# Generate Data
#
n = 1000
np.random.seed(0)
y = np.random.choice(4, p=[0.1, 0.3, 0.4, 0.2], size=n)
x = np.zeros(n)
for i in range(5):
    low = 0
    high = 100
    mymean = 20 * i
    myscale = 8
    a, b = (low - mymean) / myscale, (high - mymean) / myscale
    x[y == i] = truncnorm.rvs(a=a, b=b, loc=mymean, scale=myscale, size=np.sum(y == i))
data = pd.DataFrame({"cont_attribute": x, "class_label": y})

# make a loss function that accounts for the bad orderings
def loss(cuts):
    x1, x2, x3 = cuts
    if 0 >= x1 or x1 >= x2 or x2 >= x3 or x3 >= 100:
        return np.inf
    yhat = pd.cut(
        data["cont_attribute"],
        bins=[0, x1, x2, x3, 100],
        labels=[0, 1, 2, 3],
        # duplicates="drop",
    ).astype("int")
    return -cohen_kappa_score(data["class_label"], yhat)


# Compute the result via brute force
ranges = [(0, 100)] * 3
Ns=30
result = brute(func=loss, ranges=ranges, Ns=Ns)
print(result)
print(-loss(result))

# Evaluate the final result in a confusion matrix
x1, x2, x3 = result
data["class_pred"] = pd.cut(
    data["cont_attribute"],
    bins=[0, x1, x2, x3, 100],
    labels=[0, 1, 2, 3],
    duplicates="drop",
).astype("int")
mat = confusion_matrix(y_true=data['class_label'],y_pred=data['class_pred'])
plt.matshow(mat)
# Loop over data dimensions and create text annotations.
for i in range(4):
    for j in range(4):
        text = plt.text(j, i, mat[i, j],
                       ha="center", va="center", color="grey")
plt.xlabel('Predicted class')
plt.ylabel('True class')
plt.show()

# Evaluate result graphically
# inspect the data
fig,ax = plt.subplots(2,1)
sns.histplot(data=data, x="cont_attribute", hue="class_label",ax=ax[0],multiple='stack')
sns.histplot(data=data, x="cont_attribute", hue="class_pred",ax=ax[1],multiple='stack')
plt.show()

关于 scipy.optimize.minimize 的使用,当使用 cohen kappa 作为宾语时,这是不可能的。由于它不可微分,因此不太容易优化。考虑改用交叉熵损失函数。但在这种情况下,您将需要一个(参数)模型来执行分类任务。

statsmodels 的序数回归包中提供了标准序数分类器。它将比蛮力方法快得多,但在 cohen kappa 上评估时可能不太准确。如果要更多的垃圾箱,我可能会走那条路。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 2019-01-24
    • 2013-04-15
    相关资源
    最近更新 更多