Рак возникает, когда мутация или аномальные изменения происходят в генах, которые регулируют рост клеток, то есть неконтролируемое размножение клеток. Если клетка молочной железы мутирует и растет неконтролируемым образом, мы называем это раком молочной железы. Рак формируется либо в дольках, либо в протоках молочной железы. Среди наиболее распространенных видов рака, наблюдаемых у женщин в Соединенных Штатах, рак молочной железы занимает второе место. Это не значит, что это не произойдет у мужчин, это просто не характерно для мужчин. Показатели выживаемости при раке молочной железы увеличились, а число связанных с ним смертей неуклонно снижается благодаря более раннему выявлению. Чем раньше мы обнаружим раковую клетку, тем больше шансов на выживание.

Здесь мы подготовим модель для прогнозирования доброкачественной или злокачественной опухоли, чтобы можно было вовремя принять соответствующие меры для спасения жизни. Доброкачественные опухоли не считаются раковыми; их клетки внешне близки к нормальным, они растут медленно, не проникая в близлежащие ткани и не распространяясь на другие части тела, в то время как злокачественные опухоли агрессивны. Злокачественные опухоли являются раковыми, если их не остановить, они в конечном итоге распространятся за пределы исходной части опухоли.

Источник данных:

Источник данных, который мы будем использовать, — это Kaggle, который также можно найти в репозитории машинного обучения UCI.

В этом наборе данных у нас есть 10 признаков клетки вместе с диагностической информацией.

Информация об атрибутах:

  • идентификационный номер
  • Диагноз (M = злокачественный, B = доброкачественный)z

Для каждого клеточного ядра вычисляются десять вещественных признаков:

  • радиус (среднее расстояние от центра до точек по периметру)
  • текстура (стандартное отклонение значений оттенков серого)
  • периметр
  • область
  • гладкость (локальное изменение длины радиуса)
  • компактность (периметр² / площадь — 1,0)
  • вогнутость (степень вогнутости контура)
  • вогнутые точки (количество вогнутых частей контура)
  • симметрия

Среднее значение, стандартная ошибка и «наихудшее» или наибольшее (среднее значение трех самых больших значений) этих признаков были вычислены для каждого изображения, в результате чего было получено 30 признаков.

*z фрактальная размерность («приближение береговой линии» — 1)

Давайте импортируем библиотеку, которую мы будем использовать

# import the library

# for data load and data manupulation, data analysis
import pandas as pd

# for data computation in matrix form or mullti-dimensional arrays
import numpy as np

#for figure
from sklearn import model_selection

#sklearn data preparing
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

#sklearn scaling and labelencoder
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder

# sklearn models
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from xgboost import XGBClassifier
from sklearn.linear_model import LogisticRegression

#sklearn model optimization
from sklearn.model_selection import GridSearchCV

# sklearn metrics
from sklearn import metrics
from sklearn.metrics import confusion_matrix, classification_report,accuracy_score

#plot
import matplotlib.pyplot as plt
import seaborn as sns

#save model
import pickle

# display image
from IPython.display import Image

Загрузите данные из файла CSV.

# download data: https://github.com/user-dyp/Breast-Cancer/blob/master/datasets_breast_cancer.csv
df_raw = pd.read_csv("../input/breast-cancer-wisconsin-data/data.csv")
df_raw.head()

print('Dataset contains')
print("Number of rows:",df_raw.shape[0])
print("Number of columns:",df_raw.shape[1])

# Number of columns in dataset
df_raw.columns

Здесь нам не нужен столбец [id], так как он не предоставляет здесь никакого значения. И если вы видите столбец [Безымянный: 32], там есть значения NaN. Итак, давайте удалим эти два столбца.

df = df_raw.drop('id',axis=1).drop('Unnamed: 32',axis=1)
df.head(2)

Давайте выясним, есть ли в наших данных нулевое значение или нет

df.isnull().sum()

Ура, нулевого или пустого поля нет, так что мы готовы продолжить.

Давайте проверим тип данных столбцов и посмотрим, все ли типы данных столбцов назначены правильно или нет.

df.dtypes

Здесь тип данных диагностики — это объект, поэтому давайте более подробно рассмотрим категориальный тип.

df['diagnosis'] = df['diagnosis'].astype('category')
df.dtypes

Давайте разделим целевые данные и данные признаков

print("X is the features and Y is the target data")
X= df.drop('diagnosis',axis=1)
Y= df['diagnosis']

Нормализация и масштабирование функций

Зачем нужна нормализация?

Нормализация включает изменение масштаба признаков таким образом, чтобы они обладали свойствами стандартного нормального распределения со средним значением, равным нулю, и стандартным отклонением, равным единице. Все ли функции одинаково важны? Нет, это зависит от того, какую модель вы используете для прогнозирования, но перед запуском модели рекомендуется использовать нормализацию.

  • Кластеризация: необходима нормализация
  • PCA: необходима нормализация
  • SVM: необходима нормализация
  • РЕГРЕССИЯ ЛАССО И РИДЖА: необходима нормализация
# let do standardscaling then
model_scaler = StandardScaler()
X_norm =model_scaler.fit_transform(X)
X_norm = pd.DataFrame(X_norm,columns=X.columns)
X_norm.head(3)

Визуализация данных

Что такое корреляция и почему она важна?

Корреляция определяется как то, насколько близко две переменные должны иметь линейную связь друг с другом.

  • Положительная корреляция (+1): увеличение переменной вместе
  • Отрицательная корреляция (-1): когда одна переменная увеличивается, а другая уменьшается
  • Нет корреляции (0): переменная не имеет линейной зависимости

Чем полезна корреляция?

  • коррелированная переменная может использоваться в прогнозировании друг для друга, которые могут использоваться для вменения пропущенных значений.
  • коррелированные переменные указывают на наличие причинно-следственной связи.
  • Корреляция используется в качестве основной величины для многих методов моделирования.
  • Для проверки избыточности переменных

#explore correlation
plt.rcParams['figure.figsize']=[38,16]
sns.set(font_scale=1.4)
corr = X.corr()
mask_upper_traingle = np.triu(np.ones_like(corr, dtype=np.bool))
sns.heatmap(corr,mask=mask_upper_traingle, cmap='coolwarm', annot=True, fmt='.1')

Из матрицы корреляции мы видим, что некоторые функции сильно коррелированы:

  • perimeter_worst — радиус_среднее
  • perimeter_worst — периметр_среднее
  • area_worst — периметр_худший и т. д.

Поскольку эти функции коррелированы, их можно использовать для прогнозирования другой переменной, если есть отсутствующие переменные, но, к счастью, у нас нет отсутствующей точки данных.

# sorted correlationmatrix
def sort_and_createCorrelationMatrix(dt):
    corr_matrix = dt.corr().abs()
    corr_sorted = corr_matrix.stack().sort_values(ascending=False)
    corr_sorted = pd.DataFrame(corr_sorted)
    corr_sorted.reset_index(inplace=True)
corr_sorted.columns=['level_0', 'level_1', 'value']
    corr_sorted =corr_sorted.pivot(index ='level_0', columns ='level_1', values='value') 
    mask_upper_traingle = np.triu(np.ones_like(corr_sorted, dtype=np.bool))
    sns.heatmap(corr_sorted,mask=mask_upper_traingle, cmap='coolwarm', annot=True, fmt='.1')

Визуализация коробочного сюжета

Блочная диаграмма дает представление о распределении данных на основе:

  • Первый квартиль (Q1)
  • медиана
  • Третий квартиль (Q3)
  • Минимум
  • Максимум

С помощью этой диаграммы мы можем видеть, есть ли в данных выбросы или нет. Если есть выбросы, нам нужен план, чтобы справиться с этим. Иногда выбросы играют жизненно важную роль, поэтому не всегда идеально сразу удалять выбросы.

plt.rcParams['figure.figsize']=(18,5)
fig,(ax1,ax2,ax3,ax4,ax5,ax6,ax7,ax8,ax9,ax10)=plt.subplots(1,10)
sns.boxplot(x='diagnosis',y='radius_mean',data=df,ax=ax1)
sns.boxplot(x='diagnosis',y='texture_mean',data=df,ax=ax2)
sns.boxplot(x='diagnosis',y='perimeter_mean',data=df,ax=ax3)
sns.boxplot(x='diagnosis',y='area_mean',data=df,ax=ax4)
sns.boxplot(x='diagnosis',y='smoothness_mean',data=df,ax=ax5)
sns.boxplot(x='diagnosis',y='compactness_mean',data=df,ax=ax6)
sns.boxplot(x='diagnosis',y='concavity_mean',data=df,ax=ax7)
sns.boxplot(x='diagnosis',y='concave points_mean',data=df,ax=ax8)
sns.boxplot(x='diagnosis',y='symmetry_mean',data=df,ax=ax9)
sns.boxplot(x='diagnosis',y='fractal_dimension_mean',data=df,ax=ax10)
fig.tight_layout()
fig,(ax1,ax2,ax3,ax4,ax5,ax6,ax7,ax8,ax9,ax10)=plt.subplots(1,10)
sns.boxplot(x='diagnosis',y='radius_se',data=df,ax=ax1)
sns.boxplot(x='diagnosis',y='texture_se',data=df,ax=ax2)
sns.boxplot(x='diagnosis',y='perimeter_se',data=df,ax=ax3)
sns.boxplot(x='diagnosis',y='area_se',data=df,ax=ax4)
sns.boxplot(x='diagnosis',y='smoothness_se',data=df,ax=ax5)
sns.boxplot(x='diagnosis',y='compactness_se',data=df,ax=ax6)
sns.boxplot(x='diagnosis',y='concavity_se',data=df,ax=ax7)
sns.boxplot(x='diagnosis',y='concave points_se',data=df,ax=ax8)
sns.boxplot(x='diagnosis',y='symmetry_se',data=df,ax=ax9)
sns.boxplot(x='diagnosis',y='fractal_dimension_se',data=df,ax=ax10)
fig.tight_layout()
plt.rcParams['figure.figsize']=(17,5)
fig,(ax1,ax2,ax3,ax4,ax5,ax6,ax7,ax8,ax9,ax10)=plt.subplots(1,10)
sns.boxplot(x='diagnosis',y='radius_worst',data=df,ax=ax1)
sns.boxplot(x='diagnosis',y='texture_worst',data=df,ax=ax2)
sns.boxplot(x='diagnosis',y='perimeter_worst',data=df,ax=ax3)
sns.boxplot(x='diagnosis',y='area_worst',data=df,ax=ax4)
sns.boxplot(x='diagnosis',y='smoothness_worst',data=df,ax=ax5)
sns.boxplot(x='diagnosis',y='compactness_worst',data=df,ax=ax6)
sns.boxplot(x='diagnosis',y='concavity_worst',data=df,ax=ax7)
sns.boxplot(x='diagnosis',y='concave points_worst',data=df,ax=ax8)
sns.boxplot(x='diagnosis',y='symmetry_worst',data=df,ax=ax9)
sns.boxplot(x='diagnosis',y='fractal_dimension_worst',data=df,ax=ax10)
fig.tight_layout()

На этой блочной диаграмме мы видим, что злокачественное новообразование больше по размеру, чем доброкачественное, для большинства признаков, за исключением среднего значения fractual_dimesions. Аналогичные характеристики появляются в функции _worst. Но стандартная ошибка имеет совсем другой результат. Texture_se, Smoothness_se и симметрия_se выше для доброкачественного состояния.

Мы также можем видеть, что в данных присутствует много выбросов. Черные точки на диаграмме показывают выбросы в данных.

# check the distribution of data
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'radius_mean', hist=True, rug=True )
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'perimeter_mean', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'smoothness_mean', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'concavity_mean', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'radius_se', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'perimeter_se', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'smoothness_se', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'concavity_se', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'radius_worst', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'perimeter_worst', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'smoothness_worst', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'concavity_worst', hist=True, rug=True)
fig = sns.FacetGrid(df,col='diagnosis', hue='diagnosis')
fig.map(sns.distplot, 'symmetry_worst', hist=True, rug=True)

Из приведенного выше графика распределения данных видно, что большинство из них имеют нормальное распределение данных. Некоторые функции скошены вправо, например, radius_se, perimeter_se, smoothness_se, concavity_se и т. д. Существует другой способ обработки скошенных данных, но пока мы оставим как есть.

Кодировать категориальные переменные

Если вы проверите диагноз, есть две категории и в формате объекта, поэтому нам нужно преобразовать в числовое значение, чтобы его можно было использовать в модели.

Y.value_counts()

Мы используем LabelEncoder из sklearn, чтобы преобразовать категориальную строку в числовое значение.

model_encoder = LabelEncoder()
Y_norm = model_encoder.fit_transform(Y)
print(model_encoder.classes_)
print(model_encoder.transform(model_encoder.classes_))
Y_norm = pd.DataFrame(Y_norm,columns=['diagnosis'])
Y_norm.head(5)

Техника выборки

Мы видим, что доброкачественных данных больше, чем злокачественных, то есть 63% данных являются доброкачественными, а 37% - злокачественными. Таким образом, данные распределяются неравномерно. Если мы построим на ее основе модель, она будет работать лучше, поскольку наша модель будет знать о Доброкачественном больше, чем о Злокачественном. Итак, мы должны быть честными при обучении модели.

Здесь данных действительно мало, поэтому мы не можем уменьшить размер выборки, чтобы сделать ее равной M и B. Вместо этого давайте сделаем избыточную выборку, используя метод передискретизации, вызываемый SMOTE (метод передискретизации синтетического меньшинства)

"Ссылка на сайт"

Y.value_counts()/len(Y)

# SMOTE: Synthetic Minority Over-sampling Technique 
from imblearn.over_sampling import SMOTE

model_smote =SMOTE(random_state=13)
X_norm, Y_norm = model_smote.fit_resample(X_norm,Y_norm)

Я сделал собственный код сборки, который будет использоваться для подгонки модели по параметру настройки. Важно, чтобы мы использовали GridSearchCV для настройки параметров.

def FitModelByParameterTuning(X,Y,algo_name, algorithm,gridSearchParams,cv, result=None):
    np.random.seed(13)
    x_train, x_test, y_train, y_test = train_test_split(X,Y, test_size=0.2)
    
    gridSearch = GridSearchCV(
        estimator=algorithm,
        param_grid=gridSearchParams,
        cv = cv,
        scoring='accuracy',
        verbose=1,
        n_jobs=-1)
    gridSearch_result = gridSearch.fit(x_train,y_train)
    best_params = gridSearch_result.best_params_
    pred = gridSearch_result.predict(x_test)
    cm=confusion_matrix(y_test,pred)
    
    ### print the result
    print('Best Params:', best_params)
    print('Classification Report:\n', classification_report(y_test,pred))
    print('Accuracy Score:', str(accuracy_score(y_test,pred)))
    print('Confusion Matrix:\n',cm)
    if result is not None:
        result_data.append([str(gridSearch_result.estimator).split('(')[0],accuracy_score(y_test,pred)])
    
    return [gridSearch_result,str(accuracy_score(y_test,pred))]

Наша проблема прогнозирования рака — это проблема классификации, поэтому мы будем применять различные алгоритмы классификации, чтобы получить результат и посмотреть, какой из них будет работать лучше. Вот некоторые модели/алгоритмы классификации, которые мы можем применить:

Давайте начнем тестировать другой алгоритм:

Модель логистической регрессии

1. Логистическая регрессия

# data frame to for result
result_data =[]
#result_df = pd.DataFrame(result_data,columns=['Model','Accuracy'])

# Logistic Regression
param = {
    'penalty' : ['l1', 'l2','elastic-net']
}
model_Logistic = FitModelByParameterTuning(X_norm, Y_norm, 'LogisticRegression', LogisticRegression(), param, cv=5, result=result_data)

Логистическая регрессия имеет точность 97,2%, что хорошо. Давайте посмотрим еще один алгоритм

2. Классификация опорных векторов (SVC)¶

param = {
    'C':[0.1,1,100,1000],
    'gamma':[0.0001,0.001, 0.005, 0.1, 1, 3, 5]
}
model_SVC = FitModelByParameterTuning(X_norm, Y_norm, 'SVC', SVC(), param, cv=5, result=result_data)

3. Случайный лес¶

param={
    "n_estimators":[100,500,1000,2000]
}

model_RandomForest = FitModelByParameterTuning(X_norm, Y_norm, 'Random Forest', RandomForestClassifier(), param, cv=5, result=result_data)

4. Классификатор XGB()

param={
    'n_estimators':[100,500,1000,2000]
}

model_XGBClassifier = FitModelByParameterTuning(X_norm, Y_norm, 'XGBClassifier', XGBClassifier(), param, cv=5, result=result_data)

Сравнение точности моделей

result_dt = pd.DataFrame(result_data,columns=['Model','Accuracy'])plt.rcParams["figure.figsize"] = [10, 6]

fig, ax = plt.subplots()
ax.bar(x=result_dt['Model'],height=result_dt['Accuracy'],color=['r','g','b','y'])

#ax = result_dt.plot.bar(x='Model',y='Accuracy',color=['Red','Blue','Green','Yellow']) 
plt.xlabel("Model")
plt.ylabel("Accuracy")
plt.title("Accuracy in different Model")
for p in ax.patches:
    ax.annotate(str('{0:.3f}'.format( p.get_height())), (p.get_x() * 1.001, p.get_height() * 1.001))
    
plt.tight_layout()

Исходя из этого графика, мы можем сказать, что логистическая регрессия является лучшей моделью для прогнозирования отмены груди с точностью 97,2%.

Ссылка на полный код: https://www.kaggle.com/dipitak/in-depth-breast-cancer-prediction-step-by-step

Данные: https://github.com/user-dyp/Breast-Cancer/blob/master/datasets_breast_cancer.csv

Ссылка