<jupyter_start><jupyter_text>Supervised Anomaly Detection with Random Forest Setup<jupyter_code>%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

import sqlite3

with sqlite3.connect('../../ch_11/logs/logs.db') as conn:
    logs_2018 = pd.read_sql(
        'SELECT * FROM logs WHERE datetime BETWEEN "2018-01-01" AND "2019-01-01";', 
        conn, parse_dates=['datetime'], index_col='datetime'
    )
    hackers_2018 = pd.read_sql(
        'SELECT * FROM attacks WHERE start BETWEEN "2018-01-01" AND "2019-01-01";', 
        conn, parse_dates=['start', 'end']
    ).assign(
        duration=lambda x: x.end - x.start, 
        start_floor=lambda x: x.start.dt.floor('min'),
        end_ceil=lambda x: x.end.dt.ceil('min')
    )<jupyter_output><empty_output><jupyter_text>Get training and testing sets<jupyter_code>def get_X(log, day):
    """
    Get data we can use for the X
    
    Parameters:
        - log: The logs dataframe
        - day: A day or single value we can use as a datetime index slice
    
    Returns: 
        A pandas DataFrame
    """
    return pd.get_dummies(log[day].assign(
        failures=lambda x: 1 - x.success
    ).query('failures > 0').resample('1min').agg(
        {'username':'nunique', 'failures': 'sum'}
    ).dropna().rename(
        columns={'username':'usernames_with_failures'}
    ).assign(
        day_of_week=lambda x: x.index.dayofweek, 
        hour=lambda x: x.index.hour
    ).drop(columns=['failures']), columns=['day_of_week', 'hour'])

def get_y(datetimes, hackers, resolution='1min'):
    """
    Get data we can use for the y (whether or not a hacker attempted a log in during that time).
    
    Parameters:
        - datetimes: The datetimes to check for hackers
        - hackers: The dataframe indicating when the attacks started and stopped
        - resolution: The granularity of the datetime. Default is 1 minute.
        
    Returns:
        A pandas Series of booleans.
    """
    date_ranges = hackers.apply(
        lambda x: pd.date_range(x.start_floor, x.end_ceil, freq=resolution), 
        axis=1
    )
    dates = pd.Series()
    for date_range in date_ranges:
        dates = pd.concat([dates, date_range.to_series()])
    return datetimes.isin(dates)

def get_X_y(log, day, hackers):
    """
    Get the X, y data to build a model with.
    
    Parameters:
        - log: The logs dataframe
        - day: A day or single value we can use as a datetime index slice
        - hackers: The dataframe indicating when the attacks started and stopped
        
    Returns:
        X, y tuple where X is a pandas DataFrame and y is a pandas Series
    """
    X = get_X(log, day)
    y = get_y(X.reset_index().datetime, hackers)
    return X, y<jupyter_output><empty_output><jupyter_text>Train on January 2018; test on February 2018:<jupyter_code>X_train, y_train = get_X_y(logs_2018, '2018-01', hackers_2018)
X_test, y_test = get_X_y(logs_2018, '2018-02', hackers_2018)<jupyter_output><empty_output><jupyter_text>Random ForestAccepting all the defaults.<jupyter_code>from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(random_state=0, n_estimators=100).fit(X_train, y_train)<jupyter_output><empty_output><jupyter_text>Evaluate the ModelPerformance is pretty good:<jupyter_code>from sklearn.metrics import classification_report

print(classification_report(y_test, rf.predict(X_test)))<jupyter_output>precision    recall  f1-score   support

       False       1.00      1.00      1.00     37787
        True       0.92      0.75      0.83        16

   micro avg       1.00      1.00      1.00     37803
   macro avg       0.96      0.87      0.91     37803
weighted avg       1.00      1.00      1.00     37803<jupyter_text>Examine the plots:<jupyter_code>from ml_utils.classification import confusion_matrix_visual, plot_pr_curve, plot_roc

fig, axes = plt.subplots(1, 3, figsize=(20, 5))
plot_roc(y_test, rf.predict_proba(X_test)[:,1], ax=axes[0])
confusion_matrix_visual(y_test, rf.predict(X_test), ax=axes[1], class_labels=[False, True])
plot_pr_curve(y_test, rf.predict_proba(X_test)[:,1], ax=axes[2])
plt.suptitle('Random Forest Classifier')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10916410134975137, 'fraction_numerical': 0.039782145394269476, 'mean_word_length': 3.1452404317958784, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 25, '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': '88795', 'license': 'MIT', 'n_tokens_mistral': 1545, 'n_tokens_neox': 1403, 'n_words': 366, 'path': 'solutions/ch_11/exercise_3.ipynb', 'repo_name': 'rvmuthukumar/Hands-On-Data-Analysis-with-Pandas', 'size': '4223'}