image_output_1.png
from sklearn.model_selection import StratifiedKFold

from sklearn.neighbors import KNeighborsClassifier

from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler

from sklearn.utils import shuffle

import matplotlib.pyplot as plt

import numpy as np

import pandas as pd

import seaborn as sns

import warnings

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler
from sklearn.model_selection import StratifiedKFold
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from termcolor import colored
from sklearn.utils import shuffle
import warnings
warnings.filterwarnings('ignore')
plt.style.use('ggplot')

data = pd.read_csv('../input/personal-key-indicators-of-heart-disease/heart_2020_cleaned.csv')

data.describe().T

diabetic_map = {'No, borderline diabetes': 'No', 'Yes (during pregnancy)': 'Yes', 'Yes': 'Yes', 'No': 'No'}
data.Diabetic = data.Diabetic.map(diabetic_map)

categorical_features = ['Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race', 'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer', 'HeartDisease']
numerical_columns = [x for x in data.columns if x not in categorical_features]

def plotting_function(feature):
    fig, axes = plt.subplots(ncols = 3,figsize = (25,5))
    heart_disease_by_feature = pd.DataFrame(data[data["HeartDisease"] == "Yes"].groupby(feature).count()["HeartDisease"])
    heart_disease_by_feature.sort_values(by = "HeartDisease", ascending = False).plot(kind ="bar", ax = axes[0])
    axes[1].pie(heart_disease_by_feature["HeartDisease"], labels = heart_disease_by_feature.index, autopct = "%.2f")
    data[feature].value_counts().sort_values(ascending = False).plot(kind = "bar", ax = axes[2], color = "green")
    axes[0].set_title(f"{feature} Among People Having Heart Disease (Bar)", size = 15)
    axes[1].set_title(f"{feature} Among People Having Heart Disease (Pie)", size = 15)
    axes[2].set_title(f"{feature} Sample Count")
    axes[0].set_xlabel("")
    axes[2].set_xlabel("")
    axes[2].set_ylabel("")
    plt.show()

for categoric_col in categorical_features[:-1]:
    plotting_function(categoric_col)

class Preprocessing:
    def __init__(self, data, categorical_features, numerical_columns, scaling=False, n_splits=None, n_inner=None, broad_encoding=None, smooth=None, smote=False):
        self.smooth = smooth
        self.numerical_columns = numerical_columns
        self.smote = smote
        self.data = data
        self.categorical_features = categorical_features
        self.scaling = scaling
        self.n_splits = n_splits
        self.n_inner = n_inner
        self.broad_encoding = broad_encoding
    def scaling_func(self):
        num_features = [feature for feature in self.data.columns if feature not in self.categorical_features]
        for num_feature in num_features:
            self.data[num_feature] = (self.data[num_feature] - self.data[num_feature].mean()) / self.data[num_feature].std()
    def label_encode(self, broad_encoding=None):
        label_encoding_features = []
        for categorical_feature in self.categorical_features:
            if data[categorical_feature].nunique() == 2:
                label_encoding_features.append(categorical_feature)
        multivariable_features = [x for x in self.categorical_features if x not in label_encoding_features]
        label_encoder_map = {}
        for label_encoding_feature in label_encoding_features:
            le = LabelEncoder()
            le.fit(self.data[label_encoding_feature].values.reshape(-1, 1))
            label_encode_map = dict(zip(le.classes_, le.transform(le.classes_)))
            label_encoder_map[label_encoding_feature] = label_encode_map
            encoded_data = le.transform(self.data[label_encoding_feature])
            self.data[label_encoding_feature] = encoded_data
        if self.broad_encoding == 'OHE':
            self.data = pd.get_dummies(self.data, columns=multivariable_features, drop_first=True)
        elif self.broad_encoding == 'SMOOTH':
            for cat_col in multivariable_features:
                global_mean = self.data['HeartDisease'].mean()
                sample_size = self.data.groupby(cat_col).size()
                grouped_mean = self.data.groupby(cat_col)['HeartDisease'].mean()
                smooth_action = (sample_size * grouped_mean + global_mean * self.smooth) / (sample_size + self.smooth)
                self.data[cat_col] = self.data[cat_col].map(smooth_action)
        elif self.broad_encoding == 'CROSS_VAL':
            for multi_feat in multivariable_features:
                global_mean = self.data['HeartDisease'].mean()
                skf = StratifiedKFold(n_splits=self.n_splits, shuffle=True, random_state=42)
                for train_idx, test_idx in skf.split(self.data[multi_feat], self.data['HeartDisease']):
                    train_data, test_data = (self.data.loc[train_idx], self.data.loc[test_idx])
                    train_data_mean = train_data.mean()
                    grouped_mean_df = pd.DataFrame()
                    skf_inner = StratifiedKFold(n_splits=self.n_inner, shuffle=True, random_state=42)
                    for inner_train_idx, _ in skf_inner.split(train_data[multi_feat], train_data['HeartDisease']):
                        inner_train_data = self.data.loc[inner_train_idx]
                        grouped_mean = inner_train_data.groupby(multi_feat)['HeartDisease'].mean()
                        grouped_mean_df = pd.concat([grouped_mean_df, grouped_mean], axis=1).fillna(train_data_mean)
                    grouped_mean_map = grouped_mean_df.mean(axis=1)
                    self.data.loc[test_idx, multi_feat] = self.data.loc[test_idx, multi_feat].map(grouped_mean_map)
    def smote_upsample(self):
        data = self.data.copy()
        oversampled_numerics_0 = pd.DataFrame()
        oversampled_categorics_0 = pd.DataFrame()
        oversampled_numerics_1 = pd.DataFrame()
        for _ in range(len(data)):
            random = np.random.random()
            if random < 0.5:
                choice_0_numerics = data[data['HeartDisease'] == 'No'][self.numerical_columns].sample(n=1).reset_index(drop=True)
                choice_0_categorics = data[data['HeartDisease'] == 'No'][self.categorical_features].sample(n=1).reset_index(drop=True)
                oversampled_numerics_0 = pd.concat([oversampled_numerics_1, choice_0_numerics])
                oversampled_categorics_0 = pd.concat([oversampled_categorics_0, choice_0_categorics])
            else:
                choice_1_numerics = data[data['HeartDisease'] == 'Yes'][self.numerical_columns].sample(n=1).reset_index(drop=True)
                choice_2_numerics = data[data['HeartDisease'] == 'Yes'][self.numerical_columns].sample(n=1).reset_index(drop=True)
                constant = np.random.random()
                synthetic_numerics = choice_1_numerics * constant + choice_2_numerics * (1 - constant)
                oversampled_numerics_1 = pd.concat([oversampled_numerics_1, synthetic_numerics])
        oversampled_numerics_0.reset_index(drop=True, inplace=True)
        oversampled_numerics_1.reset_index(drop=True, inplace=True)
        oversampled_categorics_0.reset_index(drop=True, inplace=True)
        neighbors = KNeighborsClassifier(n_neighbors=3)
        neighbors.fit(data[self.numerical_columns], data[self.categorical_features[:-1]])
        oversampled_categorics_1 = pd.DataFrame(neighbors.predict(oversampled_numerics_1), columns=self.categorical_features[:-1])
        oversampled_categorics_1['HeartDisease'] = 'Yes'
        oversampled_categorics = pd.concat([oversampled_categorics_0, oversampled_categorics_1])
        oversampled_numerics = pd.concat([oversampled_numerics_0, oversampled_numerics_1])
        oversampled_data = oversampled_numerics.join(oversampled_categorics)
        oversampled_data = shuffle(oversampled_data)
        oversampled_data.reset_index(drop=True, inplace=True)
        del data
        return oversampled_data
    def transform(self):
        if self.smote:
            self.data = self.smote_upsample()
        else:
            pass
        if self.scaling:
            self.scaling_func()
        else:
            pass
        self.label_encode()
        return self.data.copy()

print('Sample Ratio:', int(len(data[data.HeartDisease == 'No']) / len(data[data.HeartDisease == 'Yes'])))
zeros = data[data['HeartDisease'] == 'No'].sample(n=int(len(data) / 50), replace=False)
ones = data[data['HeartDisease'] == 'Yes'].sample(n=int(len(data) / 500), replace=False)
small_data = shuffle(pd.concat([zeros, ones]))
small_data.reset_index(drop=True, inplace=True)
print(small_data.shape)
small_data.head()