is_debug = 1
import os
import re
import sys
import glob
import datetime
import pandas as pd
import numpy as np
HOME = os.path.expanduser('~')
sys.path.append(f"{HOME}/kaggle/data_analysis/library/")
sys.path.append(f"../py/")
import MS_utils
import utils, ml_utils, kaggle_utils
from utils import logger_func
try:
    if not logger:
        logger=logger_func()
except NameError:
    logger=logger_func()
import time
from sklearn.metrics import roc_auc_score, mean_squared_error

# Columns
key, target, ignore_list = MS_utils.get_basic_var()


from sklearn.metrics import mean_squared_error, roc_auc_score
#========================================================================
# Keras 
# Corporación Favorita Grocery Sales Forecasting
sys.path.append(f'{HOME}/kaggle/data_analysis/model')
from nn_keras import MS_NN
from keras import callbacks
from keras import optimizers
from keras import backend as K
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
#========================================================================

start_time = "{0:%Y%m%d_%H%M%S}".format(datetime.datetime.now())

base = utils.read_df_pkl('../input/base_group*')[[key, target, 'country_group']]

# feat_path_list = glob.glob('../features/4_winner/*.gz')
train, test = MS_utils.get_dataset(base=base, feat_path='../features/4_winner/*.gz', is_debug=False)
del base
gc.collect()

if is_debug:
    train = train.head(10000)
    test = test.head(500)
    
#========================================================================
# Categorical Encode
cat_cols = utils.get_categorical_features(df=train, ignore_list=ignore_list)
print(f"Categorical: {cat_cols}")

#Fit LabelEncoder
for col in cat_cols:
    # 最も頻度の多いカテゴリでimpute
    max_freq = list(train[col].value_counts().index)[0]
    train[col].fillna(max_freq, inplace=True)
    test[col].fillna(max_freq, inplace=True)
    le = LabelEncoder().fit(pd.concat([train[col], test[col]], axis=0).value_counts().index.tolist())
    train[col] = le.transform(train[col])
    test[col]  = le.transform(test[col])
#========================================================================

#========================================================================
# 正規化の前処理(Null埋め, inf, -infの処理) 
for col in train.columns:
    if col in ignore_list: continue
        
    train[col] = utils.impute_feature(train, col)
    test[col]  = utils.impute_feature(test, col)
#========================================================================

#========================================================================
# 正規化
from sklearn.preprocessing import StandardScaler

train_test = pd.concat([train, test], axis=0)
base_train = train_test[~train_test[target].isnull()]
base_test = train_test[train_test[target].isnull()]

use_cols = [col for col in train.columns if col not in ignore_list]
scaler = StandardScaler()
scaler.fit(train_test[use_cols])
    
train = train_test[~train_test[target].isnull()]
test = train_test[train_test[target].isnull()]

x_test = scaler.transform(test[use_cols])
         
Y = train[target]

print(f"Train: {train.shape} | Test: {test.shape}") 
# ========================================================================

#========================================================================
# CVの準備
seed = 1208
fold_n = 5
kfold = MS_utils.get_kfold(base=base_train)
kfold = zip(*kfold)
#========================================================================


#========================================================================
# NN Setting
from nn_keras import MS_NN
# NN Model Setting 
if is_debug:
    N_EPOCHS = 2
else:
    N_EPOCHS = 10
# learning_rate = 1e-4
learning_rate = 1e-3
first_batch=10 # 7: 128
from adabound import AdaBound

adabound = AdaBound(lr=learning_rate,
                final_lr=0.1,
                gamma=1e-03,
                weight_decay=0.,
                amsbound=False)


model = MS_NN(input_cols=len(use_cols))
metric = "accuracy"

opt = optimizers.Adam(lr=learning_rate)
model.compile(loss="binary_crossentropy", optimizer=adabound, metrics=[metric])

callbacks = [
    EarlyStopping(monitor='val_loss', patience=2, verbose=0),
    ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=7, verbose=1, epsilon=1e-4)
]
#========================================================================

#========================================================================
# Result Box
model_list = []
result_list = []
score_list = []
val_pred_list = []

oof_pred = np.zeros(len(train))
test_pred = np.zeros(len(test))
#========================================================================

#========================================================================
# Train & Prediction Start

for num_fold, (trn_idx, val_idx) in enumerate(kfold):
    
#     #========================================================================
#     # 複数スレッドでkfoldを取得する
#     get_fold = np.int(sys.argv[3])
#     if num_fold!=get_fold:
#         continue
#     #========================================================================

    #========================================================================
    # Make Dataset
    X_train, y_train = train.iloc[trn_idx, :][use_cols], Y.iloc[trn_idx]
    X_val, y_val = train.iloc[val_idx, :][use_cols], Y.iloc[val_idx]
    print(X_train.shape, X_val.shape)
    print(f"Target Min --- Train: {y_train.min()} Valid: {y_val.min()}")
    print(f"Target Min Count --- Train: {np.sum(y_train==y_train.min())} Valid: {np.sum(y_val==y_val.min())}")
    
    X_train[:] = scaler.transform(X_train)
    X_val[:] = scaler.transform(X_val)
    X_train = X_train.as_matrix()
    X_val = X_val.as_matrix()
    #========================================================================
    
    batch_size = 2**(first_batch)
    model.fit(x=X_train, y=y_train, validation_data=(X_val, y_val)
              , batch_size=2**first_batch, epochs=N_EPOCHS
              , verbose=2, callbacks=callbacks)
    

    #========================================================================
    # OOF
    y_pred = np.squeeze(model.predict(X_val))
    oof_pred[val_idx] = y_pred
    # Test
    test_pred += np.squeeze(model.predict(x_test))
    #========================================================================
    
    #========================================================================
    # Scorring
    score = roc_auc_score(y_val, y_pred)
    print(f'AUC: {score}')
    score_list.append(score)
    #========================================================================

cv_score = np.mean(score_list)
logger.info(f'''
#========================================================================
# CV SCORE AVG: {cv_score}
#========================================================================''')

test_pred /= num_+1

base_train['prediction'] = oof_pred
base_test['prediction'] = test_pred

#========================================================================
# Stacking
df_stack = pd.concat([base_train, test], axis=0, ignore_index=True)
print(f"DF Stack Shape: {df_stack.shape}")    
utils.to_pkl_gzip(obj=df_stack[[key, 'prediction']], path=f'../stack/{start_time[4:12]}_MS_stack_NN_E{set_no+1}_batch{batch_size}_epoch{N_EPOCHS}_CV{cv_score}')
#========================================================================