<jupyter_start><jupyter_text>Classification Problem : Credit Card Offer  Importing the librairies<jupyter_code>from imblearn.over_sampling import SMOTE
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pickle
import seaborn as sns
from sklearn.preprocessing import Normalizer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from scipy.stats import chi2_contingency
from scipy import stats
import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>1. Cleaning the CSV file to be uploaded on MySQLworkbench<jupyter_code>df = pd.read_csv('creditcardmarketing.csv') 
df.head()<jupyter_output><empty_output><jupyter_text>**We will clean the headers only.**<jupyter_code>def renaming(df):
    #removing special characters & following the snake case
    
    df.columns = df.columns.str.replace(' ', '_').str.lower()
    df.columns = df.columns.str.replace('#_','')
    return df.info()
renaming(df)<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 18000 entries, 0 to 17999
Data columns (total 17 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   customer_number       18000 non-null  int64  
 1   offer_accepted        18000 non-null  object 
 2   reward                18000 non-null  object 
 3   mailer_type           18000 non-null  object 
 4   income_level          18000 non-null  object 
 5   bank_accounts_open    18000 non-null  int64  
 6   overdraft_protection  18000 non-null  object 
 7   credit_rating         18000 non-null  object 
 8   credit_cards_held     18000 non-null  int64  
 9   homes_owned           18000 non-null  int64  
 10  household_size        18000 non-null  int64  
 11  own_your_home         18000 non-null  object 
 12  average_balance       17976 non-null  float64
 13  q1_balance            17976 non-null  float64
 14  q2_balance            17976 non-null  float64
 15  q3_balance         [...]<jupyter_text>There are 24 NAN values in the last 5 columns. The 24 rows with the non values will not be uploaded on MySQLWorkbench.<jupyter_code># saving the dataframe into CVS file for SQL 

df.to_csv('credit_card_data.csv')<jupyter_output><empty_output><jupyter_text>2. Cleaning and EDA<jupyter_code># customer_number are unique values so we pass this variable as the index
df = df.set_index('customer_number')
df = df.drop_duplicates()
df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Int64Index: 18000 entries, 1 to 18000
Data columns (total 16 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   offer_accepted        18000 non-null  object 
 1   reward                18000 non-null  object 
 2   mailer_type           18000 non-null  object 
 3   income_level          18000 non-null  object 
 4   bank_accounts_open    18000 non-null  int64  
 5   overdraft_protection  18000 non-null  object 
 6   credit_rating         18000 non-null  object 
 7   credit_cards_held     18000 non-null  int64  
 8   homes_owned           18000 non-null  int64  
 9   household_size        18000 non-null  int64  
 10  own_your_home         18000 non-null  object 
 11  average_balance       17976 non-null  float64
 12  q1_balance            17976 non-null  float64
 13  q2_balance            17976 non-null  float64
 14  q3_balance            17976 non-null  float64
 15  q4_balance         [...]<jupyter_text>2.1 - Starting with categorical data<jupyter_code>def categorical_information (df):
    for col in df.select_dtypes('object'):
        print (df[col].nunique(), '\n')
        print(df[col].value_counts(), '\n')

# get information on categorical data

categorical_information(df)<jupyter_output>2 

No     16977
Yes     1023
Name: offer_accepted, dtype: int64 

3 

Air Miles    6061
Cash Back    5999
Points       5940
Name: reward, dtype: int64 

2 

Postcard    9147
Letter      8853
Name: mailer_type, dtype: int64 

3 

Medium    9013
High      4526
Low       4461
Name: income_level, dtype: int64 

2 

No     15322
Yes     2678
Name: overdraft_protection, dtype: int64 

3 

High      6071
Medium    5972
Low       5957
Name: credit_rating, dtype: int64 

2 

Yes    11655
No      6345
Name: own_your_home, dtype: int64<jupyter_text>Create visuals for all categorical colums. We can see that the target variable 'offer_accepted' is highly imbalanced.<jupyter_code>def count_plot_cat(df):
    for col in df.select_dtypes('object'):
        sns.countplot(df[col])
        plt.show()
count_plot_cat(df)<jupyter_output><empty_output><jupyter_text>2.2  Then numerical variables **Dealing with NAN values**<jupyter_code>df.isna().sum()
# checking the rows that are null 
df[df['q1_balance'].isna()==True] 
# since there are only 24 rows with NAN values in the whole dataframe i.e 0,13% of the data, I will drop them.

df = df.dropna()
df = df.reset_index(drop=True)<jupyter_output><empty_output><jupyter_text>**Exploring data**<jupyter_code>df.describe()<jupyter_output><empty_output><jupyter_text>**Create a function to plot graphs of the continuous and discrete variables**<jupyter_code>def numerical_plotting(df):
    decimaux = df.select_dtypes('float64')
    entiers = df.select_dtypes('int64')
    
    for col in decimaux:
        sns.distplot(decimaux[col])
        plt.show()
        
    for col in entiers:
        sns.countplot(entiers[col])
        plt.show()
numerical_plotting(df)<jupyter_output><empty_output><jupyter_text>Correlation analysis Covariance visualization :we are only working on continuous variables since the discrete variables are featured as categorical variables.<jupyter_code>#checking distribution of variables

def distribution_distplot(df):
    for col in df.select_dtypes('float64'):
        sns.distplot(df[col])
        # save the figure
        # plt.savefig('covariance_account_balance.png', dpi=100, bbox_inches='tight')
        plt.show()
    
distribution_distplot(df)
def corr_matrix(df):
    corr_matrix=df.corr(method='pearson')
    fig, ax = plt.subplots(figsize=(10, 8))
    ax = sns.heatmap(corr_matrix, annot=True)
    plt.show()
corr_matrix(df)<jupyter_output><empty_output><jupyter_text>**Checking on outliers**<jupyter_code>def vizualizing_outliers(df):
    for col in df._get_numeric_data():
            sns.boxplot(df[col])
            plt.show() 
vizualizing_outliers(df)<jupyter_output><empty_output><jupyter_text>**The outliers should not have relevant impact on our analysis. we will not remove them.**  2.3 - Exploration of the target variable : "accepted_offer" We have already seen that the target variable is higly imbalanced. We will need to deal with this issue.First, we will explore colinearity between variables.<jupyter_code># average_balance vs offer_accepted

plt.figure(figsize=(6,4))
sns.barplot(data=df, y="average_balance", x="offer_accepted")
plt.show()
# Did the customers with the highest average balance accept the offer?

df.nlargest(20,columns="average_balance")[["average_balance","offer_accepted","mailer_type","income_level","own_your_home", "credit_rating","bank_accounts_open","credit_cards_held"]]
# Same question but with the customers with the lowest average balance?

df.nsmallest(20,columns="average_balance")[["average_balance","offer_accepted","mailer_type","income_level","own_your_home", "credit_rating","bank_accounts_open","credit_cards_held"]]
def count_plot_hue_target(df,columns=[], target = ''):
    for col in columns: 
        plt.figure(figsize=(6,4))
        sns.countplot(x = col, hue = target, data = df)
    plt.show()   
count_plot_hue_target(df, columns= df.select_dtypes('object'), target ='offer_accepted')<jupyter_output><empty_output><jupyter_text>**Checking correlation between categorical variables**The p_value is used for hypothesis testing and it can be used to measure dependency between two variables.The null hypothesis to be rejected is that there is no correlation between two variables.Our anlysis is based on the threshold of 0.05 for p_value. This threshold is related to a confidence interval of 95%. A p_value below 0.05 means that there is a considerable correlation between two variables, and it is likely that one of them can be dropped without decreasing the metrics of the model.<jupyter_code># function to perform ChiSquare-test for categoricalvariables
def chi_square_test(df, columns=[]):
    
    """This function returns 4 results in this order (chi-square statistic, p value, degrees of freedom, 
    expected frequencies matrix.
    """  
    for i in columns:
        for j in columns:
            if i != j:
                data_crosstab = pd.crosstab(df[i], df[j], margins = False)
                print('ChiSquare test for ',i,'and ',j,': ')
                print(chi2_contingency(data_crosstab, correction=False), '\n')
chi_square_test(df, columns= df.select_dtypes(np.object))<jupyter_output>ChiSquare test for  offer_accepted and  reward : 
(101.14767025681465, 1.0865819216542505e-22, 2, array([[5703.54277926, 5654.49627281, 5596.96094793],
       [ 343.45722074,  340.50372719,  337.03905207]])) 

ChiSquare test for  offer_accepted and  mailer_type : 
(169.8785866150883, 7.864741197933402e-39, 1, array([[8339.79250111, 8615.20749889],
       [ 502.20749889,  518.79250111]])) 

ChiSquare test for  offer_accepted and  income_level : 
(113.61760484801982, 2.1293673159685707e-25, 2, array([[4262.33005118, 4201.96512016, 8490.70482866],
       [ 256.66994882,  253.03487984,  511.29517134]])) 

ChiSquare test for  offer_accepted and  overdraft_protection : 
(0.1305604331486984, 0.7178516689441322, 1, array([[14430.99132176,  2524.00867824],
       [  869.00867824,   151.99132176]])) 

ChiSquare test for  offer_accepted and  credit_rating : 
(445.0621419387729, 2.2697803189923953e-97, 2, array([[5719.57721406, 5610.16577659, 5625.25700935],
       [ 344.42278594,  337.83422341,  [...]<jupyter_text>Based on the p-values obtained, we can say that :- the columns 'reward', 'mailer_type', 'income_level' and 'credit_rating' are correlated with our target variable : offer_accepted- There is no correlation between the other variablesTherefore, we can keep all the variables to run our machine learning algorithms.  3. Preprocessing and Modeling We will use different models to compare them and find the one which fits better our data.Based on the data we have, we have decided that we need to encode the categorical features.Regarding the numerical features, a boxcox transformation on the numerical columns could help to improve the model.As an alternative, we will apply normalize the whole dataframe (after the encoding).Finally, we will try two techniques to solve the imbalance of the target value : SMOTE (equal number of Yes and No and upsampling to weight the numbers of Yes and No.In order to train the model, we will apply two algorithms : Logistic Regression and KNN Classifier.<jupyter_code># create copies for the different runs
df1 = df.copy()
df2 = df.copy()<jupyter_output><empty_output><jupyter_text>3.1 - Preprocessing - using boxcox and SMOTE1. Boxcox transformation on numerical variables2. Encoding - get dummies3. Dealing with imbalanced - SMOTE4. Modeling  Boxcox transformationWe will apply it to help our features to have a more normal distribution.<jupyter_code>#Boxcox transformation

def boxcox_transform(df):
    numeric_cols = df.select_dtypes(np.number).columns
    _ci = {column: None for column in numeric_cols}
    for column in numeric_cols:
        # since i know any columns should take negative numbers, to avoid -inf in df
        df[column] = np.where(df[column]<=0, np.NAN, df[column]) 
        df[column] = df[column].fillna(df[column].mean())
        transformed_data, ci = stats.boxcox(df[column])
        df[column] = transformed_data
        _ci[column] = [ci] 
    return df, _ci

df, _ci = boxcox_transform(df)
df<jupyter_output><empty_output><jupyter_text>**Checking the distribution of the features after the boxcox transformation**<jupyter_code>distribution_distplot(df)<jupyter_output><empty_output><jupyter_text>**the boxcox transformation improves the normal distribution of the features, especially for the q2_balance and q4_balance columns.**<jupyter_code>#drop the target
X = df.drop('offer_accepted', axis=1)
y = df['offer_accepted']<jupyter_output><empty_output><jupyter_text>Encoding<jupyter_code>X= pd.get_dummies(X)
X<jupyter_output><empty_output><jupyter_text>Dealing with imbalanced data<jupyter_code># SMOTE
# Uses knn to create rows with similar features from the minority classes.

smote = SMOTE()

X_sm, y_sm = smote.fit_resample(X, y)
y_sm.value_counts()<jupyter_output><empty_output><jupyter_text>Modeling<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(X_sm, y_sm, test_size=0.2, random_state=42)<jupyter_output><empty_output><jupyter_text>**Logistic Regression**<jupyter_code>def logistic_regression_model(X_train, X_test, y_train, y_test):

    # defining a function to apply the logistic regression model
    
    classification = LogisticRegression(random_state=42, max_iter=10000)
    classification.fit(X_train, y_train)
    
    # and to evaluate the model
    score = classification.score(X_test, y_test)
    print('The accuracy score is: ', score, '\n')
      
    predictions = classification.predict(X_test)
    confusion_matrix(y_test, predictions)  
   
    
    cf_matrix = confusion_matrix(y_test, predictions)
    group_names = ['True NO', 'False NO',
               'False YES', 'True YES']

    group_counts = ["{0:0.0f}".format(value) for value in cf_matrix.flatten()]
    group_percentages = ["{0:.2%}".format(value) for value in cf_matrix.flatten()/np.sum(cf_matrix)]
    labels = [f"{v1}\n{v2}\n{v3}" for v1, v2, v3 in zip(group_names,group_counts,group_percentages)]
    labels = np.asarray(labels).reshape(2,2)
    sns.heatmap(cf_matrix, annot=labels, fmt='', cmap='Blues')
    print (cf_matrix)
logistic_regression_model(X_train, X_test, y_train, y_test)<jupyter_output>The accuracy score is:  0.9351223827779416 

[[3267  125]
 [ 315 3075]]<jupyter_text>**KNN-Classifier**<jupyter_code>#choose the best key value
def best_K(X_train, y_train, X_test, y_test, r):
    scores = []
    for i in r:
        model = KNeighborsClassifier(n_neighbors=i)
        model.fit(X_train, y_train)
        scores.append(model.score(X_test, y_test))
        
    plt.figure(figsize=(10,6))
    plt.plot(r,scores,color = 'blue', linestyle='dashed',
             marker='*', markerfacecolor='red', markersize=10)
    plt.title('accuracy scores vs. K Value')
    plt.xlabel('K')
    plt.ylabel('Accuracy')  
best_K(X_train, y_train, X_test, y_test, r=range(2,10))
def KNN_classifier_model(X_train, y_train, X_test, y_test,n):
    
    # define a function to apply the KNN Classifier Model
    
    knn = KNeighborsClassifier(n_neighbors=n)
    knn.fit(X_train, y_train)
    
    # and to evaluate the model
    
    print('Accuracy of K-NN classifier on test set: {:.2f}'
     .format(knn.score(X_test, y_test)))
    
    y_pred = knn.predict(X_test)
    print(confusion_matrix(y_test, y_pred))
    
    cf_matrix = confusion_matrix(y_test, y_pred)
    group_names = ['True NO', 'False NO',
               'False YES', 'True YES']

    group_counts = ["{0:0.0f}".format(value) for value in cf_matrix.flatten()]
    group_percentages = ["{0:.2%}".format(value) for value in cf_matrix.flatten()/np.sum(cf_matrix)]
    labels = [f"{v1}\n{v2}\n{v3}" for v1, v2, v3 in zip(group_names,group_counts,group_percentages)]
    labels = np.asarray(labels).reshape(2,2)
    sns.heatmap(cf_matrix, annot=labels, fmt='', cmap='Blues')
    print (cf_matrix)
   
KNN_classifier_model(X_train,y_train,X_test,y_test,2)<jupyter_output>Accuracy of K-NN classifier on test set: 0.86
[[2883  509]
 [ 439 2951]]
[[2883  509]
 [ 439 2951]]<jupyter_text>The accuracy scores obtained on the first round are high. Also, the models would be able to predict that the offer is accepted and that the offer is rejected.  3.2 - Preprocessing using BoxCox and UpSampling1. Boxcox transformation on numerical variables2. UpSampling (60-40)3. Encoding - get dummies4. Modeling In this second round, we will only change the method to deal with unbalanced data. With the SMOTE method, we are getting a dataframe with equal number of Yes and No. Here we are using the upsampling method in order to weight the target column.  Boxcox transformation We are transforming only the continuous variables as previously.<jupyter_code>#Boxcox function on continous variables
df1, _ci = boxcox_transform(df1)
df1<jupyter_output><empty_output><jupyter_text>Upsampling<jupyter_code>df1.offer_accepted.value_counts()
# Manually
# getting sample with the 60% as the minority class
Yes = df1[df1['offer_accepted'] == 'Yes'].sample(10173, replace=True)
No = df1[df1['offer_accepted'] == 'No'].sample(16955, replace=True)

upsampled1 = pd.concat([Yes,No]).sample(frac=1) # .sample(frac=1) here is just to shuffle the dataframe
upsampled1
upsampled1.offer_accepted.value_counts()<jupyter_output><empty_output><jupyter_text>Encoding<jupyter_code>X1 = upsampled1.drop('offer_accepted', axis=1)
y1 = upsampled1['offer_accepted']
X1 = pd.get_dummies(upsampled1)
X1<jupyter_output><empty_output><jupyter_text>Modeling **Logistic Regression**<jupyter_code>X1_train, X1_test, y1_train, y1_test = train_test_split(X1, y1, test_size=0.2, random_state=42)
logistic_regression_model(X1_train, X1_test, y1_train, y1_test)<jupyter_output>The accuracy score is:  1.0 

[[3389    0]
 [   0 2037]]<jupyter_text>This model seems to be overfitted. Therefore it should not perform well for future predictions. **KNN Classifier**<jupyter_code>KNN_classifier_model(X1_train,y1_train,X1_test,y1_test,5)<jupyter_output>Accuracy of K-NN classifier on test set: 0.90
[[2842  547]
 [  21 2016]]
[[2842  547]
 [  21 2016]]<jupyter_text>The accepted offers are predicted almost at 100%. This leads us to think that the model could not be generalized.  3.3 - Preprocessing using Upsampling and normalization1. Numerical columns in a list2. Dealing with imbalanced - UpSampling3. Encoding - get dummies4. train-test split5. Normalization of the numerical columns6. Modeling<jupyter_code>numerical = df2.select_dtypes(np.number)

numerical.columns
num_col = ['bank_accounts_open', 'credit_cards_held', 'homes_owned',
       'household_size', 'average_balance','q1_balance', 'q2_balance', 'q3_balance',
       'q4_balance']<jupyter_output><empty_output><jupyter_text>Upsampling<jupyter_code>df2['offer_accepted'].value_counts()
# Manually
# getting sample with the 60% as the minority class
Yes = df2[df2['offer_accepted'] == 'Yes'].sample(10173, replace=True)
No = df2[df2['offer_accepted'] == 'No'].sample(16955, replace=True)

upsampled2 = pd.concat([Yes,No]).sample(frac=1) # .sample(frac=1) here is just to shuffle the dataframe
upsampled2<jupyter_output><empty_output><jupyter_text>Encoding<jupyter_code>X2 = upsampled2.drop('offer_accepted', axis=1)
y2 = upsampled2['offer_accepted']
X2 = pd.get_dummies(upsampled2)
X2<jupyter_output><empty_output><jupyter_text>Normalizing We are normalizing the numerical variables that is to say that we will exclude the encoded categorical variables.Therefore, we will create the X dataframes on which to apply the normalization and then concatenate them to get the final dataframe on which the model will be trained.<jupyter_code># train-test split

X2_train, X2_test, y2_train, y2_test = train_test_split(X2, y2, random_state=0)
#normalizing training and testing set only on numerical variables and not the original categorical variables

X_train_n = X2_train.filter(['bank_accounts_open', 'credit_cards_held', 'homes_owned',
       'household_size', 'average_balance','q1_balance', 'q2_balance', 'q3_balance',
       'q4_balance'], axis = 1)

X_test_n = X2_test.filter(['bank_accounts_open', 'credit_cards_held', 'homes_owned',
       'household_size', 'average_balance','q1_balance', 'q2_balance', 'q3_balance',
       'q4_balance'], axis = 1)
#normalization

transformer = Normalizer()
transformer.fit(X_train_n)

# saving in a pickle
with open('std_transformer.pickle', 'wb') as file:
    pickle.dump(transformer, file)
    
# loading from a pickle  
with open('std_transformer.pickle', 'rb') as file:
    loaded_normalizer = pickle.load(file)

X_train_ = loaded_normalizer.transform(X_train_n)
X_test_ = loaded_normalizer.transform(X_test_n)
#Getting the final dataframe with the normalized variables and the encoded variables.

num_train = pd.DataFrame(X_train_, columns = num_col)
num_test = pd.DataFrame(X_test_, columns = num_col)
X2_train.columns
X_train_c = X2_train.filter(['reward_Air Miles', 'reward_Cash Back', 'reward_Points',
       'mailer_type_Letter', 'mailer_type_Postcard', 'income_level_High',
       'income_level_Low', 'income_level_Medium', 'overdraft_protection_No',
       'overdraft_protection_Yes', 'credit_rating_High', 'credit_rating_Low',
       'credit_rating_Medium', 'own_your_home_No', 'own_your_home_Yes'], axis = 1)
X_train_final= pd.concat([num_train.reset_index(drop=True), X_train_c.reset_index(drop=True)], axis=1, ignore_index=True)

X_train_final.info() #checking NAN values

X_test_c = X2_test.filter(['reward_Air Miles', 'reward_Cash Back', 'reward_Points',
       'mailer_type_Letter', 'mailer_type_Postcard', 'income_level_High',
       'income_level_Low', 'income_level_Medium', 'overdraft_protection_No',
       'overdraft_protection_Yes', 'credit_rating_High', 'credit_rating_Low',
       'credit_rating_Medium', 'own_your_home_No', 'own_your_home_Yes'], axis = 1)
X_test_final= pd.concat([num_test.reset_index(drop=True), X_test_c.reset_index(drop=True)], axis=1, ignore_index=True)

X_test_final.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 6782 entries, 0 to 6781
Data columns (total 24 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   0       6782 non-null   float64
 1   1       6782 non-null   float64
 2   2       6782 non-null   float64
 3   3       6782 non-null   float64
 4   4       6782 non-null   float64
 5   5       6782 non-null   float64
 6   6       6782 non-null   float64
 7   7       6782 non-null   float64
 8   8       6782 non-null   float64
 9   9       6782 non-null   uint8  
 10  10      6782 non-null   uint8  
 11  11      6782 non-null   uint8  
 12  12      6782 non-null   uint8  
 13  13      6782 non-null   uint8  
 14  14      6782 non-null   uint8  
 15  15      6782 non-null   uint8  
 16  16      6782 non-null   uint8  
 17  17      6782 non-null   uint8  
 18  18      6782 non-null   uint8  
 19  19      6782 non-null   uint8  
 20  20      6782 non-null   uint8  
 21  21      6782 non-null   uint8  
 22  [...]<jupyter_text>Modeling **Logistic Regression**<jupyter_code>logistic_regression_model(X_train_final, X_test_final, y2_train, y2_test)<jupyter_output>The accuracy score is:  0.7030374520790328 

[[3481  754]
 [1260 1287]]<jupyter_text>This model would not be good at predicting if the offer is accepted.  **KNN Classifier**<jupyter_code>KNN_classifier_model(X_train_final,y2_train,X_test_final,y2_test,2)<jupyter_output>Accuracy of K-NN classifier on test set: 0.97
[[4043  192]
 [   5 2542]]
[[4043  192]
 [   5 2542]]
{'fraction_non_alphanumeric': 0.09959246051961283, 'fraction_numerical': 0.060791305824418405, 'mean_word_length': 4.211725663716814, 'pattern_counts': {'":': 0, '<': 130, '<?xml version=': 0, '>': 129, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '34', 'dir': 'jupyter-structured-clean-dedup', 'id': '37876', 'license': 'MIT', 'n_tokens_mistral': 8922, 'n_tokens_neox': 7894, 'n_words': 2087, 'path': 'Classification_Project.ipynb', 'repo_name': 'diebland/Project-Mid-Term', 'size': '23556'}