EDIT: Спасибо за разъяснения. Я изменил ответ.
Важно понимать, что вы пытаетесь спроецировать выделение из многомерного пространства в одномерное пространство. Не в каждом случае вы сможете увидеть четкое разделение, подобное тому, которое есть у вас. Существуют также различные возможности для этого. Здесь я привел простой пример, который может помочь вашему клиенту интерпретировать модель, но, конечно, не отражает всей сложности модели.
Вы не предоставили никаких образцы данных, поэтому я сгенерирую некоторые из набора данных рака груди.
Сначала давайте импортируем то, что нам нужно:
from sklearn import datasets
from xgboost import XGBClassifier
import pandas as pd
import numpy as np
А теперь импортируйте набор данных и обучите очень простую модель XGBoost
cancer = datasets.load_breast_cancer()
X = cancer.data
y = cancer.target
xgb_model = XGBClassifier(n_estimators=5,
objective="binary:logistic",
random_state=42)
xgb_model.fit(X, y)
y_prob = pd.DataFrame(xgb_model.predict_proba(X))[0]
Есть несколько способов решить эту проблему.
Один из подходов - разделить вероятность, заданную моделью. Таким образом, вы решите, какие вероятности вы считаете «высоким риском», «средним риском» и «низким риском», а интервалы данных можно классифицировать. В этом примере я выбрал низкое значение 0 <= p <= 0.5
, среднее значение 0.5 < p <= 0.8
и высокое значение 0.8 < p <= 1
.
Сначала вы должны рассчитать вероятность для каждого прогноза. Я бы посоветовал, возможно, использовать для этого набор тестов, чтобы избежать смещения из-за возможного переобучения модели.
y_prob = pd.DataFrame(xgb_model.predict_proba(X))[0]
df = pd.DataFrame(X, columns=cancer.feature_names)
# Stores the probability of a malignant cancer
df['probability'] = y_prob
Затем вам нужно объединить свои данные и рассчитать средние вероятности для каждого из этих интервалов. Я бы предложил объединить ваши данные, используя np.histogram_bin_edges automati c вычисление:
def calculate_mean_prob(feat):
"""Calculates mean probability for a feature value, binning it."""
# Bins from the automatic rules from numpy, check docs for details
bins = np.histogram_bin_edges(df[feat], bins='auto')
binned_values = pd.cut(df[feat], bins)
return df['probability'].groupby(binned_values).mean()
Теперь вы можете классифицировать каждую ячейку в соответствии с тем, что вы считаете низким / средним / высокая вероятность:
def classify_probability(prob, medium=0.5, high=0.8, fillna_method= 'ffill'):
"""Classify the output of each bin into a risk group,
according to the probability.
Following the follow rules:
0 <= p <= medium: Low risk
medium < p <= high: Medium risk
high < p <= 1: High Risk
If a bin has no entries, it will be filled using fillna with the method
specified in fillna_method
"""
risk = pd.cut(prob, [0., medium, high, 1.0], include_lowest=True,
labels=['Low Risk', 'Medium Risk', 'High Risk'])
risk.fillna(method=fillna_method, inplace=True)
return risk
Это вернет вам риск для каждой ячейки, на которую вы разделили свои данные. Поскольку у вас, вероятно, будет несколько интервалов с последовательными значениями, вы можете объединить последовательные интервалы pd.Interval. Код для этого показан ниже:
def sum_interval(i1, i2):
if i2 is None:
return None
if i1.right == i2.left:
return pd.Interval(i1.left, i2.right)
return None
def sum_intervals(args):
"""Given a list of pd.Intervals,
returns a list summing consecutive intervals."""
result = list()
current_interval = args[0]
for next_interval in list(args[1:]) + [None]:
# Try to sum the current interval and nex interval
# The None in necessary for the last interval
sum_int = sum_interval(current_interval, next_interval)
if sum_int is not None:
# Update the current_interval in case if it is
# possible to sum
current_interval = sum_int
else:
# Otherwise tries to start a new interval
result.append(current_interval)
current_interval = next_interval
if len(result) == 1:
return result[0]
return result
def combine_bins(df):
# Group them by label
grouped = df.groupby(df).apply(lambda x: sorted(list(x.index)))
# Sum each category in intervals, if consecutive
merged_intervals = grouped.apply(sum_intervals)
return merged_intervals
Теперь вы можете объединить все функции для вычисления интервалов для каждой функции:
def generate_risk_class(feature, medium=0.5, high=0.8):
mean_prob = calculate_mean_prob(feature)
classification = classify_probability(mean_prob, medium=medium, high=high)
merged_bins = combine_bins(classification)
return merged_bins
Например, generate_risk_class('worst radius')
приводит к:
Low Risk (7.93, 17.3]
Medium Risk (17.3, 18.639]
High Risk (18.639, 36.04]
Но в случае, если вы получаете функции, которые не являются очень хорошими дискриминаторами (или которые не разделяют высокий / низкий риск линейно), у вас будут более сложные области. Например, generate_risk_class('mean symmetry')
дает:
Low Risk [(0.114, 0.209], (0.241, 0.249], (0.272, 0.288]]
Medium Risk [(0.209, 0.225], (0.233, 0.241], (0.249, 0.264]]
High Risk [(0.225, 0.233], (0.264, 0.272], (0.288, 0.304]]