<jupyter_start><jupyter_text>Import Libraries<jupyter_code>import pandas as pd
import joblib
import numpy as np
import json

from xgboost import XGBClassifier

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler,OneHotEncoder
from sklearn.impute import SimpleImputer
#In case I need to update datarobot-drum
!pip install datarobot-drum --upgrade<jupyter_output><empty_output><jupyter_text>Import Data<jupyter_code>train = pd.read_csv('../data/readmissions_train.csv')

X = train.drop('readmitted',axis=1)
X.drop(['diag_1_desc', 'diag_1', 'diag_2', 'diag_3'],axis=1,inplace=True)
y = train.pop('readmitted')<jupyter_output><empty_output><jupyter_text>Define Preprocessing step per type of column<jupyter_code>#Preprocessing for numerical features
numeric_features = list(X.select_dtypes('int64').columns)
for c in numeric_features:
    X[c] = X[c].fillna(0)
numeric_transformer = Pipeline(steps=[
    ('scaler', StandardScaler())])

#Preprocessing for categorical features
categorical_features = list(X.select_dtypes('object').columns)
for c in categorical_features:
    X[c] = X[c].fillna('missing')
categorical_transformer = Pipeline(steps=[
    ('OneHotEncoder', OneHotEncoder(handle_unknown='ignore'))])

#Preprocessor with all of the steps
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)])<jupyter_output><empty_output><jupyter_text>Fit the Preprocessing Pipeline<jupyter_code># Full preprocessing pipeline
pipeline = Pipeline(steps=[('preprocessor', preprocessor)])

#Train the model-Pipeline
pipeline.fit(X,y)

#Preprocess x
preprocessed = pipeline.transform(X)

#I could also train the model with the sparse matrix. I transform it to padnas because the hook function in custom.py expected a pandas dataframe to be used for scoring.
preprocessed = pd.DataFrame.sparse.from_spmatrix(preprocessed)<jupyter_output><empty_output><jupyter_text>Train XGboost ClassifierNormally, the XGboost classifier could be part of the final scikit-learn pipeline. I am opting to keep them separate in order to create a more complicated example with different pkl files for preprocessing and scoring<jupyter_code>model = XGBClassifier(colsample_bylevel=0.2, max_depth= 10, learning_rate = 0.02, n_estimators=300)
model.fit(preprocessed, y)<jupyter_output><empty_output><jupyter_text>Save Custom Model files<jupyter_code>joblib.dump(pipeline,'custom_model/preprocessing.pkl')
joblib.dump(model, 'custom_model/model.pkl') 
!drum validation --code-dir ./custom_model --input ../data/readmissions_test.csv --target-type binary --positive-class-label True --negative-class-label False
!drum score --code-dir ./custom_model --input ../data/readmissions_test.csv --target-type binary --positive-class-label True --negative-class-label False<jupyter_output>True     False
0    0.532502  0.467498
1    0.716724  0.283276
2    0.655804  0.344196
3    0.616853  0.383147
4    0.816218  0.183782
..        ...       ...
495  0.586178  0.413822
496  0.458597  0.541403
497  0.466822  0.533178
498  0.478825  0.521175
499  0.349466  0.650534

[500 rows x 2 columns]
{'fraction_non_alphanumeric': 0.10411547911547911, 'fraction_numerical': 0.056203931203931204, 'mean_word_length': 5.44950495049505, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 24, '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': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '282965', 'license': 'Apache-2.0', 'n_tokens_mistral': 1193, 'n_tokens_neox': 1077, 'n_words': 264, 'path': 'custom_inference/python/readmissions/Readmission_level_2/Main_Script_Level_2.ipynb', 'repo_name': 'datarobot-community/custom-models', 'size': '3256'}