# ---
# 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
# ---

# + _cell_guid="b1076dfc-b9ad-4769-8c92-a6c4dae69d19" _uuid="8f2839f25d086af736a60e9eeb907d3b93b6e0e5"
import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
from sklearn.model_selection import KFold
from sklearn.feature_selection import RFECV
import lightgbm as lgb
from tqdm import tqdm_notebook
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import LabelEncoder
import multiprocessing
from sklearn.model_selection import StratifiedKFold,TimeSeriesSplit

import gc


# + _cell_guid="79c7e3d0-c299-4dcb-8224-4455121ee9b0" _uuid="d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"
def sd(col, max_loss_limit=0.001, avg_loss_limit=0.001, na_loss_limit=0, n_uniq_loss_limit=0, fillna=0):
    """
    max_loss_limit - don't allow any float to lose precision more than this value. Any values are ok for GBT algorithms as long as you don't unique values.
                     See https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Precision_limitations_on_decimal_values_in_[0,_1]
    avg_loss_limit - same but calculates avg throughout the series.
    na_loss_limit - not really useful.
    n_uniq_loss_limit - very important parameter. If you have a float field with very high cardinality you can set this value to something like n_records * 0.01 in order to allow some field relaxing.
    """
    is_float = str(col.dtypes)[:5] == 'float'
    na_count = col.isna().sum()
    n_uniq = col.nunique(dropna=False)
    try_types = ['float16', 'float32']

    if na_count <= na_loss_limit:
        try_types = ['int8', 'int16', 'float16', 'int32', 'float32']

    for type in try_types:
        col_tmp = col

        # float to int conversion => try to round to minimize casting error
        if is_float and (str(type)[:3] == 'int'):
            col_tmp = col_tmp.copy().fillna(fillna).round()

        col_tmp = col_tmp.astype(type)
        max_loss = (col_tmp - col).abs().max()
        avg_loss = (col_tmp - col).abs().mean()
        na_loss = np.abs(na_count - col_tmp.isna().sum())
        n_uniq_loss = np.abs(n_uniq - col_tmp.nunique(dropna=False))

        if max_loss <= max_loss_limit and avg_loss <= avg_loss_limit and na_loss <= na_loss_limit and n_uniq_loss <= n_uniq_loss_limit:
            return col_tmp

    # field can't be converted
    return col


def reduce_mem_usage_sd(df, deep=True, verbose=False, obj_to_cat=False):
    numerics = ['int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64', 'float16', 'float32', 'float64']
    start_mem = df.memory_usage(deep=deep).sum() / 1024 ** 2
    for col in tqdm_notebook(df.columns):
        col_type = df[col].dtypes

        # collect stats
        na_count = df[col].isna().sum()
        n_uniq = df[col].nunique(dropna=False)
        
        # numerics
        if col_type in numerics:
            df[col] = sd(df[col])

        # strings
        if (col_type == 'object') and obj_to_cat:
            df[col] = df[col].astype('category')
        
        if verbose:
            print(f'Column {col}: {col_type} -> {df[col].dtypes}, na_count={na_count}, n_uniq={n_uniq}')
        new_na_count = df[col].isna().sum()
        if (na_count != new_na_count):
            print(f'Warning: column {col}, {col_type} -> {df[col].dtypes} lost na values. Before: {na_count}, after: {new_na_count}')
        new_n_uniq = df[col].nunique(dropna=False)
        if (n_uniq != new_n_uniq):
            print(f'Warning: column {col}, {col_type} -> {df[col].dtypes} lost unique values. Before: {n_uniq}, after: {new_n_uniq}')

    end_mem = df.memory_usage(deep=deep).sum() / 1024 ** 2
    percent = 100 * (start_mem - end_mem) / start_mem
    print('Mem. usage decreased from {:5.2f} Mb to {:5.2f} Mb ({:.1f}% reduction)'.format(start_mem, end_mem, percent))
    return df



# +
path='../input/ieee-fraud-detection/'
print('Train readed\n')
train_identity = pd.read_csv(f'{path}train_identity.csv')
train_transaction = pd.read_csv(f'{path}train_transaction.csv')
print('Test readed\n')
test_identity = pd.read_csv(f'{path}test_identity.csv')
test_transaction = pd.read_csv(f'{path}test_transaction.csv')
print('Merging\n')
train = pd.merge(train_transaction, train_identity, on='TransactionID', how='left')
test = pd.merge(test_transaction, test_identity, on='TransactionID', how='left')
del train_identity,train_transaction,test_identity,test_transaction
gc.collect()

train = reduce_mem_usage_sd(train, obj_to_cat=False)

gc.collect()

one_value_cols = [col for col in train.columns if train[col].nunique() <= 1]
one_value_cols_test = [col for col in test.columns if test[col].nunique() <= 1]

cols_to_drop =['id_22', 'id_27', 'id_08', 'dist2', 'id_07', 'id_21', 'id_24', 'id_25', 'id_18', 'id_26', 'D7']

cols_to_drop = list(set(one_value_cols+ one_value_cols_test+cols_to_drop))
train = train.sort_values('TransactionDT').drop(cols_to_drop, axis=1)
del test
gc.collect()
Cat= ['ProductCD']+['card'+str(x) for x in range(1,7)]+['addr1','addr2','P_emaildomain','R_emaildomain']+['M'+str(x) for x in range(1,10)]+['id_'+str(x) for x in range(12,39)]+['DeviceType','DeviceInfo']
Cat=[col for col in Cat if (col not in cols_to_drop)]
ToDel = ['isFraud', 'TransactionDT', 'TransactionID']
Num=[item for item in train.columns.values.tolist() if (item not in Cat+ToDel+cols_to_drop)]


# +
for f in tqdm_notebook(Cat):
  le = LabelEncoder()
  mis_pos = train[f].isnull().tolist()
  le.fit(list(train[f].astype(str).values))
  train[f] = le.transform(list(train[f].astype(str).values))
  feat_mask = [cats==(f) for cats in [x for x in train.columns]]
  train.iloc[mis_pos,feat_mask] = np.nan

gc.collect()
train = reduce_mem_usage_sd(train)
X = train.sort_values('TransactionDT').drop(ToDel, axis=1)
y = train.sort_values('TransactionDT')['isFraud']

del train
gc.collect()
X.fillna(-999, inplace=True)


# +
seed=1
params = {
                    'objective':'binary',
                    'boosting_type':'gbdt',
                    'metric':'auc',
                    'n_jobs':-1,
                    'feature_fraction': 0.44301599784064954,
                    'lambda_l1': 0.7185712774952702,
                    'lambda_l2': 0.8036657945008269,
                    'learning_rate': 0.006820638087926107,
                    'min_data_in_leaf': int(122.18518093103775),
                    'min_gain_to_split': 0.8732382864345388,
                    'min_sum_hessian_in_leaf': 0.009332742523926576,
                    'num_leaves': int(274.4907722765963),
                    'max_depth': int(30.889651140632285),
                    'tree_learner':'serial',
                    'max_bin':255,
                    'seed': seed,
                }

idxs = np.arange(X.shape[0])
cv_splits = [(idxs[:350000], idxs[400000:])]
# -

# def stupid_iter():
#     for col in [0]:
#         yield([(idxs[:350000], idxs[400000:])])
# cv_splits = stupid_iter()


fold = TimeSeriesSplit(5)
# for tr,tra in fold.split(X,y):
#     print(tra)

clf = lgb.LGBMClassifier(**params)
rfe = RFECV(estimator=clf, step=15, cv=fold, scoring='roc_auc', verbose=2)
rfe.fit(X, y)


print('Optimal number of features:', rfe.n_features_)


plt.figure(figsize=(14, 8))
plt.xlabel("Number of features selected")
plt.ylabel("Cross validation score")
plt.plot(range(1, len(rfe.grid_scores_) + 1), rfe.grid_scores_)
plt.show()


for col in X.columns[rfe.ranking_ == 1]:
    print(col)
Name=[col for col in X.columns[rfe.ranking_==1]]
pd.DataFrame(Name,columns=['Name']).to_csv('feature_selection.csv', index=False)


StarcoderdataJupyterScriptsDedupFiltered