├── excel_python ├── hello.py └── sales_data.csv ├── cpp_tutorials ├── bmi.cpp ├── retirement_contributions.cpp └── heapify_code.cpp ├── outlier_detectionpt.py ├── outlier_detectionpt2.py ├── data_cleaning.py ├── outlier_detection.py ├── cluster_analysis.py ├── quartiles_tutorial.py ├── list_tutorial.py ├── classification_performance.py ├── research_sentiment.py ├── classification_loss_functions.py ├── cc_eda.py ├── time_series_analysis.py ├── time_series_forecasting.py ├── model_selection.py ├── portfolio_opt.py ├── optimization_tutorial.py ├── regularization_tutorial.py ├── python_profiling_tutorial.py ├── empty_variables_and_datastructures.py ├── financial_data_analysis.py ├── dimensionality_reduction.py ├── model_explainability.py ├── profiling_debugging_mlworkflow.ipynb ├── python_inheritance.ipynb └── pareto_chart_er_readmission.ipynb /excel_python/hello.py: -------------------------------------------------------------------------------- 1 | print("Hello") 2 | -------------------------------------------------------------------------------- /excel_python/sales_data.csv: -------------------------------------------------------------------------------- 1 | datetime,item,price,units,revenue 2 | 9/1/23,smartphone,1200,15,18000 3 | 9/1/23,laptop,2000,5,10000 4 | 9/1/23,tablet,1500,10,15000 5 | 9/1/23,smart tv,2200,10,22000 6 | 9/2/23,laptop,2000,5,10000 7 | 9/3/23,laptop,2000,5,10000 8 | 9/3/23,wireless earbuds ,150,20,3000 9 | 9/4/23,smart tv,2200,10,22000 10 | 9/4/23,smart tv,2200,10,22000 -------------------------------------------------------------------------------- /cpp_tutorials/bmi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | float BMI_calculator(float weight, float height){ 6 | if (height == 0){ 7 | throw runtime_error("You attempted to calculate BMI with an invalid value of zero for height \n"); 8 | } 9 | return weight/(height*height); 10 | } 11 | 12 | 13 | 14 | 15 | // Main() function: where the execution of program begins 16 | int main() 17 | { 18 | string name; 19 | float weight; 20 | float height; 21 | float bmi; 22 | 23 | cout << "Please enter your name \n"; 24 | cin >> name; 25 | cout << "Hello " << name << ", please enter your weight in Kg\n"; 26 | cin >> weight; 27 | cout << "Thank you " << name << ", now please enter your height in meters \n"; 28 | cin >> height; 29 | 30 | try{ 31 | bmi = BMI_calculator(weight, height); 32 | cout << "Your BMI is: " << bmi <<"\n"; 33 | 34 | } 35 | catch (runtime_error& e){ 36 | cout << "Warning: " << e.what(); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /cpp_tutorials/retirement_contributions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include // std::out_of_range 3 | #include 4 | using namespace std; 5 | 6 | int main(){ 7 | int months; 8 | int current_month = 1; 9 | cout << "Please enter the number of months \n"; 10 | cin >> months; 11 | try{ 12 | std::vector contributions(months); //float contributions[months]; 13 | float initial_contrbution = 100; 14 | float sum_contributions = 0; 15 | contributions[1] = initial_contrbution; 16 | while (current_month <= months){ 17 | contributions[current_month + 1] =1.02*contributions[current_month]; 18 | cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl; 19 | sum_contributions += contributions[current_month]; 20 | current_month++; 21 | } 22 | cout<<"Sum of contributions for " << months << " months is: "< 2 | 3 | using namespace std; 4 | 5 | 6 | void heapify(int array_in[], int array_size, int subtree_root_index) 7 | { 8 | int largest_value = subtree_root_index; 9 | int left = 2*subtree_root_index + 1; 10 | int right = 2*subtree_root_index + 2; 11 | 12 | 13 | if (left < array_size && array_in[left] > array_in[largest_value]){ 14 | largest_value = left; 15 | } 16 | 17 | if (right < array_size && array_in[right] > array_in[largest_value]){ 18 | largest_value = right; 19 | } 20 | 21 | 22 | if (largest_value != subtree_root_index ) 23 | { 24 | swap(array_in[subtree_root_index], array_in[largest_value]); 25 | 26 | heapify(array_in, array_size, largest_value); 27 | } 28 | 29 | 30 | } 31 | 32 | 33 | void construct_heap(int array_in[], int array_size){ 34 | int last_non_leaf_node = (array_size/2) -1; 35 | 36 | for (int subtree_root_index = last_non_leaf_node; subtree_root_index >=0; subtree_root_index -=1) 37 | { 38 | heapify(array_in, array_size, subtree_root_index); 39 | } 40 | 41 | } 42 | 43 | void print_heap(int array_in[], int array_size){ 44 | cout << "Printing values at each node in heap" << endl; 45 | 46 | for (int index = 0; index < array_size; index+=1){ 47 | cout<< array_in[index] << endl; 48 | 49 | } 50 | 51 | } 52 | 53 | 54 | int main(){ 55 | int array_in[] = { 3, 5, 8, 10, 17, 11, 13, 19, 22, 24, 29}; 56 | 57 | int array_size = sizeof(array_in) / sizeof(array_in[0]); 58 | 59 | construct_heap(array_in, array_size); 60 | 61 | print_heap(array_in, array_size); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /outlier_detectionpt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Fri Apr 22 12:14:38 2022 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | 10 | df = pd.read_csv("creditcard_downsampled5000.csv") 11 | 12 | 13 | pd.set_option('display.max_columns', None) 14 | pd.set_option('display.max_rows', None) 15 | 16 | # df = df.sample(30000, random_state=42) 17 | 18 | # df.to_csv("creditcard_downsampled5000.csv", index=False) 19 | print(df.head()) 20 | 21 | 22 | import seaborn as sns 23 | import matplotlib.pyplot as plt 24 | 25 | sns.set() 26 | 27 | sns.boxplot(y = df['V14']) 28 | plt.show() 29 | 30 | 31 | 32 | Q1=df['V13'].quantile(0.25) 33 | print("Q1:", Q1) 34 | 35 | Q3=df['V13'].quantile(0.75) 36 | print("Q3:", Q3) 37 | 38 | IQR=Q3-Q1 39 | print("IQR: ", IQR) 40 | 41 | lower_bound = Q1 - 1.5*IQR 42 | print("Lower Bound:", lower_bound) 43 | 44 | upper_bound = Q3 + 1.5*IQR 45 | print("Upper Bound:", upper_bound) 46 | 47 | df_clean = df[(df['V13']>lower_bound)&(df['V13']lower_bound)&(df['V13'] df_bad['AGE'].mean() else x for x in list(df_bad['AGE']) ] 35 | 36 | df_bad['AGE'] = pd.to_numeric(df_bad['AGE'], errors = 'coerce') 37 | print(df_bad['AGE'].mean()) 38 | 39 | 40 | 41 | from scipy import stats 42 | import numpy as np 43 | print("Length before removing RM outlier:", len(df_bad)) 44 | df_bad['RM_zscore'] = np.abs(stats.zscore(df['RM'])) 45 | df_clean1 = df_bad[df_bad['RM_zscore']< 3] 46 | print("Length after removing RM outlier:", len(df_clean1)) 47 | 48 | 49 | def remove_outliers(column_name, df_in): 50 | print(f"Length before removing {column_name} outlier:", len(df_in)) 51 | df_in[f'{column_name}_zscore'] = np.abs(stats.zscore(df_in[f'{column_name}'])) 52 | df_clean = df_in[df_in[f'{column_name}_zscore']< 3] 53 | print(f"Length after removing {column_name} outlier:", len(df_clean)) 54 | return df_clean 55 | 56 | df1 = remove_outliers('DIS', df_bad) 57 | 58 | -------------------------------------------------------------------------------- /outlier_detection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Aug 18 17:32:26 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | 10 | df = pd.read_csv("banknotes.csv") 11 | 12 | 13 | print(df.head()) 14 | 15 | from collections import Counter 16 | import seaborn as sns 17 | import matplotlib.pyplot as plt 18 | 19 | def boxplot(column): 20 | sns.boxplot(data=df,x=df[f"{column}"]) 21 | plt.title(f"Boxplot of Swiss Banknote {column}") 22 | plt.show() 23 | 24 | df_outlier1 = df[df['Length']> 216].copy() 25 | print(Counter(df_outlier1['conterfeit'])) 26 | 27 | 28 | 29 | 30 | df_outlier2 = df[df['Length']> 215.5].copy() 31 | print(Counter(df_outlier2['conterfeit'])) 32 | 33 | 34 | boxplot('Length') 35 | boxplot('Right') 36 | boxplot('Left') 37 | boxplot('Bottom') 38 | boxplot('Top') 39 | boxplot('Diagonal') 40 | 41 | 42 | df_outlier3 = df[(df['Length']> 215)&(df['Right']> 130)&(df['Left']> 130)&(df['Bottom']> 10)].copy() 43 | print(Counter(df_outlier3['conterfeit'])) 44 | print(Counter(df['conterfeit'])) 45 | 46 | from sklearn.ensemble import IsolationForest 47 | from sklearn.model_selection import train_test_split 48 | from sklearn.metrics import precision_score 49 | from sklearn.svm import OneClassSVM 50 | 51 | X = df[['Length', 'Left', 'Right', 'Bottom', 'Top', 'Diagonal']] 52 | y = df['conterfeit'] 53 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) 54 | 55 | clf = IsolationForest(random_state=0) 56 | clf.fit(X_train) 57 | y_pred = clf.predict(X_test) 58 | 59 | import numpy as np 60 | pred = pd.DataFrame({'pred': y_pred}) 61 | pred['y_pred'] = np.where(pred['pred'] == -1, 1, 0) 62 | 63 | y_pred = pred['y_pred'] 64 | print("Precision:", precision_score(y_test, y_pred)) 65 | 66 | 67 | 68 | clf_svm = OneClassSVM(gamma='auto') 69 | clf_svm.fit(X_train) 70 | y_pred_svm = clf_svm.predict(X_test) 71 | 72 | pred['svm'] = y_pred_svm 73 | pred['svm_pred'] = np.where(pred['svm'] == -1, 1, 0) 74 | 75 | y_pred_svm = pred['svm_pred'] 76 | print("SVM Precision:", precision_score(y_test, y_pred_svm)) 77 | -------------------------------------------------------------------------------- /cluster_analysis.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | 5 | from sklearn.cluster import KMeans 6 | 7 | df = pd.read_csv("Mall_Customers.csv") 8 | 9 | X = df[['Age', 'Spending Score (1-100)']] 10 | wcss = [] 11 | 12 | 13 | for i in range(1, 11): 14 | kmeans = KMeans(n_clusters=i, init='k-means++', max_iter=500, n_init=20, random_state=0) 15 | kmeans.fit(X) 16 | wcss.append(kmeans.inertia_) 17 | 18 | 19 | sns.set() 20 | 21 | plt.plot(range(1, 11), wcss) 22 | plt.title('Selecting the Numbeer of Clusters using the Elbow Method') 23 | plt.xlabel('Clusters') 24 | plt.ylabel('WCSS') 25 | plt.show() 26 | 27 | 28 | 29 | kmeans = KMeans(n_clusters=4, init='k-means++', max_iter=500, n_init=20, random_state=0) 30 | y_pred = kmeans.fit_predict(X) 31 | plt.scatter(X['Age'], X['Spending Score (1-100)']) 32 | plt.ylabel("Spending Score") 33 | plt.xlabel("Age") 34 | plt.title("Clusters found by KMeans") 35 | plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], s=300, c='black') 36 | plt.show() 37 | 38 | 39 | from sklearn.mixture import GaussianMixture 40 | n_clusters = 5 41 | gmm_model = GaussianMixture(n_components=n_clusters, random_state=5) 42 | gmm_model.fit(X) 43 | 44 | 45 | cluster_labels = gmm_model.predict(X) 46 | X = pd.DataFrame(X) 47 | X['cluster'] = cluster_labels 48 | 49 | 50 | color=['blue','green','red', 'black', 'yellow'] 51 | for k in range(0,n_clusters): 52 | data = X[X["cluster"]==k].copy() 53 | plt.scatter(data["Age"],data["Spending Score (1-100)"],c=color[k]) 54 | 55 | plt.title("Clusters Identified by Guassian Mixture Model") 56 | plt.ylabel("Spending Score (1-100)") 57 | plt.xlabel("Age") 58 | plt.show() 59 | 60 | 61 | from sklearn.cluster import SpectralClustering 62 | 63 | spectral_cluster_model= SpectralClustering( 64 | n_clusters=5, 65 | random_state=64, 66 | n_neighbors=20, 67 | affinity='nearest_neighbors' 68 | ) 69 | 70 | 71 | X['cluster'] = spectral_cluster_model.fit_predict(X) 72 | 73 | 74 | fig, ax = plt.subplots() 75 | sns.scatterplot(x='Age', y='Spending Score (1-100)', data=X, hue='cluster', ax=ax) 76 | ax.set(title='Spectral Clustering') 77 | -------------------------------------------------------------------------------- /quartiles_tutorial.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | df = pd.read_csv('telco_churn.csv') 4 | 5 | print(df.head()) 6 | 7 | print("First Quartile (Q1) for Tenure: ", df['tenure'].quantile(0.25)) 8 | print("Second Quartile (Q2) for Tenure: ", df['tenure'].quantile(0.50)) 9 | print("Third Quartile (Q3) for Tenure: ", df['tenure'].quantile(0.75)) 10 | 11 | print("Third Quartile (Q3) for Tenure: ", df['tenure'].quantile(0.75)) 12 | 13 | 14 | 15 | print("Ninth Decile for Tenure: ", df['tenure'].quantile(0.9)) 16 | 17 | df_dsl = df[df['InternetService'] == 'DSL'] 18 | df_fiberoptic = df[df['InternetService'] == 'Fiber optic'] 19 | 20 | 21 | print("Third Quartile (Q3) for Tenure - DSL: ", df_dsl['tenure'].quantile(0.75)) 22 | print("Third Quartile (Q3) for Tenure - Fiber Optic: ", df_fiberoptic['tenure'].quantile(0.75)) 23 | 24 | print("Ninth Decile for Tenure - DSL: ", df_dsl['tenure'].quantile(0.9)) 25 | print("Ninth Decile for Tenure - Fiber Optic: ", df_fiberoptic['tenure'].quantile(0.9)) 26 | 27 | 28 | df_churn_yes = df[df['Churn'] == 'Yes'] 29 | df_churn_no = df[df['Churn'] == 'No'] 30 | 31 | 32 | print("Third Quartile (Q3) for Tenure - Churn: ", df_churn_yes['tenure'].quantile(0.75)) 33 | print("Third Quartile (Q3) for Tenure - No Churn: ", df_churn_no['tenure'].quantile(0.75)) 34 | 35 | 36 | print("Third Quartile (Q3) for Tenure - Churn: ", df_churn_yes['MonthlyCharges'].quantile(0.75)) 37 | print("Third Quartile (Q3) for Tenure - No Churn: ", df_churn_no['MonthlyCharges'].quantile(0.75)) 38 | 39 | 40 | import numpy as np 41 | 42 | 43 | print("Numpy Third Quartile (Q3) for Tenure - Churn: ", np.percentile(df_churn_yes['MonthlyCharges'], 75)) 44 | 45 | import seaborn as sns 46 | import matplotlib.pyplot as plt 47 | sns.set() 48 | 49 | sns.boxplot(df['tenure']) 50 | plt.show() 51 | 52 | 53 | from collections import Counter 54 | def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit): 55 | 56 | keys = [] 57 | for i in dict(Counter(df[categorical_column].values).most_common(limit)): 58 | keys.append(i) 59 | 60 | df_new = df[df[categorical_column].isin(keys)] 61 | sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column]) 62 | plt.show() 63 | 64 | 65 | get_boxplot_of_categories(df, 'Churn', 'tenure', 5) 66 | 67 | get_boxplot_of_categories(df, 'Churn', 'MonthlyCharges', 5) 68 | 69 | -------------------------------------------------------------------------------- /list_tutorial.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed May 25 18:59:33 2022 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | import numpy as np 10 | 11 | 12 | tech_company_names = ['Facebook', 'Apple', 'Amazon', 'Netflix', 'Google'] 13 | 14 | tech_company_employees = [58604, 147000, 950000, 11300, 135301] 15 | 16 | tech_company_revenue = [117, 378, 470, 30, 257] 17 | 18 | 19 | tech_company_employee_bool = [x > 60000 for x in tech_company_employees ] 20 | 21 | 22 | sort_company = sorted(tech_company_names) 23 | sort_employee = sorted(tech_company_employees) 24 | 25 | print(sort_company) 26 | print(sort_employee) 27 | 28 | new_company_info = ['Microsoft', 163000, 877, True] 29 | 30 | tech_company_names.append(new_company_info[0]) 31 | tech_company_employees.append(new_company_info[1]) 32 | tech_company_revenue.append(new_company_info[2]) 33 | tech_company_employee_bool.append(new_company_info[3]) 34 | 35 | 36 | print('Company: ', tech_company_names) 37 | print('Employees: ', tech_company_employees) 38 | print("Revenue: ", tech_company_revenue) 39 | print("Employee_threshold: ", tech_company_employee_bool) 40 | 41 | 42 | mu, sigma = 80, 40 43 | n_values = len(tech_company_names) 44 | np.random.seed(21) 45 | net_income_normal = np.random.normal(mu, sigma, n_values) 46 | print(net_income_normal) 47 | 48 | np.random.seed(64) 49 | net_income_fat_tail = np.random.gumbel(mu, sigma, n_values) 50 | print(net_income_fat_tail) 51 | 52 | 53 | company_data_dict = {'company_name': tech_company_names, 54 | 'number_of_employees': tech_company_employees, 55 | 'company_revenue': tech_company_revenue, 56 | 'employee_threshold': tech_company_employee_bool, 57 | 'net_income_normal': list(net_income_normal), 58 | 'net_income_fat_tail': list(net_income_fat_tail)} 59 | 60 | print(company_data_dict) 61 | 62 | import json 63 | with open('company_data.json', 'w') as fp: 64 | json.dump(company_data_dict, fp) 65 | 66 | f = open('company_data.json') 67 | company_json = json.loads(f.read()) 68 | print(company_json) 69 | 70 | import pandas as pd 71 | company_df = pd.DataFrame(company_data_dict) 72 | print(company_df) 73 | 74 | company_df.to_csv("comapany_csv_file.csv", index=False) 75 | 76 | read_company_df = pd.read_csv("comapany_csv_file.csv") 77 | print(read_company_df) 78 | -------------------------------------------------------------------------------- /classification_performance.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from sklearn.model_selection import train_test_split 4 | from sklearn.linear_model import LogisticRegression 5 | from sklearn.metrics import accuracy_score 6 | from sklearn.metrics import confusion_matrix 7 | from sklearn.metrics import roc_curve, roc_auc_score 8 | 9 | df = pd.read_csv('telco_churn.csv') 10 | 11 | print(df.head()) 12 | 13 | print(len(df.columns)) 14 | print(len(df)) 15 | df['Churn'] = np.where(df['Churn'] == 'Yes', 1, 0) 16 | 17 | 18 | 19 | X = df[['tenure', 'MonthlyCharges']] 20 | y = df['Churn'] 21 | 22 | 23 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) 24 | 25 | 26 | clf_model = LogisticRegression() 27 | clf_model.fit(X_train, y_train) 28 | y_pred = clf_model.predict(X_test) 29 | 30 | print("Accuracy: ", accuracy_score(y_test, y_pred)) 31 | 32 | 33 | 34 | conmat = confusion_matrix(y_test, y_pred) 35 | print(conmat) 36 | val = np.mat(conmat) 37 | print(val) 38 | 39 | classnames = list(set(y_train)) 40 | df_cm = pd.DataFrame( 41 | val, index=classnames, columns=classnames, 42 | ) 43 | 44 | print(df_cm) 45 | 46 | 47 | print(len(y_test)) 48 | 49 | from collections import Counter 50 | 51 | print(Counter(y_test)) 52 | 53 | 54 | 55 | import matplotlib.pyplot as plt 56 | import seaborn as sns 57 | df_cm = df_cm.astype('float') / df_cm.sum(axis=1)[:, np.newaxis] 58 | plt.figure() 59 | heatmap = sns.heatmap(df_cm, annot=True, cmap="Blues", fmt='g') 60 | heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha='right') 61 | heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha='right') 62 | plt.ylabel('True label') 63 | plt.xlabel('Predicted label') 64 | plt.title('Churn Logistic Regression Model Results') 65 | plt.show() 66 | 67 | y_pred_proba = clf_model.predict_proba(np.array(X_test))[:,1] 68 | 69 | fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba) 70 | 71 | 72 | sns.set() 73 | plt.plot(fpr, tpr) 74 | plt.plot(fpr, fpr, linestyle = '--', color = 'k') 75 | plt.xlabel('False positive rate') 76 | plt.ylabel('True positive rate') 77 | AUROC = np.round(roc_auc_score(y_test, y_pred_proba), 2) 78 | plt.title(f'Logistic Regression Model ROC curve; AUROC: {AUROC}'); 79 | plt.show() 80 | 81 | 82 | 83 | from sklearn.metrics import precision_recall_curve 84 | from sklearn.metrics import average_precision_score 85 | 86 | average_precision = average_precision_score(y_test, y_pred_proba) 87 | precision, recall, thresholds = precision_recall_curve(y_test, y_pred_proba) 88 | plt.plot(recall, precision, marker='.', label='Logistic') 89 | plt.xlabel('Recall') 90 | plt.ylabel('Precision') 91 | plt.legend() 92 | plt.title(f'Precision Recall Curve. AUPRC: {average_precision}') 93 | plt.show() 94 | 95 | -------------------------------------------------------------------------------- /research_sentiment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Fri Apr 23 11:58:01 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | import pandas as pd 10 | import numpy as np 11 | from collections import Counter 12 | 13 | 14 | pd.set_option('display.max_columns', None) 15 | pd.set_option('display.max_rows', None) 16 | 17 | 18 | df_new = pd.read_csv("covid.csv") 19 | 20 | 21 | 22 | df_new.dropna(inplace=True) 23 | 24 | 25 | print(Counter(df_new['journal']).most_common(100)) 26 | 27 | 28 | df_plos = df_new[df_new['journal'] == 'PLoS One'].copy() 29 | print(df_plos.head()) 30 | df_infect = df_new[df_new['journal'].str.contains('Infect Dis', regex=False)].copy() 31 | df_microbial = df_new[df_new['journal'].str.contains('Microbial', regex=False)].copy() 32 | 33 | df_abstract_microbiome = df_new[df_new['abstract'].str.contains('microbiome', regex=False)].copy() 34 | print("Number of Microbiome Studies: ", len(df_abstract_microbiome)) 35 | 36 | 37 | print(df_abstract_microbiome.head()) 38 | 39 | df_abstract_microbiome['publish_time'] = pd.to_datetime(df_abstract_microbiome['publish_time'], format='%Y/%m/%d') 40 | df_abstract_microbiome['year'] = df_abstract_microbiome['publish_time'].dt.year 41 | print(df_abstract_microbiome.head()) 42 | print(set(df_abstract_microbiome['year'])) 43 | print(df_abstract_microbiome['abstract'].iloc[20]) 44 | 45 | from textblob import TextBlob 46 | df_abstract_microbiome['abstract_sentiment'] = df_abstract_microbiome['abstract'].apply(lambda abstract: TextBlob(abstract).sentiment.polarity) 47 | print(df_abstract_microbiome.head()) 48 | 49 | 50 | df_micro_group = df_abstract_microbiome.groupby(['year'])['abstract_sentiment'].mean() 51 | 52 | 53 | import matplotlib.pyplot as plt 54 | import seaborn as sns 55 | 56 | sns.set() 57 | plt.xlabel('Year') 58 | plt.ylabel('Sentiment') 59 | plt.title('Research Sentiment in Gut Microbiome Studies') 60 | plt.plot(df_micro_group.index, df_micro_group.values) 61 | #plt.show() 62 | 63 | 64 | 65 | df_plos['publish_time'] = pd.to_datetime(df_plos['publish_time'], format='%Y/%m/%d') 66 | df_plos['year'] = df_plos['publish_time'].dt.year 67 | df_plos['abstract_sentiment'] = df_plos['abstract'].apply(lambda abstract: TextBlob(abstract).sentiment.polarity) 68 | 69 | 70 | 71 | df_plos_group = df_plos.groupby(['year'])['abstract_sentiment'].mean() 72 | 73 | 74 | import matplotlib.pyplot as plt 75 | import seaborn as sns 76 | 77 | sns.set() 78 | plt.xlabel('Year') 79 | plt.ylabel('Sentiment') 80 | #plt.title('Research Sentiment in PLoS One Publications') 81 | plt.plot(df_plos_group.index, df_plos_group.values) 82 | 83 | 84 | 85 | df_nature = df_new[df_new['journal'] == 'Nature'].copy() 86 | 87 | df_nature['publish_time'] = pd.to_datetime(df_nature['publish_time'], format='%Y/%m/%d') 88 | df_nature['year'] = df_nature['publish_time'].dt.year 89 | df_nature['abstract_sentiment'] = df_nature['abstract'].apply(lambda abstract: TextBlob(abstract).sentiment.polarity) 90 | 91 | 92 | 93 | df_nature = df_nature.groupby(['year'])['abstract_sentiment'].mean() 94 | 95 | 96 | import matplotlib.pyplot as plt 97 | import seaborn as sns 98 | 99 | sns.set() 100 | plt.xlabel('Year') 101 | plt.ylabel('Sentiment') 102 | plt.title('Research Sentiment in Publications') 103 | plt.plot(df_nature.index, df_nature.values) 104 | -------------------------------------------------------------------------------- /classification_loss_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Jan 4 17:40:26 2022 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | import numpy as np 10 | from tensorflow.keras.layers import Dense, Conv2D, MaxPooling2D, Flatten 11 | from tensorflow.keras.models import Sequential 12 | from sklearn.model_selection import train_test_split 13 | from tensorflow.keras.datasets import mnist 14 | import matplotlib.pyplot as plt 15 | 16 | df = pd.read_csv('telco_churn.csv') 17 | 18 | print(df.head()) 19 | 20 | 21 | 22 | df['Churn'] = np.where(df['Churn'] == 'Yes', 1, 0) 23 | 24 | 25 | 26 | 27 | def convert_categories(cat_list): 28 | for col in cat_list: 29 | df[col] = df[col].astype('category') 30 | df[f'{col}_cat'] = df[f'{col}'].cat.codes 31 | 32 | 33 | category_list = ['gender', 'Partner', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService', 34 | 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 35 | 'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod'] 36 | 37 | convert_categories(category_list) 38 | df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce') 39 | df['TotalCharges'].fillna(0, inplace=True) 40 | 41 | cols = ['gender_cat', 'Partner_cat', 'Dependents_cat', 'PhoneService_cat', 'MultipleLines_cat', 'InternetService_cat', 42 | 'OnlineSecurity_cat', 'OnlineBackup_cat', 'DeviceProtection_cat', 'TechSupport_cat', 'StreamingTV_cat', 43 | 'StreamingMovies_cat', 'Contract_cat', 'PaperlessBilling_cat', 'PaymentMethod_cat','MonthlyCharges', 44 | 'TotalCharges', 'SeniorCitizen'] 45 | 46 | X = df[cols] 47 | 48 | y= df['Churn'] 49 | 50 | 51 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33) 52 | 53 | model_bce = Sequential() 54 | model_bce.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu')) 55 | model_bce.add(Dense(32, activation='relu')) 56 | model_bce.add(Dense(32, activation='relu')) 57 | model_bce.add(Dense(32, activation='relu')) 58 | model_bce.add(Dense(1, activation='softmax')) 59 | model_bce.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy']) 60 | model_bce.fit(X_train, y_train,epochs =10) 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | (X_train_mnist, y_train_mnist), (X_test_mnist, y_test_mnist) = mnist.load_data() 69 | 70 | 71 | 72 | plt.imshow(X_train_mnist[0]) 73 | plt.show() 74 | 75 | 76 | plt.imshow(X_train_mnist[1]) 77 | plt.show() 78 | 79 | plt.imshow(X_train_mnist[4]) 80 | plt.show() 81 | 82 | X_train_mnist = X_train_mnist.reshape((X_train_mnist.shape[0], 28, 28, 1)) 83 | X_test_mnist = X_test_mnist.reshape((X_test_mnist.shape[0], 28, 28, 1)) 84 | 85 | y_train_mnist = np.where(y_train_mnist == 9, 1, 0) 86 | y_test_mnist = np.where(y_test_mnist == 9, 1, 0) 87 | 88 | 89 | model_cce = Sequential() 90 | model_cce.add(Conv2D(16, (3, 3), activation='relu', kernel_initializer='normal', input_shape=(28, 28, 1))) 91 | model_cce.add(MaxPooling2D((2, 2))) 92 | model_cce.add(Flatten()) 93 | model_cce.add(Dense(16, activation='relu', kernel_initializer='normal')) 94 | model_cce.add(Dense(2, activation='softmax')) 95 | model_cce.compile(optimizer = 'SGD',loss='sparse_categorical_crossentropy', metrics =['accuracy']) 96 | model_cce.fit(X_train_mnist, y_train_mnist, epochs =5) 97 | -------------------------------------------------------------------------------- /cc_eda.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import seaborn as sns 4 | import matplotlib.pyplot as plt 5 | import plotly.express as px 6 | 7 | 8 | 9 | df = pd.read_csv("synthetic_transaction_data_Dining.csv") 10 | 11 | 12 | df['log_transaction_amnt'] = np.log(df['transaction_amount']) 13 | 14 | print(df.head()) 15 | 16 | 17 | df['transaction_date'] = pd.to_datetime(df['transaction_date']) 18 | df= df[df['merchant_name'] == "Cheesecake Factory"] 19 | 20 | z_scores = np.abs((df['transaction_amount'] - df['transaction_amount'].mean()) / df['transaction_amount'].std()) 21 | # Define a threshold (e.g., Z-score > 3) to identify outliers 22 | threshold = 3 23 | # Remove outliers from the DataFrame 24 | df = df[z_scores < threshold] 25 | 26 | 27 | df['year'] = df['transaction_date'].dt.year 28 | df['year'] = df['year'].astype(str) 29 | df['month'] = df['transaction_date'].dt.month 30 | df = df[df['month'] <= 12] 31 | df = df[df['month'] >= 1] 32 | df['month'] = df['month'].astype(str) 33 | df['month_year'] = df['year'] + "-"+ df['month'] 34 | df['month_year'] = pd.to_datetime(df['month_year']) 35 | 36 | 37 | df_grouped = df.groupby('month_year')['transaction_amount'].sum().reset_index() 38 | df_grouped = df_grouped.set_index('month_year').sort_index() 39 | df_grouped.index = pd.to_datetime(df_grouped.index) 40 | plt.plot(df_grouped.index, df_grouped['transaction_amount']) 41 | 42 | 43 | data = df[['transaction_amount', 'log_transaction_amnt']] 44 | sns.pairplot(data) 45 | plt.show() 46 | 47 | state_abbreviations = { 48 | 'Alabama': 'AL', 49 | 'Alaska': 'AK', 50 | 'Arizona': 'AZ', 51 | 'Arkansas': 'AR', 52 | 'California': 'CA', 53 | 'Colorado': 'CO', 54 | 'Connecticut': 'CT', 55 | 'Delaware': 'DE', 56 | 'Florida': 'FL', 57 | 'Georgia': 'GA', 58 | 'Hawaii': 'HI', 59 | 'Idaho': 'ID', 60 | 'Illinois': 'IL', 61 | 'Indiana': 'IN', 62 | 'Iowa': 'IA', 63 | 'Kansas': 'KS', 64 | 'Kentucky': 'KY', 65 | 'Louisiana': 'LA', 66 | 'Maine': 'ME', 67 | 'Maryland': 'MD', 68 | 'Massachusetts': 'MA', 69 | 'Michigan': 'MI', 70 | 'Minnesota': 'MN', 71 | 'Mississippi': 'MS', 72 | 'Missouri': 'MO', 73 | 'Montana': 'MT', 74 | 'Nebraska': 'NE', 75 | 'Nevada': 'NV', 76 | 'New Hampshire': 'NH', 77 | 'New Jersey': 'NJ', 78 | 'New Mexico': 'NM', 79 | 'New York': 'NY', 80 | 'North Carolina': 'NC', 81 | 'North Dakota': 'ND', 82 | 'Ohio': 'OH', 83 | 'Oklahoma': 'OK', 84 | 'Oregon': 'OR', 85 | 'Pennsylvania': 'PA', 86 | 'Rhode Island': 'RI', 87 | 'South Carolina': 'SC', 88 | 'South Dakota': 'SD', 89 | 'Tennessee': 'TN', 90 | 'Texas': 'TX', 91 | 'Utah': 'UT', 92 | 'Vermont': 'VT', 93 | 'Virginia': 'VA', 94 | 'Washington': 'WA', 95 | 'West Virginia': 'WV', 96 | 'Wisconsin': 'WI', 97 | 'Wyoming': 'WY' 98 | } 99 | 100 | 101 | df['merchant_state_abbr'] = df['merchant_state'].map(state_abbreviations) 102 | state_counts = df.groupby('merchant_state_abbr')['cardholder_name'].nunique().reset_index() 103 | state_counts.columns = ['State', 'Customer_Count'] 104 | 105 | fig = px.choropleth(state_counts, locations='State', locationmode='USA-states', 106 | color='Customer_Count', scope='usa', 107 | color_continuous_scale='Blues', 108 | title='Number of Customers by State') 109 | 110 | fig.show() 111 | 112 | 113 | -------------------------------------------------------------------------------- /time_series_analysis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Jul 14 13:37:58 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | 10 | import pandas as pd 11 | import matplotlib.pyplot as plt 12 | import seaborn as sns 13 | from statsmodels.tsa.stattools import adfuller 14 | from statsmodels.tsa.seasonal import seasonal_decompose 15 | 16 | df = pd.read_csv("AirPassengers.csv") 17 | 18 | print(df.head()) 19 | 20 | print(df.tail()) 21 | 22 | df['Month'] = pd.to_datetime(df['Month'], format='%Y-%m') 23 | 24 | 25 | 26 | df.index = df['Month'] 27 | del df['Month'] 28 | print(df.head()) 29 | 30 | 31 | # sns.lineplot(data=df) 32 | # plt.ylabel("Number of Passengers") 33 | # plt.show() 34 | 35 | rolling_mean = df.rolling(7).mean() 36 | rolling_std = df.rolling(7).std() 37 | 38 | 39 | plt.plot(df, color="blue",label="Original Passenger Data") 40 | plt.plot(rolling_mean, color="red", label="Rolling Mean #Passenger") 41 | plt.plot(rolling_std, color="black", label = "Rolling Standard Deviation in #Passenger") 42 | plt.title("Passenger Time Series, Rolling Mean, Standard Deviation") 43 | plt.legend(loc="best") 44 | plt.show() 45 | 46 | adft = adfuller(df,autolag="AIC") 47 | 48 | output_df = pd.DataFrame({"Values":[adft[0],adft[1],adft[2],adft[3], adft[4]['1%'], adft[4]['5%'], adft[4]['10%']] , "Metric":["Test Statistics","p-value","No. of lags used","Number of observations used", 49 | "critical value (1%)", "critical value (5%)", "critical value (10%)"]}) 50 | print(output_df) 51 | 52 | 53 | autocorrelation_lag1 = df['#Passengers'].autocorr(lag=1) 54 | print("One Month Lag: ", autocorrelation_lag1) 55 | 56 | autocorrelation_lag3 = df['#Passengers'].autocorr(lag=3) 57 | print("Three Month Lag: ", autocorrelation_lag3) 58 | 59 | autocorrelation_lag6 = df['#Passengers'].autocorr(lag=6) 60 | print("Six Month Lag: ", autocorrelation_lag6) 61 | 62 | autocorrelation_lag9 = df['#Passengers'].autocorr(lag=9) 63 | print("Nine Month Lag: ", autocorrelation_lag9) 64 | 65 | 66 | decompose = seasonal_decompose(df['#Passengers'],model='additive', period=7) 67 | decompose.plot() 68 | plt.show() 69 | df['Date'] = df.index 70 | 71 | train = df[df['Date'] < pd.to_datetime("1960-08", format='%Y-%m')] 72 | train['train'] = train['#Passengers'] 73 | del train['Date'] 74 | del train['#Passengers'] 75 | test = df[df['Date'] >= pd.to_datetime("1960-08", format='%Y-%m')] 76 | del test['Date'] 77 | test['test'] = test['#Passengers'] 78 | del test['#Passengers'] 79 | plt.plot(train, color = "black") 80 | plt.plot(test, color = "red") 81 | plt.title("Train/Test split for Passenger Data") 82 | plt.ylabel("Passenger Number") 83 | plt.xlabel('Year-Month') 84 | sns.set() 85 | plt.show() 86 | 87 | 88 | 89 | 90 | from pmdarima.arima import auto_arima 91 | model = auto_arima(train, trace=True, error_action='ignore', suppress_warnings=True) 92 | model.fit(train) 93 | forecast = model.predict(n_periods=len(test)) 94 | forecast = pd.DataFrame(forecast,index = test.index,columns=['Prediction']) 95 | 96 | 97 | 98 | plt.plot(train, label='Train') 99 | plt.plot(test, label='Test') 100 | plt.plot(forecast, label='Prediction') 101 | plt.title('#Passenger Prediction') 102 | plt.xlabel('Date') 103 | plt.ylabel('Actual #Passenger') 104 | plt.legend(loc='upper left', fontsize=8) 105 | plt.show() 106 | 107 | 108 | 109 | from math import sqrt 110 | from sklearn.metrics import mean_squared_error 111 | print("RMSE: ", rms) 112 | -------------------------------------------------------------------------------- /time_series_forecasting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Sep 28 11:11:06 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | import pandas_datareader as web 10 | import datetime 11 | 12 | import matplotlib.pyplot as plt 13 | import seaborn as sns 14 | 15 | from statsmodels.tsa.statespace.sarimax import SARIMAX 16 | from statsmodels.tsa.arima.model import ARIMA 17 | pd.set_option('display.max_columns', None) 18 | pd.set_option('display.max_rows', None) 19 | 20 | 21 | 22 | 23 | # btc = web.get_data_yahoo(['BTC-USD'], start=datetime.datetime(2018, 1, 1), end=datetime.datetime(2020, 12, 2)) 24 | 25 | # btc = btc['Close'] 26 | 27 | # btc.to_csv("btc.csv") 28 | 29 | btc = pd.read_csv("btc.csv") 30 | 31 | 32 | btc.index = pd.to_datetime(btc['Date'], format='%Y-%m-%d') 33 | del btc['Date'] 34 | 35 | print(btc.head()) 36 | sns.set() 37 | plt.ylabel('BTC Price') 38 | plt.xlabel('Date') 39 | plt.xticks(rotation=45) 40 | plt.plot(btc.index, btc['BTC-USD'], ) 41 | plt.show() 42 | 43 | train = btc[btc.index < pd.to_datetime("2020-11-01", format='%Y-%m-%d')] 44 | test = btc[btc.index >= pd.to_datetime("2020-11-01", format='%Y-%m-%d')] 45 | print(test) 46 | plt.plot(train, color = "black", label = 'Training') 47 | plt.plot(test, color = "red", label = 'Testing') 48 | plt.ylabel('BTC Price') 49 | plt.xlabel('Date') 50 | plt.xticks(rotation=45) 51 | plt.title("Train/Test split for BTC Data") 52 | 53 | y = train['BTC-USD'] 54 | 55 | ARMAmodel = SARIMAX(y, order = (1, 0, 1)) 56 | ARMAmodel = ARMAmodel.fit() 57 | 58 | y_pred = ARMAmodel.get_forecast(len(test.index)) 59 | y_pred_df = y_pred.conf_int(alpha = 0.05) 60 | y_pred_df["Predictions"] = ARMAmodel.predict(start = y_pred_df.index[0], end = y_pred_df.index[-1]) 61 | y_pred_df.index = test.index 62 | y_pred_out = y_pred_df["Predictions"] 63 | plt.plot(y_pred_out, color='green', label = 'ARMA Predictions') 64 | plt.legend() 65 | 66 | 67 | import numpy as np 68 | from sklearn.metrics import mean_squared_error 69 | 70 | arma_rmse = np.sqrt(mean_squared_error(test["BTC-USD"].values, y_pred_df["Predictions"])) 71 | print("ARMA RMSE: ",arma_rmse) 72 | 73 | 74 | 75 | 76 | ARIMAmodel = ARIMA(y, order = (5, 4, 2)) 77 | ARIMAmodel = ARIMAmodel.fit() 78 | 79 | y_pred = ARIMAmodel.get_forecast(len(test.index)) 80 | y_pred_df = y_pred.conf_int(alpha = 0.05) 81 | y_pred_df["Predictions"] = ARIMAmodel.predict(start = y_pred_df.index[0], end = y_pred_df.index[-1]) 82 | y_pred_df.index = test.index 83 | y_pred_out = y_pred_df["Predictions"] 84 | plt.plot(y_pred_out, color='Yellow', label = 'ARIMA Predictions') 85 | plt.legend() 86 | 87 | 88 | import numpy as np 89 | from sklearn.metrics import mean_squared_error 90 | 91 | arma_rmse = np.sqrt(mean_squared_error(test["BTC-USD"].values, y_pred_df["Predictions"])) 92 | print("ARIMA RMSE: ",arma_rmse) 93 | 94 | 95 | 96 | SARIMAXmodel = SARIMAX(y, order = (5, 4, 2), seasonal_order=(2,2,2,12)) 97 | SARIMAXmodel = SARIMAXmodel.fit() 98 | 99 | y_pred = SARIMAXmodel.get_forecast(len(test.index)) 100 | y_pred_df = y_pred.conf_int(alpha = 0.05) 101 | y_pred_df["Predictions"] = SARIMAXmodel.predict(start = y_pred_df.index[0], end = y_pred_df.index[-1]) 102 | y_pred_df.index = test.index 103 | y_pred_out = y_pred_df["Predictions"] 104 | plt.plot(y_pred_out, color='Blue', label = 'SARIMA Predictions') 105 | plt.legend() 106 | 107 | 108 | import numpy as np 109 | from sklearn.metrics import mean_squared_error 110 | 111 | arma_rmse = np.sqrt(mean_squared_error(test["BTC-USD"].values, y_pred_df["Predictions"])) 112 | print("SARIMA RMSE: ",arma_rmse) 113 | -------------------------------------------------------------------------------- /model_selection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Jun 16 13:11:36 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | import pandas as pd 10 | import numpy as np 11 | from sklearn.model_selection import KFold 12 | from sklearn.model_selection import LeaveOneOut 13 | from sklearn.ensemble import RandomForestClassifier 14 | 15 | from sklearn.model_selection import train_test_split 16 | pd.set_option('display.max_columns', None) 17 | pd.set_option('display.max_rows', None) 18 | 19 | df = pd.read_csv("telco_churn.csv") 20 | 21 | print(df.head()) 22 | 23 | 24 | df['Churn_binary'] = np.where(df['Churn'] == 'Yes', 1, 0) 25 | 26 | 27 | X = df[['tenure', 'MonthlyCharges']] 28 | 29 | y = df['Churn'] 30 | 31 | X_train, X_test, y_train, y_test = train_test_split(X, y, random_state= 42) 32 | 33 | 34 | 35 | 36 | # folds = KFold(n_splits=5) 37 | # folds.get_n_splits(X) 38 | 39 | 40 | 41 | # from sklearn.metrics import accuracy_score 42 | # fold = 0 43 | # for train_index, test_index in folds.split(X): 44 | # X_train, X_test, y_train, y_test = X.iloc[train_index], X.iloc[test_index], y.iloc[train_index], y.iloc[test_index] 45 | # model = RandomForestClassifier() 46 | # model.fit(X_train, y_train) 47 | # y_pred = model.predict(X_test) 48 | # fold+=1 49 | # print(f"Accuracy in fold {fold}:", accuracy_score(y_pred, y_test)) 50 | # loo = LeaveOneOut() 51 | # for train_index, test_index in loo.split(X): 52 | # X_train, X_test, y_train, y_test = X.iloc[train_index], X.iloc[test_index], y.iloc[train_index], y.iloc[test_index] 53 | 54 | 55 | 56 | import numpy as np 57 | from sklearn.feature_selection import SelectKBest, f_classif, chi2 58 | import matplotlib.pyplot as plt 59 | 60 | 61 | df['TotalCharges'] = df['TotalCharges'].replace(' ', np.nan) 62 | df['TotalCharges'].fillna(0, inplace = True) 63 | df['TotalCharges'] = df['TotalCharges'].astype(float) 64 | 65 | 66 | X = df[['tenure', 'MonthlyCharges', 'TotalCharges']] 67 | 68 | y = df['Churn'] 69 | 70 | X_train, X_test, y_train, y_test = train_test_split(X, y, random_state= 42) 71 | 72 | 73 | numerical_predictors = ["MonthlyCharges", "TotalCharges", "tenure" ] 74 | numerical_selector = SelectKBest(f_classif, k=3) 75 | numerical_selector.fit(X_train[numerical_predictors], y_train) 76 | 77 | 78 | 79 | num_scores = -np.log10(numerical_selector.pvalues_) 80 | 81 | 82 | plt.bar(range(len(numerical_predictors)), num_scores) 83 | plt.xticks(range(len(numerical_predictors)), numerical_predictors, rotation='vertical') 84 | plt.xlabel("Feature") 85 | plt.ylabel("Score") 86 | plt.show() 87 | 88 | from sklearn.model_selection import RandomizedSearchCV 89 | 90 | 91 | n_estimators = [50, 100, 200] 92 | max_features = ['auto', 'sqrt', 'log2'] 93 | max_depth = [int(x) for x in np.linspace(10, 30, num = 5)] 94 | max_depth.append(None) 95 | min_samples_split = [2, 5, 10] 96 | min_samples_leaf = [1, 2, 4] 97 | bootstrap = [True, False] 98 | 99 | 100 | 101 | random_grid = {'n_estimators': n_estimators, 102 | 'max_features': max_features, 103 | 'max_depth': max_depth, 104 | 'min_samples_split': min_samples_split, 105 | 'min_samples_leaf': min_samples_leaf, 106 | 'bootstrap': bootstrap} 107 | 108 | model = RandomForestClassifier() 109 | 110 | rf_random = RandomizedSearchCV(estimator = model, param_distributions = random_grid, 111 | n_iter = 3, cv =3, verbose=2, random_state=42) 112 | rf_random.fit(X_train, y_train) 113 | 114 | parameters = rf_random.best_params_ 115 | print(parameters) 116 | 117 | -------------------------------------------------------------------------------- /portfolio_opt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Sep 15 15:45:53 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | 10 | import pandas_datareader.data as web 11 | import datetime 12 | import pandas as pd 13 | from functools import reduce 14 | 15 | 16 | pd.set_option('display.max_columns', None) 17 | pd.set_option('display.max_rows', None) 18 | 19 | 20 | # start = datetime.datetime(2019,9,15) 21 | # end = datetime.datetime(2021,9,15) 22 | 23 | # def get_stock(ticker): 24 | # data = web.DataReader(f"{ticker}","yahoo",start,end) 25 | # data[f'{ticker}'] = data["Close"]#(data["Close"] - data["Open"])/data["Open"] 26 | # data = data[[f'{ticker}']] 27 | # print(data.head()) 28 | # return data 29 | 30 | # pfizer = get_stock("PFE") 31 | # jnj = get_stock("JNJ") 32 | 33 | 34 | 35 | 36 | # def combine_stocks(tickers): 37 | # data_frames = [] 38 | # for i in tickers: 39 | # data_frames.append(get_stock(i)) 40 | 41 | # df_merged = reduce(lambda left,right: pd.merge(left,right,on=['Date'], 42 | # how='outer'), data_frames) 43 | # print(df_merged.head()) 44 | # return df_merged 45 | 46 | 47 | # stocks = ["MRNA", "PFE", "JNJ", "GOOGL", 48 | # "FB", "AAPL", "COST", "WMT", "KR", "JPM", 49 | # "BAC", "HSBC"] 50 | 51 | 52 | 53 | 54 | # portfolio = combine_stocks(stocks) 55 | 56 | 57 | 58 | # portfolio.to_csv("portfolio.csv", index=False) 59 | 60 | 61 | portfolio = pd.read_csv("portfolio.csv") 62 | print(portfolio.head()) 63 | 64 | from pypfopt.efficient_frontier import EfficientFrontier 65 | from pypfopt.expected_returns import mean_historical_return 66 | from pypfopt.risk_models import CovarianceShrinkage 67 | 68 | 69 | mu = mean_historical_return(portfolio) 70 | S = CovarianceShrinkage(portfolio).ledoit_wolf() 71 | 72 | 73 | ef = EfficientFrontier(mu, S) 74 | weights = ef.max_sharpe() 75 | 76 | cleaned_weights = ef.clean_weights() 77 | print(dict(cleaned_weights)) 78 | 79 | ef.portfolio_performance(verbose=True) 80 | 81 | 82 | 83 | from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices 84 | 85 | latest_prices = get_latest_prices(portfolio) 86 | 87 | da = DiscreteAllocation(cleaned_weights, latest_prices, total_portfolio_value=100000) 88 | 89 | allocation, leftover = da.greedy_portfolio() 90 | print("Discrete allocation:", allocation) 91 | print("Funds remaining: ${:.2f}".format(leftover)) 92 | 93 | 94 | 95 | 96 | from pypfopt import HRPOpt 97 | returns = portfolio.pct_change().dropna() 98 | hrp = HRPOpt(returns) 99 | hrp_weights = hrp.optimize() 100 | hrp.portfolio_performance(verbose=True) 101 | print(dict(hrp_weights)) 102 | 103 | da_hrp = DiscreteAllocation(hrp_weights, latest_prices, total_portfolio_value=100000) 104 | 105 | allocation, leftover = da_hrp.greedy_portfolio() 106 | print("Discrete allocation (HRP):", allocation) 107 | print("Funds remaining (HRP): ${:.2f}".format(leftover)) 108 | 109 | 110 | 111 | 112 | from pypfopt.efficient_frontier import EfficientCVaR 113 | S = portfolio.cov() 114 | ef_cvar = EfficientCVaR(mu, S) 115 | cvar_weights = ef_cvar.min_cvar() 116 | 117 | cleaned_weights = ef_cvar.clean_weights() 118 | print(dict(cleaned_weights)) 119 | 120 | ef_cvar.portfolio_performance(verbose=True) 121 | 122 | da_cvar = DiscreteAllocation(cvar_weights, latest_prices, total_portfolio_value=100000) 123 | 124 | allocation, leftover = da_cvar.greedy_portfolio() 125 | print("Discrete allocation (CVAR):", allocation) 126 | print("Funds remaining (CVAR): ${:.2f}".format(leftover)) 127 | -------------------------------------------------------------------------------- /optimization_tutorial.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Feb 22 00:34:19 2022 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | from sklearn.model_selection import train_test_split 10 | from sklearn.ensemble import RandomForestRegressor 11 | from sklearn.metrics import mean_squared_error 12 | import numpy as np 13 | from scipy.optimize import differential_evolution 14 | 15 | 16 | df = pd.read_csv("Concrete_Data_Yeh.csv") 17 | 18 | print(df.head()) 19 | 20 | 21 | X = df[['cement', 'slag', 'flyash', 'water', 'superplasticizer', 22 | 'coarseaggregate', 'fineaggregate', 'age']] 23 | 24 | y = df['csMPa'] 25 | 26 | X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state = 42) 27 | 28 | model = RandomForestRegressor(n_estimators=100, max_depth=100, random_state =42) 29 | model.fit(X_train, y_train) 30 | 31 | y_pred = model.predict(X_test) 32 | 33 | rmse = np.sqrt(mean_squared_error(y_test, y_pred)) 34 | 35 | print("RMSE: ", rmse) 36 | 37 | 38 | import matplotlib.pyplot as plt 39 | 40 | plt.scatter(y_test, y_pred) 41 | plt.title("Actual vs. Predicted") 42 | plt.xlabel("Actual") 43 | plt.ylabel("Predicted") 44 | 45 | 46 | model_full= RandomForestRegressor(n_estimators=100, max_depth=100, random_state =42) 47 | model_full.fit(X, y) 48 | 49 | 50 | def obj_fun(X): 51 | X = [X] 52 | results = model_full.predict(X) 53 | obj_fun.counter += 1 54 | print(obj_fun.counter) 55 | return -results 56 | 57 | 58 | 59 | boundaries = [(df['cement'].min(), df['cement'].max()), (df['slag'].min(), df['slag'].max()), (df['flyash'].min(), df['flyash'].max()), 60 | (df['water'].min(), df['water'].max()), (df['superplasticizer'].min(), df['superplasticizer'].max()), 61 | (df['coarseaggregate'].min(), df['coarseaggregate'].max()), (df['fineaggregate'].min(), df['fineaggregate'].max()), (df['age'].min(), df['age'].max())] 62 | 63 | 64 | obj_fun.counter = 0 65 | 66 | if __name__ == '__main__': 67 | 68 | 69 | opt_results = differential_evolution(obj_fun, boundaries) 70 | 71 | 72 | print('cement:', opt_results.x[0]) 73 | print('slag:', opt_results.x[1]) 74 | print('flyash:', opt_results.x[2]) 75 | print('water:', opt_results.x[3]) 76 | print('superplasticizer:', opt_results.x[4]) 77 | print('coarseaggregate:', opt_results.x[5]) 78 | print('fineaggregate:', opt_results.x[6]) 79 | print('age:', opt_results.x[7]) 80 | 81 | 82 | print("Max Strength: ", -opt_results.fun) 83 | 84 | 85 | import dlib 86 | 87 | lbounds = [df['cement'].min(), df['slag'].min(), df['flyash'].min(), df['water'].min(), df['superplasticizer'].min(), df['coarseaggregate'].min(), 88 | df['fineaggregate'].min(), df['age'].min()] 89 | ubounds = [df['cement'].max(), df['slag'].max(), df['flyash'].max(), df['water'].max(), df['superplasticizer'].max(), df['coarseaggregate'].max(), 90 | df['fineaggregate'].max(), df['age'].max()] 91 | max_fun_calls = 1000 92 | 93 | def maxlip_obj_fun(X1, X2, X3, X4, X5, X6, X7, X8): 94 | X = [[X1, X2, X3, X4, X5, X6, X7, X8]] 95 | results = model_full.predict(X) 96 | return results 97 | 98 | 99 | sol, obj_val = dlib.find_max_global(maxlip_obj_fun, lbounds, ubounds, max_fun_calls) 100 | 101 | print("MAXLIPO Results: ") 102 | print('cement:', sol[0]) 103 | print('slag:', sol[1]) 104 | print('flyash:', sol[2]) 105 | print('water:', sol[3]) 106 | print('superplasticizer:', sol[4]) 107 | print('coarseaggregate:', sol[5]) 108 | print('fineaggregate:', sol[6]) 109 | print('age:', sol[7]) 110 | 111 | 112 | print("Max Strength: ", obj_val) 113 | 114 | 115 | -------------------------------------------------------------------------------- /regularization_tutorial.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Nov 15 14:55:53 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | from sklearn.model_selection import train_test_split 10 | from sklearn.preprocessing import StandardScaler 11 | 12 | df = pd.read_csv("telco_churn.csv") 13 | df.fillna(0,inplace=True) 14 | print(df.head()) 15 | 16 | import numpy as np 17 | df['Churn'] = np.where(df['Churn'] == 'Yes', 1, 0) 18 | 19 | def convert_categories(cat_list): 20 | for col in cat_list: 21 | df[col] = df[col].astype('category') 22 | df[f'{col}_cat'] = df[col].cat.codes 23 | df[f'{col}_cat'] = df[f'{col}_cat'].astype(float) 24 | 25 | category_list = ['gender', 'Partner', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService', 26 | 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 27 | 'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod'] 28 | convert_categories(category_list) 29 | 30 | print(df.head()) 31 | df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce') 32 | df['TotalCharges'].fillna(0, inplace=True) 33 | cols = ['gender_cat', 'Partner_cat', 'Dependents_cat', 'PhoneService_cat', 'MultipleLines_cat', 'InternetService_cat', 34 | 'OnlineSecurity_cat', 'OnlineBackup_cat', 'DeviceProtection_cat', 'TechSupport_cat', 'StreamingTV_cat', 35 | 'StreamingMovies_cat', 'Contract_cat', 'PaperlessBilling_cat', 'PaymentMethod_cat','MonthlyCharges', 36 | 'TotalCharges', 'SeniorCitizen', 'tenure'] 37 | 38 | X = df[cols] 39 | print(X.head()) 40 | df['Churn'] = df['Churn'].astype(int) 41 | y = df['Churn'] 42 | 43 | 44 | 45 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) 46 | 47 | from tensorflow.keras.layers import Dense 48 | from tensorflow.keras.models import Sequential 49 | from sklearn.metrics import accuracy_score 50 | 51 | 52 | model = Sequential() 53 | model.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu')) 54 | model.add(Dense(32, activation='relu')) 55 | model.add(Dense(32, activation='relu')) 56 | model.add(Dense(1, activation='sigmoid')) 57 | model.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy']) 58 | model.fit(X_train, y_train,epochs =20) 59 | 60 | y_pred = model.predict(X_test) 61 | y_pred = np.where(y_pred > 0.5, 1, 0) 62 | print("Accuracy: ", accuracy_score(y_pred, y_test)) 63 | 64 | 65 | from tensorflow.keras import regularizers 66 | 67 | model_lasso = Sequential() 68 | model_lasso.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu', kernel_regularizer = regularizers.l1(1e-6))) 69 | model_lasso.add(Dense(32, activation='relu')) 70 | model_lasso.add(Dense(32, activation='relu')) 71 | model_lasso.add(Dense(1, activation='sigmoid')) 72 | model_lasso.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy']) 73 | model_lasso.fit(X_train, y_train,epochs =20) 74 | 75 | y_pred = model_lasso.predict(X_test) 76 | y_pred = np.where(y_pred > 0.5, 1, 0) 77 | print("Accuracy With Lasso: ", accuracy_score(y_pred, y_test)) 78 | 79 | 80 | model_ridge = Sequential() 81 | model_ridge.add(Dense(len(cols),input_shape=(len(cols),), kernel_initializer='normal', activation='relu', kernel_regularizer = regularizers.l2(1e-6))) 82 | model_ridge.add(Dense(32, activation='relu')) 83 | model_ridge.add(Dense(32, activation='relu')) 84 | model_ridge.add(Dense(1, activation='sigmoid')) 85 | model_ridge.compile(optimizer = 'adam',loss='binary_crossentropy', metrics =['accuracy']) 86 | model_ridge.fit(X_train, y_train,epochs =20) 87 | 88 | y_pred = model_ridge.predict(X_test) 89 | y_pred = np.where(y_pred > 0.5, 1, 0) 90 | print("Accuracy With Ridge: ", accuracy_score(y_pred, y_test)) 91 | -------------------------------------------------------------------------------- /python_profiling_tutorial.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed May 4 18:02:29 2022 5 | 6 | @author: sadrachpierre 7 | """ 8 | 9 | from memory_profiler import profile 10 | import pandas as pd 11 | from sklearn.model_selection import train_test_split 12 | from sklearn.linear_model import LogisticRegression 13 | from sklearn.metrics import average_precision_score 14 | 15 | from catboost import CatBoostClassifier 16 | 17 | from timeit import default_timer as timer 18 | 19 | import cProfile, pstats, io 20 | from pstats import SortKey 21 | 22 | 23 | ''' 24 | df = pd.read_csv("creditcard.csv") 25 | 26 | print(df.head()) 27 | 28 | print("Number of rows: ", len(df)) 29 | 30 | print("Number of columns: ", len(df.columns)) 31 | 32 | df = df.sample(10000, random_state=42) 33 | 34 | df.to_csv("creditcard_subsample10000.csv", index=False) 35 | ''' 36 | 37 | @profile 38 | def read_data(filename): 39 | df = pd.read_csv(filename) 40 | return df 41 | 42 | @profile 43 | def data_prep(dataframe, columns): 44 | df_select = dataframe[columns] 45 | return df_select 46 | 47 | @profile 48 | def feature_engineering(dataframe, inputs, output): 49 | X = dataframe[inputs] 50 | y = dataframe[output] 51 | return X, y 52 | @profile 53 | def split_data(X, y): 54 | X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42, test_size = 0.33) 55 | return X_train, X_test, y_train, y_test 56 | 57 | @profile 58 | def model_training(X_train, y_train, model_type): 59 | if model_type == 'Logistic Regression': 60 | model = LogisticRegression() 61 | model.fit(X_train, y_train) 62 | elif model_type == 'CatBoost': 63 | model = CatBoostClassifier() 64 | model.fit(X_train, y_train) 65 | return model 66 | 67 | @profile 68 | def predict(model, X_test): 69 | y_pred = model.predict(X_test) 70 | return y_pred 71 | @profile 72 | def evaluate(y_pred, y_test): 73 | precision = average_precision_score(y_test, y_pred) 74 | print("Precision: ", precision) 75 | 76 | def main(): 77 | runtime_metrics = dict() 78 | #read in data 79 | start = timer() 80 | data = read_data('creditcard.csv') 81 | end = timer() 82 | read_time = end - start 83 | runtime_metrics['read_time'] = read_time 84 | 85 | #slect relevant columns 86 | start = timer() 87 | columns = ['V1', 'V2', 'V3', 'Amount', 'Class'] 88 | df_select = data_prep(data, columns) 89 | end = timer() 90 | select_time = end - start 91 | runtime_metrics['select_time'] = select_time 92 | 93 | 94 | #define input and output 95 | start = timer() 96 | inputs = ['V1', 'V2', 'V3'] 97 | output = 'Class' 98 | X, y = feature_engineering(df_select, inputs, output) 99 | end = timer() 100 | data_prep_time = end - start 101 | runtime_metrics['data_prep_time'] = data_prep_time 102 | 103 | 104 | #split data for training and testing 105 | start = timer() 106 | X_train, X_test, y_train, y_test = split_data(X, y) 107 | end = timer() 108 | split_time = end - start 109 | runtime_metrics['split_time'] = split_time 110 | 111 | 112 | #fit model 113 | start = timer() 114 | model_type = 'CatBoost' 115 | model = model_training(X_train, y_train, model_type) 116 | end = timer() 117 | fit_time = end - start 118 | runtime_metrics['fit_time'] = fit_time 119 | 120 | #make predictions 121 | start = timer() 122 | y_pred = predict(model, X_test) 123 | end = timer() 124 | pred_time = end - start 125 | runtime_metrics['pred_time'] = pred_time 126 | 127 | #evaluate model predictions 128 | start = timer() 129 | evaluate(y_pred, y_test) 130 | end = timer() 131 | pred_time = end - start 132 | runtime_metrics['pred_time'] = pred_time 133 | 134 | print(runtime_metrics) 135 | 136 | 137 | if __name__ == "__main__": 138 | main() 139 | 140 | 141 | -------------------------------------------------------------------------------- /empty_variables_and_datastructures.py: -------------------------------------------------------------------------------- 1 | age1 = 35 2 | name1 = "Fred Philips" 3 | income1= 55250.15 4 | senior_citizen1 = False 5 | 6 | 7 | age2 = 42 8 | name2 = "Josh Rogers" 9 | income2=65240.25 10 | senior_citizen2 = False 11 | 12 | 13 | age3 = 28 14 | name3 = "Bill Hanson" 15 | income3=79250.65 16 | senior_citizen3 = False 17 | 18 | 19 | #age4 = "" 20 | #name4 = 100 21 | #income4 = 45250.65 22 | #senior_citizen4 = True 23 | 24 | #age4 = None 25 | #name4 = None 26 | #income4 = 45250.65 27 | #senior_citizen4 = True 28 | 29 | 30 | import numpy as np 31 | 32 | age4 = np.nan 33 | name4 = np.nan 34 | income4 = 45250.65 35 | senior_citizen4 = np.nan 36 | 37 | avg_age = (age1 + age2 + age3 + age4)/4 38 | 39 | print(avg_age) 40 | 41 | 42 | ages = [] 43 | names = [] 44 | incomes = [] 45 | senior_citizen = [] 46 | 47 | ages.append(age1) 48 | ages.append(age2) 49 | ages.append(age3) 50 | ages.append(age4) 51 | print("List of ages: ", ages) 52 | 53 | names.append(name1) 54 | names.append(name2) 55 | names.append(name3) 56 | names.append(name4) 57 | 58 | print("List of names: ", names) 59 | 60 | 61 | incomes.append(income1) 62 | incomes.append(income2) 63 | incomes.append(income3) 64 | incomes.append(income4) 65 | 66 | 67 | print("List of incomes: ", incomes) 68 | 69 | 70 | senior_citizen.append(senior_citizen1) 71 | senior_citizen.append(senior_citizen2) 72 | senior_citizen.append(senior_citizen3) 73 | senior_citizen.append(senior_citizen4) 74 | 75 | 76 | print("List of senior citizen status: ", senior_citizen) 77 | 78 | demo_dict = {} 79 | 80 | demo_dict['age'] = ages 81 | demo_dict['name'] = names 82 | demo_dict['income'] = incomes 83 | demo_dict['senior_citizen'] = senior_citizen 84 | 85 | print("Demographics Dictionary") 86 | print(demo_dict) 87 | 88 | 89 | import pandas as pd 90 | 91 | demo_df = pd.DataFrame() 92 | 93 | demo_df['age'] = ages 94 | demo_df['name'] = names 95 | demo_df['income'] = incomes 96 | demo_df['senior_citizen'] = senior_citizen 97 | 98 | print("Demographics Dataframe") 99 | print(demo_df) 100 | 101 | 102 | 103 | 104 | def income_after_tax(income, after_tax = np.nan): 105 | if income is float: 106 | after_tax = income - 0.22*income 107 | return after_tax 108 | 109 | 110 | 111 | after_tax1 = income_after_tax(income1) 112 | print("Before: ", income1) 113 | 114 | print("After: ", after_tax1) 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | after_tax_invalid1 = income_after_tax('') 123 | after_tax_invalid2 = income_after_tax(None) 124 | after_tax_invalid3 = income_after_tax("income") 125 | after_tax_invalid4 = income_after_tax(True) 126 | after_tax_invalid5 = income_after_tax({}) 127 | 128 | print("after_tax_invalid1: ", after_tax_invalid1) 129 | print("after_tax_invalid2: ", after_tax_invalid2) 130 | print("after_tax_invalid3: ", after_tax_invalid3) 131 | print("after_tax_invalid4: ", after_tax_invalid4) 132 | print("after_tax_invalid5: ", after_tax_invalid5) 133 | 134 | 135 | def get_after_tax_list(input_list, out_list = []): 136 | if type(input_list) is list: 137 | out_list = [x - 0.22*x for x in input_list] 138 | print("After Tax Incomes: ", out_list) 139 | return out_list 140 | 141 | 142 | 143 | out_list1 = get_after_tax_list(incomes) 144 | out_list2 = get_after_tax_list(5) 145 | 146 | 147 | def get_income_truth_values(input_dict, output_dict={'avg_income': np.nan}): 148 | if type(input_dict) is dict and 'income' in input_dict: 149 | output_dict= {'avg_income': np.mean(input_dict['income'])} 150 | print(output_dict) 151 | return output_dict 152 | 153 | get_income_truth_values(demo_dict) 154 | get_income_truth_values(10000) 155 | 156 | demo_df['state'] = ['NY', 'MA', 'NY', 'CA'] 157 | demo_df['age'].fillna(demo_df['age'].mean(), inplace=True) 158 | demo_df['income'].fillna(demo_df['income'].mean(), inplace=True) 159 | 160 | def income_age_groupby(input_df, output_df = pd.DataFrame({'state': [np.nan], 'age': [np.nan], 'income':[np.nan]})): 161 | if type(input_df) is type(pd.DataFrame()) and set(['age', 'income', 'state']).issubset(input_df.columns): 162 | output_df = input_df.groupby(['state'])['age', 'income'].mean().reset_index() 163 | print(output_df) 164 | return output_df 165 | 166 | income_age_groupby(demo_df) 167 | income_age_groupby([1,2,3]) 168 | -------------------------------------------------------------------------------- /financial_data_analysis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Jun 23 12:19:13 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas_datareader.data as web 9 | import datetime 10 | import pandas as pd 11 | import matplotlib.pyplot as plt 12 | import seaborn as sns 13 | 14 | sns.set() 15 | start = datetime.datetime(2019,6,23) 16 | end = datetime.datetime(2021,6,23) 17 | 18 | # amzn = web.DataReader('AMZN','yahoo',start,end) 19 | # amzn['Date'] = amzn.index 20 | # print(amzn.head()) 21 | # amzn.to_csv(f"amzn_{start}_{end}.csv", index=False) 22 | 23 | 24 | # googl = web.DataReader('GOOGL','yahoo',start,end) 25 | # googl['Date'] = googl.index 26 | # print(googl.head()) 27 | # googl.to_csv(f"googl_{start}_{end}.csv", index=False) 28 | 29 | # aapl = web.DataReader('AAPL','yahoo',start,end) 30 | # aapl['Date'] = aapl.index 31 | # print(aapl.head()) 32 | # aapl.to_csv(f"aapl_{start}_{end}.csv", index=False) 33 | 34 | 35 | amzn_df = pd.read_csv(f'amzn_{start}_{end}.csv') 36 | googl_df = pd.read_csv(f'googl_{start}_{end}.csv') 37 | aapl_df = pd.read_csv(f'aapl_{start}_{end}.csv') 38 | 39 | # print(aapl_df.head()) 40 | 41 | amzn_df['Returns'] = (amzn_df['Open'] - amzn_df['Close'])/amzn_df['Open'] 42 | amzn_df['Returns'].hist() 43 | import numpy as np 44 | mean_amnz_returns = np.round(amzn_df['Returns'].mean(), 5) 45 | std_amnz_returns = np.round(amzn_df['Returns'].std(), 2) 46 | plt.title(f'AMZN Stock Price Returns Distribution; Mean {mean_amnz_returns}, STD: {std_amnz_returns}') 47 | plt.show() 48 | 49 | 50 | googl_df['Returns'] = (googl_df['Open'] - googl_df['Close'])/googl_df['Open'] 51 | googl_df['Returns'].hist() 52 | mean_googl_returns = np.round(googl_df['Returns'].mean(), 5) 53 | std_googl_returns = np.round(googl_df['Returns'].std(), 2) 54 | plt.title(f'GOOGL Stock Price Returns Distribution; Mean {mean_googl_returns}, STD: {std_googl_returns}') 55 | plt.show() 56 | 57 | aapl_df['Returns'] = (aapl_df['Open'] - aapl_df['Close'])/aapl_df['Open'] 58 | aapl_df['Returns'].hist() 59 | mean_aapl_returns = np.round(aapl_df['Returns'].mean(), 5) 60 | std_aapl_returns = np.round(aapl_df['Returns'].std(), 2) 61 | plt.title(f'AAPL Stock Price Returns Distribution; Mean {mean_aapl_returns}, STD: {std_aapl_returns}') 62 | plt.show() 63 | 64 | 65 | amzn_df['Ticker'] = 'AMZN' 66 | googl_df['Ticker'] = 'GOOGL' 67 | aapl_df['Ticker'] = 'AAPL' 68 | 69 | df = pd.concat([amzn_df, googl_df, aapl_df]) 70 | df = df[['Ticker', 'Returns']] 71 | print(df.head()) 72 | 73 | sns.boxplot(x= df['Ticker'], y = df['Returns']) 74 | plt.title('Box Plot for AMZN, GOOGL and AAPL Returns') 75 | plt.show() 76 | 77 | df_corr = pd.DataFrame({'AMZN':amzn_df['Returns'], 'GOOGL':googl_df['Returns'], 'AAPL':aapl_df['Returns']}) 78 | print(df_corr.head()) 79 | corr = df_corr.corr() 80 | sns.heatmap(corr, annot= True) 81 | plt.show() 82 | 83 | 84 | cutoff = datetime.datetime(2021,1,23) 85 | amzn_df['Date'] = pd.to_datetime(amzn_df['Date'], format='%Y/%m/%d') 86 | amzn_df = amzn_df[amzn_df['Date'] > cutoff] 87 | amzn_df['SMA_10'] = amzn_df['Close'].rolling(window=10).mean() 88 | print(amzn_df.head()) 89 | plt.plot(amzn_df['Date'], amzn_df['SMA_10']) 90 | plt.plot(amzn_df['Date'], amzn_df['Adj Close']) 91 | plt.title("Moving average and Adj Close price for AMZN") 92 | plt.ylabel('Adj Close Price') 93 | plt.xlabel('Date') 94 | plt.show() 95 | 96 | 97 | googl_df['Date'] = pd.to_datetime(googl_df['Date'], format='%Y/%m/%d') 98 | googl_df = googl_df[googl_df['Date'] > cutoff] 99 | googl_df['SMA_10'] = googl_df['Close'].rolling(window=10).mean() 100 | print(googl_df.head()) 101 | plt.plot(googl_df['Date'], googl_df['SMA_10']) 102 | plt.plot(googl_df['Date'], googl_df['Adj Close']) 103 | plt.title("Moving average and Adj Close price for GOOGL") 104 | plt.ylabel('Adj Close Price') 105 | plt.xlabel('Date') 106 | plt.show() 107 | 108 | 109 | aapl_df['Date'] = pd.to_datetime(aapl_df['Date'], format='%Y/%m/%d') 110 | aapl_df = aapl_df[aapl_df['Date'] > cutoff] 111 | aapl_df['SMA_10'] = aapl_df['Close'].rolling(window=10).mean() 112 | print(googl_df.head()) 113 | plt.plot(aapl_df['Date'], aapl_df['SMA_10']) 114 | plt.plot(aapl_df['Date'], aapl_df['Adj Close']) 115 | plt.title("Moving average and Adj Close price for AAPL") 116 | plt.ylabel('Adj Close Price') 117 | plt.xlabel('Date') 118 | plt.show() 119 | 120 | amzn_df['SMA_10_STD'] = amzn_df['Adj Close'].rolling(window=10).std() 121 | amzn_df['Upper Band'] = amzn_df['SMA_10'] + (amzn_df['SMA_10_STD'] * 2) 122 | amzn_df['Lower Band'] = amzn_df['SMA_10'] - (amzn_df['SMA_10_STD'] * 2) 123 | amzn_df.index = amzn_df['Date'] 124 | amzn_df[['Adj Close', 'SMA_10', 'Upper Band', 'Lower Band']].plot(figsize=(12,6)) 125 | plt.title('10 Day Bollinger Band for Amazon') 126 | plt.ylabel('Adjusted Close Price') 127 | plt.show() 128 | 129 | 130 | 131 | googl_df['SMA_10_STD'] = googl_df['Adj Close'].rolling(window=10).std() 132 | googl_df['Upper Band'] = googl_df['SMA_10'] + (googl_df['SMA_10_STD'] * 2) 133 | googl_df['Lower Band'] = googl_df['SMA_10'] - (googl_df['SMA_10_STD'] * 2) 134 | googl_df.index = googl_df['Date'] 135 | googl_df[['Adj Close', 'SMA_10', 'Upper Band', 'Lower Band']].plot(figsize=(12,6)) 136 | plt.title('10 Day Bollinger Band for Google') 137 | plt.ylabel('Adjusted Close Price') 138 | plt.show() 139 | 140 | 141 | aapl_df['SMA_10_STD'] = aapl_df['Adj Close'].rolling(window=10).std() 142 | aapl_df['Upper Band'] = aapl_df['SMA_10'] + (aapl_df['SMA_10_STD'] * 2) 143 | aapl_df['Lower Band'] = aapl_df['SMA_10'] - (aapl_df['SMA_10_STD'] * 2) 144 | aapl_df.index = aapl_df['Date'] 145 | aapl_df[['Adj Close', 'SMA_10', 'Upper Band', 'Lower Band']].plot(figsize=(12,6)) 146 | plt.title('10 Day Bollinger Band for Apple') 147 | plt.ylabel('Adjusted Close Price') 148 | plt.show() 149 | -------------------------------------------------------------------------------- /dimensionality_reduction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Nov 8 16:10:03 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | import numpy as np 10 | from sklearn.ensemble import RandomForestClassifier 11 | from sklearn.model_selection import train_test_split 12 | import seaborn as sns 13 | import matplotlib.pyplot as plt 14 | 15 | 16 | pd.set_option('display.max_columns', None) 17 | pd.set_option('display.max_rows', None) 18 | 19 | 20 | df = pd.read_csv("Loan_status_2007-2020Q3.gzip") 21 | 22 | 23 | 24 | print("Number of Columns: ", len(list(df.columns))) 25 | print("Number of rows: ", len(df)) 26 | 27 | 28 | print(df.head()) 29 | 30 | 31 | df = df[df['purpose'] == 'credit_card'] 32 | 33 | 34 | columns = ['loan_amnt', 'loan_status', 'funded_amnt', 'funded_amnt_inv', 'term', 'int_rate','mths_since_recent_revol_delinq','home_ownership', 'verification_status', 35 | 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high'] 36 | 37 | 38 | df = df[columns] 39 | df.to_csv("credit_card_loan.csv", index=False) 40 | 41 | 42 | 43 | df_credit = pd.read_csv("credit_card_loan.csv") 44 | 45 | print("Number of Columns: ", len(list(df_credit.columns))) 46 | print("Number of rows: ", len(df_credit)) 47 | 48 | 49 | def fill_na(numerical_column): 50 | df_credit[numerical_column].fillna(df_credit[numerical_column].mean(), inplace=True) 51 | 52 | fill_na('mths_since_recent_revol_delinq') 53 | fill_na('num_accts_ever_120_pd') 54 | fill_na('num_actv_bc_tl') 55 | fill_na('num_actv_rev_tl') 56 | fill_na('avg_cur_bal') 57 | fill_na('bc_open_to_buy') 58 | fill_na('bc_util') 59 | 60 | 61 | 62 | 63 | def convert_categories(categorical_columnn): 64 | df_credit[categorical_columnn] = df_credit[categorical_columnn].astype('category') 65 | df_credit[f'{categorical_columnn}_cat'] = df_credit[categorical_columnn].cat.codes 66 | 67 | convert_categories('home_ownership') 68 | convert_categories('verification_status') 69 | convert_categories('term') 70 | 71 | 72 | print(set(df_credit['loan_status'])) 73 | 74 | df_credit = df_credit[df_credit['loan_status'].isin(['Fully Paid', 'Default', 'Charged Off'])] 75 | 76 | print(df_credit.head()) 77 | 78 | 79 | 80 | df_credit['loan_status_label'] = np.where(df_credit['loan_status'] == 'Fully Paid', 0, 1) 81 | columns2 = ['loan_amnt', 'loan_status_label', 'funded_amnt', 'funded_amnt_inv', 'term_cat', 'int_rate','mths_since_recent_revol_delinq','home_ownership_cat', 'verification_status_cat', 82 | 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high'] 83 | df_credit = df_credit[columns2] 84 | print(df_credit.head()) 85 | 86 | df_credit['int_rate'] = df_credit['int_rate'].str.rstrip('%') 87 | df_credit['int_rate'] = df_credit['int_rate'].astype(float) 88 | df_credit.fillna(0, inplace=True) 89 | 90 | 91 | X = df_credit[['loan_amnt', 'funded_amnt', 'funded_amnt_inv', 'term_cat', 'int_rate','mths_since_recent_revol_delinq','home_ownership_cat', 'verification_status_cat', 92 | 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high']] 93 | y = df_credit['loan_status_label'] 94 | 95 | X_train, X_test, y_train, y_test = train_test_split(X, y , random_state=42, test_size = 0.33) 96 | 97 | 98 | import seaborn as sns 99 | import matplotlib.pyplot as plt 100 | model = RandomForestClassifier() 101 | model.fit(X_train, y_train) 102 | 103 | features = ['loan_amnt', 'funded_amnt', 'funded_amnt_inv', 'term_cat', 'int_rate','mths_since_recent_revol_delinq','home_ownership_cat', 'verification_status_cat', 104 | 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high'] 105 | 106 | 107 | feature_df = pd.DataFrame({"Importance":model.feature_importances_, "Features": features }) 108 | sns.set() 109 | plt.bar(feature_df["Features"], feature_df["Importance"]) 110 | plt.xticks(rotation=90) 111 | plt.title("Random Forest Model Feature Importance") 112 | plt.show() 113 | 114 | from sklearn.decomposition import PCA 115 | from sklearn.preprocessing import StandardScaler 116 | 117 | features2 = ['loan_amnt', 'loan_status_label', 'funded_amnt', 'funded_amnt_inv', 'term_cat', 'int_rate','mths_since_recent_revol_delinq','home_ownership_cat', 'verification_status_cat', 118 | 'num_accts_ever_120_pd', 'num_actv_bc_tl', 'num_actv_rev_tl', 'avg_cur_bal', 'bc_open_to_buy', 'bc_util', 'chargeoff_within_12_mths', 'delinq_amnt', 'last_fico_range_low', 'last_fico_range_high'] 119 | 120 | 121 | 122 | 123 | X = df_credit[features2] 124 | scaler = StandardScaler() 125 | 126 | scaler.fit(X) 127 | X_scaled=scaler.transform(X) 128 | 129 | pca=PCA(n_components=4) 130 | pca.fit(X_scaled) 131 | X_components=pca.transform(X_scaled) 132 | 133 | components_df = pd.DataFrame({'component_one': list(X_components[:,0]), 'component_two': list(X_components[:,1]), 134 | 'component_three': list(X_components[:,2]), 'component_four': list(X_components[:,3])}) 135 | 136 | print(components_df.head()) 137 | 138 | 139 | 140 | 141 | labels=X.loan_status_label 142 | color_dict={0:'Red',1:'Blue'} 143 | 144 | 145 | 146 | fig,ax=plt.subplots(figsize=(7,5)) 147 | 148 | sns.set() 149 | for i in np.unique(labels): 150 | index=np.where(labels==i) 151 | ax.scatter(components_df['component_one'].loc[index],components_df['component_two'].loc[index],c=color_dict[i],s=10, 152 | label=i) 153 | 154 | 155 | plt.xlabel("1st Component",fontsize=14) 156 | plt.ylabel("2nd Component",fontsize=14) 157 | plt.title('Scatter Plot of Principal Components') 158 | plt.legend() 159 | plt.show() 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /model_explainability.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Jul 20 12:31:27 2021 5 | 6 | @author: sadrachpierre 7 | """ 8 | import pandas as pd 9 | import numpy as np 10 | 11 | 12 | pd.set_option('display.max_columns', None) 13 | pd.set_option('display.max_rows', None) 14 | 15 | df = pd.read_csv("telco_churn.csv") 16 | 17 | df['gender_cat'] = df['gender'].astype('category') 18 | df['gender_cat'] = df['gender_cat'].cat.codes 19 | 20 | df['PaperlessBilling_cat'] = df['PaperlessBilling'].astype('category') 21 | df['PaperlessBilling_cat'] = df['PaperlessBilling_cat'].cat.codes 22 | 23 | 24 | 25 | df['Contract_cat'] = df['Contract'].astype('category') 26 | df['Contract_cat'] = df['Contract_cat'].cat.codes 27 | 28 | 29 | df['PaymentMethod_cat'] = df['PaymentMethod'].astype('category') 30 | df['PaymentMethod_cat'] = df['PaymentMethod_cat'].cat.codes 31 | 32 | 33 | df['Partner_cat'] = df['Partner'].astype('category') 34 | df['Partner_cat'] = df['Partner_cat'].cat.codes 35 | 36 | 37 | 38 | df['Dependents_cat'] = df['Dependents'].astype('category') 39 | df['Dependents_cat'] = df['Dependents_cat'].cat.codes 40 | 41 | 42 | df['DeviceProtection_cat'] = df['DeviceProtection'].astype('category') 43 | df['DeviceProtection_cat'] = df['DeviceProtection_cat'].cat.codes 44 | 45 | 46 | print(df.head()) 47 | 48 | df['churn_score'] = np.where(df['Churn']=='Yes', 1, 0) 49 | 50 | X = df[[ 'tenure', 'MonthlyCharges', 'gender_cat', 'PaperlessBilling_cat', 51 | 'Contract_cat','PaymentMethod_cat', 'Partner_cat', 'Dependents_cat', 'DeviceProtection_cat' ]] 52 | y = df['churn_score'] 53 | 54 | from sklearn.model_selection import train_test_split 55 | 56 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) 57 | 58 | 59 | from sklearn.linear_model import LogisticRegression 60 | 61 | lr_model = LogisticRegression() 62 | lr_model.fit(X_train, y_train) 63 | 64 | y_pred = lr_model.predict(X_test) 65 | 66 | 67 | from sklearn.metrics import confusion_matrix 68 | conmat = confusion_matrix(y_test, y_pred) 69 | 70 | val = np.mat(conmat) 71 | classnames = list(set(y_train)) 72 | 73 | df_cm = pd.DataFrame( 74 | val, index=classnames, columns=classnames, 75 | ) 76 | df_cm = df_cm.astype('float') / df_cm.sum(axis=1)[:, np.newaxis] 77 | 78 | import matplotlib.pyplot as plt 79 | import seaborn as sns 80 | 81 | plt.figure() 82 | heatmap = sns.heatmap(df_cm, annot=True, cmap="Blues") 83 | heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha='right') 84 | heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha='right') 85 | plt.ylabel('True label') 86 | plt.xlabel('Predicted label') 87 | plt.title('Churn Logistic Regression Model Results') 88 | plt.show() 89 | 90 | from sklearn.inspection import plot_partial_dependence 91 | features = [0, 1, (1, 0)] 92 | plot_partial_dependence(lr_model, X_train, features, target=1) 93 | 94 | 95 | from sklearn.ensemble import RandomForestClassifier 96 | 97 | rf_model = RandomForestClassifier(n_estimators=100, max_depth=10) 98 | rf_model.fit(X_train, y_train) 99 | 100 | y_pred_rf = rf_model.predict(X_test) 101 | 102 | 103 | 104 | 105 | conmat = confusion_matrix(y_test, y_pred_rf) 106 | 107 | val = np.mat(conmat) 108 | classnames = list(set(y_train)) 109 | 110 | df_cm_rf = pd.DataFrame( 111 | val, index=classnames, columns=classnames, 112 | ) 113 | df_cm_rf = df_cm_rf.astype('float') / df_cm_rf.sum(axis=1)[:, np.newaxis] 114 | 115 | 116 | 117 | plt.figure() 118 | heatmap = sns.heatmap(df_cm_rf, annot=True, cmap="Blues") 119 | heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha='right') 120 | heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha='right') 121 | plt.ylabel('True label') 122 | plt.xlabel('Predicted label') 123 | plt.title('Churn Random Forest Model Results') 124 | plt.show() 125 | 126 | features = ['tenure', 'MonthlyCharges', 'gender_cat', 'PaperlessBilling_cat', 127 | 'Contract_cat','PaymentMethod_cat', 'Partner_cat', 'Dependents_cat', 'DeviceProtection_cat' ] 128 | 129 | print(rf_model.feature_importances_) 130 | feature_df = pd.DataFrame({'Importance':rf_model.feature_importances_, 'Features': features }) 131 | 132 | sns.set() 133 | plt.bar(feature_df['Features'], feature_df['Importance']) 134 | plt.xticks(rotation=90) 135 | plt.title('Random Forest Model Feature Importance') 136 | plt.show() 137 | 138 | from tensorflow.keras.models import Sequential 139 | from tensorflow.keras.layers import Dense 140 | 141 | model = Sequential() 142 | model.add(Dense(8, input_shape = (len(features),))) 143 | model.add(Dense(8, activation='relu')) 144 | 145 | model.add(Dense(1, activation='sigmoid')) 146 | model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) 147 | 148 | model.fit(X_train, y_train, epochs = 1) 149 | 150 | y_pred_nn = [round(float(x)) for x in model.predict(X_test)] 151 | 152 | conmat = confusion_matrix(y_test, y_pred_nn) 153 | 154 | val = np.mat(conmat) 155 | classnames = list(set(y_train)) 156 | 157 | df_cm_nn = pd.DataFrame( 158 | val, index=classnames, columns=classnames, 159 | ) 160 | df_cm_nn = df_cm_nn.astype('float') / df_cm_nn.sum(axis=1)[:, np.newaxis] 161 | 162 | 163 | plt.figure() 164 | heatmap = sns.heatmap(df_cm_nn, annot=True, cmap="Blues") 165 | heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha='right') 166 | heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha='right') 167 | plt.ylabel('True label') 168 | plt.xlabel('Predicted label') 169 | plt.title('Churn Neural Network Model Results') 170 | plt.show() 171 | 172 | import shap 173 | 174 | f = lambda x: model.predict(x) 175 | med = X_train.median().values.reshape((1,X_train.shape[1])) 176 | 177 | explainer = shap.Explainer(f, med) 178 | shap_values = explainer(X_test.iloc[0:1000,:]) 179 | 180 | shap.plots.beeswarm(shap_values) 181 | 182 | 183 | import lime 184 | from lime import lime_tabular 185 | 186 | explainer = lime_tabular.LimeTabularExplainer(training_data=np.array(X_train),feature_names=X_train.columns,class_names=['Yes', 'No'], 187 | mode='classification') 188 | 189 | exp = explainer.explain_instance(data_row=X_test.iloc[1], predict_fn=model.predict, labels=(0,)) 190 | 191 | exp.show_in_notebook(show_table=True) 192 | -------------------------------------------------------------------------------- /profiling_debugging_mlworkflow.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","source":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.metrics import f1_score, accuracy_score, precision_score\nimport functools\nimport time","metadata":{"tags":[],"cell_id":"1a3b7777486a4415836cf8421c910742","allow_embed":"code_output","source_hash":"7dc94c80","execution_start":1666298424461,"execution_millis":759,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":1},{"cell_type":"code","source":"#runtime \ndef runtime_monitor(input_function):\n @functools.wraps(input_function)\n def runtime_wrapper(*args, **kwargs):\n start_value = time.perf_counter() \n return_value = input_function(*args, **kwargs)\n end_value = time.perf_counter() \n runtime_value = end_value - start_value \n print(f\"Finished executing {input_function.__name__} in {runtime_value} seconds\")\n return return_value\n return runtime_wrapper","metadata":{"tags":[],"cell_id":"062b50752fa44f09a0a6db5c1c45f2c8","source_hash":"3e47cae","execution_start":1666298425233,"execution_millis":3,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":2},{"cell_type":"code","source":"#debugger\ndef debugging_method(input_function):\n @functools.wraps(input_function)\n def debugging_wrapper(*args, **kwargs):\n arguments = []\n keyword_arguments = []\n for a in args:\n arguments.append(repr(a)) \n for key, value in kwargs.items():\n keyword_arguments.append(f\"{key}={value}\")\n function_signature = arguments + keyword_arguments \n function_signature = \"; \".join(function_signature) \n print(f\"{input_function.__name__} has the following signature: {function_signature}\")\n return_value = input_function(*args, **kwargs)\n print(f\"{input_function.__name__} has the following return: {return_value}\") \n return return_value\n return debugging_wrapper","metadata":{"tags":[],"cell_id":"10dea5c6939b458885ce0df3a76c5c15","source_hash":"d517a62f","execution_start":1666298425245,"execution_millis":2,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":3},{"cell_type":"code","source":"@debugging_method\n@runtime_monitor\ndef data_preparation(columns, test_size, datatype_dict):\n df = pd.read_csv(\"telco_churn.csv\")\n df_subset = df[columns].copy()\n \n for col in columns:\n df_subset[col] = df_subset[col].astype(datatype_dict[col])\n\n for col in columns:\n if datatype_dict[col] == \"category\":\n df_subset[col] = df_subset[col].cat.codes\n X = df_subset[[\"gender\", \"tenure\", \"PhoneService\", \"MultipleLines\",\"MonthlyCharges\",]]\n y = df_subset[\"Churn\"]\n X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42)\n return X_train, X_test, y_train, y_test\n\ncolumns = [\"gender\", \"tenure\", \"PhoneService\", \"MultipleLines\",\"MonthlyCharges\", \"Churn\"]\ndatatype_dict = {\"gender\":\"category\", \"tenure\":\"float\", \"PhoneService\":\"category\", \"MultipleLines\":\"category\", \"MonthlyCharges\":\"float\", \"Churn\":\"category\"}\nX_train, X_test, y_train, y_test = data_preparation(columns, 0.33, datatype_dict)","metadata":{"tags":[],"cell_id":"e656f853d7a848dcbf536b1a7a73c461","allow_embed":"code_output","source_hash":"1eb8f45e","execution_start":1666298425257,"execution_millis":122,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stdout","text":"data_preparation has the following signature: ['gender', 'tenure', 'PhoneService', 'MultipleLines', 'MonthlyCharges', 'Churn']; 0.33; {'gender': 'category', 'tenure': 'float', 'PhoneService': 'category', 'MultipleLines': 'category', 'MonthlyCharges': 'float', 'Churn': 'category'}\nFinished executing data_preparation in 0.04060269000183325 seconds\ndata_preparation has the following return: ( gender tenure PhoneService MultipleLines MonthlyCharges\n298 1 40.0 1 2 74.55\n3318 1 10.0 0 1 29.50\n5586 0 27.0 1 0 19.15\n6654 0 7.0 1 2 86.50\n5362 1 65.0 1 2 24.75\n... ... ... ... ... ...\n3772 1 1.0 1 0 95.00\n5191 0 23.0 1 2 91.10\n5226 1 12.0 1 0 21.15\n5390 1 12.0 1 2 99.45\n860 1 26.0 1 0 19.80\n\n[4718 rows x 5 columns], gender tenure PhoneService MultipleLines MonthlyCharges\n185 0 1.0 0 1 24.80\n2715 1 41.0 1 2 25.25\n3825 0 52.0 1 0 19.35\n1807 0 1.0 1 0 76.35\n132 1 67.0 1 0 50.55\n... ... ... ... ... ...\n4147 1 71.0 1 2 24.85\n3542 1 29.0 0 1 55.35\n3759 1 7.0 1 2 89.35\n1114 1 32.0 1 2 98.85\n4958 0 59.0 1 2 94.75\n\n[2325 rows x 5 columns], 298 0\n3318 1\n5586 0\n6654 1\n5362 0\n ..\n3772 1\n5191 0\n5226 0\n5390 1\n860 0\nName: Churn, Length: 4718, dtype: int8, 185 1\n2715 0\n3825 0\n1807 1\n132 0\n ..\n4147 0\n3542 0\n3759 1\n1114 0\n4958 0\nName: Churn, Length: 2325, dtype: int8)\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"@debugging_method\n@runtime_monitor\ndef fit_model(X_train,y_train):\n model = RandomForestClassifier(random_state=42)\n model.fit(X_train,y_train)\n return model\n\nmodel = fit_model(X_train,y_train)","metadata":{"tags":[],"cell_id":"635aabc0e8524b0b93a573a8e76c18a9","source_hash":"bec93e9b","execution_start":1666298425378,"execution_millis":482,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stdout","text":"fit_model has the following signature: gender tenure PhoneService MultipleLines MonthlyCharges\n298 1 40.0 1 2 74.55\n3318 1 10.0 0 1 29.50\n5586 0 27.0 1 0 19.15\n6654 0 7.0 1 2 86.50\n5362 1 65.0 1 2 24.75\n... ... ... ... ... ...\n3772 1 1.0 1 0 95.00\n5191 0 23.0 1 2 91.10\n5226 1 12.0 1 0 21.15\n5390 1 12.0 1 2 99.45\n860 1 26.0 1 0 19.80\n\n[4718 rows x 5 columns]; 298 0\n3318 1\n5586 0\n6654 1\n5362 0\n ..\n3772 1\n5191 0\n5226 0\n5390 1\n860 0\nName: Churn, Length: 4718, dtype: int8\nFinished executing fit_model in 0.5037549450025836 seconds\nfit_model has the following return: RandomForestClassifier(random_state=42)\n","output_type":"stream"}],"execution_count":5},{"cell_type":"code","source":"@debugging_method\n@runtime_monitor\ndef predict(X_test, model):\n y_pred = model.predict(X_test)\n return y_pred \n\ny_pred = predict(X_test, model)","metadata":{"tags":[],"cell_id":"24bff66e9b314d7c8b4b97f458b1ecfc","source_hash":"f081a43d","execution_start":1666298425870,"execution_millis":62,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stdout","text":"predict has the following signature: gender tenure PhoneService MultipleLines MonthlyCharges\n185 0 1.0 0 1 24.80\n2715 1 41.0 1 2 25.25\n3825 0 52.0 1 0 19.35\n1807 0 1.0 1 0 76.35\n132 1 67.0 1 0 50.55\n... ... ... ... ... ...\n4147 1 71.0 1 2 24.85\n3542 1 29.0 0 1 55.35\n3759 1 7.0 1 2 89.35\n1114 1 32.0 1 2 98.85\n4958 0 59.0 1 2 94.75\n\n[2325 rows x 5 columns]; RandomForestClassifier(random_state=42)\nFinished executing predict in 0.05748910900001647 seconds\npredict has the following return: [1 0 0 ... 1 1 0]\n","output_type":"stream"}],"execution_count":6},{"cell_type":"code","source":"@debugging_method\n@runtime_monitor\ndef model_performance(y_pred, y_test):\n print(\"f1_score\", f1_score(y_test, y_pred))\n print(\"accuracy_score\", accuracy_score(y_test, y_pred))\n print(\"precision_score\", precision_score(y_test, y_pred))\n \nmodel_performance(y_pred, y_test)","metadata":{"tags":[],"cell_id":"40c1589e5d8e4bd184bb50594c374530","source_hash":"7f601050","execution_start":1666298425942,"execution_millis":44,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stdout","text":"model_performance has the following signature: array([1, 0, 0, ..., 1, 1, 0], dtype=int8); 185 1\n2715 0\n3825 0\n1807 1\n132 0\n ..\n4147 0\n3542 0\n3759 1\n1114 0\n4958 0\nName: Churn, Length: 2325, dtype: int8\nf1_score 0.5083848190644307\naccuracy_score 0.7604301075268817\nprecision_score 0.5702970297029702\nFinished executing model_performance in 0.0064978350019373465 seconds\nmodel_performance has the following return: None\n","output_type":"stream"}],"execution_count":7},{"cell_type":"markdown","source":"\nCreated in deepnote.com \nCreated in Deepnote","metadata":{"tags":[],"created_in_deepnote_cell":true,"deepnote_cell_type":"markdown"}}],"nbformat":4,"nbformat_minor":0,"metadata":{"deepnote":{},"orig_nbformat":2,"deepnote_notebook_id":"c1f86b571e3e46e9a7a160785e87a9df","deepnote_persisted_session":{"createdAt":"2022-10-20T19:16:40.943Z"},"deepnote_execution_queue":[]}} -------------------------------------------------------------------------------- /python_inheritance.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\ndf = pd.read_csv('telco_churn.csv')","metadata":{"tags":[],"cell_id":"56fdc3cca3fa4d5398566ec31bfc2529","source_hash":"d5ef50cb","execution_start":1677703521895,"execution_millis":71,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":5},{"cell_type":"code","source":"df['gender'] = df['gender'].astype('category')\ndf['gender_cat'] = df['gender'].cat.codes\n\ndf['InternetService'] = df['InternetService'].astype('category')\ndf['InternetService_cat'] = df['InternetService'].cat.codes\n\ndf['OnlineSecurity'] = df['OnlineSecurity'].astype('category')\ndf['OnlineSecurity_cat'] = df['OnlineSecurity'].cat.codes\n\ndf['Churn'] = np.where(df['Churn']=='Yes', 1, 0)\ncols = ['MonthlyCharges', 'tenure', 'gender_cat', 'InternetService_cat', 'OnlineSecurity_cat']","metadata":{"tags":[],"cell_id":"0cc961b4e48348e0a0e3c524afc8b65e","source_hash":"229ca32c","execution_start":1677703523251,"execution_millis":10,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":6},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nX = df[cols]\ny = df['Churn']\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)","metadata":{"tags":[],"cell_id":"231be36cfe4f47d99b55f9eb9212f783","source_hash":"e60adda4","execution_start":1677703536430,"execution_millis":888,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":7},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.model_selection import train_test_split\n\nclass CustomClassifier(RandomForestClassifier):\n def __init__(self, test_size=0.2, **kwargs):\n super().__init__(**kwargs)\n self.test_size = test_size\n \n def split_data(self):\n self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size=self.test_size, random_state=42)","metadata":{"tags":[],"cell_id":"aab6726c002f4360818991abdefc7290","source_hash":"e43011cf","execution_start":1677703686000,"execution_millis":3,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":10},{"cell_type":"code","source":"rf_model = CustomClassifier(0.2)\nrf_model.split_data()\nrf_model.fit(rf_model.X_train, rf_model.y_train)","metadata":{"tags":[],"cell_id":"4bdd5752b11a460591fac71ce83399ac","source_hash":"187c58df","execution_start":1677703747150,"execution_millis":339,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"output_type":"execute_result","execution_count":12,"data":{"text/plain":"CustomClassifier()","text/html":"
CustomClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
"},"metadata":{}}],"execution_count":12},{"cell_type":"code","source":"importances = dict(zip(rf_model.feature_names_in_, rf_model.feature_importances_))\nprint(\"Feature Importances: \", importances)","metadata":{"tags":[],"cell_id":"8f4b23aa7ec74a50a3c6382a89c279cf","source_hash":"99041d00","execution_start":1677703749076,"execution_millis":11,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stdout","text":"Feature Importances: {'MonthlyCharges': 0.5180558283262937, 'tenure': 0.3413313756123183, 'gender_cat': 0.016723313513091347, 'InternetService_cat': 0.041761327477205484, 'OnlineSecurity_cat': 0.08212815507109114}\n","output_type":"stream"}],"execution_count":13},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\n\nclass Model:\n def __init__(self):\n self.n_estimators = 10\n self.max_depth = 10\n self.y_test = y_test\n self.y_train = y_train\n self.X_train = X_train\n self.X_test = X_test\n def fit(self):\n self.model = RandomForestClassifier(n_estimators = self.n_estimators, max_depth = self.max_depth, random_state=42)\n self.model.fit(self.X_train, self.y_train)\n\n def predict(self):\n self.y_pred = self.model.predict(X_test) \n return self.y_pred \n ","metadata":{"tags":[],"cell_id":"60e0c062244a4a739afb32cecd10ffc9","source_hash":"76d76ad1","execution_start":1677704087118,"execution_millis":3,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":20},{"cell_type":"code","source":"class ModelVisualization(Model):\n def __init__(self):\n super().__init__()\n\n def generate_confusion_matrix(self):\n cm = confusion_matrix(self.y_test, self.y_pred)\n cm = cm / cm.astype(np.float).sum(axis=1)\n sns.heatmap(cm, annot=True, cmap='Blues')","metadata":{"tags":[],"cell_id":"b2933702c0a94a79a2837c15174a2362","source_hash":"76b32020","execution_start":1677704097146,"execution_millis":6,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[],"execution_count":21},{"cell_type":"code","source":"results = ModelVisualization()\nresults.fit()\nresults.predict()\nresults.generate_confusion_matrix()","metadata":{"tags":[],"cell_id":"123098651d7541ed9d181e38d101609a","source_hash":"89d5ec2e","execution_start":1677704099484,"execution_millis":351,"deepnote_to_be_reexecuted":false,"deepnote_cell_type":"code"},"outputs":[{"name":"stderr","text":"/tmp/ipykernel_81/4231721142.py:7: DeprecationWarning: `np.float` is a deprecated alias for the builtin `float`. To silence this warning, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.\nDeprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n cm = cm / cm.astype(np.float).sum(axis=1)\n","output_type":"stream"},{"data":{"text/plain":"
","image/png":"iVBORw0KGgoAAAANSUhEUgAAAgMAAAGdCAYAAACPX3D5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAoIElEQVR4nO3de3xU9Z3/8fdMIBMiEi6RCcZoRMpNkNCExKC2XkbjahHaqlGrxKh4LfXhVCtRSVSqg6KYKtFYJIqoS1aLl1+1QR1lu5ZobCJolUKRChWdIeEWCTCBmfn94e7YOQmYwQkT/L6e+ziPx/LlzPd8zz4W887n8z1nbOFwOCwAAGAse6IXAAAAEoswAACA4QgDAAAYjjAAAIDhCAMAABiOMAAAgOEIAwAAGI4wAACA4QgDAAAYrleiF/B/+oz/ZaKXAPQ4K+vuT/QSgB5puDO1W+eP58+kXR/Mi9tc3aXHhAEAAHoMm1mFc7PuFgAAdEBlAAAAK5st0Ss4qAgDAABYGdYmIAwAAGBlWGXArOgDAAA6oDIAAIAVbQIAAAxHmwAAAJiEygAAAFa0CQAAMBxtAgAAYBIqAwAAWNEmAADAcLQJAACASagMAABgRZsAAADDGdYmIAwAAGBlWGXArLsFAAAdUBkAAMDKsMoAYQAAACu7WXsGzIo+AACgAyoDAABY0SYAAMBwhj1aaFb0AQAAHVAZAADAijYBAACGo00AAABMQmUAAAAr2gQAABjOsDYBYQAAACvDKgNm3S0AAOiAygAAAFa0CQAAMBxtAgAAYBIqAwAAWNEmAADAcLQJAACASagMAABgZVhlgDAAAICVYXsGzIo+AACgAyoDAABY0SYAAMBwhrUJCAMAAFgZVhkw624BAOjhqqqqlJ2drZSUFBUUFKihoWG/51dWVmrEiBHq06ePsrKydNNNN2n37t0xXZMwAACAlc0WvyMGtbW1crvdqqioUFNTk8aNG6eioiJt2rSp0/Ofe+45zZgxQxUVFVq1apUWLFig2tpa3XbbbTFdlzAAAICFzWaL2xGLuXPnatq0aSotLdXo0aNVXV2t1NRU1dTUdHr+8uXLddJJJ+mSSy5Rdna2zjrrLF188cXfWk2wIgwAANADtLe3q7GxUS6XKzJmt9vlcrlUX1/f6WcmTpyoxsbGyA//devW6bXXXtM555wT07XZQAgAgEWsv9HvTyAQUCAQiBpzOBxyOBxRYy0tLQoGg3I6nVHjTqdTf//73zud+5JLLlFLS4tOPvlkhcNh7d27V9deey1tAgAAvjNb/A6Px6O0tLSow+PxxGWZy5Yt07333qtHH31UTU1NWrJkiV599VXNmjUrpnmoDAAA0I3KysrkdrujxqxVAUlKT09XUlKS/H5/1Ljf71dGRkanc8+cOVOXXXaZrrrqKknS2LFj1dbWpquvvlq333677Pau/c5PZQAAAIt4biB0OBzq169f1NFZGEhOTlZubq68Xm9kLBQKyev1qrCwsNN17ty5s8MP/KSkJElSOBzu8v1SGQAAwCKeewZi4Xa7VVJSory8POXn56uyslJtbW0qLS2VJE2dOlWZmZmRNsOkSZM0d+5cjR8/XgUFBVq7dq1mzpypSZMmRUJBVxAGAADoIYqLi9Xc3Kzy8nL5fD7l5OSorq4usqlww4YNUZWAO+64QzabTXfccYc2btyoI444QpMmTdI999wT03Vt4VjqCN2oz/hfJnoJQI+zsu7+RC8B6JGGO1O7df5+Fz0dt7laF0+N21zdhcoAAAAWiWoTJAphAAAAK7OyAE8TAABgOioDAABY0CYAAMBwpoUB2gQAABiOygAAABamVQYIAwAAWJgWBmgTAABgOCoDAABYmVUYIAwAAGBFmwAAABiFygAAABamVQYIAwAAWBAGAAAwnVlZgD0DAACYjsoAAAAWtAkAADCcaWGANgEAAIajMgAAgIVplQHCAAAAFqaFAdoEAAAYjsoAAABWZhUGCAMAAFjRJgAAAEahMgAAgIVplQHCAAAAFoQBAABMZ1YWYM8AAACmozIAAIAFbQJ8711z4Y90U8kZcg7qp4/WbJT7vuf114/Xd3pur1523XLFWbr0JwU6cnB/rVnv1x2/e1lvLF91kFcNxNerS2q1ZPFCbd2yWcceN1zX3Hirho8e0+m5S//fEr219I9av26tJGnYiFGaOm161Pm7du7Uwscf1rvvvK2vtm+Xc8iRmnT+xfqPyRcclPtBfJkWBmgTGOb8s36o+379U93z+J9UeMl9+nDNRr3y6A06YkDfTs+/8/pJuurnJ8t9//Ma//Pf6okX3lHtg9M0bsRRB3nlQPz8j3epnqh6UBdffo0qn3hOxw4brvKbr9e2rVs6Pf+jD/6qH51xtu793XzNeWyh0gdnqPzm67S5eVPknAVVD6qpYbl+fcc9enTREp13wS9UXXmf3ntn2UG6K+DAEQYM86tLT9eTS5Zr0Svv6u/rfJp+z2Lt2t2ukimFnZ5/yU/ydf+C17X0nU/02cbNmv/8O1r6l09042WnH+SVA/Hz0n89o6Kf/Eyucybr6OzjdP2vb5cjJUVvvPpSp+ffXH6vzv3phRr6gxHKOuZYTf9NuUKhsFY2vhc5Z9XfVur0s3+isePz5BxypM4+7+c69rjhWrPq44N0V4gnm80Wt+NQQBgwSO9eSRo/Kktvvbc6MhYOh/XWe6uVf8KxnX4muXcv7W7fEzW2a3e7Jo4/rlvXCnSXPXv2aO2aVRqXVxAZs9vtyskt0OqPP+zSHIHAbgX37lXffmmRsVFjxum9v/y3NjdvUjgc1odN7+uLf63X+Aknxv0e0P1MCwMx7xloaWlRTU2N6uvr5fP5JEkZGRmaOHGiLr/8ch1xxBFxXyTiI31AX/XqlaRNW76KGt+0uVUjsp2dfubN+lX61aWn652mtVr3rxadlj9Ck0/PUVLSofH/4IBV6/atCgWDGjBgYNR4/4GD9PmGz7o0x1PVv9PA9COUk/tNoLjmxls1b84sXf7zIiUl9ZLNbtP0W2ZqTE5uPJcPdIuYwsD777+voqIipaamyuVyafjw4ZIkv9+vhx9+WLNnz9bSpUuVl5e333kCgYACgUDUWDgUlM2eFOPy0d1unvOCHp15sVYumalwOKx1n7fo6VfeVclkftuBmZ5/pkb/412qex+er2SHIzL+//6wWKs/+UgzPZU6ImOIPl7RpOqHZn8dGvL493LIMez3nZjCwPTp03XBBReourq6Q+kjHA7r2muv1fTp01VfX7/feTwej+66666osSTnBPUekh/LchCjlq07tHdvUIMHHh41PnhQP/k2t+7zMxe658uR3EuD0g7TF83b9dtfTdY/N24+GEsG4q5f2gDZk5K01bJZcNuWzRowcNB+P7vkP5/WH557UrPmVuvY44ZHxgOB3Vo0/xHdds9cTSg8RZJ07HHDtW7tar24eBFh4BB0qJT34yWmPQMrV67UTTfd1On/kWw2m2666SatWLHiW+cpKyvT9u3bo45eTkpp3W3P3qA+WPUvnVYwIjJms9l0Wv5wNXz4z/1+NtC+V180b1evXnZNOSNHf1zWtd4q0NP07t1bw4aP0of/tvkvFAppZVODRhx/wj4/94fnnlLt0/N155wq/WDk8VF/F9y7V3v37u3w30a7PUmhUCi+NwB0g5gqAxkZGWpoaNDIkSM7/fuGhgY5nZ33nv+dw+GQ49/Ka5JoERwkDz/zlubffZkaP9mgv/7tM/3yktOU2sehp19+V5L0xKzL9MWm7Sp/5BVJ0oQxx+jIwf21cvXnyhzcX7dfc47sdpvmPvVmIm8D+E6mXHipHvKUa9iI0Ro+aoxefv457d61S65zJkuS5t5zhwalD1bJNb+SJL3w7JN6tuYx3TzzXjkzjtTWzS2SpJQ+qeqTmqrUw/pqTE6unnysUg5Hio5wDtHfVjbq7aV/1JW/dCfsPnHgTKsMxBQGbr75Zl199dVqbGzUGWecEfnB7/f75fV6NX/+fD3wwAPdslDExwuvNyl9QF+VX3eunIMO14erN2ryDVWRTYVZGQMVCoUj5zscvVVxw090bGa6duwMaOlfPtaVM5/W9h27EnULwHd2yhlF2r5tq56teUxbt2zW0GEjdNcDVZE2QbPfJ5vtm8Lpn15+Xnv37NHs8lui5rn48mt0yRXXSpJ+UzFbC3//iB6YdZt2tLbqiIwhumzaDbx06BBlWBaQLRwOh7/9tG/U1tbqoYceUmNjo4LBoCQpKSlJubm5crvduvDCCw9oIX3G//KAPgd8n62suz/RSwB6pOHO1G6d/we31MVtrn/MOTtuc3WXmN8zUFxcrHfffVc7d+7Uxo0btXHjRu3cuVPvvvvuAQcBAADwtaqqKmVnZyslJUUFBQVqaGjY57mnnnpqp+82OPfcc2O65gF/N0Hv3r01ZMiQA/04AAA9VqLaBLW1tXK73aqurlZBQYEqKytVVFSk1atXa/DgwR3OX7Jkidrb2yN/3rx5s8aNG6cLLoitPcUbCAEAsEjUGwjnzp2radOmqbS0VKNHj1Z1dbVSU1NVU1PT6fkDBw5URkZG5HjjjTeUmppKGAAAoCcJBAJqbW2NOqwv3pOk9vZ2NTY2yuVyRcbsdrtcLte3vr/n/yxYsEAXXXSRDjvssJjWSBgAAMDCZovf4fF4lJaWFnV4PJ4O12xpaVEwGOzwiL7T6Yy8/n9/Ghoa9Le//U1XXXVVzPd7wHsGAAD4vrLb47dpoKysTG539PsmrO/aiYcFCxZo7Nixys+P/W2+hAEAALpRZy/a60x6erqSkpLk9/ujxv1+vzIyMvb72ba2Ni1evFh33333Aa2RNgEAABbxbBN0VXJysnJzc+X1eiNjoVBIXq9XhYWF+/3s888/r0AgoEsvvfSA7pfKAAAAFol6HbHb7VZJSYny8vKUn5+vyspKtbW1qbS0VJI0depUZWZmdthzsGDBAk2ZMkWDBu3/y7b2hTAAAEAPUVxcrObmZpWXl8vn8yknJ0d1dXWRTYUbNmyQ3R5d1F+9erXeeecdvf766wd83ZhfR9xdeB0x0BGvIwY6192vIx478424zfXRrDPjNld3oTIAAIAF31oIAIDhTAsDPE0AAIDhqAwAAGBhWGGAMAAAgBVtAgAAYBQqAwAAWBhWGCAMAABgRZsAAAAYhcoAAAAWhhUGCAMAAFjRJgAAAEahMgAAgIVhhQHCAAAAVqa1CQgDAABYGJYF2DMAAIDpqAwAAGBBmwAAAMMZlgVoEwAAYDoqAwAAWNAmAADAcIZlAdoEAACYjsoAAAAWtAkAADCcaWGANgEAAIajMgAAgIVhhQHCAAAAVqa1CQgDAABYGJYF2DMAAIDpqAwAAGBBmwAAAMMZlgVoEwAAYDoqAwAAWNgNKw0QBgAAsDAsC9AmAADAdFQGAACw4GkCAAAMZzcrCxAGAACwMq0ywJ4BAAAMR2UAAAALwwoDVAYAALCyxfF/YlVVVaXs7GylpKSooKBADQ0N+z1/27ZtuuGGGzRkyBA5HA4NHz5cr732WkzXpDIAAEAPUVtbK7fbrerqahUUFKiyslJFRUVavXq1Bg8e3OH89vZ2nXnmmRo8eLBeeOEFZWZmav369erfv39M1yUMAABgkainCebOnatp06aptLRUklRdXa1XX31VNTU1mjFjRofza2pqtGXLFi1fvly9e/eWJGVnZ8d8XdoEAABY2Gy2uB2BQECtra1RRyAQ6HDN9vZ2NTY2yuVyRcbsdrtcLpfq6+s7Xecrr7yiwsJC3XDDDXI6nRozZozuvfdeBYPBmO6XMAAAQDfyeDxKS0uLOjweT4fzWlpaFAwG5XQ6o8adTqd8Pl+nc69bt04vvPCCgsGgXnvtNc2cOVMPPvigfvvb38a0RtoEAABYxPNpgrKyMrnd7qgxh8MRl7lDoZAGDx6s3//+90pKSlJubq42btyoOXPmqKKiosvzEAYAALCI57cWOhyOLv3wT09PV1JSkvx+f9S43+9XRkZGp58ZMmSIevfuraSkpMjYqFGj5PP51N7eruTk5C6tkTYBAAA9QHJysnJzc+X1eiNjoVBIXq9XhYWFnX7mpJNO0tq1axUKhSJja9as0ZAhQ7ocBCTCAAAAHdhs8Tti4Xa7NX/+fC1cuFCrVq3Sddddp7a2tsjTBVOnTlVZWVnk/Ouuu05btmzRjTfeqDVr1ujVV1/VvffeqxtuuCGm69ImAADAIlHfTVBcXKzm5maVl5fL5/MpJydHdXV1kU2FGzZskN3+ze/xWVlZWrp0qW666SadcMIJyszM1I033qhbb701puvawuFwOK53coD6jP9lopcA9Dgr6+5P9BKAHmm4M7Vb57/gqaa4zfX85T+M21zdhTYBAACGo00AAIBFPJ8mOBQQBgAAsDArCtAmAADAeFQGAACwSNTTBIlCGAAAwCJR31qYKLQJAAAwHJUBAAAsaBMAAGA4w7IAbQIAAExHZQAAAAvaBAAAGM60pwkIAwAAWJhWGWDPAAAAhqMyAACAhVl1AcIAAAAdmPathbQJAAAwHJUBAAAsDCsMEAYAALDiaQIAAGAUKgMAAFgYVhggDAAAYMXTBAAAwChUBgAAsDCsMEAYAADAyrSnCXpMGNj6/rxELwHoce58fU2ilwD0SLPPGd6t85vWQzftfgEAgEWPqQwAANBT0CYAAMBwdrOyAG0CAABMR2UAAAAL0yoDhAEAACxM2zNAmwAAAMNRGQAAwII2AQAAhjOsS0CbAAAA01EZAADAwrSvMCYMAABgYVrZnDAAAICFYYUB48IPAACwIAwAAGBht9nidsSqqqpK2dnZSklJUUFBgRoaGvZ57lNPPSWbzRZ1pKSkxH6/MX8CAIDvOZstfkcsamtr5Xa7VVFRoaamJo0bN05FRUXatGnTPj/Tr18/ffnll5Fj/fr1Md8vYQAAgB5i7ty5mjZtmkpLSzV69GhVV1crNTVVNTU1+/yMzWZTRkZG5HA6nTFflzAAAICF3Ra/IxAIqLW1NeoIBAIdrtne3q7Gxka5XK5v1mG3y+Vyqb6+fp9r3bFjh4455hhlZWVp8uTJ+vjjj2O/35g/AQDA91w89wx4PB6lpaVFHR6Pp8M1W1paFAwGO/xm73Q65fP5Ol3niBEjVFNTo5dfflnPPPOMQqGQJk6cqM8//zym++XRQgAAulFZWZncbnfUmMPhiMvchYWFKiwsjPx54sSJGjVqlB5//HHNmjWry/MQBgAAsIjnewYcDkeXfvinp6crKSlJfr8/atzv9ysjI6NL1+rdu7fGjx+vtWvXxrRG2gQAAFjEc89AVyUnJys3N1derzcyFgqF5PV6o377359gMKiPPvpIQ4YMiel+qQwAANBDuN1ulZSUKC8vT/n5+aqsrFRbW5tKS0slSVOnTlVmZmZkz8Hdd9+tE088UcOGDdO2bds0Z84crV+/XldddVVM1yUMAABgYVNi3kdcXFys5uZmlZeXy+fzKScnR3V1dZFNhRs2bJDd/k1Rf+vWrZo2bZp8Pp8GDBig3NxcLV++XKNHj47purZwOByO650coN17E70CoOe58/U1iV4C0CPNPmd4987/1qdxm2vG6cfFba7uQmUAAACLWHr93wdsIAQAwHBUBgAAsLAZ9h3GhAEAACxoEwAAAKNQGQAAwMKwLgFhAAAAK7thaYA2AQAAhqMyAACAhWkbCAkDAABYGNYloE0AAIDpqAwAAGBhT9AXFSUKYQAAAAvT2gSEAQAALEzbQMieAQAADEdlAAAAC9NeOkQYAADAwrAsQJsAAADTURkAAMCCNgEAAIYzLAvQJgAAwHRUBgAAsDDtN2XCAAAAFjbD+gSmhR8AAGBBZQAAAAuz6gKEAQAAOuDRQgAADGdWFGDPAAAAxqMyAACAhWFdAsIAAABWPFoIAACMQmUAAAAL035TJgwAAGBBmwAAABiFygAAABZm1QUIAwAAdECbAAAAGIXKAAAAFqb9pkwYAADAwrQ2AWEAAAALs6KAeZUQAAB6tKqqKmVnZyslJUUFBQVqaGjo0ucWL14sm82mKVOmxHxNwgAAABY2W/yOWNTW1srtdquiokJNTU0aN26cioqKtGnTpv1+7rPPPtPNN9+sU0455YDulzAAAICFXba4HbGYO3eupk2bptLSUo0ePVrV1dVKTU1VTU3NPj8TDAb1i1/8QnfddZeGDh16gPcLAAC6TSAQUGtra9QRCAQ6nNfe3q7Gxka5XK7ImN1ul8vlUn19/T7nv/vuuzV48GBdeeWVB7xGwgAAABbxbBN4PB6lpaVFHR6Pp8M1W1paFAwG5XQ6o8adTqd8Pl+n63znnXe0YMECzZ8//zvdL08TAABgYYvj8wRlZWVyu91RYw6H4zvP+9VXX+myyy7T/PnzlZ6e/p3mIgwAANCNHA5Hl374p6enKykpSX6/P2rc7/crIyOjw/mffvqpPvvsM02aNCkyFgqFJEm9evXS6tWrddxxx3VpjbQJAACwSMTTBMnJycrNzZXX642MhUIheb1eFRYWdjh/5MiR+uijj7RixYrIcd555+m0007TihUrlJWV1eVrUxkAAMAi1qcA4sXtdqukpER5eXnKz89XZWWl2traVFpaKkmaOnWqMjMz5fF4lJKSojFjxkR9vn///pLUYfzbEAYAAOghiouL1dzcrPLycvl8PuXk5Kiuri6yqXDDhg2y2+Nf1LeFw+Fw3Gc9ALv3JnoFQM9z5+trEr0EoEeafc7wbp1/6SfNcZuraPQRcZuru1AZAADAwrDvKSIMAABgFc9HCw8FPE0AAIDhqAwAAGBhN6swQBgAAMCKNgEAADAKlQEAACx4mgAAAMPRJgAAAEahMgAAgAVPE+B7ZfFzz2rhkwvU0tKs4SNGasZtMzX2hBM6PXft2n/o0Uce1qpPPtYXX2zULbeW6dKpl0ed8x9nnq4vvtjY4bPFF12i22ZWdMctAN1i3Tuv6h9vLdHur7Yq7chjdcLPrtHAYzp/xe36hjfV9J+/ixqz9+qtyXOWRP688cPl+uwvf9LWzz/Vnp1f6bSbf6f+mUO79R7QfUxrExAGvsfq/vSaHrjfozsq7tLYseP07KKFuu6aK/XyH+s0aNCgDufv3rVLR2UdpTOLztYD93k6nfPZ2hcUCgYjf1679h+65qpSnVl0drfdBxBvn3/wP/ropSeUc8ENGnDMcH36369o+ePlOrOsWo7D+3f6mV4pqTqzrPqbAcvPimBgtwYNHa3M8Sfrg9p53bd4oBsQBr7HFi18Uj87/0JN+enPJUl3VNylP/95mV5a8gddOe3qDuePGXuCxoz9umrw8EMPdjrnwIEDo/5c88TvlZV1tPIm5Md59UD3WbvsJWUXFumYApckKeeC6+Vb9b4+e+8NjXBd0OlnbLIppd+Afc559ITTJUltW/zxXzAOOp4mwPfCnvZ2rfrkY1057ZrImN1u14knTtSHKz+I2zVe/eMruqykVDbT/uXgkBXau0fbPl+r4a7zI2M2u11H/CBHW9av3ufn9rbvUt3dV0jhsNKOOk7Hn3OZ+g055mAsGQlg2n/ReJrge2rrtq0KBoMd2gGDBg1SS0tLXK7x1ltv6quvvtJ5U34al/mAgyHQ1qpwKCTH4dG/5acc3l+B1q2dfqbv4KP0w4tu1IlX3qG8X7ilUEj//fBvtGtbfP4toeex22xxOw4FcQ8D//rXv3TFFVfs95xAIKDW1taoIxAIxHsp6GYv/uEPOunkH2nwYGeilwJ0q0HZI3X0hNPVP3Oo0oeNVcEVt8nRN03/XF6X6KUBcRH3MLBlyxYtXLhwv+d4PB6lpaVFHXP2sWENB2ZA/wFKSkrS5s2bo8Y3b96s9PT07zz/F19s1HvvLtfPzj//208GehDHYf1ks9sV+Cq6CrD7q21y7GdPwL+zJ/VSWuZQtbV82R1LRA9gi+NxKIh5z8Arr7yy379ft27dt85RVlYmt9sdNRZOcsS6FOxH7+RkjRp9vN57t16nn/H1JqlQKKT33qvXRRdf+p3nf/nFJRo4cJBO+dGp33ku4GCy9+qt/kcNU/OaD3Xk2EJJUjgUUvM/Vmroyed2aY5wKKjWLz+Tc1Redy4ViXSo/BSPk5jDwJQpU2Sz2RQOh/d5zrdtJnM4HHI4on/4794b60rwbS4rKdXM227V8ceP0ZixJ+iZRQu1a9cuTfnpzyRJt5f9RoMHO3XjTb+W9PWGwE8//fTr/31PuzZt8uvvq1YpNTVVRx/zzUapUCikl19cokmTp6hXL/ag4tAz7NQpanzuIfXPGva/jxa+rGD77sjTBX99dq76pA3S8T8pkST9fel/asAxI9Q3/Ujt2bVD/3j7Re3c2qzsE8+KzNne9pV2bmvW7u1bJEk7Nn39Po6Uwwfs9ykEoCeI+b/kQ4YM0aOPPqrJkyd3+vcrVqxQbm7ud14Yvruz/+Mcbd2yRY/Oe1gtLc0aMXKUHn38CQ363zaB78svZbd90yna1LxJxedPifx54ZM1WvhkjfIm5GvBU4si4+/WL9eXX36hKT/7+UG7FyCejhp/igI7tmtV3bMKtG5VWuZQTbzmLqX876bCXVubo36pad+5Qx/81zwFWreqd2pf9T9qmH78q/vVL+PoyDlffvxe1IuJ3n/6fknSyKKLNersSw7SnSFeTHvpkC28v1/xO3HeeecpJydHd999d6d/v3LlSo0fP16hUCimhVAZADq68/U1iV4C0CPNPqfzt0XGS8O67XGbK39oWtzm6i4xVwZuueUWtbW17fPvhw0bprfffvs7LQoAABw8MYeBU045Zb9/f9hhh+nHP/7xAS8IAIBEM6tJwBsIAQDoyLA0wBsIAQAwHJUBAAAsTHuagDAAAIDFIfKVAnFDGAAAwMKwLMCeAQAATEdlAAAAK8NKA4QBAAAsTNtASJsAAADDURkAAMCCpwkAADCcYVmANgEAAKajMgAAgJVhpQHCAAAAFjxNAAAAjEJlAAAAC54mAADAcIZlAdoEAAB0YIvjEaOqqiplZ2crJSVFBQUFamho2Oe5S5YsUV5envr376/DDjtMOTk5WrRoUczXJAwAANBD1NbWyu12q6KiQk1NTRo3bpyKioq0adOmTs8fOHCgbr/9dtXX1+vDDz9UaWmpSktLtXTp0piuawuHw+F43MB3tXtvolcA9Dx3vr4m0UsAeqTZ5wzv1vk/3tgWt7mOzzysy+cWFBRowoQJmjdvniQpFAopKytL06dP14wZM7o0xw9/+EOde+65mjVrVpevS2UAAAALmy1+RyAQUGtra9QRCAQ6XLO9vV2NjY1yuVyRMbvdLpfLpfr6+m9dczgcltfr1erVq/WjH/0opvslDAAA0I08Ho/S0tKiDo/H0+G8lpYWBYNBOZ3OqHGn0ymfz7fP+bdv366+ffsqOTlZ5557rh555BGdeeaZMa2RpwkAALCI59MEZWVlcrvdUWMOhyNu8x9++OFasWKFduzYIa/XK7fbraFDh+rUU0/t8hyEAQAArOKYBhwOR5d++KenpyspKUl+vz9q3O/3KyMjY5+fs9vtGjZsmCQpJydHq1atksfjiSkM0CYAAKAHSE5OVm5urrxeb2QsFArJ6/WqsLCwy/OEQqFO9yTsD5UBAAAsEvXdBG63WyUlJcrLy1N+fr4qKyvV1tam0tJSSdLUqVOVmZkZ2XPg8XiUl5en4447ToFAQK+99poWLVqkxx57LKbrEgYAALBI1OuIi4uL1dzcrPLycvl8PuXk5Kiuri6yqXDDhg2y278p6re1ten666/X559/rj59+mjkyJF65plnVFxcHNN1ec8A0IPxngGgc939noHVvp1xm2tERmrc5uouVAYAALAw7bsJCAMAAFgZlgYIAwAAWCRqA2Gi8GghAACGozIAAIBFop4mSBTCAAAAFoZlAdoEAACYjsoAAABWhpUGCAMAAFjwNAEAADAKlQEAACx4mgAAAMMZlgVoEwAAYDoqAwAAWBlWGiAMAABgYdrTBIQBAAAsTNtAyJ4BAAAMR2UAAAALwwoDhAEAAKxoEwAAAKNQGQAAoAOzSgOEAQAALGgTAAAAo1AZAADAwrDCAGEAAAAr2gQAAMAoVAYAALDguwkAADCdWVmAMAAAgJVhWYA9AwAAmI7KAAAAFqY9TUAYAADAwrQNhLQJAAAwHJUBAACszCoMEAYAALAyLAvQJgAAwHRUBgAAsOBpAgAADMfTBAAAwChUBgAAsDCtTUBlAACAHqSqqkrZ2dlKSUlRQUGBGhoa9nnu/Pnzdcopp2jAgAEaMGCAXC7Xfs/fF8IAAAAWNlv8jljU1tbK7XaroqJCTU1NGjdunIqKirRp06ZOz1+2bJkuvvhivf3226qvr1dWVpbOOussbdy4Mbb7DYfD4diW2j127030CoCe587X1yR6CUCPNPuc4d06/7ZdwbjN1b9PUpfPLSgo0IQJEzRv3jxJUigUUlZWlqZPn64ZM2Z86+eDwaAGDBigefPmaerUqV2+LnsGAACwiOfTBIFAQIFAIGrM4XDI4XBEjbW3t6uxsVFlZWWRMbvdLpfLpfr6+i5da+fOndqzZ48GDhwY0xppEwAAYBHPNoHH41FaWlrU4fF4OlyzpaVFwWBQTqczatzpdMrn83Vp3bfeequOPPJIuVyumO6XygAAAN2orKxMbrc7asxaFYiH2bNna/HixVq2bJlSUlJi+ixhAAAAi3g+WdhZS6Az6enpSkpKkt/vjxr3+/3KyMjY72cfeOABzZ49W2+++aZOOOGEmNdImwAAACtbHI8uSk5OVm5urrxeb2QsFArJ6/WqsLBwn5+7//77NWvWLNXV1SkvL6/rF/w3VAYAAOgh3G63SkpKlJeXp/z8fFVWVqqtrU2lpaWSpKlTpyozMzOy5+C+++5TeXm5nnvuOWVnZ0f2FvTt21d9+/bt8nUJAwAAWCTquwmKi4vV3Nys8vJy+Xw+5eTkqK6uLrKpcMOGDbLbvynqP/bYY2pvb9f5558fNU9FRYXuvPPOLl+X9wwAPRjvGQA6193vGWhrj9+PxsOSe/67jdkzAACA4WgTAABg0fN/l48vwgAAAFaGpQHCAAAAFonaQJgo7BkAAMBwVAYAALCI9auHD3U95tFC9AyBQEAej0dlZWXd8u5s4FDEvwt83xEGEKW1tVVpaWnavn27+vXrl+jlAD0C/y7wfceeAQAADEcYAADAcIQBAAAMRxhAFIfDoYqKCjZJAf+Gfxf4vmMDIQAAhqMyAACA4QgDAAAYjjAAAIDhCAMAABiOMICIqqoqZWdnKyUlRQUFBWpoaEj0koCE+vOf/6xJkybpyCOPlM1m00svvZToJQHdgjAASVJtba3cbrcqKirU1NSkcePGqaioSJs2bUr00oCEaWtr07hx41RVVZXopQDdikcLIUkqKCjQhAkTNG/ePElSKBRSVlaWpk+frhkzZiR4dUDi2Ww2vfjii5oyZUqilwLEHZUBqL29XY2NjXK5XJExu90ul8ul+vr6BK4MAHAwEAaglpYWBYNBOZ3OqHGn0ymfz5egVQEADhbCAAAAhiMMQOnp6UpKSpLf748a9/v9ysjISNCqAAAHC2EASk5OVm5urrxeb2QsFArJ6/WqsLAwgSsDABwMvRK9APQMbrdbJSUlysvLU35+viorK9XW1qbS0tJELw1ImB07dmjt2rWRP//zn//UihUrNHDgQB199NEJXBkQXzxaiIh58+Zpzpw58vl8ysnJ0cMPP6yCgoJELwtImGXLlum0007rMF5SUqKnnnrq4C8I6CaEAQAADMeeAQAADEcYAADAcIQBAAAMRxgAAMBwhAEAAAxHGAAAwHCEAQAADEcYAADAcIQBAAAMRxgAAMBwhAEAAAxHGAAAwHD/H73OJLslHc7tAAAAAElFTkSuQmCC\n"},"metadata":{"image/png":{"width":515,"height":413}},"output_type":"display_data"}],"execution_count":22},{"cell_type":"markdown","source":"\nCreated in deepnote.com \nCreated in Deepnote","metadata":{"tags":[],"created_in_deepnote_cell":true,"deepnote_cell_type":"markdown"}}],"nbformat":4,"nbformat_minor":0,"metadata":{"deepnote":{},"orig_nbformat":2,"deepnote_notebook_id":"13925050acc04cc2a57a44357cb20293","deepnote_execution_queue":[]}} -------------------------------------------------------------------------------- /pareto_chart_er_readmission.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 24, 6 | "id": "9ea9df5a", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "data": { 11 | "text/plain": [ 12 | "\"\\n 'patient_id': 'Unique identifier for each patient',\\n 'readmission_flag': 'Indicates whether the patient was readmitted (1 for readmission, 0 for no readmission)',\\n 'readmission_cause': 'Specific cause or reason for patient readmission',\\n 'condition': 'Patient's medical condition or reason for initial visit/admission',\\n 'visit_date': 'Date of the patient's visit or admission'\\n\\n\"" 13 | ] 14 | }, 15 | "execution_count": 24, 16 | "metadata": {}, 17 | "output_type": "execute_result" 18 | } 19 | ], 20 | "source": [ 21 | "'''\n", 22 | " 'patient_id': 'Unique identifier for each patient',\n", 23 | " 'readmission_flag': 'Indicates whether the patient was readmitted (1 for readmission, 0 for no readmission)',\n", 24 | " 'readmission_cause': 'Specific cause or reason for patient readmission',\n", 25 | " 'condition': 'Patient\\'s medical condition or reason for initial visit/admission',\n", 26 | " 'visit_date': 'Date of the patient\\'s visit or admission'\n", 27 | "\n", 28 | "'''" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 25, 34 | "id": "8d6c208b", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import pandas as pd \n", 39 | "\n", 40 | "df = pd.read_csv(\"emergency_room_readmission_data.csv\")" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 26, 46 | "id": "7b987dc1", 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/html": [ 52 | "
\n", 53 | "\n", 66 | "\n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | "
patient_idreadmission_flagreadmission_causeconditionvisit_date
0C252061SepsisCoronary Artery Bypass Grafting2022-05-22
1B429421Adverse Drug ReactionMedication Side Effects2022-08-01
2C989471FallsTotal Hip Arthroplasty2022-08-13
3B634021FallsTotal Hip Arthroplasty2022-02-18
4B308221SepsisCoronary Artery Bypass Grafting2022-12-22
\n", 120 | "
" 121 | ], 122 | "text/plain": [ 123 | " patient_id readmission_flag readmission_cause \\\n", 124 | "0 C25206 1 Sepsis \n", 125 | "1 B42942 1 Adverse Drug Reaction \n", 126 | "2 C98947 1 Falls \n", 127 | "3 B63402 1 Falls \n", 128 | "4 B30822 1 Sepsis \n", 129 | "\n", 130 | " condition visit_date \n", 131 | "0 Coronary Artery Bypass Grafting 2022-05-22 \n", 132 | "1 Medication Side Effects 2022-08-01 \n", 133 | "2 Total Hip Arthroplasty 2022-08-13 \n", 134 | "3 Total Hip Arthroplasty 2022-02-18 \n", 135 | "4 Coronary Artery Bypass Grafting 2022-12-22 " 136 | ] 137 | }, 138 | "execution_count": 26, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "df.head()" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 27, 150 | "id": "53ea957a", 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "readmitted = df[df['readmission_flag'] == 1]" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 28, 160 | "id": "72668263", 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "cause_counts = readmitted['readmission_cause'].value_counts()" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 29, 170 | "id": "52e90dcd", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "cumulative_percent = (cause_counts.cumsum() /cause_counts.sum()) * 100" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "id": "773cdbfb", 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 30, 188 | "id": "4dc8700d", 189 | "metadata": {}, 190 | "outputs": [ 191 | { 192 | "data": { 193 | "image/png": "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\n", 194 | "text/plain": [ 195 | "
" 196 | ] 197 | }, 198 | "metadata": {}, 199 | "output_type": "display_data" 200 | } 201 | ], 202 | "source": [ 203 | "_, ax1 = plt.subplots()\n", 204 | "\n", 205 | "ax1.bar(cause_counts.index, cause_counts.values, color='tab:cyan', label='Frequency')\n", 206 | "ax1.set_xlabel('Readmission Cause')\n", 207 | "ax1.set_ylabel('Frequency', color='tab:cyan')\n", 208 | "ax1.tick_params(axis='y', labelcolor='tab:cyan')\n", 209 | "ax1.legend(loc='upper left')\n", 210 | "\n", 211 | "\n", 212 | "ax2 = ax1.twinx()\n", 213 | "ax2.plot(cause_counts.index, cumulative_percent , zorder=2, color='tab:red', label='Cumulative Percentage', marker='o')\n", 214 | "ax2.set_ylabel('Cumulative Percentage', color='tab:red')\n", 215 | "ax2.tick_params(axis='y', labelcolor='tab:red')\n", 216 | "ax2.legend(loc='upper right')\n", 217 | "\n", 218 | "\n", 219 | "plt.title('ER Readmission Pareto Chart')\n", 220 | "\n", 221 | "\n", 222 | "plt.setp(ax1.get_xticklabels(), rotation=30, horizontalalignment='right')\n", 223 | "plt.tight_layout()\n", 224 | "plt.show()" 225 | ] 226 | } 227 | ], 228 | "metadata": { 229 | "kernelspec": { 230 | "display_name": "Python 3 (ipykernel)", 231 | "language": "python", 232 | "name": "python3" 233 | }, 234 | "language_info": { 235 | "codemirror_mode": { 236 | "name": "ipython", 237 | "version": 3 238 | }, 239 | "file_extension": ".py", 240 | "mimetype": "text/x-python", 241 | "name": "python", 242 | "nbconvert_exporter": "python", 243 | "pygments_lexer": "ipython3", 244 | "version": "3.9.13" 245 | } 246 | }, 247 | "nbformat": 4, 248 | "nbformat_minor": 5 249 | } 250 | --------------------------------------------------------------------------------