<jupyter_start><jupyter_text>Building anomaly detector<jupyter_code>import pandas as pd
import numpy as np
import pprint
import itertools
import pickle
import datetime as datetime

import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score, accuracy_score, roc_auc_score, precision_recall_curve,f1_score, fbeta_score, confusion_matrix, roc_curve, log_loss, make_scorer
from sklearn.preprocessing import StandardScaler,PolynomialFeatures

from numpy import mean
from sklearn import svm, model_selection
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, GridSearchCV, RepeatedStratifiedKFold, cross_validate

from sklearn.naive_bayes import BernoulliNB, MultinomialNB, GaussianNB
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, ExtraTreesClassifier, VotingClassifier, AdaBoostClassifier, BaggingRegressor
from sklearn.neighbors import KNeighborsClassifier


plt.style.use('ggplot')

%matplotlib inline
pd.set_option('display.max_rows', 22)
pd.set_option('display.max_columns', None)
pd.set_option('display.float_format', '{:.2f}'.format)
np.set_printoptions(suppress=True)
df = pd.read_csv("./data/WA_cngstn.csv")
df.head()
df
len(df)
df_graph = df.copy()
df_graph['StartTime(UTC)'] = pd.to_datetime(df_graph['StartTime(UTC)'])
dates = df_graph['StartTime(UTC)'].dt.date
df_2017 = df_graph[(dates>pd.Timestamp('2016-12-31').date()) & (dates<pd.Timestamp('2018-01-01'))]
df_2017.describe()
plt.figure(figsize=(20,10))
plt.plot_date(df_2017['StartTime(UTC)'], df_2017['Duration'])
df_2018 = df_graph[(dates>pd.Timestamp('2017-12-31').date()) & (dates<pd.Timestamp('2019-01-01'))]
df_2018.describe()
plt.figure(figsize=(20,10))
plt.plot_date(df_2018['StartTime(UTC)'], df_2018['Duration'])
df_2019 = df_graph[(dates>pd.Timestamp('2018-12-31').date()) & (dates<pd.Timestamp('2020-01-01'))]
df_2019.describe()
plt.figure(figsize=(20,10))
plt.plot_date(df_2019['StartTime(UTC)'], df_2019['Duration'])
df.IsAnomaly.value_counts()
df_prune = df.drop(['Type', 'TMC', 'Distance(mi)', 'EndTime(UTC)', 'Side', 'IsAnomaly', 'ZipCode'], axis = 1)
def Mon(x):
    return x.split(" ")[0].split('-')[1]
def Day(x):
    return x.split(" ")[0].split('-')[2]
def Hour(x):
    return x.split(" ")[1].split(':')[0]
def Minute(x):
    return x.split(" ")[1].split(':')[1]
df_prune['StartMonth'] = df_prune['StartTime(UTC)'].apply(lambda x: Mon(x)).astype(int)
df_prune['StartDay'] = df_prune['StartTime(UTC)'].apply(lambda x: Day(x)).astype(int)
df_prune['StartHour'] = df_prune['StartTime(UTC)'].apply(lambda x: Hour(x)).astype(int)
df_prune['StartMinute'] = df_prune['StartTime(UTC)'].apply(lambda x: Minute(x)).astype(int)
df_prune.drop('StartTime(UTC)', inplace=True, axis = 1)
df_prune.isnull().sum().sum()
df_prune.dropna(inplace=True)
df_prune.head()<jupyter_output><empty_output><jupyter_text>percentile(Duration, CAST(0.95 AS DOUBLE)123.0<jupyter_code>df_prune['IsAnomaly'] = np.where(df_prune['Duration'] >= 123, 1, 0) 
df_prune.head()
df_prune.to_csv('./data/WA_pruned.csv', index=False)
df_prune.IsAnomaly.value_counts()
df_graph = df_prune.copy()
530740/28041
def classification(X, y, classweight={0:1, 1:1}):
    ''' 
    A function to run all classification models.
    It also automatically filter and selects the best model based on predefined metric (precision, recall, f1). 
    It also prints out detailed information (scores and graph) from each model, including feature importances from Random Forest and Gradient Boosting. 

    Inputs:
    X = Your dataset without the target (y)
    y = Your target, whatever you are trying to predict ---Binary.
    classweight = Your class weights, default is 1 to 1.

    Output:
    Returns the best model based on predefined metric (precision, recall, f1).
    '''
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state=4444)
    
    model_list=[]
    
    # KNN
    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(X_train, y_train)
    
    model_list.append(knn)
    
    # Logistic
    lm = LogisticRegression(C=0.95, class_weight = classweight)
    lm.fit(X_train, y_train)
    lm_pred = lm.predict(X_test)
    
    model_list.append(lm)
    
    # Gaussian Naive Bayes
    gnb = GaussianNB()
    gnb.fit(X_train, y_train)
    
    model_list.append(gnb)
    
#     # Support Vector Machine   
#     svm = SVC(gamma='auto',probability=True, class_weight = classweight)
#     svm.fit(X_train, y_train)
    
#     model_list.append(svm)
    
    # Decision Tree
    tree = DecisionTreeClassifier(random_state=5, class_weight = classweight)
    tree.fit(X_train, y_train)
    
    model_list.append(tree)
    
    # Random Forest
    ran = RandomForestClassifier(random_state=5, class_weight = classweight)
    ran.fit(X_train, y_train)
    
    model_list.append(ran)
    
    # Gradient Boosting
    gb = GradientBoostingClassifier(n_estimators = 90, max_depth = 100)
    gb.fit(X_train, y_train)
    
    model_list.append(gb)
    
    best_f1_score = 0
    best_model = ran
    
    for model in model_list:
        print (type(model).__name__)
        print ('Accuracy: {:6.4f}'.format(accuracy_score(y_test, model.predict(X_test))))
        print("Precision: {:6.4f},   Recall: {:6.4f},   f1: {:6.4f}".format(precision_score(y_test, model.predict(X_test)), recall_score(y_test, model.predict(X_test)), f1_score(y_test, model.predict(X_test))), '\n')
        
        if f1_score(y_test, model.predict(X_test)) > best_f1_score:
            best_f1_score = f1_score(y_test, model.predict(X_test))
            best_model = model
        
        
        fpr, tpr, thresholds = roc_curve(y_test, model.predict_proba(X_test)[:,1])
        plt.plot(fpr, tpr,lw=2)
        plt.plot([0,1],[0,1],c='violet',ls='--')
        plt.xlim([-0.05,1.05])
        plt.ylim([-0.05,1.05])
        plt.xlabel('False positive rate')
        plt.ylabel('True positive rate')
        plt.title('ROC curve for ' + type(model).__name__);
        print("ROC AUC score = {:6.4f}".format(roc_auc_score(y_test, model.predict_proba(X_test)[:,1])))
        plt.show()
    
    k = list(X.columns)
#     k.remove(y.columns[0])
    pp = pprint.PrettyPrinter(indent=4)
    
    print('\n')
    print("Random Forest feature importances:",'\n')
    pp.pprint(sorted(list(zip(k, ran.feature_importances_)), key=lambda x: x[1], reverse=True))
    
    print('\n')
    print("Gradient Boosting feature importances:",'\n')
    pp.pprint(sorted(list(zip(k, gb.feature_importances_)), key=lambda x: x[1], reverse=True))
    
    print('\n')
    print("Best model is ", type(best_model).__name__)
    print("With f1 score of {:6.4f} and ROC AUC of {:6.4f}".format(f1_score(y_test, best_model.predict(X_test)), roc_auc_score(y_test, best_model.predict_proba(X_test)[:,1])))
    
    return best_model
X, y = df_prune.drop('IsAnomaly',axis=1), df_prune['IsAnomaly']
classification(X, y, classweight={0:1, 1:19})<jupyter_output>F:\Anaconda\lib\site-packages\sklearn\linear_model\_logistic.py:940: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG)<jupyter_text>Colinearity is found as expected, perhaps taking out the label and try unsupervised model would work and be more interesting than arbitrarily defined outliers?  Unsupervised Classification<jupyter_code>df_prune.head()
df_label = pd.DataFrame(np.where(df_prune['Duration'] >= 123, 1, 0))
df_label.head()<jupyter_output><empty_output><jupyter_text>Import engineered Data<jupyter_code>classi_tr = pd.read_csv('./data/Engineered/classi_tr.csv')
classi_va = pd.read_csv('./data/Engineered/classi_va.csv')
classi_all = pd.concat([classi_tr, classi_va])<jupyter_output><empty_output><jupyter_text>Kmeans<jupyter_code>from sklearn.cluster import KMeans
def finding_elbow(df, max_cluster=10):
    iner = []
    list_num_clusters = list(range(1,max_cluster+1))
    for i in list_num_clusters:
        km = KMeans(n_clusters=i,random_state=42)
        km.fit(df)
        iner.append(km.inertia_)

    plt.scatter(list_num_clusters,iner,color ='k')    
    plt.plot(list_num_clusters,iner,'gray')
    plt.xlabel('Number of Clusters')
    plt.ylabel('Inertia')
finding_elbow(df_prune, 10)<jupyter_output><empty_output><jupyter_text>n_cluster = 2 seems to be most reasonable<jupyter_code>df_prune.drop('IsAnomaly', axis = 1, inplace=True)<jupyter_output><empty_output><jupyter_text>Fitting Pre-Engineered dataset<jupyter_code>model = KMeans(n_clusters=2)
model.fit(df_prune)
clusters = pd.DataFrame(model.cluster_centers_)
clusters.columns  = list(df_prune.columns)
clusters
lab = pd.DataFrame(model.labels_)
lab[0].value_counts()<jupyter_output><empty_output><jupyter_text>Fitting Post-Engineered dataset<jupyter_code>finding_elbow(classi_all, 10)
model = KMeans(n_clusters=2)
model.fit(classi_all)
clusters = pd.DataFrame(model.cluster_centers_)
clusters.columns  = list(classi_all.columns)
clusters
lab = pd.DataFrame(model.labels_)
lab[0].value_counts()<jupyter_output><empty_output><jupyter_text>Isolation Forest  Fitting Pre-Engineered dataset<jupyter_code>from sklearn.ensemble import IsolationForest
df_enc_all= pd.read_csv('./data/classi_/df_enc_all.csv')
df_enc_all.head(5)
df_enc_all.columns
clf = IsolationForest(random_state=42, n_jobs=2,contamination = 0.05).fit(df_enc_all.drop('StartTime(UTC)', axis=1))
pred = clf.predict(df_enc_all.drop('StartTime(UTC)', axis=1))
preds_df = pd.DataFrame(pred)
preds_df[0].value_counts()
de_enc_pred = df_enc_all.copy()
de_enc_pred['isNormal'] = pred

enc_nor = de_enc_pred[de_enc_pred['isNormal'] == 1]
enc_ano = de_enc_pred[de_enc_pred['isNormal'] == -1]
nor_grp
nor_grp = enc_nor.groupby(['StartTime(UTC)','ZipCode'])[['Duration', 'Density(/sqmi)', 'Population Estimate (as of July 1) - 2018 - Both Sexes; Median age (years)', 'isWeekday', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Spring', 'Summer', 'Autum','Winter', 'Morning(6-12)', 'Noon(12-18)', 'Night(18-24)', 'midNight(24-6)', 'firstHalfHour', 'SecondHalfHour']].mean().reset_index()
nor_grp_avg = nor_grp.describe().loc[['mean']]
nor_grp_avg.rename({'mean': 'nor_mean'},inplace=True)
nor_grp_avg
ano_grp = enc_ano.groupby(['StartTime(UTC)','ZipCode'])[['Duration', 'Density(/sqmi)', 'Population Estimate (as of July 1) - 2018 - Both Sexes; Median age (years)', 'isWeekday', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Spring', 'Summer', 'Autum','Winter', 'Morning(6-12)', 'Noon(12-18)', 'Night(18-24)', 'midNight(24-6)', 'firstHalfHour', 'SecondHalfHour']].mean().reset_index()
ano_grp_avg = ano_grp.describe().loc[['mean']]
ano_grp_avg.rename({'mean': 'ano_mean'},inplace=True)
ano_grp_avg
nor_vs_ano_pre = nor_grp_avg.append(ano_grp_avg)
nor_vs_ano_pre.rename({'Population Estimate (as of July 1) - 2018 - Both Sexes; Median age (years)': 'avgAge'},axis = 1, inplace=True)
nor_vs_ano_pre_T = nor_vs_ano_pre.T
nor_vs_ano_pre_T['diff'] = abs(nor_vs_ano_pre_T['ano_mean'] - nor_vs_ano_pre_T['nor_mean'])
nor_vs_ano_pre_T['diffPercent'] = abs(nor_vs_ano_pre_T['ano_mean'] - nor_vs_ano_pre_T['nor_mean'])/nor_vs_ano_pre_T['nor_mean']*100
nor_vs_ano_pre_T.head(22)
def Mon(x):
    return x.split(" ")[0].split('-')[1]
def Day(x):
    return x.split(" ")[0].split('-')[2]
def Hour(x):
    return x.split(" ")[1].split(':')[0]

de_enc_pred['StartMonth'] = de_enc_pred['StartTime(UTC)'].apply(lambda x: Mon(x)).astype(int)
de_enc_pred['StartDay'] = de_enc_pred['StartTime(UTC)'].apply(lambda x: Day(x)).astype(int)
de_enc_pred['StartHour'] = de_enc_pred['StartTime(UTC)'].apply(lambda x: Hour(x)).astype(int)
de_enc_pred_grp = de_enc_pred.groupby(['ZipCode', 'StartMonth', 'StartDay', 'StartHour'])[['Duration', 'Density(/sqmi)', 'Population Estimate (as of July 1) - 2018 - Both Sexes; Median age (years)', 'isWeekday', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Spring', 'Summer', 'Autum','Winter', 'Morning(6-12)', 'Noon(12-18)', 'Night(18-24)', 'midNight(24-6)', 'firstHalfHour', 'SecondHalfHour', 'isNormal']].mean().reset_index()
df_temp = de_enc_pred.groupby(['ZipCode', 'StartMonth', 'StartDay', 'StartHour'])[['isNormal']].nunique()
df_temp[df_temp['isNormal'] == 2]
to_show = de_enc_pred[(de_enc_pred['ZipCode'] == 98003) & 
                (de_enc_pred['StartMonth'] == 2) & 
                (de_enc_pred['StartDay'] == 9) & 
                (de_enc_pred['StartHour'] == 7)]
to_show
to_show[['LocationLat', 'LocationLng', 'ZipCode', 'Density(/sqmi)','StartTime(UTC)', 'Severity', 'Duration', 'isNormal']]
de_enc_pred[(de_enc_pred['ZipCode'] == 99371) & 
                (de_enc_pred['StartMonth'] == 7) & 
                (de_enc_pred['StartDay'] == 17) & 
                (de_enc_pred['StartHour'] == 18)]
zip_list = list(de_enc_pred_grp['ZipCode'].unique())
mon_list = list(de_enc_pred_grp['StartMonth'].unique())
day_list = list(de_enc_pred_grp['StartDay'].unique())
hr_list = list(de_enc_pred_grp['StartHour'].unique())
for zipc in zip_list:
    print (zipc)
    for mon in mon_list:
        for day in day_list:
            for hr in hr_list:

                if len(de_enc_pred_grp.loc[(de_enc_pred_grp['ZipCode'] == zipc) & 
                                    (de_enc_pred_grp['StartMonth'] == mon) & 
                                    (de_enc_pred_grp['StartDay'] == day) &
                                    (de_enc_pred_grp['StartHour'] == hr), 'isNormal']) >= 2:
                    print (de_enc_pred_grp.loc[(de_enc_pred_grp['ZipCode'] == zipc) & 
                                    (de_enc_pred_grp['StartMonth'] == mon) & 
                                    (de_enc_pred_grp['StartDay'] == day) &
                                    (de_enc_pred_grp['StartHour'] == hr), 'isNormal'])
                    print ("zipc: " , zipc) 
                    print ("mon: " , mon) 
                    print ("day: " , day) 
                    print ("hr: " , hr) 
de_enc_pred_grp.to_csv('./data/de_enc_pred_grp.csv', index=False)
# clf.pickle
with open("models/clf.pickle", "wb") as pfile:
    pickle.dump(clf, pfile)<jupyter_output><empty_output><jupyter_text>Fitting Post-Engineered dataset<jupyter_code>clf = IsolationForest(random_state=42, n_jobs=2,contamination = 0.05).fit(classi_all)
pred = clf.predict(classi_all)
preds_df = pd.DataFrame(pred)
preds_df[0].value_counts()
classi_all_pred = classi_all.copy()
classi_all_pred['isNormal'] = pred
df_nor = classi_all_pred[classi_all_pred['isNormal'] == 1]
df_ano = classi_all_pred[classi_all_pred['isNormal'] == -1]
df_nor.describe()
df_ano.describe()
df_nor_avg = df_nor.describe().loc[['mean']]
df_nor_avg.rename({'mean': 'nor_mean'},inplace=True)
df_nor_avg
df_ano_avg = df_ano.describe().loc[['mean']]
df_ano_avg.rename({'mean': 'ano_mean'},inplace=True)
df_ano_avg
nor_vs_ano = df_nor_avg.append(df_ano_avg)
nor_vs_ano
difference = nor_vs_ano.diff().loc[['ano_mean']]
difference.rename({'ano_mean': 'difference'},inplace=True)
nor_vs_ano = nor_vs_ano.append(difference)
nor_vs_ano_T = nor_vs_ano.T
nor_vs_ano_T
nor_vs_ano_T['percentDiff'] = abs((nor_vs_ano_T['ano_mean'] - nor_vs_ano_T['nor_mean'])/nor_vs_ano_T['nor_mean'] * 100)
nor_vs_ano_T[nor_vs_ano_T['percentDiff'] > 20]<jupyter_output><empty_output><jupyter_text>Hard to interpret without actual time data  T-SNE (Abandoned, left in for future reference)<jupyter_code>from sklearn.manifold import TSNE
tsne = TSNE(learning_rate=100)
transformed = tsne.fit_transform(df_prune)
x_axis = transformed[:, 0]
y_axis = transformed[:, 1]

plt.scatter(x_axis, y_axis, c=df_label)
plt.show()<jupyter_output><empty_output><jupyter_text>DBSCAN  (Abandoned, left in for future reference)<jupyter_code>from sklearn.cluster import DBSCAN
from sklearn.decomposition import PCA
from sklearn import metrics
# Declaring Model
dbscan = DBSCAN(eps=10)

# Fitting
dbscan.fit(df_prune)
db_labels = pd.DataFrame(dbscan.labels_)
db_labels[0].value_counts()
# Transoring Using PCA
pca = PCA(n_components=1).fit(df_prune)
pca_2d = pca.transform(df_prune)
pca.explained_variance_ratio_
pca.components_
for i in range(0, pca_2d.shape[0]):
    if dbscan.labels_[i] == 0:
        c1 = plt.scatter(pca_2d[i, 0], pca_2d[i, 1], c='r', marker='+')
    elif dbscan.labels_[i] == 1:
        c2 = plt.scatter(pca_2d[i, 0], pca_2d[i, 1], c='g', marker='o')
    elif dbscan.labels_[i] == -1:
        c3 = plt.scatter(pca_2d[i, 0], pca_2d[i, 1], c='b', marker='*')

plt.legend([c1, c2, c3 ], ['Cluster 1', 'Cluster 2', 'Noise'])
plt.title('DBSCAN finds 2 clusters and Noise')
plt.show()
labels_true = np.where(df_prune['Duration'] >= 123, 1, 0)
X = StandardScaler().fit_transform(df_prune)
# #############################################################################
# Compute DBSCAN
db = DBSCAN(eps=0.5, min_samples=10).fit(X)
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_

# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)

print('Estimated number of clusters: %d' % n_clusters_)
print('Estimated number of noise points: %d' % n_noise_)
print("Homogeneity: %0.3f" % metrics.homogeneity_score(labels_true, labels))
print("Completeness: %0.3f" % metrics.completeness_score(labels_true, labels))
print("V-measure: %0.3f" % metrics.v_measure_score(labels_true, labels))
print("Adjusted Rand Index: %0.3f"
      % metrics.adjusted_rand_score(labels_true, labels))
print("Adjusted Mutual Information: %0.3f"
      % metrics.adjusted_mutual_info_score(labels_true, labels))
print("Silhouette Coefficient: %0.3f"
      % metrics.silhouette_score(X, labels))

# #############################################################################
# Plot result
import matplotlib.pyplot as plt

# Black removed and is used for noise instead.
unique_labels = set(labels)
colors = [plt.cm.Spectral(each)
          for each in np.linspace(0, 1, len(unique_labels))]
for k, col in zip(unique_labels, colors):
    if k == -1:
        # Black used for noise.
        col = [0, 0, 0, 1]

    class_member_mask = (labels == k)

    xy = X[class_member_mask & core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=14)

    xy = X[class_member_mask & ~core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=6)

plt.title('Estimated number of clusters: %d' % n_clusters_)
plt.show()<jupyter_output><empty_output><jupyter_text>OPTIC  (Abandoned, left in for future reference)<jupyter_code>from sklearn.cluster import OPTICS
clustering = OPTICS(min_samples=10, cluster_method='dbscan').fit(X)<jupyter_output><empty_output><jupyter_text>Autoencoder  (Abandoned, left in for future reference)<jupyter_code>from keras.layers import Input, Dropout, Dense, LSTM, TimeDistributed, RepeatVector
from keras.models import Model
from keras.callbacks import EarlyStopping
from keras import regularizers
# define the autoencoder network model
def autoencoder_model(X):
    inputs = Input(shape=(X.shape[1], X.shape[2]))
    L1 = LSTM(16, activation='relu', return_sequences=True, 
              kernel_regularizer=regularizers.l2(0.00))(inputs)
    L2 = LSTM(4, activation='relu', return_sequences=False)(L1)
    L3 = RepeatVector(X.shape[1])(L2)
    L4 = LSTM(4, activation='relu', return_sequences=True)(L3)
    L5 = LSTM(16, activation='relu', return_sequences=True)(L4)
    output = TimeDistributed(Dense(X.shape[2]))(L5)    
    model = Model(inputs=inputs, outputs=output)
    return model
train, test = train_test_split(df_prune, test_size=0.2)
X_train=train.copy()
X_test = test.copy()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16055431823805988, 'fraction_numerical': 0.023410047018064834, 'mean_word_length': 5.080650015046644, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 65, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '411737', 'license': 'MIT', 'n_tokens_mistral': 7902, 'n_tokens_neox': 7467, 'n_words': 1319, 'path': 'notebooks/2_Classify_Anomaly.ipynb', 'repo_name': 'opophehu/traffic_congestion_anomaly_detection', 'size': '20205'}