# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# +
import cuml
import cudf
import nvcategory

import xgboost as xgb
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error, roc_auc_score
# -

#Read in the data. Notice how it decompresses as it reads the data into memory. 
gdf = cudf.read_csv('/rapids/Data/black-friday.zip')

#Taking a look at the data. We use "to_pandas()" to get the pretty printing. 
gdf.head().to_pandas()

# +
#Exercise: Let's do some descriptive statistics 

# +
#Hint: try some of the function you may know from Pandas like DataFrame.Series.max() or look up the documentation here:
# -

#grabbing the first character of the years in city string to get rid of plus sign, and converting to int
gdf['city_years'] = gdf.Stay_In_Current_City_Years.str.get(0).stoi()

#Here we can see how we can control what the value of our dummies with the replace method and turn strings to ints
gdf['City_Category'] = gdf.City_Category.str.replace('A', '1')
gdf['City_Category'] = gdf.City_Category.str.replace('B', '2')
gdf['City_Category'] = gdf.City_Category.str.replace('C', '3')
gdf['City_Category'] = gdf['City_Category'].str.stoi()

# +
#EXERCISE: replace city in the same way as City Category

# +
#Hint: the Gender column only has values 'M' and 'F'
# -

#Solution
gdf['Gender'] = gdf.Gender.str.replace('F', '1')
gdf['Gender'] = gdf.Gender.str.replace('M', '0')
gdf['Gender'] = gdf.Gender.str.stoi()

#Let's take a look at how many products we have
prod_count = cudf.Series(nvcategory.from_strings(gdf.Product_ID.data).values()).unique().count() #hideous one-liner
print("Unique Products: {}".format(prod_count))

#Let's take a look at how many primary product categories we have
#We do it differently here because the variable is a number, not a string
prod1_count = gdf.Product_Category_1.unique().count()
print("Unique Product Categories: {}".format(prod1_count))

#Filling missing values
gdf['Product_Category_2'] = gdf['Product_Category_2'].fillna(0)

# +
#EXERCISE: Make a variable that's 1 if the product is multi-category, 0 otherwise

# +
#Hint: think about how to combine the Product Category 2 and Product Category 3
# -

#Solution: 
gdf['Product_Category_3'] = gdf['Product_Category_3'].fillna(0)
gdf['multi'] = ((gdf['Product_Category_2'] + gdf['Product_Category_3'])>0).astype('int')

# +
#EXERCISE: Create a Gender/Marital Status Interaction Effect

# +
#Hint: bother Gender and Marital Status are 0/1
# -

#Solution:
gdf['gen_mar_interaction'] = gdf['Gender']*gdf['Marital_Status']

#Because Occupation is a code, it should converted into indicator variables
gdf = gdf.one_hot_encoding('Occupation', 'occ_dummy', gdf.Occupation.unique())

# +
#Dummy variable from Int
gdf = gdf.one_hot_encoding('City_Category', 'city_cat', gdf.City_Category.unique())

#Dummy from string
cat = nvcategory.from_strings(gdf.Age.data)
gdf['Age'] = cudf.Series(cat.values())
gdf = gdf.one_hot_encoding('Age', 'age', gdf.Age.unique())

#EXERCISE: Create dummy variables from Product Category 1
# -

#Solution:
gdf = gdf.one_hot_encoding('Product_Category_1', 'product', gdf.Product_Category_1.unique())

#We're going to drop th variables we've transformed
drop_list = ['User_ID', 'Age', 'Stay_In_Current_City_Years', 'City_Category','Product_ID', 'Product_Category_1', 'Product_Category_2', 'Product_Category_3']
gdf = gdf.drop(drop_list)

# +
#We're going to make a list of all the first indicator variables in a series now so it will be
#easier to exclude them when we're doing regressions later
# -

dummy_list = ['occ_dummy_0', 'city_cat_1', 'age_0', 'product_1', 'Purchase']

#All variables currently have to have the same type for some methods in cuML
for col in gdf.columns.tolist():
    gdf[col] = gdf[col].astype('float64')

train_size = round(len(gdf)*0.2)
test_size = round(len(gdf)-train_size)

train = gdf.iloc[0:train_size]

# +
#EXERCISE: Make the test set in a similar way
# -

#Solution:
gdf_train = gdf.iloc[train_size:]

#Deleting the main gdf because we're going to be making other subsets and other stuff, so it will be nice to have the memory. 
del(gdf)

y_train = gdf_train['Purchase']
X_reg = gdf_train.drop(dummy_list)

# +
# # I'm going to perform a hyperparameter search for alpha in a ridge regression
# for alpha in np.arange(0.0, 1, 0.01):
    
#     Ridge = cuml.Ridge(alpha=alpha, fit_intercept=True)
#     _fit = Ridge.fit(X_reg, y_train)
#     _y_hat = _fit.predict(X_reg)
#     _roc = roc_auc_score(y_train, _y_hat)
#     output['MSE_RIDGE_{}'.format(alpha)] = _roc

# print('MAX AUC: {}'.format(min(output, key=output.get)))

# +
# Ridge = cuml.Ridge(alpha=.1, fit_intercept=True)
# _fit = Ridge.fit(X_reg, y_train)
# _y_hat = _fit.predict(X_reg)
# _roc = roc_auc_score(y_train, _y_hat)
# output['MSE_RIDGE_{}'.format(alpha)] = _roc

# +
# y_xgb = gdf_train[['Purchase']]
# X_xgb = gdf_train.drop('Purchase')
# xgb_train_set = xgb.DMatrix(data=X_xgb, label=y_xgb)

# +
# xgb_params = {
#     'nround':100,
#     'max_depth':4,
#     'max_leaves':2**4,
#     'tree_method':'gpu_hist',
#     'n_gpus':1,
#     'loss':'ls',
#     'objective':'reg:linear',
#     'max_features':'auto',
#     'criterion':'friedman_mse',
#     'grow_policy':'lossguide',
#     'verbose':True
# }

# +
# xgb_model = xgb.train(xgb_params, dtrain=xgb_train_set)

# +
# y_hat_xgb = xgb_model.predict(xgb_train_set)

# +
# RMSE = np.sqrt(mean_squared_error(y_xgb['Purchase'].to_pandas(), y_hat_xgb)) #get out of sample RMSE too

# +
# print(RMSE)

# +
#EXERCISE: Change XGB around to predict if someone is married based on the data we have

# +
#Hint: in the xgb parameters, change the objective function to 'reg:logistic'

# +
#Solution
# y_xgb = gdf_train[['Marital_Status']]
# X_xgb = gdf_train.drop('Marital_Status')
# xgb_train_set = xgb.DMatrix(data=X_xgb, label=y_xgb)

# xgb_params = {
#     'nround':100,
#     'max_depth':4,
#     'max_leaves':2**4,
#     'tree_method':'gpu_hist',
#     'n_gpus':1,
#     'loss':'ls',
#     'objective':'reg:logistic',
#     'max_features':'auto',
#     'criterion':'friedman_mse',
#     'grow_policy':'lossguide',
#     'verbose':True
# }

# xgb_model = xgb.train(xgb_params, dtrain=xgb_train_set)
# y_hat_xgb = xgb_model.predict(xgb_train_set)
# AUC = roc_auc_score(y_xgb['Marital_Status'].to_pandas(), y_hat_xgb)
# print(AUC)

# +
#EXTRA EXERCISE: Apply kNN to the customers
#EXTRA EXERCISE: Apply PCA to data

StarcoderdataJupyterScriptsDedupFiltered