Project conducted by R.P.M. Kras¶
Personal development project¶
Source: https://www.kaggle.com/datasets/bhargavchirumamilla/thyroid-cancer-risk-dataset
Used in accordance to the Attribution 4.0 International license.
Libraries¶
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
data = pd.read_csv('thyroid_cancer_risk_data.csv')
data.head()
| Patient_ID | Age | Gender | Country | Ethnicity | Family_History | Radiation_Exposure | Iodine_Deficiency | Smoking | Obesity | Diabetes | TSH_Level | T3_Level | T4_Level | Nodule_Size | Thyroid_Cancer_Risk | Diagnosis | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 66 | Male | Russia | Caucasian | No | Yes | No | No | No | No | 9.37 | 1.67 | 6.16 | 1.08 | Low | Benign |
| 1 | 2 | 29 | Male | Germany | Hispanic | No | Yes | No | No | No | No | 1.83 | 1.73 | 10.54 | 4.05 | Low | Benign |
| 2 | 3 | 86 | Male | Nigeria | Caucasian | No | No | No | No | No | No | 6.26 | 2.59 | 10.57 | 4.61 | Low | Benign |
| 3 | 4 | 75 | Female | India | Asian | No | No | No | No | No | No | 4.10 | 2.62 | 11.04 | 2.46 | Medium | Benign |
| 4 | 5 | 35 | Female | Germany | African | Yes | Yes | No | No | No | No | 9.10 | 2.11 | 10.71 | 2.11 | High | Benign |
The dataset contains zero missing values! Good Kaggle datasets are so convenient.
data.isna().sum()
Patient_ID 0 Age 0 Gender 0 Country 0 Ethnicity 0 Family_History 0 Radiation_Exposure 0 Iodine_Deficiency 0 Smoking 0 Obesity 0 Diabetes 0 TSH_Level 0 T3_Level 0 T4_Level 0 Nodule_Size 0 Thyroid_Cancer_Risk 0 Diagnosis 0 dtype: int64
data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 212691 entries, 0 to 212690 Data columns (total 17 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Patient_ID 212691 non-null int64 1 Age 212691 non-null int64 2 Gender 212691 non-null object 3 Country 212691 non-null object 4 Ethnicity 212691 non-null object 5 Family_History 212691 non-null object 6 Radiation_Exposure 212691 non-null object 7 Iodine_Deficiency 212691 non-null object 8 Smoking 212691 non-null object 9 Obesity 212691 non-null object 10 Diabetes 212691 non-null object 11 TSH_Level 212691 non-null float64 12 T3_Level 212691 non-null float64 13 T4_Level 212691 non-null float64 14 Nodule_Size 212691 non-null float64 15 Thyroid_Cancer_Risk 212691 non-null object 16 Diagnosis 212691 non-null object dtypes: float64(4), int64(2), object(11) memory usage: 27.6+ MB
data['Diagnosis'].nunique()
2
data['Diagnosis']
0 Benign
1 Benign
2 Benign
3 Benign
4 Benign
...
212686 Benign
212687 Benign
212688 Benign
212689 Benign
212690 Malignant
Name: Diagnosis, Length: 212691, dtype: object
data.dtypes.value_counts()
object 11 float64 4 int64 2 Name: count, dtype: int64
Data Preparation¶
This section includes the required label encoding, primarily.
le = LabelEncoder()
categorical_cols = data.select_dtypes(include=['object']).columns
for col in categorical_cols:
data[col] = le.fit_transform(data[col])
data.head()
| Patient_ID | Age | Gender | Country | Ethnicity | Family_History | Radiation_Exposure | Iodine_Deficiency | Smoking | Obesity | Diabetes | TSH_Level | T3_Level | T4_Level | Nodule_Size | Thyroid_Cancer_Risk | Diagnosis | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 66 | 1 | 6 | 2 | 0 | 1 | 0 | 0 | 0 | 0 | 9.37 | 1.67 | 6.16 | 1.08 | 1 | 0 |
| 1 | 2 | 29 | 1 | 2 | 3 | 0 | 1 | 0 | 0 | 0 | 0 | 1.83 | 1.73 | 10.54 | 4.05 | 1 | 0 |
| 2 | 3 | 86 | 1 | 5 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 6.26 | 2.59 | 10.57 | 4.61 | 1 | 0 |
| 3 | 4 | 75 | 0 | 3 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 4.10 | 2.62 | 11.04 | 2.46 | 2 | 0 |
| 4 | 5 | 35 | 0 | 2 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 9.10 | 2.11 | 10.71 | 2.11 | 0 | 0 |
After encoding all categorical labels, we can now analyse the difference between the quantity of features between classes.
Data analysis¶
From plotting the recorded ages, we can tell that overall the dataset is relatively balanced.
print("Youngest recorded instance: ", data['Age'].min())
print("Oldest age recorded: ", data['Age'].max())
print("Unique number of ages: ", data['Age'].nunique())
data.plot.hist(y="Age", bins=75)
Youngest recorded instance: 15 Oldest age recorded: 89 Unique number of ages: 75
<Axes: ylabel='Frequency'>
columns = ["Smoking", "Obesity", "Diabetes"]
fig, axes = plt.subplots(1, len(columns), figsize=(15, 5))
for ax, col in zip(axes, columns):
counts = data[col].value_counts()
ax.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90, colors=plt.cm.Paired.colors)
ax.set_title(col)
plt.tight_layout()
plt.show()
Training the model¶
X = data.drop('Diagnosis', axis=1)
y = data['Diagnosis']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state=42)
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score, classification_report
from sklearn.model_selection import GridSearchCV
Logistic Regression¶
log_reg = linear_model.LogisticRegression(solver='lbfgs', random_state=42, max_iter=10000)
log_reg.fit(X_train, y_train)
y_pred_log = log_reg.predict(X_test)
print(classification_report(y_test, y_pred_log))
precision recall f1-score support
0 0.84 0.94 0.89 32615
1 0.69 0.42 0.52 9924
accuracy 0.82 42539
macro avg 0.77 0.68 0.71 42539
weighted avg 0.81 0.82 0.80 42539
Now for some max iterations finetuning:
parameters = [
{"max_iter":[100, 1000, 10000]}
]
clf = GridSearchCV(LogisticRegression(solver='lbfgs', random_state=42), param_grid=parameters, cv=5, scoring="recall")
clf.fit(X_train, y_train)
clf_y_pred = clf.predict(X_test)
print(classification_report(y_test, clf_y_pred))
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
precision recall f1-score support
0 0.84 0.94 0.89 32615
1 0.69 0.42 0.53 9924
accuracy 0.82 42539
macro avg 0.77 0.68 0.71 42539
weighted avg 0.81 0.82 0.81 42539
c:\Users\robkr\AppData\Local\Programs\Python\Python311\Lib\site-packages\sklearn\linear_model\_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, clf_y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.title('Confusion Matrix')
plt.show()
Random Forest¶
ran_for = RandomForestClassifier(n_estimators=100, random_state=42)
ran_for.fit(X_train, y_train)
y_pred_rf = ran_for.predict(X_test)
print(classification_report(y_test, y_pred_log))
precision recall f1-score support
0 0.84 0.94 0.89 32615
1 0.69 0.42 0.52 9924
accuracy 0.82 42539
macro avg 0.77 0.68 0.71 42539
weighted avg 0.81 0.82 0.80 42539
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred_rf)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.title('Confusion Matrix')
plt.show()
Conclusion¶
The results suggest that the ability of both models to correctly classify someone as having heart disease is rather low. This is supported by the relatively low recall score for patients who were diagnosed with the disease.
This was just a for-fun project for me to learn and grow more during my free time, albeit it being very simple and not having enough finetuning.