График ROC-прямой и расчет площади AUC
Есть готовые реализации функций, однако я пытаюсь написать код вручную. И построить график. Имеем следующий подход: мы по обученной модели соотносим истинные и ложные предсказания с тестовой выборкой. Если спрогнозировали с вероятностью больше 0.5, то величина берется за единицу и при тесте, равном также единице, относится к категории tp (то есть true positive), при нулевом тесте - к fp ( то есть false positive). Если тест единица, а у нас предсказание ноль, то имеем fn (false negative), если везде нули - tn (true negative). На основе рассчитанных счетчиков, получаем метрики tpr (true positive rate) и fpr (false positive rate).
Проделав все это, затем пытаюсь готовой функцией рассчитать площадь под графиком (AUC - area under curve) и построить сам график. Площадь и график не получаются: AUC - потому что передаются float (хотя чему там еще передаваться - это же прогноз методом predict_proba!); график - пытался передать точки циклом, но тоже споткнулся о float (этот тип в цикле не перебирается)
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from matplotlib import pyplot as plt
from sklearn import metrics
# Создаём сэмпл
n_samples = 890
# числа в диапазоне до 3 в количестве n_samples (т.е. 890 штук)+2 к каждому числу:
number_of_passengers = np.random.choice(3, n_samples) + 2
price_of_ticket = np.random.choice(35000, n_samples) + 28
invoice_amount = ((np.random.choice(12, n_samples)) * price_of_ticket) + 1538
age_of_passengers = np.random.choice(78, n_samples) + 5
data = pd.DataFrame({'Number_of_passengers': number_of_passengers, 'Age': age_of_passengers, 'Price': price_of_ticket, 'Amount': invoice_amount})
# вообще я использовал файл с датасетом, однако здесь попробовал реализовать линейную модель, где
# переменная 'Amount' при обучении может вступить целевой:
X = data[['Number_of_passengers', 'Age', 'Price']]
y = data['Amount']
model = LogisticRegression()
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.3,
random_state=42 )
model.fit(X_train, y_train)
tp = 0 # True positive
fp = 0 # False positive
fn = 0 # False negative
tn = 0 # True negative
y_pred = model.predict_proba( X_test )
for predicted_prob, actual in zip( y_pred[:,1], y_test ):
if predicted_prob >= 0.5:
predicted = 1
else:
predicted = 0
if predicted == 1:
if actual == 1:
tp += 1
else:
fp += 1
else:
if actual == 1:
fn += 1
else:
tn += 1
tpr = tp/(tp+fn)
fpr = fp/(fp+tn)
print('Площадь, рассчитанная ручным способом: ', metrics.auc(fpr, tpr))
plt.plot(fpr, tpr)
Ответы (1 шт):
Переделал немного ваш код, добавил бинарную целевую переменную. Поскольку, как я уже писал в комментарии, ROC-AUC можно считать для задачи бинарной классификации, но никак не для задачи регрессии, которая у вас получалась с вашими данными.
В итоге получился ROC-AUC = 0.5 и прямая диагональная линия на графике, как это и должно быть для случайных данных, в которых отсутствует сигнал:
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression,LinearRegression
from sklearn.model_selection import train_test_split
from matplotlib import pyplot as plt
from sklearn import metrics
# Создаём сэмпл
n_samples = 890
# числа в диапазоне до 3 в количестве n_samples (т.е. 890 штук)+2 к каждому числу:
number_of_passengers = np.random.choice(3, n_samples) + 2
price_of_ticket = np.random.choice(35000, n_samples) + 28
invoice_amount = ((np.random.choice(12, n_samples)) * price_of_ticket) + 1538
age_of_passengers = np.random.choice(78, n_samples) + 5
survival = np.random.choice(2, n_samples)
data = pd.DataFrame({'Number_of_passengers': number_of_passengers, 'Age': age_of_passengers, 'Price': price_of_ticket, 'Amount': invoice_amount, 'Survival': survival})
# вообще я использовал файл с датасетом, однако здесь попробовал реализовать линейную модель, где
# переменная 'Amount' при обучении может вступить целевой:
X = data[['Number_of_passengers', 'Age', 'Price', 'Amount']]
y = data['Survival']
model = LogisticRegression()
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.3,
random_state=42 )
model.fit(X_train, y_train)
tpr = []
fpr = []
for threshold in np.arange(0,1,0.1):
tp = 0 # True positive
fp = 0 # False positive
fn = 0 # False negative
tn = 0 # True negative
y_pred = model.predict_proba( X_test )
for predicted_prob, actual in zip( y_pred[:,1], y_test ):
if predicted_prob >= threshold:
predicted = 1
else:
predicted = 0
if predicted == 1:
if actual == 1:
tp += 1
else:
fp += 1
else:
if actual == 1:
fn += 1
else:
tn += 1
tpr.append(tp/(tp+fn))
fpr.append(fp/(fp+tn))
print('Площадь, рассчитанная ручным способом: ', metrics.auc(fpr, tpr))
plt.plot(fpr, tpr)
Вывод:
Площадь, рассчитанная ручным способом: 0.5
