Se entendi a pergunta corretamente, você treinou um algoritmo que divide seus dados em clusters disjuntos. Agora você deseja atribuir a previsão 1 a algum subconjunto dos clusters e 0 ao restante deles. E entre esses subconjuntos, você deseja encontrar os pareto-ótimos, ou seja, aqueles que maximizam a taxa positiva verdadeira, dado um número fixo de previsões positivas (isso é equivalente à fixação do PPV). Está correto?N10 0
Isso parece muito com o problema da mochila ! Os tamanhos de cluster são "pesos" e o número de amostras positivas em um cluster é "valores", e você deseja encher sua mochila de capacidade fixa com o máximo de valor possível.
O problema da mochila possui vários algoritmos para encontrar soluções exatas (por exemplo, por programação dinâmica). Mas uma solução gananciosa útil é classificar seus clusters em ordem decrescente de (ou seja, compartilhamento de amostras positivas) e pegue o primeirok. Se você levarkde0aN, poderá fazer um croqui muito barato da sua curva ROC.v a l u ew e i gh tkk0 0N
E se você atribuir aos primeiros clusters k - 1 e à fração aleatória p ∈ [ 0 , 1 ] de amostras no k- ésimo cluster, obterá o limite superior para o problema da mochila. Com isso, você pode desenhar o limite superior da sua curva ROC.1k - 1p ∈ [ 0 , 1 ]k
Aqui está um exemplo de python:
import numpy as np
from itertools import combinations, chain
import matplotlib.pyplot as plt
np.random.seed(1)
n_obs = 1000
n = 10
# generate clusters as indices of tree leaves
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_predict
X, target = make_classification(n_samples=n_obs)
raw_clusters = DecisionTreeClassifier(max_leaf_nodes=n).fit(X, target).apply(X)
recoding = {x:i for i, x in enumerate(np.unique(raw_clusters))}
clusters = np.array([recoding[x] for x in raw_clusters])
def powerset(xs):
""" Get set of all subsets """
return chain.from_iterable(combinations(xs,n) for n in range(len(xs)+1))
def subset_to_metrics(subset, clusters, target):
""" Calculate TPR and FPR for a subset of clusters """
prediction = np.zeros(n_obs)
prediction[np.isin(clusters, subset)] = 1
tpr = sum(target*prediction) / sum(target) if sum(target) > 0 else 1
fpr = sum((1-target)*prediction) / sum(1-target) if sum(1-target) > 0 else 1
return fpr, tpr
# evaluate all subsets
all_tpr = []
all_fpr = []
for subset in powerset(range(n)):
tpr, fpr = subset_to_metrics(subset, clusters, target)
all_tpr.append(tpr)
all_fpr.append(fpr)
# evaluate only the upper bound, using knapsack greedy solution
ratios = [target[clusters==i].mean() for i in range(n)]
order = np.argsort(ratios)[::-1]
new_tpr = []
new_fpr = []
for i in range(n):
subset = order[0:(i+1)]
tpr, fpr = subset_to_metrics(subset, clusters, target)
new_tpr.append(tpr)
new_fpr.append(fpr)
plt.figure(figsize=(5,5))
plt.scatter(all_tpr, all_fpr, s=3)
plt.plot(new_tpr, new_fpr, c='red', lw=1)
plt.xlabel('TPR')
plt.ylabel('FPR')
plt.title('All and Pareto-optimal subsets')
plt.show();
Este código fará uma boa imagem para você:
Os pontos azuis são tuplas (FPR, TPR) para todos os subconjuntos e a linha vermelha se conecta (FPR, TPR) para os subconjuntos pareto-ideais.210
E agora o pouco de sal: você não precisava se preocupar com subconjuntos ! O que fiz foi classificar as folhas das árvores pela fração de amostras positivas em cada uma. Mas o que consegui é exatamente a curva ROC para a previsão probabilística da árvore. Isso significa que você não pode superar a árvore escolhendo manualmente suas folhas com base nas frequências alvo no conjunto de treinamento.
Você pode relaxar e continuar usando a previsão probabilística comum :)