Source code for lucidmode.tools.visualizations


"""
# -- --------------------------------------------------------------------------------------------------- -- #
# -- Project: lucidmode                                                                                  -- #
# -- Description: A Lightweight Framework with Transparent and Interpretable Machine Learning Models.    -- #
# -- visualizations.py: python script with visualization functions                                       -- #
# -- Author: IFFranciscoME - if.francisco.me@gmail.com                                                   -- #
# -- license: GPL-3.0 License                                                                            -- #
# -- Repository: https://github.com/lucidmode/lucidmode                                                  -- #
# -- --------------------------------------------------------------------------------------------------- -- #
"""

# -- Load libraries for script
import numpy as np
import pandas as pd
import plotly.graph_objects as go

# ------------------------------------------------------------------------------------- WEIGHTS ON LAYER -- #
# --------------------------------------------------------------------------------------------------------- #

# - Weight values per layer (Colored bar for each neuron, separation of all layers).

# ------------------------------------------------------------------------------ COST FUNCTION EVOLUTION -- #
# --------------------------------------------------------------------------------------------------------- #

# - CostFunction (train-val) evolution (two lines plot with two y-axis).

# plot cost evolution
# import numpy as np
# import matplotlib.pyplot as plt
# plt.style.use('seaborn-whitegrid')
# plt.figure(figsize=(16, 4))
# plt.plot(list(J.keys()), list(J.values()), color='r', linewidth=3)
# plt.title('Cost over epochs')
# plt.xlabel('epochs')
# plt.ylabel('cost');
# plt.show()

# -------------------------------------------------------------------------------- CONVOLUTION OPERATION -- #
# --------------------------------------------------------------------------------------------------------- #

# - Convolution operation between layers.

# ---------------------------------------------------------------------------------------- IMAGE CATALOG -- #
# --------------------------------------------------------------------------------------------------------- #

# - A matrix of nxm randomly seleceted images for visual exploration

# cols = 10
# rows = 4
# fig, axs = plt.subplots(rows, cols, figsize=(16, 5))
# for i in range(rows):
    #img = 0
    #l = np.nonzero(labels == i)
    #for j in np.random.choice(l[0], cols):
        #axs[i, img].axis('off')
        #hm = images[j, :].reshape(28, 28)
        #axs[i, img].imshow(hm.astype(np.uint8))
        #img += 1

# -- -------------------------------------------- PLOT: OHLC Candlesticks + Colored Classificator Result -- #
# -- --------------------------------------------------------------------------------------------------- -- #

[docs]def ohlc_class(p_ohlc, p_theme, p_data_class, p_vlines): """ OHLC Candlestick plot with color indicator of class prediction success or failure. Parameters ---------- p_ohlc: pd.DataFrame, dict With OHLC Price data Open, Hight, Low, Close for one particular time period p_theme: dict, optional Aesthetics and labels for the plot p_data_class: array, list With the correct class, so a visual distinction will be made if prediction is correct or incorrect p_vlines: list, optional With timestamp values to generate vertical lines at those values. Returns ------- plot_ohlc_class: plotly A plotly object to use in a .show() or iplot(), plot() """ # default value for lables to use in main title, and both x and y axisp_fonts if p_theme['p_labels'] is not None: p_labels = p_theme['p_labels'] else: p_labels = {'title': 'Main title', 'x_title': 'x axis title', 'y_title': 'y axis title'} # tick values calculation for simetry in y axes y0_ticks_vals = np.arange(min(p_ohlc['low']), max(p_ohlc['high']), (max(p_ohlc['high']) - min(p_ohlc['low'])) / 5) y0_ticks_vals = np.append(y0_ticks_vals, max(p_ohlc['high'])) y0_ticks_vals = np.round(y0_ticks_vals, 4) # reset the index of the input data p_ohlc.reset_index(inplace=True, drop=True) # auxiliar lists train_val_error = [] train_val_success = [] if 'train_y' in list(p_data_class.keys())[0]: # p_ohlc has all the prices and p_data_class has the prediction classes # since p_data_class is smaller than p_ohlc, a lagged shift is needed feature_lag = int(np.where(p_ohlc['timestamp'] == p_data_class['train_y'].index[0])[0]) ohlc_lag = list(np.arange(0, feature_lag, 1)) # add vertical line to indicate where ends the ohlc lag for feature engineering p_vlines.append(p_ohlc['timestamp'][feature_lag]) p_vlines = sorted(p_vlines) # error and success in train for row_e in np.arange(0, len(p_data_class['train_y'].index.to_list()), 1): if p_data_class['train_y'][row_e] != p_data_class['train_y_pred'][row_e]: train_val_error.append(feature_lag + row_e) else: train_val_success.append(feature_lag + row_e) # accuracy in train data set train_val_acc = round(len(train_val_success) / (len(train_val_error) + len(train_val_success)), 2) # ------------------------------------------------------------------------------ In case of val set -- # if 'val_y' in list(p_data_class.keys())[0]: # p_ohlc has all the prices and p_data_class has the prediction classes # since p_data_class is smaller than p_ohlc, a lagged shift is needed feature_lag = 4# int(np.where(p_ohlc['timestamp'] == p_data_class['val_y'].index[0])[0]) ohlc_lag = list(np.arange(0, feature_lag, 1)) # add vertical line to indicate where ends the ohlc lag for feature engineering p_vlines.append(p_ohlc['timestamp'][feature_lag]) p_vlines = sorted(p_vlines) # error and success in val for row_s in np.arange(0, len(p_data_class['val_y'].index.to_list()), 1): if p_data_class['val_y'].iloc[row_s, 1] != p_data_class['val_y_pred'].iloc[row_s,1]: train_val_error.append(feature_lag + row_s) else: train_val_success.append(feature_lag + row_s) # overall accuracy train_val_acc = round(len(train_val_success) / (len(train_val_error) + len(train_val_success)), 2) # Instantiate a figure object fig_g_ohlc = go.Figure() # Layout for margin, and both x and y axes fig_g_ohlc.update_layout(margin=go.layout.Margin(l=50, r=50, b=20, t=60, pad=20), xaxis=dict(title_text=p_labels['x_title']), yaxis=dict(title_text=p_labels['y_title'])) # Add layer for coloring in gray the non predicted candles in OHLC candlestick chart fig_g_ohlc.add_trace(go.Candlestick( x=[p_ohlc['timestamp'].iloc[i] for i in ohlc_lag], open=[p_ohlc['open'].iloc[i] for i in ohlc_lag], high=[p_ohlc['high'].iloc[i] for i in ohlc_lag], low=[p_ohlc['low'].iloc[i] for i in ohlc_lag], close=[p_ohlc['close'].iloc[i] for i in ohlc_lag], increasing={'line': {'color': 'grey'}}, decreasing={'line': {'color': 'grey'}}, name='Subset/Feature-Lag')) # Add layer for the success based color of candles in OHLC candlestick chart fig_g_ohlc.add_trace(go.Candlestick( x=[p_ohlc['timestamp'].iloc[i] for i in train_val_success], open=[p_ohlc['open'].iloc[i] for i in train_val_success], high=[p_ohlc['high'].iloc[i] for i in train_val_success], low=[p_ohlc['low'].iloc[i] for i in train_val_success], close=[p_ohlc['close'].iloc[i] for i in train_val_success], increasing={'line': {'color': 'skyblue'}}, decreasing={'line': {'color': 'skyblue'}}, name='Prediction Success')) # Add layer for the error based color of candles in OHLC candlestick chart fig_g_ohlc.add_trace(go.Candlestick( x=[p_ohlc['timestamp'].iloc[i] for i in train_val_error], open=[p_ohlc['open'].iloc[i] for i in train_val_error], high=[p_ohlc['high'].iloc[i] for i in train_val_error], low=[p_ohlc['low'].iloc[i] for i in train_val_error], close=[p_ohlc['close'].iloc[i] for i in train_val_error], increasing={'line': {'color': 'red'}}, decreasing={'line': {'color': 'red'}}, name='Prediction Error')) # Update layout for the background fig_g_ohlc.update_layout(yaxis=dict(tickfont=dict(color='grey', size=p_theme['p_fonts']['font_axis']), tickvals=y0_ticks_vals), xaxis=dict(tickfont=dict(color='grey', size=p_theme['p_fonts']['font_axis']))) # Update layout for the y axis fig_g_ohlc.update_xaxes(rangebreaks=[dict(pattern="day of week", bounds=['sat', 'sun'])]) # If parameter vlines is used if p_vlines is not None: # Dynamically add vertical lines according to the provided list of x dates. shapes_list = list() for i in p_vlines: shapes_list.append({'type': 'line', 'fillcolor': p_theme['p_colors']['color_1'], 'line': {'color': p_theme['p_colors']['color_1'], 'dash': 'dashdot', 'width': 2}, 'x0': i, 'x1': i, 'xref': 'x', 'y0': min(p_ohlc['low']), 'y1': max(p_ohlc['high']), 'yref': 'y'}) # add v_lines to the layout fig_g_ohlc.update_layout(shapes=shapes_list) # Legend format fig_g_ohlc.update_layout(legend=go.layout.Legend(x=.2, y=-.3, orientation='h', bordercolor='dark grey', borderwidth=1, font=dict(size=p_theme['p_fonts']['font_axis']))) # Update layout for the background fig_g_ohlc.update_layout(title_font_size=p_theme['p_fonts']['font_title'], title=dict(x=0.5, text=p_labels['title'] + ' | acc: ' + str(train_val_acc)), yaxis=dict(title=p_labels['y_title'], titlefont=dict(size=p_theme['p_fonts']['font_axis'] + 4)), xaxis=dict(title=p_labels['x_title'], rangeslider=dict(visible=False), titlefont=dict(size=p_theme['p_fonts']['font_axis'] + 4))) # Final plot dimensions fig_g_ohlc.layout.autosize = True fig_g_ohlc.layout.width = p_theme['p_dims']['width'] fig_g_ohlc.layout.height = p_theme['p_dims']['height'] return fig_g_ohlc