python/examples/basic/Notebook_Profile_Visualizer.ipynb
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
">### 🚩 *Create a free WhyLabs account to get more value out of whylogs!*<br> \n",
">*Did you know you can store, visualize, and monitor whylogs profiles with the [WhyLabs Observability Platform](https://whylabs.ai/whylogs-free-signup?utm_source=whylogs-Github&utm_medium=whylogs-example&utm_campaign=Notebook_Profile_Visualizer)? Sign up for a [free WhyLabs account](https://whylabs.ai/whylogs-free-signup?utm_source=whylogs-Github&utm_medium=whylogs-example&utm_campaign=Notebook_Profile_Visualizer) to leverage the power of whylogs and WhyLabs together!*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Drift Analysis with Profile Visualizer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/whylabs/whylogs/blob/mainline/python/examples/basic/Notebook_Profile_Visualizer.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> This is a `whylogs v1` example. For the analog feature in `v0`, please refer to [this example](https://github.com/whylabs/whylogs/blob/maintenance/0.7.x/examples/Notebook_Profile_Visualizer.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook, we'll show how you can use whylog's Notebook Profile Visualizer to compare two different sets of the same data. This includes:\n",
"- __Data Drift__: Detecting feature drift between two datasets' distributions\n",
"- __Data Visualization__: Comparing feature's histograms and bar charts\n",
"- __Summary Statistics__: Visualizing Summary Statistics of individual features"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data Drift on Wine Quality"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To demonstrate the Profile Visualizer, let's use [UCI's Wine Quality Dataset](https://archive.ics.uci.edu/ml/datasets/wine+quality), frequently used for learning purposes. Classification is one possible task, where we predict the wine's quality based on its features, like pH, density and percent alcohol content.\n",
"\n",
"In this example, we will split the available dataset in two groups: wines with alcohol content (`alcohol` feature) below and above 11. The first group is considered our baseline (or reference) dataset, while the second will be our target dataset. The goal here is to induce a case of __Sample Selection Bias__, where the training sample is not representative of the population.\n",
"\n",
"The example used here was inspired by the article [A Primer on Data Drift](https://medium.com/data-from-the-trenches/a-primer-on-data-drift-18789ef252a6). If you're interested in more information on this use case, or the theory behind Data Drift, it's a great read!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installing Dependencies"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To use the Profile Visualizer, we'll install whylogs with the extra package `viz`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Note: you may need to restart the kernel to use updated packages.\n",
"%pip install 'whylogs[viz]'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Loading the data"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>fixed acidity</th>\n",
" <th>volatile acidity</th>\n",
" <th>citric acid</th>\n",
" <th>residual sugar</th>\n",
" <th>chlorides</th>\n",
" <th>free sulfur dioxide</th>\n",
" <th>total sulfur dioxide</th>\n",
" <th>density</th>\n",
" <th>pH</th>\n",
" <th>sulphates</th>\n",
" <th>alcohol</th>\n",
" <th>quality</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>7.4</td>\n",
" <td>0.70</td>\n",
" <td>0.00</td>\n",
" <td>1.9</td>\n",
" <td>0.076</td>\n",
" <td>11.0</td>\n",
" <td>34.0</td>\n",
" <td>0.9978</td>\n",
" <td>3.51</td>\n",
" <td>0.56</td>\n",
" <td>9.4</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>7.8</td>\n",
" <td>0.88</td>\n",
" <td>0.00</td>\n",
" <td>2.6</td>\n",
" <td>0.098</td>\n",
" <td>25.0</td>\n",
" <td>67.0</td>\n",
" <td>0.9968</td>\n",
" <td>3.20</td>\n",
" <td>0.68</td>\n",
" <td>9.8</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>7.8</td>\n",
" <td>0.76</td>\n",
" <td>0.04</td>\n",
" <td>2.3</td>\n",
" <td>0.092</td>\n",
" <td>15.0</td>\n",
" <td>54.0</td>\n",
" <td>0.9970</td>\n",
" <td>3.26</td>\n",
" <td>0.65</td>\n",
" <td>9.8</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>11.2</td>\n",
" <td>0.28</td>\n",
" <td>0.56</td>\n",
" <td>1.9</td>\n",
" <td>0.075</td>\n",
" <td>17.0</td>\n",
" <td>60.0</td>\n",
" <td>0.9980</td>\n",
" <td>3.16</td>\n",
" <td>0.58</td>\n",
" <td>9.8</td>\n",
" <td>6</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>7.4</td>\n",
" <td>0.70</td>\n",
" <td>0.00</td>\n",
" <td>1.9</td>\n",
" <td>0.076</td>\n",
" <td>11.0</td>\n",
" <td>34.0</td>\n",
" <td>0.9978</td>\n",
" <td>3.51</td>\n",
" <td>0.56</td>\n",
" <td>9.4</td>\n",
" <td>5</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" fixed acidity volatile acidity citric acid residual sugar chlorides \\\n",
"0 7.4 0.70 0.00 1.9 0.076 \n",
"1 7.8 0.88 0.00 2.6 0.098 \n",
"2 7.8 0.76 0.04 2.3 0.092 \n",
"3 11.2 0.28 0.56 1.9 0.075 \n",
"4 7.4 0.70 0.00 1.9 0.076 \n",
"\n",
" free sulfur dioxide total sulfur dioxide density pH sulphates \\\n",
"0 11.0 34.0 0.9978 3.51 0.56 \n",
"1 25.0 67.0 0.9968 3.20 0.68 \n",
"2 15.0 54.0 0.9970 3.26 0.65 \n",
"3 17.0 60.0 0.9980 3.16 0.58 \n",
"4 11.0 34.0 0.9978 3.51 0.56 \n",
"\n",
" alcohol quality \n",
"0 9.4 5 \n",
"1 9.8 5 \n",
"2 9.8 5 \n",
"3 9.8 6 \n",
"4 9.4 5 "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"pd.options.mode.chained_assignment = None # Disabling false positive warning\n",
"\n",
"# this is the same data as encountered in http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv\n",
"url = \"https://whylabs-public.s3.us-west-2.amazonaws.com/whylogs_examples/WineQuality/winequality-red.csv\"\n",
"wine = pd.read_csv(url)\n",
"wine.head()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'pandas.core.frame.DataFrame'>\n",
"RangeIndex: 1599 entries, 0 to 1598\n",
"Data columns (total 12 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 fixed acidity 1599 non-null float64\n",
" 1 volatile acidity 1599 non-null float64\n",
" 2 citric acid 1599 non-null float64\n",
" 3 residual sugar 1599 non-null float64\n",
" 4 chlorides 1599 non-null float64\n",
" 5 free sulfur dioxide 1599 non-null float64\n",
" 6 total sulfur dioxide 1599 non-null float64\n",
" 7 density 1599 non-null float64\n",
" 8 pH 1599 non-null float64\n",
" 9 sulphates 1599 non-null float64\n",
" 10 alcohol 1599 non-null float64\n",
" 11 quality 1599 non-null int64 \n",
"dtypes: float64(11), int64(1)\n",
"memory usage: 150.0 KB\n"
]
}
],
"source": [
"wine.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll split the wines in two groups. The ones with `alcohol` below 11 will form our reference sample, and the ones above 11 will form our target dataset."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"cond_reference = (wine['alcohol']<=11)\n",
"wine_reference = wine.loc[cond_reference]\n",
"\n",
"cond_target = (wine['alcohol']>11)\n",
"wine_target = wine.loc[cond_target]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's also add some missing values to `citric acid`, to see how this is reflected in the Profile Visualizer later on."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"ixs = wine_target.iloc[100:110].index\n",
"wine_target.loc[ixs,'citric acid'] = None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `quality` feature is a numerical one, representing the wine's quality. Let's tranform it to a categorical feature, where each wine is classified as Good or Bad. Anything above 6.5 is a good a wine. Otherwise, it's bad."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"bins = (2, 6.5, 8)\n",
"group_names = ['bad', 'good']\n",
"\n",
"wine_reference['quality'] = pd.cut(wine_reference['quality'], bins = bins, labels = group_names)\n",
"wine_target['quality'] = pd.cut(wine_target['quality'], bins = bins, labels = group_names)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we can profile our dataframes with `whylogs`.\n",
"The `NotebookProfileVisualizer` accepts `profile_views` as arguments. Profile views are obtained from the profiles, and are used for visualization and merging purposes."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚠️ No session found. Call whylogs.init() to initialize a session and authenticate. See https://docs.whylabs.ai/docs/whylabs-whylogs-init for more information.\n"
]
}
],
"source": [
"import whylogs as why\n",
"result = why.log(pandas=wine_target)\n",
"prof_view = result.view()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"result_ref = why.log(pandas=wine_reference)\n",
"prof_view_ref = result_ref.view()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's instantiate `NotebookProfileVisualizer` and set the reference and target profile views:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"from whylogs.viz import NotebookProfileVisualizer\n",
"\n",
"visualization = NotebookProfileVisualizer()\n",
"visualization.set_profiles(target_profile_view=prof_view, reference_profile_view=prof_view_ref)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we're able to generate all sorts of plots and reports.\n",
"Let's take a look at some of them."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Profile Summary"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"__Profile Summary__ brings you a summary for a single profile. It requires only the existence of `target_profile_view`. The report shows simple histograms for each feature, along with key statistics, such as number of missing values, mean, minimum and maximum values."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
" :root {\n",
" /* CONSTANTS */\n",
" --SIDE-PANEL-WIDTH: 320px;\n",
" --PROPERTY-PANEL-WIDTH: 420px;\n",
"\n",
" /* COLOR VARIABLES */\n",
" /** Standard colors */\n",
" --red: #d11010;\n",
" --orange: #f07028;\n",
" --yellow: #faaf40;\n",
" --olive: #b5cc18;\n",
" --green: #1dbb42;\n",
" --teal: #00b5ad;\n",
" --blue: #2683c9;\n",
" --violet: #6435c9;\n",
" --purple: #a333c8;\n",
" --pink: #ed45a4;\n",
" --brown: #ac724d;\n",
" --grey: #778183;\n",
" --black: #1b1c1d;\n",
" --white: #ffffff;\n",
"\n",
" /** Branded colors */\n",
" --brandPrimary900: #0e7384;\n",
" --brandPrimary800: #228798;\n",
" --brandPrimary700: #369bac;\n",
" --brandPrimary600: #4aafc0;\n",
" --brandPrimary500: #5ec3d4;\n",
" --brandPrimary400: #72d7e8;\n",
" --brandPrimary300: #86ebfc;\n",
" --brandPrimary200: #a6f2ff;\n",
" --brandPrimary100: #cdf8ff;\n",
" --brandSecondary900: #4f595b;\n",
" --brandSecondary800: #636d6f;\n",
" --brandSecondary700: #778183;\n",
" --brandSecondary600: #8b9597;\n",
" --brandSecondary500: #9fa9ab;\n",
" --brandSecondary400: #b3bdbf;\n",
" --brandSecondary300: #c7d1d3;\n",
" --brandSecondary200: #dbe5e7;\n",
" --brandSecondary100: #ebf2f3;\n",
" --secondaryLight1000: #313b3d;\n",
" --brandRed4: #b30000;\n",
" --brandRed3: #d72424;\n",
" --brandRed2: #eb5656;\n",
" --brandRed1: #ff8282;\n",
" --night1: #021826;\n",
" /** Purpose colors */\n",
" --textColor: #4f595b;\n",
" --linkColor: #369bac;\n",
" --infoColor: #2683c9;\n",
" --warningColor: #faaf40;\n",
" --tealBackground: #eaf2f3;\n",
" --pageBackground: #e5e5e5;\n",
" --contrastTableRow: #fafafa;\n",
" --whiteBackground: #ffffff;\n",
" --primaryBackground: #e5e5e5;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" font-family: "Asap", Arial, Helvetica, sans-serif;\n",
" }\n",
"\n",
" /*\n",
" * Main content\n",
" */\n",
" .main {\n",
" position: relative;\n",
" background: #FFFFFF;\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" }\n",
" .main .page-header {\n",
" margin-top: 0;\n",
" }\n",
"\n",
" /* CSS DIV TABLE BASIC STYLE */\n",
"\n",
" .wl-table-row .wl-table-head:first-child {\n",
" min-width: 360px;\n",
" z-index: 2;\n",
" }\n",
"\n",
" .wl-table-wrap {\n",
" position: relative;\n",
" }\n",
"\n",
" .wl-table {\n",
" display: table;\n",
" width: 100%;\n",
" }\n",
"\n",
" .row>* {\n",
" padding-right: 0 !important;\n",
" padding-left: 0 !important;\n",
" }\n",
"\n",
" .wl-table-row {\n",
" display: table-row;\n",
" }\n",
"\n",
" .wl-table-row:hover,\n",
" .wl-table-row:hover .wl-table-cell:first-child {\n",
" background-color: var(--brandSecondary100);\n",
" }\n",
" .wl-table-row--clickable:hover .wl-table-cell__title-button {\n",
" visibility: visible;\n",
" }\n",
"\n",
" .wl-table-heading {\n",
" position: sticky;\n",
" top: 0;\n",
" z-index: 900;\n",
" display: table-header-group;\n",
" font-weight: 700;\n",
" }\n",
"\n",
" .wl-table-cell,\n",
" .wl-table-head {\n",
" border-bottom: 1px solid var(--brandSecondary200);\n",
" display: table-cell;\n",
" padding: 12px 18px;\n",
" }\n",
"\n",
" .wl-table-cell {\n",
" font-size: 14px;\n",
" }\n",
"\n",
" .wl-table-cell--top-spacing {\n",
" padding-top: 35px; /* cell-top-padding + cell-title-height */\n",
" }\n",
"\n",
" .wl-table-head-wraper {\n",
" background-color: var(--white);\n",
" }\n",
"\n",
" .wl-table-head {\n",
" position: sticky;\n",
" left: 0;\n",
" min-width: 100px;\n",
" border-bottom: 2px solid var(--brandSecondary100);\n",
" font-size: 12px;\n",
" line-height: 1.67;\n",
" white-space: nowrap;\n",
" }\n",
"\n",
" .wl-sub-table-head {\n",
" position: relative;\n",
" }\n",
"\n",
" .wl-table-body {\n",
" display: table-row-group;\n",
" }\n",
"\n",
" .wl-table-row .wl-table-cell:first-child{\n",
" position: sticky;\n",
" left: 0;\n",
" background-color: var(--white);\n",
" border-right-width: 2px;\n",
" }\n",
"\n",
" /* Table custom style */\n",
"\n",
" .wl-table-cell__title-wrap {\n",
" display: flex;\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .wl-table-cell__title {\n",
" height: 25px;\n",
" margin: 0;\n",
" font-size: 14px;\n",
" font-weight: 700;\n",
" overflow: hidden;\n",
" white-space: nowrap;\n",
" text-overflow: ellipsis;\n",
" }\n",
"\n",
" .wl-table-cell__bedge-wrap {\n",
" padding: 2px 0;\n",
" white-space: nowrap;\n",
" overflow: hidden;\n",
" font-style: italic;\n",
" text-align: center;\n",
" color: var(--brandSecondary400);\n",
" }\n",
" .wl-table-cell__bedge {\n",
" height: 24px;\n",
" margin: 1px;\n",
" padding: 2px 8px;\n",
" border: 1px solid var(--brandSecondary400);\n",
" font-style: normal;\n",
" color: var(--brandSecondary900);\n",
" border-radius: 20px;\n",
" white-space: nowrap;\n",
" }\n",
"\n",
" /* Property side panel */\n",
"\n",
" .wl-compare-profile {\n",
" z-index: 999;\n",
" position: absolute;\n",
" right: 0;\n",
" background: var(--white);;\n",
" border-bottom: 1px solid var(--brandSecondary200);\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .space-between {\n",
" display: flex;\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .align-items {\n",
" display: flex;\n",
" align-items: center;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .table-border-none {\n",
" padding: 0;\n",
" border: none;\n",
" }\n",
"\n",
" .flex-direction-colum {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .align-items {\n",
" align-items: center;\n",
" }\n",
"\n",
" .search-input{\n",
" padding-top: 0 !important;\n",
" padding-bottom: 0 !important;\n",
" }\n",
"\n",
" .search-input input{\n",
" border: none;\n",
" background: none;\n",
" outline: none;\n",
" height: 40px;\n",
" width: 100%;\n",
" font-size: 14px;\n",
" }\n",
"\n",
" .search-input img{\n",
" height: 19px;\n",
" pointer-events: none;\n",
" }\n",
"\n",
" input::placeholder {\n",
" color: var(--secondaryLight1000);\n",
" }\n",
"\n",
" .text-align-center {\n",
" text-align: center;\n",
" }\n",
"\n",
" .text-align-end {\n",
" text-align: end;\n",
" }\n",
"\n",
" .drift-detection {\n",
" justify-content: space-between;\n",
" align-items: center;\n",
" }\n",
"\n",
" .drift-detection-info-circle {\n",
" width: 15px;\n",
" height: 15px;\n",
" border-radius: 50px;\n",
" display: inline-block;\n",
" margin-right: 8px;\n",
" }\n",
"\n",
" .severe-drift-circle-color {\n",
" background: #D40D00;\n",
" }\n",
"\n",
" .moderate-drift-circle-color {\n",
" background: #F5843C;\n",
" }\n",
"\n",
" .mild-drift-circle-color {\n",
" background: #F2C142;\n",
" }\n",
"\n",
" .minimal-drift-circle-color {\n",
" background: #ABCA52;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item {\n",
" padding-right: 20px;\n",
" }\n",
"\n",
" .drift-detection-info-title {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 16px;\n",
" line-height: 130%;\n",
" color: #313B3D;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-count {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 14px;\n",
" line-height: 16px;\n",
" color: #000000;\n",
" padding-right: 8px;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-name {\n",
" font-family: Arial;\n",
" font-style: normal;\n",
" font-weight: normal;\n",
" font-size: 12px;\n",
" line-height: 14px;\n",
" color: #000000;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-range {\n",
" font-family: Arial;\n",
" font-style: normal;\n",
" font-weight: normal;\n",
" font-size: 11px;\n",
" line-height: 13px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .drift-detection-search-input {\n",
" display: flex;\n",
" align-items: center;\n",
" background: rgba(255, 255, 255, 0.7);\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" width: 170px;\n",
" padding-left: 10px;\n",
" }\n",
"\n",
" .drift-detection-search-input img{\n",
" margin-right: 5px;\n",
" }\n",
"\n",
" .drift-detection-search-input input::placeholder {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 13px;\n",
" line-height: 16px;\n",
" color: #313B3D;\n",
" }\n",
"\n",
" .close-filter-button {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" background: rgba(255, 255, 255, 0.7);\n",
" border: 1px solid #369BAC;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" width: 40px;\n",
" height: 40px;\n",
" cursor: pointer;\n",
" margin-left: 10px;\n",
" }\n",
"\n",
" .filter-options-title {\n",
" width: 240px;\n",
" }\n",
"\n",
" .filter-options-title p {\n",
" margin: 0;\n",
" }\n",
"\n",
" .dropdown-container {\n",
" position: absolute;\n",
" right: 18px;\n",
" z-index: 999;\n",
" background: #FFFFFF;\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.05);\n",
" border-radius: 4px;\n",
" padding: 10px !important;\n",
" border: none !important;\n",
" }\n",
"\n",
" .form-check-input:checked {\n",
" background-color: #0E7384;\n",
" border-color: #0E7384;\n",
" }\n",
"\n",
" .form-check-input[type=checkbox] {\n",
" border-radius: 2px;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .wl-table-cell__graph-wrap {\n",
" width: 0;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 85%;\n",
" padding-bottom: 17%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .reference-table-head {\n",
" min-width: 250px;\n",
" }\n",
"\n",
" .wl__dropdown_arrow-icon {\n",
" position: relative;\n",
" }\n",
"\n",
" .notif-circle-container{\n",
" position: absolute;\n",
" top: -4px;\n",
" right: -4px;\n",
" padding: 5.3px;\n",
" border-radius: 50%;\n",
" background-color: white;\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .notif-circle {\n",
" position: absolute;\n",
" top: 2px;\n",
" right: 2px;\n",
" padding: 3.3px;\n",
" border-radius: 50%;\n",
" background-color: #F2994A;\n",
" }\n",
"\n",
"\n",
" .header-title {\n",
" font-size: 26px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistic-number-title {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 14px;\n",
" line-height: 20px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .statistic-number {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 20px;\n",
" line-height: 140%;\n",
" display: flex;\n",
" align-items: center;\n",
" color: #0E7384;\n",
" }\n",
"\n",
" .statistic-measurement {\n",
" font-size: 15px !important;\n",
" margin-left: 3px;\n",
" }\n",
"\n",
" .statistic-measurement-percent {\n",
" font-size: 15px !important;\n",
" }\n",
"\n",
" .question-mark {\n",
" font-size: 10px;\n",
" font-weight: 900;\n",
" color: #0E7384;\n",
" border-radius: 50px;\n",
" border: 2px solid #0E7384;\n",
" padding: 0px 4px;\n",
" transition: 0.5s;\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .question-mark:hover {\n",
" color: white;\n",
" background: #0E7384;\n",
" border: 2px solid none;\n",
" transition: 0.5s;\n",
" }\n",
"\n",
" .tooltip-full-number {\n",
" position: relative;\n",
" display: inline-block;\n",
" }\n",
"\n",
" .tooltip-full-number .tooltiptext {\n",
" visibility: hidden;\n",
" background: black;\n",
" color: white;\n",
" border: 1px solid black;\n",
" text-align: start;\n",
" padding: 3px;\n",
" position: absolute;\n",
" z-index: 1002;\n",
" top: 0;\n",
" left: 100%;\n",
" margin-left: 5px;\n",
" opacity: 0;\n",
" transition: opacity 0.5s;\n",
" font-size: 13px;\n",
" font-weight: normal;\n",
" line-height: 100%;\n",
" }\n",
"\n",
" .tooltip-full-number:hover .tooltiptext {\n",
" visibility: visible;\n",
" opacity: 1;\n",
" }\n",
"\n",
" .display-flex {\n",
" display: flex;\n",
" }\n",
"\n",
" .flex-direction-column {\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .justify-content-space-between {\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .align-items-center {\n",
" align-items: center;\n",
" }\n",
"\n",
" .padding-right-30 {\n",
" padding-right: 30px;\n",
" }\n",
"\n",
" .padding-5 {\n",
" padding: 5px;\n",
" }\n",
"\n",
" .text-color {\n",
" color: var(--secondaryLight1000);\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
"\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
" <div class="container-fluid">\n",
" <div class="feature-summary-statistics-wrap">\n",
" <div class="feature-summary-statistics">\n",
" <div class="mb-4">\n",
" <strong class="header-title">Profile Summary</strong>\n",
" </div>\n",
" <div class="display-flex statistics">\n",
" <div class="padding-right-30">\n",
" <div class="statistic-number-title">Observations\n",
" <div class="tooltip-full-number">\n",
" <span class="question-mark">?</span>\n",
" <span class="tooltiptext">\n",
" <div class="mb-1">The sum of counts for each feature. For a single dataframe, it is equal to the number of cells, i.e., rows * columns.</div>\n",
" </span>\n",
" </div>\n",
" </div>\n",
" <div class="statistic-number">{{{observations this}}}</div>\n",
" </div>\n",
" <div class="padding-right-30">\n",
" <div class="statistic-number-title">Missing Cells</div>\n",
" <div class="statistic-number">\n",
" {{{missingCells this}}}\n",
" <div>{{{missingCellsPercentage this}}}</div>\n",
" </div>\n",
" </div>\n",
" <div class="wl-compare-profile" id="compare-profile" style="display: flex; justify-content: space-around">\n",
" <div class="drift-detection-wrap">\n",
" <div class="drift-detection display-flex">\n",
" <div class="drift-detection-search-input-wrap display-flex">\n",
" <div class="drift-detection-search-input search-input ">\n",
" <input type="text" id="wl__feature-search" placeholder="Quick search..."/>\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGdSURBVHgBpZK7TkJBEIZnZoVocdTYyQNALxpNKPQBMEaNJsbCRMKhl8ISWwt6AaksCF5iTHgAGhJD4AHkAaAzGiwUsjvOQnO4SYh/cXbPzO43OxcEj9Zy92EiFSXNIfvPyE1kKFfdoxJMENpP6DrvLC0vJoEwCgwto7DWcxoIIHBYbA3NmKwnDltjAeuZhyul1DaTTlfPB6Nt5Z53DOgky4P875+nlctY2+unjZviLklkJhi5bPUa3y/7qJuQUM7PinMy7CdQc1Gh16vnBxPzrMROmlKQEgKNASAHLQCmSIGpS75O+O5pdQAgVXaIqTkNwDDXHmcnW3VmHZoGMLoTsOt88+NrAMCIZdu+iLTyTwKRa1Md6YKfOgXbzO7K8sWku5u5RxcRV5EpPezrzcHGbXEXWaUkgkweZ/UC9YrK3zqggFw5FBZfm8EUavHj7AjAKpIvBDrGn+pNnlcyhYgqbcC41idr1gvB4SdZkDbzQa21gwv0Vj07aPTtL07XdDOyDXohCDNoHIRmAVRie20f+RKybRDQDvxHkXy/7b/DrayncLbMwQAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" <div class="wl__dropdown_arrow-icon" >\n",
" <div onclick="openFilter()" class="close-filter-button">\n",
" <div class="display-flex close-filter-icon d-none">\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADFSURBVHgBfY+xDcIwEEXvnLQBZQkYAEhDwwKpEEK0CCZgAEjJCEmgjYSAygxAHTZgFRSOsyUjY5mcZFnn/+78PwBXf3+MoKWUPuYjVBPFnTwpr9t/oNJfcTfXsAhRAlDqDhhQIPYgpAqNMDqcUqSAYZT1epr9gAHt6uXshvYme4DYHQJNDKh0dD0m5WXB10Y3Fqjtuh7fROn3oREDWxfeMLyRsMnc0OgDzdduaA0Pi3Plgr7Q2kaAePeBqh6rueSNBVt6fgCwBV1JLF3rlAAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" <div class="display-flex filter-icon">\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAPCAYAAADtc08vAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACSSURBVHgBrZLBCYAwDEWTUjw7igcdwI1cxkNXUJzBEVzFAbSVKoKmaVrEB6GHJv/w+AACtRk7P9IONv1QOYUl96k0zv61m2tjARoLtSDI3EFsgIJ4uoXrMLazO72CRG2mzg/8BSdVlEjhpGZJjAWdAZJECpWalEhJSs1pHuUlMad5FFai1Lwg4Ckx1TxKIPFL8w55mEWd8VjPGAAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" </div>\n",
" <span class="notif-circle-container">\n",
" <span class="notif-circle"></span>\n",
" </span>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="dropdown-container flex-direction-colum mb-2 d-none" id="dropdown-container">\n",
" <div class="filter-options">\n",
" <div class="filter-options-title space-between dropdown">\n",
" <p>Filter by type</p>\n",
" </div>\n",
" <div class="form-check mb-1 mt-2">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Discrete"\n",
" id="inferredDiscrete"\n",
" onclick="changeDiscreteValue()"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="inferredDiscrete">\n",
" Inferred discrete (<span class="wl__feature-count--discrete">{{getDiscreteTypeCount}}</span>)\n",
" </label>\n",
" </div>\n",
" <div class="form-check mb-1">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Non-discrete"\n",
" id="inferredNonDiscrete"\n",
" onclick="changeNonDiscreteValue()"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="inferredNonDiscrete">\n",
" Inferred non-discrete (<span\n",
" class="wl__feature-count--non-discrete"\n",
" >{{getNonDiscreteTypeCount}}</span>)\n",
" </label>\n",
" </div>\n",
" <div class="form-check mb-1">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Unknown"\n",
" id="inferredUnknown"\n",
" onclick="changeUnknwonValue()"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="inferredUnknown">\n",
" Unknown (<span class="wl__feature-count--unknown">{{getUnknownTypeCount}}</span>)\n",
" </label>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="row">\n",
" <div class="main">\n",
" <div class="wl-table-wrap" id="table-content">\n",
" <div class="wl-table">\n",
" <div class="wl-table-heading">\n",
" <div class="wl-table-row wl-table-row--bottom-shadow">\n",
" <div class="wl-table-head wl-table-head-wraper">\n",
" <div class="wl-table-head table-border-none graph-table-head">Target</div>\n",
" </div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Total count</div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Missing</div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Mean</div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Min</div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Max</div>\n",
"\n",
" </div>\n",
" </div>\n",
" <ul class="wl-table-body wl__table-body" id="table-body">\n",
"{{#each this.columns}} <li\n",
" {{#if this.numberSummary}} class="wl-table-row wl-table-row--clickable" {{else}} class="wl-table-row" {{/if}}\n",
" data-feature-name={{@key}}\n",
" data-inferred-type={{inferredType this}}\n",
" data-scroll-to-feature-name={{@key}}\n",
" >\n",
" <div class="wl-table-cell wl-table-cell__graph-wrap">\n",
" <div class="wl-table-cell__title-wrap">\n",
" <h4 class="wl-table-cell__title">{{@key}}</h4>\n",
" <div></div>\n",
" </div>\n",
" <div class="display-flex">\n",
" {{{getGraphHtml this}}}\n",
" </div>\n",
" </div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{totalCount this}}</div></div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{missing this}}</div></div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{mean this}}</div></div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{minimumValue this}}</div></div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{maximumValue this}}</div></div>\n",
"\n",
" </li>\n",
"{{/each}} </ul>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" const getTargetProfile = () => { return {"columns": {"fixed acidity": {"histogram": {"start": 4.6, "end": 15.90000159, "width": 0, "counts": [3, 17, 16, 17, 28, 39, 39, 30, 22, 30, 24, 35, 12, 13, 22, 23, 9, 6, 3, 4, 2, 3, 3, 2, 1, 0, 0, 0, 2, 3], "max": 15.9, "min": 4.6, "bins": [4.6, 4.9766667196666665, 5.353333439333333, 5.730000158999999, 6.106666878666666, 6.483333598333333, 6.860000318, 7.236667037666667, 7.613333757333333, 7.990000477000001, 8.366667196666667, 8.743333916333334, 9.120000636, 9.496667355666666, 9.873334075333332, 10.250000795, 10.626667514666668, 11.003334234333334, 11.380000954, 11.756667673666666, 12.133334393333334, 12.510001113000001, 12.886667832666667, 13.263334552333333, 13.640001272000001, 14.016667991666667, 14.393334711333335, 14.770001431, 15.146668150666667, 15.523334870333334, 15.90000159], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 20.588239538027807, "min": 4.6, "max": 15.9, "range": 11.3, "quantile_statistics": {"fifth_percentile": 5.4, "iqr": 2.7, "q1": 6.7, "median": 7.9, "q3": 9.4, "ninety_fifth_percentile": 11.6}, "descriptive_statistics": {"stddev": 2.013014441140909, "coefficient_of_variation": 0.24676558362692394, "sum": 3328.3, "variance": 4.052227140241847, "mean": 8.157598039215687}}}, "volatile acidity": {"histogram": {"start": 0.12, "end": 1.1150001115, "width": 0, "counts": [3, 8, 5, 8, 21, 32, 38, 37, 30, 35, 23, 24, 25, 23, 15, 15, 15, 14, 11, 4, 3, 3, 6, 2, 1, 2, 1, 2, 0, 2], "max": 1.115, "min": 0.12, "bins": [0.12, 0.15316667038333331, 0.18633334076666666, 0.21950001115, 0.2526666815333333, 0.28583335191666664, 0.3190000223, 0.35216669268333334, 0.38533336306666666, 0.41850003345, 0.4516667038333333, 0.48483337421666667, 0.5180000445999999, 0.5511667149833333, 0.5843333853666667, 0.61750005575, 0.6506667261333333, 0.6838333965166666, 0.7170000669, 0.7501667372833333, 0.7833334076666666, 0.81650007805, 0.8496667484333333, 0.8828334188166667, 0.9160000892, 0.9491667595833333, 0.9823334299666666, 1.01550010035, 1.0486667707333335, 1.0818334411166668, 1.1150001115000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.05882840516061, "min": 0.12, "max": 1.115, "range": 0.995, "quantile_statistics": {"fifth_percentile": 0.24, "iqr": 0.23999999999999994, "q1": 0.34, "median": 0.44, "q3": 0.58, "ninety_fifth_percentile": 0.8}, "descriptive_statistics": {"stddev": 0.17791965320497752, "coefficient_of_variation": 0.3771756131540623, "sum": 192.45999999999998, "variance": 0.03165540299657947, "mean": 0.47171568627450977}}}, "citric acid": {"histogram": {"start": 0.0, "end": 0.790000079, "width": 0, "counts": [57, 13, 7, 23, 10, 9, 4, 6, 7, 10, 12, 14, 28, 9, 24, 35, 11, 23, 31, 8, 10, 9, 5, 3, 8, 12, 2, 3, 4, 1], "max": 0.79, "min": 0.0, "bins": [0.0, 0.026333335966666666, 0.05266667193333333, 0.0790000079, 0.10533334386666666, 0.13166667983333333, 0.1580000158, 0.18433335176666665, 0.21066668773333333, 0.2370000237, 0.26333335966666666, 0.2896666956333333, 0.3160000316, 0.34233336756666666, 0.3686667035333333, 0.3950000395, 0.42133337546666666, 0.4476667114333333, 0.4740000474, 0.5003333833666667, 0.5266667193333333, 0.5530000553, 0.5793333912666666, 0.6056667272333334, 0.6320000632, 0.6583333991666667, 0.6846667351333333, 0.7110000711, 0.7373334070666666, 0.7636667430333334, 0.790000079], "n": 398}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 398, "missing": 10, "distinct": 18.090455451204008, "min": 0.0, "max": 0.79, "range": 0.79, "quantile_statistics": {"fifth_percentile": 0.0, "iqr": 0.36, "q1": 0.1, "median": 0.34, "q3": 0.46, "ninety_fifth_percentile": 0.66}, "descriptive_statistics": {"stddev": 0.20859943016152155, "coefficient_of_variation": 0.6620619872750046, "sum": 125.4, "variance": 0.04351372226371151, "mean": 0.3150753768844221}}}, "residual sugar": {"histogram": {"start": 0.9, "end": 12.90000129, "width": 0, "counts": [8, 39, 118, 124, 40, 16, 11, 8, 13, 8, 4, 2, 2, 5, 3, 0, 1, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "max": 12.9, "min": 0.9, "bins": [0.9, 1.300000043, 1.7000000860000002, 2.100000129, 2.500000172, 2.900000215, 3.3000002580000003, 3.700000301, 4.100000344000001, 4.500000387, 4.90000043, 5.300000473000001, 5.700000516000001, 6.100000559000001, 6.500000602000001, 6.900000645, 7.300000688000001, 7.700000731000001, 8.100000774, 8.500000817, 8.90000086, 9.300000903, 9.700000946000001, 10.100000989000002, 10.500001032000002, 10.900001075, 11.300001118, 11.700001161000001, 12.100001204000002, 12.500001247000002, 12.90000129], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 15.931375081243552, "min": 0.9, "max": 12.9, "range": 12.0, "quantile_statistics": {"fifth_percentile": 1.6, "iqr": 0.7000000000000002, "q1": 2.0, "median": 2.3, "q3": 2.7, "ninety_fifth_percentile": 5.15}, "descriptive_statistics": {"stddev": 1.2990000858665753, "coefficient_of_variation": 0.49437249664993493, "sum": 1072.05, "variance": 1.6874012230813702, "mean": 2.6275735294117646}}}, "chlorides": {"histogram": {"start": 0.012, "end": 0.230000023, "width": 0, "counts": [2, 0, 0, 9, 21, 40, 45, 76, 64, 43, 33, 21, 20, 8, 9, 9, 3, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1], "max": 0.23, "min": 0.012, "bins": [0.012, 0.019266667433333332, 0.026533334866666668, 0.0338000023, 0.04106666973333333, 0.04833333716666667, 0.0556000046, 0.06286667203333333, 0.07013333946666667, 0.0774000069, 0.08466667433333333, 0.09193334176666666, 0.0992000092, 0.10646667663333333, 0.11373334406666666, 0.12100001149999999, 0.12826667893333332, 0.13553334636666667, 0.14280001380000001, 0.15006668123333333, 0.15733334866666668, 0.1646000161, 0.17186668353333334, 0.17913335096666666, 0.1864000184, 0.19366668583333332, 0.20093335326666667, 0.2082000207, 0.21546668813333333, 0.22273335556666665, 0.230000023], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 21.813730257594944, "min": 0.012, "max": 0.23, "range": 0.218, "quantile_statistics": {"fifth_percentile": 0.046, "iqr": 0.025999999999999995, "q1": 0.06, "median": 0.071, "q3": 0.086, "ninety_fifth_percentile": 0.12}, "descriptive_statistics": {"stddev": 0.024213181268290383, "coefficient_of_variation": 0.3226526212509791, "sum": 30.618, "variance": 0.0005862781471310883, "mean": 0.07504411764705882}}}, "free sulfur dioxide": {"histogram": {"start": 3.0, "end": 51.0000051, "width": 0, "counts": [31, 79, 21, 21, 36, 16, 27, 26, 29, 22, 3, 13, 12, 11, 10, 5, 6, 4, 8, 6, 4, 8, 2, 1, 3, 0, 1, 0, 1, 2], "max": 51.0, "min": 3.0, "bins": [3.0, 4.60000017, 6.200000340000001, 7.80000051, 9.400000680000002, 11.000000850000001, 12.60000102, 14.200001190000002, 15.800001360000001, 17.40000153, 19.000001700000002, 20.600001870000003, 22.20000204, 23.800002210000002, 25.400002380000004, 27.00000255, 28.600002720000003, 30.200002890000004, 31.80000306, 33.40000323, 35.000003400000004, 36.600003570000005, 38.20000374000001, 39.80000391, 41.40000408, 43.00000425, 44.600004420000005, 46.200004590000006, 47.80000476000001, 49.40000493, 51.0000051], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 11.029412969947018, "min": 3.0, "max": 51.0, "range": 48.0, "quantile_statistics": {"fifth_percentile": 3.0, "iqr": 13.0, "q1": 6.0, "median": 13.0, "q3": 19.0, "ninety_fifth_percentile": 36.0}, "descriptive_statistics": {"stddev": 9.994223698460777, "coefficient_of_variation": 0.6774056431550788, "sum": 6019.5, "variance": 99.884507334875, "mean": 14.753676470588236}}}, "total sulfur dioxide": {"histogram": {"start": 6.0, "end": 289.0000289, "width": 0, "counts": [85, 92, 70, 47, 40, 14, 16, 10, 10, 10, 6, 2, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], "max": 289.0, "min": 6.0, "bins": [6.0, 15.433334296666667, 24.866668593333333, 34.30000289, 43.73333718666667, 53.16667148333333, 62.600005780000004, 72.03334007666666, 81.46667437333333, 90.90000867, 100.33334296666666, 109.76667726333334, 119.20001156000001, 128.63334585666666, 138.06668015333332, 147.50001445, 156.93334874666667, 166.36668304333332, 175.80001734, 185.23335163666667, 194.66668593333333, 204.10002023, 213.53335452666667, 222.96668882333333, 232.40002312000001, 241.83335741666667, 251.26669171333333, 260.70002601, 270.13336030666665, 279.56669460333336, 289.0000289], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.303926553943704, "min": 6.0, "max": 289.0, "range": 283.0, "quantile_statistics": {"fifth_percentile": 10.0, "iqr": 29.0, "q1": 17.0, "median": 27.0, "q3": 46.0, "ninety_fifth_percentile": 93.0}, "descriptive_statistics": {"stddev": 31.68721099598923, "coefficient_of_variation": 0.8554477659209692, "sum": 15112.999999999998, "variance": 1004.0793407043408, "mean": 37.041666666666664}}}, "density": {"histogram": {"start": 0.99007, "end": 1.0032001003200002, "width": 0, "counts": [3, 4, 1, 9, 4, 12, 8, 32, 32, 36, 39, 49, 45, 25, 25, 19, 15, 8, 4, 8, 5, 6, 2, 5, 4, 1, 1, 2, 0, 4], "max": 1.0032, "min": 0.99007, "bins": [0.99007, 0.9905076700106666, 0.9909453400213334, 0.991383010032, 0.9918206800426667, 0.9922583500533334, 0.992696020064, 0.9931336900746667, 0.9935713600853334, 0.9940090300960001, 0.9944467001066667, 0.9948843701173334, 0.9953220401280001, 0.9957597101386667, 0.9961973801493335, 0.9966350501600001, 0.9970727201706667, 0.9975103901813335, 0.9979480601920001, 0.9983857302026667, 0.9988234002133335, 0.9992610702240001, 0.9996987402346668, 1.0001364102453334, 1.0005740802560001, 1.0010117502666669, 1.0014494202773334, 1.0018870902880002, 1.002324760298667, 1.0027624303093334, 1.0032001003200002], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 55.88238444546564, "min": 0.99007, "max": 1.0032, "range": 0.013130000000000086, "quantile_statistics": {"fifth_percentile": 0.9922, "iqr": 0.0023400000000000087, "q1": 0.99396, "median": 0.99516, "q3": 0.9963, "ninety_fifth_percentile": 0.9994}, "descriptive_statistics": {"stddev": 0.0021784570136347352, "coefficient_of_variation": 0.002188682344428877, "sum": 406.09385999999995, "variance": 4.745674960254368e-06, "mean": 0.995328088235294}}}, "pH": {"histogram": {"start": 2.89, "end": 4.010000401, "width": 0, "counts": [5, 1, 3, 4, 7, 5, 17, 20, 38, 36, 44, 29, 35, 41, 30, 11, 19, 17, 14, 10, 2, 9, 4, 2, 0, 1, 0, 2, 0, 2], "max": 4.01, "min": 2.89, "bins": [2.89, 2.9273333467000002, 2.9646666934000003, 3.0020000401, 3.0393333868, 3.0766667335, 3.1140000802000003, 3.1513334269, 3.1886667736, 3.2260001203, 3.2633334670000003, 3.3006668137, 3.3380001604, 3.3753335071, 3.4126668538000002, 3.4500002005, 3.4873335472, 3.5246668939, 3.5620002406, 3.5993335873000003, 3.636666934, 3.6740002807, 3.7113336274, 3.7486669741, 3.7860003208000004, 3.8233336675, 3.8606670142, 3.8980003609000002, 3.9353337076, 3.9726670543, 4.010000401], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 18.8725525817653, "min": 2.89, "max": 4.01, "range": 1.1199999999999997, "quantile_statistics": {"fifth_percentile": 3.09, "iqr": 0.2200000000000002, "q1": 3.23, "median": 3.33, "q3": 3.45, "ninety_fifth_percentile": 3.67}, "descriptive_statistics": {"stddev": 0.17649177660211463, "coefficient_of_variation": 0.05273349702213279, "sum": 1365.52, "variance": 0.031149347208170735, "mean": 3.346862745098039}}}, "sulphates": {"histogram": {"start": 0.37, "end": 1.130000113, "width": 0, "counts": [3, 2, 4, 4, 6, 18, 19, 45, 22, 37, 22, 32, 21, 29, 28, 27, 22, 12, 24, 7, 10, 1, 5, 1, 3, 1, 1, 0, 0, 2], "max": 1.13, "min": 0.37, "bins": [0.37, 0.3953333371, 0.4206666742, 0.4460000113, 0.47133334839999996, 0.4966666855, 0.5220000226, 0.5473333597, 0.5726666968, 0.5980000339, 0.623333371, 0.6486667080999999, 0.6740000452, 0.6993333823, 0.7246667194, 0.7500000565, 0.7753333936, 0.8006667306999999, 0.8260000677999999, 0.8513334049, 0.8766667419999999, 0.9020000791, 0.9273334162, 0.9526667533, 0.9780000904, 1.0033334274999999, 1.0286667646, 1.0540001016999998, 1.0793334388, 1.1046667759, 1.130000113], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 14.215688286897349, "min": 0.37, "max": 1.13, "range": 0.7599999999999999, "quantile_statistics": {"fifth_percentile": 0.5, "iqr": 0.19000000000000006, "q1": 0.58, "median": 0.66, "q3": 0.77, "ninety_fifth_percentile": 0.88}, "descriptive_statistics": {"stddev": 0.12701037402923776, "coefficient_of_variation": 0.18776807233831805, "sum": 275.97999999999996, "variance": 0.016131635111046875, "mean": 0.6764215686274508}}}, "alcohol": {"histogram": {"start": 11.066666666666698, "end": 14.900001490000001, "width": 0, "counts": [28, 68, 32, 30, 38, 29, 21, 21, 25, 12, 13, 27, 9, 17, 9, 8, 1, 3, 3, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1], "max": 14.9, "min": 11.066666666666698, "bins": [11.066666666666698, 11.194444494111142, 11.322222321555586, 11.45000014900003, 11.577777976444471, 11.705555803888915, 11.833333631333359, 11.961111458777802, 12.088889286222246, 12.21666711366669, 12.344444941111133, 12.472222768555575, 12.600000596000019, 12.727778423444462, 12.855556250888906, 12.98333407833335, 13.111111905777793, 13.238889733222237, 13.36666756066668, 13.494445388111123, 13.622223215555566, 13.75000104300001, 13.877778870444454, 14.005556697888897, 14.13333452533334, 14.261112352777783, 14.388890180222226, 14.51666800766667, 14.644445835111114, 14.772223662555557, 14.900001490000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 7.59803978178436, "min": 11.066666666666698, "max": 14.9, "range": 3.833333333333302, "quantile_statistics": {"fifth_percentile": 11.1, "iqr": 1.0, "q1": 11.4, "median": 11.8, "q3": 12.4, "ninety_fifth_percentile": 13.2}, "descriptive_statistics": {"stddev": 0.6824986635512768, "coefficient_of_variation": 0.05725985668809312, "sum": 4863.083333333334, "variance": 0.4658044257492789, "mean": 11.919321895424838}}}, "quality": {"histogram": null, "frequentItems": [{"value": "bad", "estimate": 267}, {"value": "good", "estimate": 141}], "drift_from_ref": null, "isDiscrete": true, "featureStats": {"total_count": 408, "missing": 0, "distinct": 0.4901960796487877, "min": NaN, "max": NaN, "range": NaN, "quantile_statistics": null, "descriptive_statistics": {"stddev": null, "coefficient_of_variation": null, "sum": null, "variance": null, "mean": null}}}}, "properties": {"observations": 4896, "missing_cells": 10, "missing_percentage": 0.20424836601307192}} }\n",
" const targetProfile = getTargetProfile()\n",
"\n",
" function fixNumberTo(number, decimals = 3) {\n",
" const fractionalDigits = String(number % 1).split('').slice(2, 2 + decimals).join('')\n",
" return `${Math.trunc(number)}.${fractionalDigits}`\n",
" }\n",
"\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, data, bottomMargin=20, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", "30 0 240 400")\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(data, (d) => Math.abs(d.axisY));\n",
" this.xScale = d3\n",
" .scaleBand()\n",
" .domain(data.map((d) => d.axisX))\n",
" .range([this.MARGIN.LEFT, this.MARGIN.LEFT + this.CHART_WIDTH]);\n",
" this.yScale = d3\n",
" .scaleLinear()\n",
" .domain([0, this.maxYValue * 1.02])\n",
" .range([this.CHART_HEIGHT, 0]);\n",
" }\n",
" }\n",
"\n",
" function generateChart(data, height = 70, width = 250, index = 0, referenceProfileExist = false) {\n",
" const sizes = new GenerateChartParams(height, width, data, 5)\n",
" const {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" maxYValue,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
"\n",
" // Add the y Axis\n",
" if (!referenceProfileExist) {\n",
" svgEl\n",
" .append("g")\n",
" .attr("transform", "translate(" + MARGIN.LEFT + ", " + MARGIN.TOP + ")")\n",
" .call(d3.axisLeft(yScale).tickValues([0, maxYValue/2, maxYValue]))\n",
" .selectAll("text")\n",
" .style("font-size", "8")\n",
" }\n",
"\n",
" const gChart = svgEl.append("g");\n",
" gChart\n",
" .attr("transform", "translate(" + 20 + ", " + 0 + ")")\n",
" .selectAll(".bar")\n",
" .data(data)\n",
" .enter()\n",
" .append("rect")\n",
" .classed("bar", true)\n",
" .attr("width", xScale.bandwidth() - 1)\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", (d) => xScale(d.axisX))\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP)\n",
" .attr("fill", rectColors[index]);\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column.histogram) {\n",
" column.histogram.counts.slice(0, 30).forEach((count, index) => {\n",
" data.push({\n",
" axisY: count,\n",
" axisX: index,\n",
" });\n",
" });\n",
" } else if (column.frequentItems) {\n",
" Object.entries(column.frequentItems).forEach(([key, {value, estimate}], index) => {\n",
" data.push({\n",
" axisY: estimate,\n",
" axisX: value,\n",
" });\n",
" });\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function graph(column, key, referenceColumn) {\n",
" let data = [];\n",
" const columnKey = key.data.key\n",
" let chartValue = false\n",
" let chartColor = undefined\n",
" if(!!referenceColumn){\n",
" column = referenceColumn.columns[columnKey]\n",
" chartValue = true\n",
" chartColor = 1\n",
" } else if (referenceColumn === undefined) {\n",
" column = ""\n",
" }\n",
"\n",
" if (column.histogram || column.frequentItems) {\n",
" data = chartData(column)\n",
" } else if (referenceColumn === null ) {\n",
" $(".svg-container").css("padding-bottom", "0")\n",
" return '<span class="wl-table-cell__bedge-wrap">No data to show the chart</span>';\n",
" } else if (referenceColumn === undefined) {\n",
" $(document).ready(function() {\n",
" $(".reference-table-head").addClass("d-none")\n",
" });\n",
" return ''\n",
" } else if (referenceColumn.frequentItems === undefined){\n",
" return '';\n",
" }\n",
" return `\n",
" <div class="svg-container">\n",
" ${generateChart(data, ...[,,], chartColor, chartValue)}\n",
" </div>\n",
" `;\n",
" }\n",
"\n",
" function formatLabelDate(timestamp) {\n",
" const date = new Date(timestamp);\n",
" const format = d3.timeFormat("%Y-%m-%d %I:%M:%S %p %Z");\n",
" return format(date);\n",
" }\n",
"\n",
" abbreviate_number = function(value, fixed = 0) {\n",
" value = +value\n",
" if (value === null) { return null; } // terminate early\n",
" if (value === 0) { return '0'; } // terminate early\n",
" fixed = (!fixed || fixed < 0) ? 0 : fixed; // number of decimal places to show\n",
" var b = (value).toPrecision(2).split("e"), // get power\n",
" k = b.length === 1 ? 0 : Math.floor(Math.min(b[1].slice(1), 14) / 3), // floor at decimals, ceiling at trillions\n",
" c = k < 1 ? value.toFixed(0 + fixed) : (value / Math.pow(10, k * 3) ).toFixed(1 + fixed), // divide by power\n",
" d = c < 0 ? c : Math.abs(c), // enforce -0 is 0\n",
" newValue = d,\n",
" suffixe = ['', 'K', 'M', 'B', 'T'][k]; // append power\n",
" return {value, newValue, suffixe};\n",
" }\n",
"\n",
" function formatBytes(bytes, decimals = 2) {\n",
" let newValue,\n",
" suffixe = ""\n",
" if (bytes === 0) return '0 Bytes';\n",
"\n",
" const k = 1024;\n",
" const dm = decimals < 0 ? 0 : decimals;\n",
" const sizes = ['Bytes', 'KiB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n",
"\n",
" const i = Math.floor(Math.log(bytes) / Math.log(k));\n",
" newValue = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));\n",
" suffixe = sizes[i]\n",
" return {bytes, newValue, suffixe};\n",
" }\n",
"\n",
" const valueSuffixe = (newNumber) => `\n",
" <div class="statistic-measurement">\n",
" ${newNumber}\n",
" </div>`\n",
" const valueNumber = (newNumber) => `\n",
" <div class="statistic-number">\n",
" ${newNumber}\n",
" </div>`\n",
"\n",
" const numberWithSuffixe = (number, newNumber, suffixe) =>\n",
" `<div class="tooltip-full-number">\n",
" <div class="statistic-number">\n",
" ${newNumber}\n",
" <div class="statistic-measurement">${suffixe}</div>\n",
" </div>\n",
" <span class="tooltiptext">${number}</span>\n",
" </div>`\n",
"\n",
" Handlebars.registerHelper("observations", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(targetProfile.properties.observations)\n",
" return numberWithSuffixe(value, valueNumber(newValue), valueNumber(suffixe));\n",
" });\n",
"\n",
" Handlebars.registerHelper("missingCells", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(targetProfile.properties.missing_cells)\n",
" if (typeof value !== 'undefined' && typeof newValue !== 'undefined' && typeof suffixe !== 'undefined' ) {\n",
" return numberWithSuffixe(value, valueSuffixe(`${newValue}`), suffixe);\n",
" }\n",
" return numberWithSuffixe(0, valueNumber(0), valueNumber(''));\n",
" });\n",
"\n",
" Handlebars.registerHelper("missingCellsPercentage", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(targetProfile.properties.missing_percentage)\n",
" if (typeof value !== 'undefined' && typeof newValue !== 'undefined' && typeof suffixe !== 'undefined' ) {\n",
" return numberWithSuffixe(value, valueSuffixe(`(${newValue}%)`), suffixe);\n",
" }\n",
" return numberWithSuffixe(0, valueSuffixe(`(${0}%)`), '');\n",
" });\n",
"\n",
" Handlebars.registerHelper("getProfileTimeStamp", function (properties) {\n",
" return formatLabelDate(+properties.properties.dataTimestamp)\n",
" });\n",
"\n",
" Handlebars.registerHelper("getProfileName", function (properties) {\n",
" return properties.properties.tags.name\n",
" });\n",
"\n",
" const cheqValueTypeNumber = (profile, profileValue) => {\n",
" let validValue;\n",
" if (profile && profileValue !== undefined && typeof profileValue === "number") {\n",
" return true\n",
" } else if (profileValue !== undefined && typeof profileValue !== "number") {\n",
" return false\n",
" }\n",
" }\n",
"\n",
"\n",
" Handlebars.registerHelper("inferredType", function (column) {\n",
" let infferedType = "";\n",
"\n",
" if (column.isDiscrete) {\n",
" infferedType = "Discrete";\n",
" } else {\n",
" infferedType = "Non-discrete";\n",
" }\n",
" return infferedType;\n",
" });\n",
"\n",
" Handlebars.registerHelper("frequentItems", function (column) {\n",
" frequentItemsElemString = "";\n",
" if (column.isDiscrete) {\n",
" const slicedFrequentItems = column.frequentItems.items.slice(0, 5);\n",
" for (let fi = 0; fi < slicedFrequentItems.length; fi++) {\n",
" frequentItemsElemString +=\n",
" '<span class="wl-table-cell__bedge">' + slicedFrequentItems[fi].jsonValue + "</span>";\n",
" }\n",
" } else {\n",
" frequentItemsElemString += "No data to show";\n",
" }\n",
" return frequentItemsElemString;\n",
" });\n",
"\n",
" Handlebars.registerHelper("totalCount", function (column) {\n",
" if (column.featureStats) {\n",
" return column.featureStats.total_count;\n",
" }\n",
"\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("missing", function (column) {\n",
" if (column.featureStats) {\n",
" return column.featureStats.missing;\n",
" }\n",
"\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("minimumValue", function (column) {\n",
" if (column.featureStats) {\n",
" if (isNaN(column.featureStats.min)){return "-"}\n",
" return fixNumberTo(column.featureStats.min);\n",
" }\n",
"\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("maximumValue", function (column) {\n",
" if (column.featureStats) {\n",
" if (isNaN(column.featureStats.max)){return "-"}\n",
" return fixNumberTo(column.featureStats.max);\n",
" }\n",
"\n",
" return "-";\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("mean", function (column) {\n",
" if (column.featureStats?.descriptive_statistics) {\n",
" if (isNaN(column.featureStats.descriptive_statistics.mean)){return "-"}\n",
" if (column.featureStats.descriptive_statistics.mean==null){return "-"}\n",
" return fixNumberTo(column.featureStats.descriptive_statistics.mean);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("getGraphHtml",(column,key) => graph(column, key, null));\n",
"\n",
" Handlebars.registerHelper("getDiscreteTypeCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(this.columns).forEach((feature) => {\n",
" if (feature[1].isDiscrete === true) {\n",
" count++;\n",
" }\n",
" });\n",
" return count.toString();\n",
" });\n",
"\n",
" Handlebars.registerHelper("getNonDiscreteTypeCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(this.columns).forEach((feature) => {\n",
" if (feature[1].isDiscrete === false) {\n",
" count++;\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
" Handlebars.registerHelper("getUnknownTypeCount", function () {\n",
" let count = 0;\n",
" return count;\n",
" Object.entries(this.columns).forEach((feature) => {\n",
" if (!feature[1].isDiscrete) {\n",
" count++;\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"columns": {"fixed acidity": {"histogram": {"start": 4.6, "end": 15.90000159, "width": 0, "counts": [3, 17, 16, 17, 28, 39, 39, 30, 22, 30, 24, 35, 12, 13, 22, 23, 9, 6, 3, 4, 2, 3, 3, 2, 1, 0, 0, 0, 2, 3], "max": 15.9, "min": 4.6, "bins": [4.6, 4.9766667196666665, 5.353333439333333, 5.730000158999999, 6.106666878666666, 6.483333598333333, 6.860000318, 7.236667037666667, 7.613333757333333, 7.990000477000001, 8.366667196666667, 8.743333916333334, 9.120000636, 9.496667355666666, 9.873334075333332, 10.250000795, 10.626667514666668, 11.003334234333334, 11.380000954, 11.756667673666666, 12.133334393333334, 12.510001113000001, 12.886667832666667, 13.263334552333333, 13.640001272000001, 14.016667991666667, 14.393334711333335, 14.770001431, 15.146668150666667, 15.523334870333334, 15.90000159], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 20.588239538027807, "min": 4.6, "max": 15.9, "range": 11.3, "quantile_statistics": {"fifth_percentile": 5.4, "iqr": 2.7, "q1": 6.7, "median": 7.9, "q3": 9.4, "ninety_fifth_percentile": 11.6}, "descriptive_statistics": {"stddev": 2.013014441140909, "coefficient_of_variation": 0.24676558362692394, "sum": 3328.3, "variance": 4.052227140241847, "mean": 8.157598039215687}}}, "volatile acidity": {"histogram": {"start": 0.12, "end": 1.1150001115, "width": 0, "counts": [3, 8, 5, 8, 21, 32, 38, 37, 30, 35, 23, 24, 25, 23, 15, 15, 15, 14, 11, 4, 3, 3, 6, 2, 1, 2, 1, 2, 0, 2], "max": 1.115, "min": 0.12, "bins": [0.12, 0.15316667038333331, 0.18633334076666666, 0.21950001115, 0.2526666815333333, 0.28583335191666664, 0.3190000223, 0.35216669268333334, 0.38533336306666666, 0.41850003345, 0.4516667038333333, 0.48483337421666667, 0.5180000445999999, 0.5511667149833333, 0.5843333853666667, 0.61750005575, 0.6506667261333333, 0.6838333965166666, 0.7170000669, 0.7501667372833333, 0.7833334076666666, 0.81650007805, 0.8496667484333333, 0.8828334188166667, 0.9160000892, 0.9491667595833333, 0.9823334299666666, 1.01550010035, 1.0486667707333335, 1.0818334411166668, 1.1150001115000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.05882840516061, "min": 0.12, "max": 1.115, "range": 0.995, "quantile_statistics": {"fifth_percentile": 0.24, "iqr": 0.23999999999999994, "q1": 0.34, "median": 0.44, "q3": 0.58, "ninety_fifth_percentile": 0.8}, "descriptive_statistics": {"stddev": 0.17791965320497752, "coefficient_of_variation": 0.3771756131540623, "sum": 192.45999999999998, "variance": 0.03165540299657947, "mean": 0.47171568627450977}}}, "citric acid": {"histogram": {"start": 0.0, "end": 0.790000079, "width": 0, "counts": [57, 13, 7, 23, 10, 9, 4, 6, 7, 10, 12, 14, 28, 9, 24, 35, 11, 23, 31, 8, 10, 9, 5, 3, 8, 12, 2, 3, 4, 1], "max": 0.79, "min": 0.0, "bins": [0.0, 0.026333335966666666, 0.05266667193333333, 0.0790000079, 0.10533334386666666, 0.13166667983333333, 0.1580000158, 0.18433335176666665, 0.21066668773333333, 0.2370000237, 0.26333335966666666, 0.2896666956333333, 0.3160000316, 0.34233336756666666, 0.3686667035333333, 0.3950000395, 0.42133337546666666, 0.4476667114333333, 0.4740000474, 0.5003333833666667, 0.5266667193333333, 0.5530000553, 0.5793333912666666, 0.6056667272333334, 0.6320000632, 0.6583333991666667, 0.6846667351333333, 0.7110000711, 0.7373334070666666, 0.7636667430333334, 0.790000079], "n": 398}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 398, "missing": 10, "distinct": 18.090455451204008, "min": 0.0, "max": 0.79, "range": 0.79, "quantile_statistics": {"fifth_percentile": 0.0, "iqr": 0.36, "q1": 0.1, "median": 0.34, "q3": 0.46, "ninety_fifth_percentile": 0.66}, "descriptive_statistics": {"stddev": 0.20859943016152155, "coefficient_of_variation": 0.6620619872750046, "sum": 125.4, "variance": 0.04351372226371151, "mean": 0.3150753768844221}}}, "residual sugar": {"histogram": {"start": 0.9, "end": 12.90000129, "width": 0, "counts": [8, 39, 118, 124, 40, 16, 11, 8, 13, 8, 4, 2, 2, 5, 3, 0, 1, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "max": 12.9, "min": 0.9, "bins": [0.9, 1.300000043, 1.7000000860000002, 2.100000129, 2.500000172, 2.900000215, 3.3000002580000003, 3.700000301, 4.100000344000001, 4.500000387, 4.90000043, 5.300000473000001, 5.700000516000001, 6.100000559000001, 6.500000602000001, 6.900000645, 7.300000688000001, 7.700000731000001, 8.100000774, 8.500000817, 8.90000086, 9.300000903, 9.700000946000001, 10.100000989000002, 10.500001032000002, 10.900001075, 11.300001118, 11.700001161000001, 12.100001204000002, 12.500001247000002, 12.90000129], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 15.931375081243552, "min": 0.9, "max": 12.9, "range": 12.0, "quantile_statistics": {"fifth_percentile": 1.6, "iqr": 0.7000000000000002, "q1": 2.0, "median": 2.3, "q3": 2.7, "ninety_fifth_percentile": 5.15}, "descriptive_statistics": {"stddev": 1.2990000858665753, "coefficient_of_variation": 0.49437249664993493, "sum": 1072.05, "variance": 1.6874012230813702, "mean": 2.6275735294117646}}}, "chlorides": {"histogram": {"start": 0.012, "end": 0.230000023, "width": 0, "counts": [2, 0, 0, 9, 21, 40, 45, 76, 64, 43, 33, 21, 20, 8, 9, 9, 3, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1], "max": 0.23, "min": 0.012, "bins": [0.012, 0.019266667433333332, 0.026533334866666668, 0.0338000023, 0.04106666973333333, 0.04833333716666667, 0.0556000046, 0.06286667203333333, 0.07013333946666667, 0.0774000069, 0.08466667433333333, 0.09193334176666666, 0.0992000092, 0.10646667663333333, 0.11373334406666666, 0.12100001149999999, 0.12826667893333332, 0.13553334636666667, 0.14280001380000001, 0.15006668123333333, 0.15733334866666668, 0.1646000161, 0.17186668353333334, 0.17913335096666666, 0.1864000184, 0.19366668583333332, 0.20093335326666667, 0.2082000207, 0.21546668813333333, 0.22273335556666665, 0.230000023], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 21.813730257594944, "min": 0.012, "max": 0.23, "range": 0.218, "quantile_statistics": {"fifth_percentile": 0.046, "iqr": 0.025999999999999995, "q1": 0.06, "median": 0.071, "q3": 0.086, "ninety_fifth_percentile": 0.12}, "descriptive_statistics": {"stddev": 0.024213181268290383, "coefficient_of_variation": 0.3226526212509791, "sum": 30.618, "variance": 0.0005862781471310883, "mean": 0.07504411764705882}}}, "free sulfur dioxide": {"histogram": {"start": 3.0, "end": 51.0000051, "width": 0, "counts": [31, 79, 21, 21, 36, 16, 27, 26, 29, 22, 3, 13, 12, 11, 10, 5, 6, 4, 8, 6, 4, 8, 2, 1, 3, 0, 1, 0, 1, 2], "max": 51.0, "min": 3.0, "bins": [3.0, 4.60000017, 6.200000340000001, 7.80000051, 9.400000680000002, 11.000000850000001, 12.60000102, 14.200001190000002, 15.800001360000001, 17.40000153, 19.000001700000002, 20.600001870000003, 22.20000204, 23.800002210000002, 25.400002380000004, 27.00000255, 28.600002720000003, 30.200002890000004, 31.80000306, 33.40000323, 35.000003400000004, 36.600003570000005, 38.20000374000001, 39.80000391, 41.40000408, 43.00000425, 44.600004420000005, 46.200004590000006, 47.80000476000001, 49.40000493, 51.0000051], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 11.029412969947018, "min": 3.0, "max": 51.0, "range": 48.0, "quantile_statistics": {"fifth_percentile": 3.0, "iqr": 13.0, "q1": 6.0, "median": 13.0, "q3": 19.0, "ninety_fifth_percentile": 36.0}, "descriptive_statistics": {"stddev": 9.994223698460777, "coefficient_of_variation": 0.6774056431550788, "sum": 6019.5, "variance": 99.884507334875, "mean": 14.753676470588236}}}, "total sulfur dioxide": {"histogram": {"start": 6.0, "end": 289.0000289, "width": 0, "counts": [85, 92, 70, 47, 40, 14, 16, 10, 10, 10, 6, 2, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], "max": 289.0, "min": 6.0, "bins": [6.0, 15.433334296666667, 24.866668593333333, 34.30000289, 43.73333718666667, 53.16667148333333, 62.600005780000004, 72.03334007666666, 81.46667437333333, 90.90000867, 100.33334296666666, 109.76667726333334, 119.20001156000001, 128.63334585666666, 138.06668015333332, 147.50001445, 156.93334874666667, 166.36668304333332, 175.80001734, 185.23335163666667, 194.66668593333333, 204.10002023, 213.53335452666667, 222.96668882333333, 232.40002312000001, 241.83335741666667, 251.26669171333333, 260.70002601, 270.13336030666665, 279.56669460333336, 289.0000289], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.303926553943704, "min": 6.0, "max": 289.0, "range": 283.0, "quantile_statistics": {"fifth_percentile": 10.0, "iqr": 29.0, "q1": 17.0, "median": 27.0, "q3": 46.0, "ninety_fifth_percentile": 93.0}, "descriptive_statistics": {"stddev": 31.68721099598923, "coefficient_of_variation": 0.8554477659209692, "sum": 15112.999999999998, "variance": 1004.0793407043408, "mean": 37.041666666666664}}}, "density": {"histogram": {"start": 0.99007, "end": 1.0032001003200002, "width": 0, "counts": [3, 4, 1, 9, 4, 12, 8, 32, 32, 36, 39, 49, 45, 25, 25, 19, 15, 8, 4, 8, 5, 6, 2, 5, 4, 1, 1, 2, 0, 4], "max": 1.0032, "min": 0.99007, "bins": [0.99007, 0.9905076700106666, 0.9909453400213334, 0.991383010032, 0.9918206800426667, 0.9922583500533334, 0.992696020064, 0.9931336900746667, 0.9935713600853334, 0.9940090300960001, 0.9944467001066667, 0.9948843701173334, 0.9953220401280001, 0.9957597101386667, 0.9961973801493335, 0.9966350501600001, 0.9970727201706667, 0.9975103901813335, 0.9979480601920001, 0.9983857302026667, 0.9988234002133335, 0.9992610702240001, 0.9996987402346668, 1.0001364102453334, 1.0005740802560001, 1.0010117502666669, 1.0014494202773334, 1.0018870902880002, 1.002324760298667, 1.0027624303093334, 1.0032001003200002], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 55.88238444546564, "min": 0.99007, "max": 1.0032, "range": 0.013130000000000086, "quantile_statistics": {"fifth_percentile": 0.9922, "iqr": 0.0023400000000000087, "q1": 0.99396, "median": 0.99516, "q3": 0.9963, "ninety_fifth_percentile": 0.9994}, "descriptive_statistics": {"stddev": 0.0021784570136347352, "coefficient_of_variation": 0.002188682344428877, "sum": 406.09385999999995, "variance": 4.745674960254368e-06, "mean": 0.995328088235294}}}, "pH": {"histogram": {"start": 2.89, "end": 4.010000401, "width": 0, "counts": [5, 1, 3, 4, 7, 5, 17, 20, 38, 36, 44, 29, 35, 41, 30, 11, 19, 17, 14, 10, 2, 9, 4, 2, 0, 1, 0, 2, 0, 2], "max": 4.01, "min": 2.89, "bins": [2.89, 2.9273333467000002, 2.9646666934000003, 3.0020000401, 3.0393333868, 3.0766667335, 3.1140000802000003, 3.1513334269, 3.1886667736, 3.2260001203, 3.2633334670000003, 3.3006668137, 3.3380001604, 3.3753335071, 3.4126668538000002, 3.4500002005, 3.4873335472, 3.5246668939, 3.5620002406, 3.5993335873000003, 3.636666934, 3.6740002807, 3.7113336274, 3.7486669741, 3.7860003208000004, 3.8233336675, 3.8606670142, 3.8980003609000002, 3.9353337076, 3.9726670543, 4.010000401], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 18.8725525817653, "min": 2.89, "max": 4.01, "range": 1.1199999999999997, "quantile_statistics": {"fifth_percentile": 3.09, "iqr": 0.2200000000000002, "q1": 3.23, "median": 3.33, "q3": 3.45, "ninety_fifth_percentile": 3.67}, "descriptive_statistics": {"stddev": 0.17649177660211463, "coefficient_of_variation": 0.05273349702213279, "sum": 1365.52, "variance": 0.031149347208170735, "mean": 3.346862745098039}}}, "sulphates": {"histogram": {"start": 0.37, "end": 1.130000113, "width": 0, "counts": [3, 2, 4, 4, 6, 18, 19, 45, 22, 37, 22, 32, 21, 29, 28, 27, 22, 12, 24, 7, 10, 1, 5, 1, 3, 1, 1, 0, 0, 2], "max": 1.13, "min": 0.37, "bins": [0.37, 0.3953333371, 0.4206666742, 0.4460000113, 0.47133334839999996, 0.4966666855, 0.5220000226, 0.5473333597, 0.5726666968, 0.5980000339, 0.623333371, 0.6486667080999999, 0.6740000452, 0.6993333823, 0.7246667194, 0.7500000565, 0.7753333936, 0.8006667306999999, 0.8260000677999999, 0.8513334049, 0.8766667419999999, 0.9020000791, 0.9273334162, 0.9526667533, 0.9780000904, 1.0033334274999999, 1.0286667646, 1.0540001016999998, 1.0793334388, 1.1046667759, 1.130000113], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 14.215688286897349, "min": 0.37, "max": 1.13, "range": 0.7599999999999999, "quantile_statistics": {"fifth_percentile": 0.5, "iqr": 0.19000000000000006, "q1": 0.58, "median": 0.66, "q3": 0.77, "ninety_fifth_percentile": 0.88}, "descriptive_statistics": {"stddev": 0.12701037402923776, "coefficient_of_variation": 0.18776807233831805, "sum": 275.97999999999996, "variance": 0.016131635111046875, "mean": 0.6764215686274508}}}, "alcohol": {"histogram": {"start": 11.066666666666698, "end": 14.900001490000001, "width": 0, "counts": [28, 68, 32, 30, 38, 29, 21, 21, 25, 12, 13, 27, 9, 17, 9, 8, 1, 3, 3, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1], "max": 14.9, "min": 11.066666666666698, "bins": [11.066666666666698, 11.194444494111142, 11.322222321555586, 11.45000014900003, 11.577777976444471, 11.705555803888915, 11.833333631333359, 11.961111458777802, 12.088889286222246, 12.21666711366669, 12.344444941111133, 12.472222768555575, 12.600000596000019, 12.727778423444462, 12.855556250888906, 12.98333407833335, 13.111111905777793, 13.238889733222237, 13.36666756066668, 13.494445388111123, 13.622223215555566, 13.75000104300001, 13.877778870444454, 14.005556697888897, 14.13333452533334, 14.261112352777783, 14.388890180222226, 14.51666800766667, 14.644445835111114, 14.772223662555557, 14.900001490000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 7.59803978178436, "min": 11.066666666666698, "max": 14.9, "range": 3.833333333333302, "quantile_statistics": {"fifth_percentile": 11.1, "iqr": 1.0, "q1": 11.4, "median": 11.8, "q3": 12.4, "ninety_fifth_percentile": 13.2}, "descriptive_statistics": {"stddev": 0.6824986635512768, "coefficient_of_variation": 0.05725985668809312, "sum": 4863.083333333334, "variance": 0.4658044257492789, "mean": 11.919321895424838}}}, "quality": {"histogram": null, "frequentItems": [{"value": "bad", "estimate": 267}, {"value": "good", "estimate": 141}], "drift_from_ref": null, "isDiscrete": true, "featureStats": {"total_count": 408, "missing": 0, "distinct": 0.4901960796487877, "min": NaN, "max": NaN, "range": NaN, "quantile_statistics": null, "descriptive_statistics": {"stddev": null, "coefficient_of_variation": null, "sum": null, "variance": null, "mean": null}}}}, "properties": {"observations": 4896, "missing_cells": 10, "missing_percentage": 0.20424836601307192}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" const $featureSearch = document.getElementById("wl__feature-search");\n",
" const $tableBody = document.getElementById("table-body");\n",
" const $discrete = document.getElementById("inferredDiscrete");\n",
" const $nonDiscrete = document.getElementById("inferredNonDiscrete");\n",
" const $unknown = document.getElementById("inferredUnknown");\n",
"\n",
" const activeTypes = {\n",
" discrete: true,\n",
" "non-discrete": true,\n",
" unknown: true,\n",
" };\n",
"\n",
" let searchString = "";\n",
"\n",
" function debounce(func, wait, immediate) {\n",
" let timeout;\n",
"\n",
" return function () {\n",
" const context = this;\n",
" const args = arguments;\n",
" const later = function () {\n",
" timeout = null;\n",
" if (!immediate) func.apply(context, args);\n",
" };\n",
"\n",
" const callNow = immediate && !timeout;\n",
" clearTimeout(timeout);\n",
" timeout = setTimeout(later, wait);\n",
" if (callNow) func.apply(context, args);\n",
" };\n",
" }\n",
"\n",
" function filterNotification() {\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
" const $boxes = $('.wl_filter-options>.form-check>input[name=checkbox]:checked');\n",
" const item = Object.values($boxes).find(function(value) { return $(value)[0] === undefined});\n",
" if (item === undefined) {\n",
" $notifCircleContainer.removeClass("d-none")\n",
" } else {\n",
" $notifCircleContainer.addClass("d-none")\n",
" }\n",
" }\n",
"\n",
" function handleSearch() {\n",
" const tableBodyChildren = $tableBody.children;\n",
"\n",
" for (let i = 0; i < tableBodyChildren.length; i++) {\n",
" const type = tableBodyChildren[i].dataset.inferredType.toLowerCase();\n",
" const name = tableBodyChildren[i].dataset.featureName.toLowerCase();\n",
"\n",
" if (activeTypes[type] && name.startsWith(searchString)) {\n",
" tableBodyChildren[i].style.display = "";\n",
" } else {\n",
" tableBodyChildren[i].style.display = "none";\n",
" }\n",
" }\n",
" }\n",
"\n",
" $featureSearch.addEventListener(\n",
" "keyup",\n",
" debounce((event) => {\n",
" searchString = event.target.value.toLowerCase();\n",
" handleSearch();\n",
" }, 100),\n",
" );\n",
"\n",
" $discrete.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["discrete"] = true;\n",
" } else {\n",
" activeTypes["discrete"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" $nonDiscrete.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["non-discrete"] = true;\n",
" } else {\n",
" activeTypes["non-discrete"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" $unknown.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["unknown"] = true;\n",
" } else {\n",
" activeTypes["unknown"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
"\n",
" $(".svg-container").css("padding-bottom", "27%")\n",
" }\n",
"\n",
" function checkedBoxes() {\n",
" const $boxes = $('input[name=checkbox]:checked');\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
"\n",
" if ($boxes.length) {\n",
" $notifCircleContainer.removeClass("d-none")\n",
" }\n",
" }\n",
"\n",
" function openFilter() {\n",
" const $filterOptions = $(".dropdown-container");\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
" const filterClass = $filterOptions.attr("class");\n",
"\n",
" if (filterClass.indexOf("d-none") > 0) {\n",
" $notifCircleContainer.addClass("d-none")\n",
" $filterOptions.removeClass("d-none");\n",
" $(".filter-icon").addClass("d-none")\n",
" $(".close-filter-icon").removeClass("d-none")\n",
" } else {\n",
" $filterOptions.addClass("d-none");\n",
" $(".close-filter-icon").addClass("d-none")\n",
" $(".filter-icon").removeClass("d-none")\n",
" checkedBoxes()\n",
" }\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=1000px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.profile_summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Drift Summary"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also compare two different profiles. With `summary_drift_report`, we have overview statistics, such as number of observations and missing cells, as well as a comparison between both profile's features, with regards to each feature's distribution, and drift calculations for numerical or categorical features.\n",
"\n",
"The report also displays alerts related to each of the feature's drift severity.\n",
"\n",
"You can also search for a specific feature, or filter by drift severity."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
" :root {\n",
" /* CONSTANTS */\n",
" --SIDE-PANEL-WIDTH: 320px;\n",
" --PROPERTY-PANEL-WIDTH: 420px;\n",
"\n",
" /* COLOR VARIABLES */\n",
" /** Standard colors */\n",
" --red: #d11010;\n",
" --orange: #f07028;\n",
" --yellow: #faaf40;\n",
" --olive: #b5cc18;\n",
" --green: #1dbb42;\n",
" --teal: #00b5ad;\n",
" --blue: #2683c9;\n",
" --violet: #6435c9;\n",
" --purple: #a333c8;\n",
" --pink: #ed45a4;\n",
" --brown: #ac724d;\n",
" --grey: #778183;\n",
" --black: #1b1c1d;\n",
" --white: #ffffff;\n",
"\n",
" /** Branded colors */\n",
" --brandPrimary900: #0e7384;\n",
" --brandPrimary800: #228798;\n",
" --brandPrimary700: #369bac;\n",
" --brandPrimary600: #4aafc0;\n",
" --brandPrimary500: #5ec3d4;\n",
" --brandPrimary400: #72d7e8;\n",
" --brandPrimary300: #86ebfc;\n",
" --brandPrimary200: #a6f2ff;\n",
" --brandPrimary100: #cdf8ff;\n",
" --brandSecondary900: #4f595b;\n",
" --brandSecondary800: #636d6f;\n",
" --brandSecondary700: #778183;\n",
" --brandSecondary600: #8b9597;\n",
" --brandSecondary500: #9fa9ab;\n",
" --brandSecondary400: #b3bdbf;\n",
" --brandSecondary300: #c7d1d3;\n",
" --brandSecondary200: #dbe5e7;\n",
" --brandSecondary100: #ebf2f3;\n",
" --secondaryLight1000: #313b3d;\n",
" --brandRed4: #b30000;\n",
" --brandRed3: #d72424;\n",
" --brandRed2: #eb5656;\n",
" --brandRed1: #ff8282;\n",
" --night1: #021826;\n",
" /** Purpose colors */\n",
" --textColor: #4f595b;\n",
" --linkColor: #369bac;\n",
" --infoColor: #2683c9;\n",
" --warningColor: #faaf40;\n",
" --tealBackground: #eaf2f3;\n",
" --pageBackground: #e5e5e5;\n",
" --contrastTableRow: #fafafa;\n",
" --whiteBackground: #ffffff;\n",
" --primaryBackground: #e5e5e5;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" font-family: "Asap", Arial, Helvetica, sans-serif;\n",
" }\n",
"\n",
" /*\n",
" * Main content\n",
" */\n",
" .main {\n",
" position: relative;\n",
" background: #FFFFFF;\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" }\n",
" .main .page-header {\n",
" margin-top: 0;\n",
" }\n",
"\n",
" /* CSS DIV TABLE BASIC STYLE */\n",
"\n",
" .wl-table-row .wl-table-head:first-child {\n",
" min-width: 360px;\n",
" z-index: 2;\n",
" }\n",
"\n",
" .wl-table-wrap {\n",
" position: relative;\n",
" }\n",
"\n",
" .wl-table {\n",
" display: table;\n",
" width: 100%;\n",
" }\n",
"\n",
" .row>* {\n",
" padding-right: 0 !important;\n",
" padding-left: 0 !important;\n",
" }\n",
"\n",
" .wl-table-row {\n",
" display: table-row;\n",
" }\n",
"\n",
" .wl-table-row:hover,\n",
" .wl-table-row:hover .wl-table-cell:first-child {\n",
" background-color: var(--brandSecondary100);\n",
" }\n",
" .wl-table-row--clickable:hover .wl-table-cell__title-button {\n",
" visibility: visible;\n",
" }\n",
"\n",
" .wl-table-heading {\n",
" position: sticky;\n",
" top: 0;\n",
" z-index: 900;\n",
" display: table-header-group;\n",
" font-weight: 700;\n",
" }\n",
"\n",
" .wl-table-cell,\n",
" .wl-table-head {\n",
" border-bottom: 1px solid var(--brandSecondary200);\n",
" display: table-cell;\n",
" padding: 12px 18px;\n",
" }\n",
"\n",
" .wl-table-cell {\n",
" font-size: 14px;\n",
" }\n",
"\n",
" .wl-table-cell--top-spacing {\n",
" padding-top: 35px; /* cell-top-padding + cell-title-height */\n",
" }\n",
"\n",
" .wl-table-head-wraper {\n",
" background-color: var(--white);\n",
" }\n",
"\n",
" .wl-table-head {\n",
" position: relative;\n",
" left: 0;\n",
" min-width: 100px;\n",
" border-bottom: 2px solid var(--brandSecondary100);\n",
" font-size: 12px;\n",
" line-height: 1.67;\n",
" white-space: nowrap;\n",
" }\n",
"\n",
" .wl-sub-table-head {\n",
" position: relative;\n",
" }\n",
"\n",
" .wl-table-body {\n",
" display: table-row-group;\n",
" }\n",
"\n",
" .wl-table-row .wl-table-cell:first-child{\n",
" position: relative;\n",
" left: 0;\n",
" background-color: var(--white);\n",
" border-right-width: 2px;\n",
" }\n",
"\n",
" /* Table custom style */\n",
"\n",
" .wl-table-cell__title-wrap {\n",
" display: flex;\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .wl-table-cell__title {\n",
" height: 25px;\n",
" margin: 0;\n",
" font-size: 14px;\n",
" font-weight: 700;\n",
" overflow: hidden;\n",
" white-space: nowrap;\n",
" text-overflow: ellipsis;\n",
" }\n",
"\n",
" .wl-table-cell__bedge-wrap {\n",
" padding: 2px 0;\n",
" white-space: nowrap;\n",
" overflow: hidden;\n",
" font-style: italic;\n",
" text-align: center;\n",
" color: var(--brandSecondary400);\n",
" }\n",
" .wl-table-cell__bedge {\n",
" height: 24px;\n",
" margin: 1px;\n",
" padding: 2px 8px;\n",
" border: 1px solid var(--brandSecondary400);\n",
" font-style: normal;\n",
" color: var(--brandSecondary900);\n",
" border-radius: 20px;\n",
" white-space: nowrap;\n",
" }\n",
"\n",
" /* Property side panel */\n",
"\n",
" .wl-compare-profile {\n",
" position: relative;\n",
" left: 0;\n",
" padding: 18px;\n",
" background: var(--white);;\n",
" border-bottom: 1px solid var(--brandSecondary200);\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .space-between {\n",
" display: flex;\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .align-items {\n",
" display: flex;\n",
" align-items: center;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .table-border-none {\n",
" padding: 0;\n",
" border: none;\n",
" }\n",
"\n",
" .flex-direction-colum {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .align-items {\n",
" align-items: center;\n",
" }\n",
"\n",
" .search-input{\n",
" padding-top: 0 !important;\n",
" padding-bottom: 0 !important;\n",
" }\n",
"\n",
" .search-input input{\n",
" border: none;\n",
" background: none;\n",
" outline: none;\n",
" height: 40px;\n",
" width: 100%;\n",
" font-size: 14px;\n",
" }\n",
"\n",
" .search-input img{\n",
" height: 19px;\n",
" pointer-events: none;\n",
" }\n",
"\n",
" input::placeholder {\n",
" color: var(--secondaryLight1000);\n",
" }\n",
"\n",
" .text-align-center {\n",
" text-align: center;\n",
" }\n",
"\n",
" .text-align-end {\n",
" text-align: end;\n",
" }\n",
"\n",
" .drift-detection {\n",
" justify-content: space-between;\n",
" align-items: center;\n",
" }\n",
"\n",
" .drift-detection-info-circle {\n",
" width: 15px;\n",
" height: 15px;\n",
" border-radius: 50px;\n",
" display: inline-block;\n",
" margin-right: 8px;\n",
" }\n",
"\n",
" .severe-drift-circle-color {\n",
" background: #D40D00;\n",
" }\n",
"\n",
" .moderate-drift-circle-color {\n",
" background: #F5843C;\n",
" }\n",
"\n",
" .mild-drift-circle-color {\n",
" background: #F2C142;\n",
" }\n",
"\n",
" .minimal-drift-circle-color {\n",
" background: #ABCA52;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item {\n",
" padding-right: 20px;\n",
" }\n",
"\n",
" .drift-detection-info-title {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 16px;\n",
" line-height: 130%;\n",
" color: #313B3D;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-count {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 14px;\n",
" line-height: 16px;\n",
" color: #000000;\n",
" padding-right: 8px;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-name {\n",
" font-family: Arial;\n",
" font-style: normal;\n",
" font-weight: normal;\n",
" font-size: 12px;\n",
" line-height: 14px;\n",
" color: #000000;\n",
" }\n",
"\n",
" .drift-detection-info-drifts-item-range {\n",
" font-family: Arial;\n",
" font-style: normal;\n",
" font-weight: normal;\n",
" font-size: 11px;\n",
" line-height: 13px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .drift-detection-search-input {\n",
" display: flex;\n",
" align-items: center;\n",
" background: rgba(255, 255, 255, 0.7);\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" width: 170px;\n",
" padding-left: 10px;\n",
" }\n",
"\n",
" .drift-detection-search-input img{\n",
" margin-right: 5px;\n",
" }\n",
"\n",
" .drift-detection-search-input input::placeholder {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 13px;\n",
" line-height: 16px;\n",
" color: #313B3D;\n",
" }\n",
"\n",
" .close-filter-button {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" background: rgba(255, 255, 255, 0.7);\n",
" border: 1px solid #369BAC;\n",
" box-sizing: border-box;\n",
" border-radius: 4px;\n",
" width: 40px;\n",
" height: 40px;\n",
" cursor: pointer;\n",
" margin-left: 10px;\n",
" }\n",
"\n",
" .filter-options-title {\n",
" width: 240px;\n",
" }\n",
"\n",
" .filter-options-title p {\n",
" margin: 0;\n",
" }\n",
"\n",
" .dropdown-container {\n",
" position: absolute;\n",
" right: 18px;\n",
" z-index: 999;\n",
" background: #FFFFFF;\n",
" border: 1px solid #DBE5E7;\n",
" box-sizing: border-box;\n",
" box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.05);\n",
" border-radius: 4px;\n",
" padding: 10px !important;\n",
" border: none !important;\n",
" }\n",
"\n",
" .form-check-input:checked {\n",
" background-color: #0E7384;\n",
" border-color: #0E7384;\n",
" }\n",
"\n",
" .form-check-input[type=checkbox] {\n",
" border-radius: 2px;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .wl-table-cell__graph-wrap {\n",
" width: 0;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 85%;\n",
" padding-bottom: 17%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .reference-table-head {\n",
" min-width: 250px;\n",
" }\n",
"\n",
" .wl__dropdown_arrow-icon {\n",
" position: relative;\n",
" }\n",
"\n",
" .notif-circle-container{\n",
" position: absolute;\n",
" top: -4px;\n",
" right: -4px;\n",
" padding: 5.3px;\n",
" border-radius: 50%;\n",
" background-color: white;\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .notif-circle {\n",
" position: absolute;\n",
" top: 2px;\n",
" right: 2px;\n",
" padding: 3.3px;\n",
" border-radius: 50%;\n",
" background-color: #F2994A;\n",
" }\n",
"\n",
"\n",
" .header-title {\n",
" font-size: 26px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistic-number-title {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 14px;\n",
" line-height: 20px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .statistic-number {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 20px;\n",
" line-height: 140%;\n",
" display: flex;\n",
" align-items: center;\n",
" color: #0E7384;\n",
" }\n",
"\n",
" .statistic-measurement {\n",
" font-size: 15px !important;\n",
" margin-left: 3px;\n",
" }\n",
"\n",
" .statistic-measurement-percent {\n",
" font-size: 15px !important;\n",
" }\n",
"\n",
" .question-mark {\n",
" font-size: 10px;\n",
" font-weight: 900;\n",
" color: #0E7384;\n",
" border-radius: 50px;\n",
" border: 2px solid #0E7384;\n",
" padding: 0px 4px;\n",
" transition: 0.5s;\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .question-mark:hover {\n",
" color: white;\n",
" background: #0E7384;\n",
" border: 2px solid none;\n",
" transition: 0.5s;\n",
" }\n",
"\n",
" .tooltip-full-number {\n",
" position: relative;\n",
" display: inline-block;\n",
" }\n",
"\n",
" .tooltip-full-number .tooltiptext {\n",
" visibility: hidden;\n",
" background: black;\n",
" color: white;\n",
" border: 1px solid black;\n",
" text-align: start;\n",
" padding: 3px;\n",
" position: absolute;\n",
" z-index: 1002;\n",
" top: 0;\n",
" left: 100%;\n",
" margin-left: 5px;\n",
" opacity: 0;\n",
" transition: opacity 0.5s;\n",
" font-size: 13px;\n",
" font-weight: normal;\n",
" line-height: 100%;\n",
" }\n",
"\n",
" .tooltip-full-number:hover .tooltiptext {\n",
" visibility: visible;\n",
" opacity: 1;\n",
" }\n",
"\n",
" .display-flex {\n",
" display: flex;\n",
" }\n",
"\n",
" .flex-direction-column {\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .justify-content-space-between {\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .align-items-center {\n",
" align-items: center;\n",
" }\n",
"\n",
" .padding-right-30 {\n",
" padding-right: 30px;\n",
" }\n",
"\n",
" .padding-5 {\n",
" padding: 5px;\n",
" }\n",
"\n",
" .text-color {\n",
" color: var(--secondaryLight1000);\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
"\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
" <div class="container-fluid">\n",
" <div class="feature-summary-statistics-wrap">\n",
" <div class="feature-summary-statistics">\n",
" <div class="mb-4">\n",
" <strong class="header-title">Profile Summary</strong>\n",
" </div>\n",
" <div class="display-flex statistics">\n",
" <div class="padding-right-30">\n",
" <div class="statistic-number-title">Observations\n",
" <div class="tooltip-full-number">\n",
" <span class="question-mark">?</span>\n",
" <span class="tooltiptext">\n",
" <div class="mb-1">The sum of counts for each feature. For a single dataframe, it is equal to the number of cells, i.e., rows * columns.</div>\n",
" </span>\n",
" </div>\n",
" </div>\n",
" <div class="statistic-number">{{{observations this}}}</div>\n",
"\n",
" </div>\n",
" <div class="padding-right-30">\n",
" <div class="statistic-number-title">Missing Cells</div>\n",
" <div class="statistic-number">\n",
" {{{missingCells this}}}\n",
" <div>{{{missingCellsPercentage this}}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="row">\n",
" <div class="main">\n",
" <div class="wl-compare-profile" id="compare-profile">\n",
" <div class="drift-detection-wrap">\n",
" <div class="drift-detection display-flex">\n",
" <div class="drift-detection-info flex-direction-colum">\n",
" <div class="drift-detection-info-title-wrap display-flex">\n",
" <p class="drift-detection-info-title">\n",
" Drift detected in\n",
" <span class="drift-count"></span>\n",
" of\n",
" <span class="all-features"></span>\n",
" features\n",
" </p>\n",
" </div>\n",
" <div class="drift-detection-info-drifts display-flex" id="drift-detection-info-drifts">\n",
" </div>\n",
" </div>\n",
" <div class="drift-detection-search-input-wrap display-flex">\n",
" <div class="drift-detection-search-input search-input">\n",
" <input type="text" id="wl__feature-search" placeholder="Quick search..."/>\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGdSURBVHgBpZK7TkJBEIZnZoVocdTYyQNALxpNKPQBMEaNJsbCRMKhl8ISWwt6AaksCF5iTHgAGhJD4AHkAaAzGiwUsjvOQnO4SYh/cXbPzO43OxcEj9Zy92EiFSXNIfvPyE1kKFfdoxJMENpP6DrvLC0vJoEwCgwto7DWcxoIIHBYbA3NmKwnDltjAeuZhyul1DaTTlfPB6Nt5Z53DOgky4P875+nlctY2+unjZviLklkJhi5bPUa3y/7qJuQUM7PinMy7CdQc1Gh16vnBxPzrMROmlKQEgKNASAHLQCmSIGpS75O+O5pdQAgVXaIqTkNwDDXHmcnW3VmHZoGMLoTsOt88+NrAMCIZdu+iLTyTwKRa1Md6YKfOgXbzO7K8sWku5u5RxcRV5EpPezrzcHGbXEXWaUkgkweZ/UC9YrK3zqggFw5FBZfm8EUavHj7AjAKpIvBDrGn+pNnlcyhYgqbcC41idr1gvB4SdZkDbzQa21gwv0Vj07aPTtL07XdDOyDXohCDNoHIRmAVRie20f+RKybRDQDvxHkXy/7b/DrayncLbMwQAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" <div class="wl__dropdown_arrow-icon">\n",
" <div onclick="openFilter()" class="close-filter-button">\n",
" <div class="display-flex close-filter-icon d-none">\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADFSURBVHgBfY+xDcIwEEXvnLQBZQkYAEhDwwKpEEK0CCZgAEjJCEmgjYSAygxAHTZgFRSOsyUjY5mcZFnn/+78PwBXf3+MoKWUPuYjVBPFnTwpr9t/oNJfcTfXsAhRAlDqDhhQIPYgpAqNMDqcUqSAYZT1epr9gAHt6uXshvYme4DYHQJNDKh0dD0m5WXB10Y3Fqjtuh7fROn3oREDWxfeMLyRsMnc0OgDzdduaA0Pi3Plgr7Q2kaAePeBqh6rueSNBVt6fgCwBV1JLF3rlAAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" <div class="display-flex filter-icon">\n",
" <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAPCAYAAADtc08vAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACSSURBVHgBrZLBCYAwDEWTUjw7igcdwI1cxkNXUJzBEVzFAbSVKoKmaVrEB6GHJv/w+AACtRk7P9IONv1QOYUl96k0zv61m2tjARoLtSDI3EFsgIJ4uoXrMLazO72CRG2mzg/8BSdVlEjhpGZJjAWdAZJECpWalEhJSs1pHuUlMad5FFai1Lwg4Ckx1TxKIPFL8w55mEWd8VjPGAAAAABJRU5ErkJggg=="/>\n",
" </div>\n",
" </div>\n",
" <span class="notif-circle-container">\n",
" <span class="notif-circle"></span>\n",
" </span>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="dropdown-container flex-direction-colum mb-2 d-none" id="dropdown-container">\n",
" <div class="filter-options">\n",
" <div class="filter-options-title space-between dropdown">\n",
" <p>Filter by type</p>\n",
" </div>\n",
" <div class="form-check mb-1 mt-2">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Nodrift"\n",
" id="nodrift"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="nodrift">\n",
" No evidence of drift (<span class="wl__feature-count--nodrift">{{getNoDriftCount}}</span>)\n",
" </label>\n",
" </div>\n",
" <div class="form-check mb-1">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Possibledrift"\n",
" id="possibledrift"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="possibledrift">\n",
" Possible drift (<span\n",
" class="wl__feature-count--possibledrift"\n",
" >{{getPossibleDriftCount}}</span>)\n",
" </label>\n",
" </div>\n",
" <div class="form-check mb-1">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Detecteddrift"\n",
" id="detecteddrift"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="detecteddrift">\n",
" Detected drift (<span class="wl__feature-count--drift">{{getDriftCount}}</span>)\n",
" </label>\n",
" </div>\n",
" <div class="form-check mb-1">\n",
" <input\n",
" class="form-check-input wl__feature-filter-input"\n",
" type="checkbox"\n",
" name="checkbox"\n",
" value="Unknowndrift"\n",
" id="unknowndrift"\n",
" checked\n",
" />\n",
" <label class="form-check-label" for="unknowndrift">\n",
" Unknown (<span\n",
" class="wl__feature-count--unknown"\n",
" >{{getUnknownDriftCount}}</span>)\n",
" </label>\n",
" </div>\n",
"\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="wl-table-wrap" id="table-content">\n",
" <div class="wl-table">\n",
" <div class="wl-table-heading">\n",
" <div class="wl-table-row wl-table-row--bottom-shadow">\n",
" <div class="wl-table-head wl-table-head-wraper">\n",
" <div class="wl-table-head table-border-none graph-table-head">Target</div>\n",
" <div class="wl-table-head table-border-none reference-table-head">Reference</div>\n",
" </div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-center" style="cursor: pointer;" id="diff-from-ref">\n",
" <div class="pvalue">Drift Score\n",
" <div class="tooltip-full-number">\n",
" <span class="question-mark">?</span>\n",
" <span class="tooltiptext">\n",
" <div class="mb-1">Drift is evaluated against pvalue, if present, and against statistic otherwise.</div>\n",
" </span>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Data Drift</div>\n",
" <div class="wl-table-head wl-table-head-wraper text-align-end">Total count</div>\n",
" </div>\n",
" </div>\n",
" <ul class="wl-table-body wl__table-body" id="table-body">\n",
"{{#each this.columns}} <li\n",
" {{#if this.numberSummary}} class="wl-table-row wl-table-row--clickable" {{else}} class="wl-table-row" {{/if}}\n",
" data-feature-name={{@key}}\n",
" data-drift-category={{driftCategory this}}\n",
" data-scroll-to-feature-name={{@key}}\n",
" data-p-value={{getpvalue this}}\n",
" >\n",
" <div class="wl-table-cell wl-table-cell__graph-wrap">\n",
" <div class="wl-table-cell__title-wrap">\n",
" <h4 class="wl-table-cell__title">{{@key}}</h4>\n",
" <div></div>\n",
" </div>\n",
" <div class="display-flex">\n",
" {{{getGraphHtml this}}}\n",
" {{{getReferenceGraphHtml this}}}\n",
" </div>\n",
" </div>\n",
" <div\n",
" class="diff-from-ref-table-cell wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" style="max-width: 270px; padding-right: 18px"\n",
" ><div class="wl-table-cell__bedge-wrap text-align-end">\n",
" {{{getDiffFromRef this}}}\n",
" </div></div><div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{getDriftCategory this}}\n",
" <div class="tooltip-full-number">\n",
" <span class="question-mark">?</span>\n",
" <span class="tooltiptext">\n",
" <div class="mb-1"><pre class="mb-1">{{getThresholds this}}</pre></div>\n",
" </span>\n",
" </div>\n",
" </div>\n",
"\n",
" </div>\n",
" <div\n",
" class="wl-table-cell wl-table-cell--top-spacing align-middle"\n",
" ><div class="text-align-end">{{totalCount this}}</div></div>\n",
" </li>\n",
"{{/each}} </ul>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" const getReferenceProfile = () => { return {"columns": {"fixed acidity": {"histogram": {"start": 5.0, "end": 15.0000015, "width": 0, "counts": [4, 5, 16, 24, 50, 124, 140, 120, 155, 90, 54, 85, 52, 38, 53, 24, 30, 22, 14, 28, 21, 10, 12, 8, 6, 0, 4, 0, 0, 2], "max": 15.0, "min": 5.0, "bins": [5.0, 5.333333383333334, 5.666666766666666, 6.00000015, 6.333333533333334, 6.666666916666667, 7.0000003, 7.333333683333334, 7.666667066666667, 8.00000045, 8.333333833333334, 8.666667216666667, 9.0000006, 9.333333983333333, 9.666667366666667, 10.00000075, 10.333334133333334, 10.666667516666667, 11.0000009, 11.333334283333333, 11.666667666666667, 12.00000105, 12.333334433333334, 12.666667816666667, 13.0000012, 13.333334583333334, 13.666667966666667, 14.00000135, 14.333334733333334, 14.666668116666667, 15.0000015], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 6.69437210173219e-08, "statistic": 0.1672408258013533, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 6.69437210173219e-08}, "isDiscrete": false, "featureStats": null}, "volatile acidity": {"histogram": {"start": 0.18, "end": 1.580000158, "width": 0, "counts": [14, 38, 63, 65, 115, 115, 92, 109, 161, 123, 100, 61, 46, 20, 23, 16, 8, 9, 5, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 2], "max": 1.58, "min": 0.18, "bins": [0.18, 0.22666667193333334, 0.27333334386666663, 0.3200000158, 0.36666668773333333, 0.4133333596666667, 0.46000003159999997, 0.5066667035333333, 0.5533333754666667, 0.6000000474, 0.6466667193333333, 0.6933333912666666, 0.7400000631999999, 0.7866667351333334, 0.8333334070666667, 0.880000079, 0.9266667509333333, 0.9733334228666666, 1.0200000948, 1.0666667667333334, 1.1133334386666667, 1.1600001106, 1.2066667825333333, 1.2533334544666666, 1.3000001263999998, 1.3466667983333334, 1.3933334702666667, 1.4400001422, 1.4866668141333332, 1.5333334860666665, 1.580000158], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 1.160530015945676e-13, "statistic": 0.2221748901071764, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 1.160530015945676e-13}, "isDiscrete": false, "featureStats": null}, "citric acid": {"histogram": {"start": 0.0, "end": 1.0000001, "width": 0, "counts": [179, 64, 91, 49, 38, 77, 74, 106, 67, 63, 40, 51, 35, 39, 95, 28, 27, 23, 10, 16, 12, 0, 4, 1, 0, 0, 0, 0, 0, 2], "max": 1.0, "min": 0.0, "bins": [0.0, 0.03333333666666667, 0.06666667333333334, 0.10000001000000001, 0.13333334666666669, 0.16666668333333334, 0.20000002000000003, 0.2333333566666667, 0.26666669333333337, 0.30000003000000003, 0.3333333666666667, 0.3666667033333334, 0.40000004000000006, 0.4333333766666667, 0.4666667133333334, 0.50000005, 0.5333333866666667, 0.5666667233333335, 0.6000000600000001, 0.6333333966666668, 0.6666667333333334, 0.7000000700000001, 0.7333334066666668, 0.7666667433333334, 0.8000000800000001, 0.8333334166666668, 0.8666667533333334, 0.9000000900000001, 0.9333334266666669, 0.9666667633333335, 1.0000001], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 1.3615177857137967e-12, "statistic": 0.21519435970785916, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 1.3615177857137967e-12}, "isDiscrete": false, "featureStats": null}, "residual sugar": {"histogram": {"start": 1.2, "end": 15.50000155, "width": 0, "counts": [109, 475, 360, 108, 33, 26, 13, 4, 8, 17, 11, 3, 4, 1, 4, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 3, 0, 0, 4], "max": 15.5, "min": 1.2, "bins": [1.2, 1.6766667183333333, 2.1533334366666668, 2.6300001550000003, 3.1066668733333334, 3.5833335916666664, 4.06000031, 4.5366670283333335, 5.0133337466666665, 5.4900004650000005, 5.966667183333334, 6.443333901666667, 6.920000620000001, 7.396667338333334, 7.873334056666667, 8.350000775, 8.826667493333334, 9.303334211666666, 9.78000093, 10.256667648333332, 10.733334366666666, 11.210001085, 11.686667803333332, 12.163334521666666, 12.64000124, 13.116667958333332, 13.593334676666666, 14.070001395, 14.546668113333332, 15.023334831666666, 15.50000155], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 0.0033840255260516663, "statistic": 0.10182372697189707, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 0.0033840255260516663}, "isDiscrete": false, "featureStats": null}, "chlorides": {"histogram": {"start": 0.038, "end": 0.6110000610999999, "width": 0, "counts": [36, 379, 545, 131, 31, 10, 13, 6, 4, 6, 5, 3, 0, 0, 0, 4, 2, 2, 2, 8, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2], "max": 0.611, "min": 0.038, "bins": [0.038, 0.05710000203666667, 0.07620000407333333, 0.09530000611, 0.11440000814666665, 0.13350001018333332, 0.15260001222, 0.17170001425666664, 0.19080001629333332, 0.20990001833, 0.22900002036666664, 0.24810002240333331, 0.26720002443999996, 0.28630002647666664, 0.30540002851333325, 0.32450003054999993, 0.3436000325866666, 0.3627000346233333, 0.38180003665999995, 0.40090003869666657, 0.42000004073333325, 0.4391000427699999, 0.4582000448066666, 0.47730004684333327, 0.49640004887999994, 0.5155000509166666, 0.5346000529533332, 0.55370005499, 0.5728000570266666, 0.5919000590633333, 0.6110000610999999], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 1.784913412173917e-27, "statistic": 0.3159418679310515, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 1.784913412173917e-27}, "isDiscrete": false, "featureStats": null}, "free sulfur dioxide": {"histogram": {"start": 1.0, "end": 72.0000072, "width": 0, "counts": [31, 107, 186, 105, 105, 132, 92, 63, 77, 47, 72, 36, 31, 39, 20, 5, 17, 3, 2, 4, 2, 6, 4, 0, 0, 0, 0, 1, 2, 2], "max": 72.0, "min": 1.0, "bins": [1.0, 3.3666669066666666, 5.733333813333333, 8.10000072, 10.466667626666666, 12.833334533333332, 15.20000144, 17.566668346666667, 19.933335253333333, 22.30000216, 24.666669066666664, 27.033335973333333, 29.40000288, 31.766669786666665, 34.133336693333334, 36.5000036, 38.866670506666665, 41.23333741333333, 43.60000432, 45.96667122666666, 48.33333813333333, 50.70000504, 53.066671946666666, 55.43333885333333, 57.80000576, 60.16667266666666, 62.53333957333333, 64.90000648, 67.26667338666667, 69.63334029333333, 72.0000072], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 0.016036399887931697, "statistic": 0.08850076554551295, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 0.016036399887931697}, "isDiscrete": false, "featureStats": null}, "total sulfur dioxide": {"histogram": {"start": 6.0, "end": 160.000016, "width": 0, "counts": [44, 96, 100, 99, 103, 68, 79, 98, 60, 56, 50, 51, 36, 28, 21, 37, 25, 23, 13, 18, 15, 5, 20, 10, 8, 6, 10, 6, 4, 2], "max": 160.0, "min": 6.0, "bins": [6.0, 11.133333866666666, 16.266667733333332, 21.400001599999996, 26.533335466666664, 31.66666933333333, 36.80000319999999, 41.93333706666666, 47.06667093333333, 52.200004799999995, 57.33333866666666, 62.46667253333332, 67.60000639999998, 72.73334026666666, 77.86667413333332, 83.000008, 88.13334186666665, 93.26667573333332, 98.40000959999999, 103.53334346666665, 108.66667733333333, 113.80001119999999, 118.93334506666665, 124.06667893333332, 129.20001279999997, 134.33334666666664, 139.46668053333332, 144.6000144, 149.73334826666664, 154.8666821333333, 160.000016], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 2.0762015466878437e-12, "statistic": 0.21148812169704156, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 2.0762015466878437e-12}, "isDiscrete": false, "featureStats": null}, "density": {"histogram": {"start": 0.99236, "end": 1.003690100369, "width": 0, "counts": [1, 1, 3, 2, 4, 11, 34, 42, 74, 95, 132, 136, 127, 128, 100, 84, 43, 43, 36, 31, 28, 16, 4, 6, 2, 0, 4, 2, 0, 2], "max": 1.00369, "min": 0.99236, "bins": [0.99236, 0.9927376700123001, 0.9931153400246, 0.9934930100369, 0.9938706800492, 0.9942483500615, 0.9946260200738, 0.9950036900861, 0.9953813600984001, 0.9957590301107, 0.996136700123, 0.9965143701353, 0.9968920401476, 0.9972697101599, 0.9976473801722, 0.9980250501845, 0.9984027201968, 0.9987803902091, 0.9991580602214, 0.9995357302337, 0.999913400246, 1.0002910702582999, 1.0006687402706, 1.0010464102829, 1.0014240802952, 1.0018017503075, 1.0021794203198, 1.0025570903321, 1.0029347603443999, 1.0033124303567, 1.003690100369], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 7.9303277449007265e-81, "statistic": 0.531918308885267, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 7.9303277449007265e-81}, "isDiscrete": false, "featureStats": null}, "pH": {"histogram": {"start": 2.74, "end": 3.750000375, "width": 0, "counts": [2, 0, 0, 2, 5, 8, 0, 10, 23, 24, 45, 42, 79, 99, 65, 87, 147, 92, 106, 126, 58, 62, 27, 35, 19, 15, 2, 7, 4, 0], "max": 3.75, "min": 2.74, "bins": [2.74, 2.773666679166667, 2.8073333583333335, 2.8410000375, 2.874666716666667, 2.9083333958333335, 2.942000075, 2.975666754166667, 3.0093334333333335, 3.0430001125, 3.076666791666667, 3.1103334708333334, 3.14400015, 3.1776668291666668, 3.2113335083333334, 3.2450001875, 3.2786668666666667, 3.3123335458333334, 3.346000225, 3.3796669041666667, 3.4133335833333334, 3.4470002625, 3.4806669416666667, 3.5143336208333333, 3.5480003, 3.5816669791666667, 3.6153336583333333, 3.6490003375, 3.6826670166666666, 3.7163336958333333, 3.750000375], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 0.0003157364134524475, "statistic": 0.11929545117795237, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 0.0003157364134524475}, "isDiscrete": false, "featureStats": null}, "sulphates": {"histogram": {"start": 0.33, "end": 2.0000002, "width": 0, "counts": [3, 30, 100, 219, 231, 215, 110, 84, 72, 31, 29, 14, 8, 13, 6, 10, 0, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4], "max": 2.0, "min": 0.33, "bins": [0.33, 0.3856666733333334, 0.4413333466666667, 0.49700002, 0.5526666933333333, 0.6083333666666666, 0.66400004, 0.7196667133333334, 0.7753333866666667, 0.83100006, 0.8866667333333333, 0.9423334066666667, 0.99800008, 1.0536667533333333, 1.1093334266666668, 1.1650001, 1.2206667733333334, 1.2763334466666667, 1.33200012, 1.3876667933333333, 1.4433334666666666, 1.4990001400000001, 1.5546668133333335, 1.6103334866666668, 1.66600016, 1.7216668333333334, 1.7773335066666667, 1.83300018, 1.8886668533333335, 1.9443335266666668, 2.0000002], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 1.3923726824145813e-09, "statistic": 0.18502741146836565, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 1.3923726824145813e-09}, "isDiscrete": false, "featureStats": null}, "alcohol": {"histogram": {"start": 8.4, "end": 11.0000011, "width": 0, "counts": [2, 1, 0, 2, 2, 0, 31, 1, 22, 74, 59, 103, 139, 63, 53, 0, 79, 50, 69, 47, 45, 33, 0, 42, 69, 28, 26, 43, 50, 58], "max": 11.0, "min": 8.4, "bins": [8.4, 8.486666703333334, 8.573333406666666, 8.66000011, 8.746666813333334, 8.833333516666666, 8.92000022, 9.006666923333334, 9.093333626666666, 9.18000033, 9.266667033333334, 9.353333736666666, 9.44000044, 9.526667143333334, 9.613333846666666, 9.70000055, 9.786667253333334, 9.873333956666666, 9.96000066, 10.046667363333334, 10.133334066666666, 10.22000077, 10.306667473333334, 10.393334176666666, 10.48000088, 10.566667583333334, 10.653334286666666, 10.74000099, 10.826667693333334, 10.913334396666666, 11.0000011], "n": 1191}, "frequentItems": null, "drift_from_ref": {"algorithm": "ks", "pvalue": 0.0, "statistic": 1.0, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 1.0}, "isDiscrete": false, "featureStats": null}, "quality": {"histogram": null, "frequentItems": [{"value": "bad", "estimate": 1115}, {"value": "good", "estimate": 76}], "drift_from_ref": {"algorithm": "chi-square", "pvalue": 4.72188097163335e-120, "statistic": 542.7640823743186, "thresholds": {"NO_DRIFT": [0.15, 1], "POSSIBLE_DRIFT": [0.05, 0.15], "DRIFT": [0, 0.05]}, "drift_category": "DRIFT", "primary_value": 4.72188097163335e-120}, "isDiscrete": true, "featureStats": null}}, "properties": {"observations": 4896, "missing_cells": 10, "missing_percentage": 0.20424836601307192}} }\n",
" const referenceProfile = getReferenceProfile()\n",
"\n",
" function fixNumberTo(number, decimals = 3) {\n",
" const fractionalDigits = String(number % 1).split('').slice(2, 2 + decimals).join('')\n",
" return `${Math.trunc(number)}.${fractionalDigits}`\n",
" }\n",
"\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, data, bottomMargin=20, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", "30 0 240 400")\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(data, (d) => Math.abs(d.axisY));\n",
" this.xScale = d3\n",
" .scaleBand()\n",
" .domain(data.map((d) => d.axisX))\n",
" .range([this.MARGIN.LEFT, this.MARGIN.LEFT + this.CHART_WIDTH]);\n",
" this.yScale = d3\n",
" .scaleLinear()\n",
" .domain([0, this.maxYValue * 1.02])\n",
" .range([this.CHART_HEIGHT, 0]);\n",
" }\n",
" }\n",
"\n",
" function generateChart(data, height = 70, width = 250, index = 0, referenceProfileExist = false) {\n",
" const sizes = new GenerateChartParams(height, width, data, 5)\n",
" const {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" maxYValue,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
"\n",
" // Add the y Axis\n",
" if (!referenceProfileExist) {\n",
" svgEl\n",
" .append("g")\n",
" .attr("transform", "translate(" + MARGIN.LEFT + ", " + MARGIN.TOP + ")")\n",
" .call(d3.axisLeft(yScale).tickValues([0, maxYValue/2, maxYValue]))\n",
" .selectAll("text")\n",
" .style("font-size", "8")\n",
" }\n",
"\n",
" const gChart = svgEl.append("g");\n",
" gChart\n",
" .attr("transform", "translate(" + 20 + ", " + 0 + ")")\n",
" .selectAll(".bar")\n",
" .data(data)\n",
" .enter()\n",
" .append("rect")\n",
" .classed("bar", true)\n",
" .attr("width", xScale.bandwidth() - 1)\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", (d) => xScale(d.axisX))\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP)\n",
" .attr("fill", rectColors[index]);\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column.histogram) {\n",
" column.histogram.counts.slice(0, 30).forEach((count, index) => {\n",
" data.push({\n",
" axisY: count,\n",
" axisX: index,\n",
" });\n",
" });\n",
" } else if (column.frequentItems) {\n",
" Object.entries(column.frequentItems).forEach(([key, {value, estimate}], index) => {\n",
" data.push({\n",
" axisY: estimate,\n",
" axisX: value,\n",
" });\n",
" });\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function graph(column, key, referenceColumn) {\n",
" let data = [];\n",
" const columnKey = key.data.key\n",
" let chartValue = false\n",
" let chartColor = undefined\n",
" if(!!referenceColumn){\n",
" column = referenceColumn.columns[columnKey]\n",
" chartValue = true\n",
" chartColor = 1\n",
" } else if (referenceColumn === undefined) {\n",
" column = ""\n",
" }\n",
"\n",
" if (column.histogram || column.frequentItems) {\n",
" data = chartData(column)\n",
" } else if (referenceColumn === null ) {\n",
" $(".svg-container").css("padding-bottom", "0")\n",
" return '<span class="wl-table-cell__bedge-wrap">No data to show the chart</span>';\n",
" } else if (referenceColumn === undefined) {\n",
" $(document).ready(function() {\n",
" $(".reference-table-head").addClass("d-none")\n",
" });\n",
" return ''\n",
" } else if (referenceColumn.frequentItems === undefined){\n",
" return '';\n",
" }\n",
" return `\n",
" <div class="svg-container">\n",
" ${generateChart(data, ...[,,], chartColor, chartValue)}\n",
" </div>\n",
" `;\n",
" }\n",
"\n",
" function formatLabelDate(timestamp) {\n",
" const date = new Date(timestamp);\n",
" const format = d3.timeFormat("%Y-%m-%d %I:%M:%S %p %Z");\n",
" return format(date);\n",
" }\n",
"\n",
" const driftCountElement = (driftCount, driftColor, driftName, driftRange) => `\n",
" <div class="display-flex flex-direction-column">\n",
" <div class="drift-detection-info-drifts-item display-flex align-items">\n",
" <div class="drift-detection-info-drifts-item-text display-flex align-items">\n",
" <p class="drift-detection-info-drifts-item-count mb-0">${driftCount}</p>\n",
" <p class="drift-detection-info-drifts-item-range display-flex justify-content-center mb-0">${driftRange}</p>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" `\n",
"\n",
" const drifts = {\n",
" severe: {\n",
" count: 0,\n",
" range: "with detected drift",\n",
" },\n",
" moderate: {\n",
" count: 0,\n",
" range: "with possible drift",\n",
" },\n",
" mild: {\n",
" count: 0,\n",
" range: "with no evidence of drift",\n",
" }\n",
" };\n",
"\n",
" const countOfDrifts = (drift_from_ref, increment = true) => {\n",
" drift_class = drift_from_ref.drift_category\n",
" if (drift_class=="NO_DRIFT") {\n",
" if (increment){Object.values(drifts)[2].count++}\n",
" return 0\n",
" }\n",
" if (drift_class=="POSSIBLE_DRIFT") {\n",
" if (increment){Object.values(drifts)[1].count++}\n",
" return 1\n",
" }\n",
" if (drift_class=="DRIFT") {\n",
" if (increment){Object.values(drifts)[0].count++}\n",
" return 2\n",
" }\n",
" }\n",
"\n",
" abbreviate_number = function(value, fixed = 0) {\n",
" value = +value\n",
" if (value === null) { return null; } // terminate early\n",
" if (value === 0) { return '0'; } // terminate early\n",
" fixed = (!fixed || fixed < 0) ? 0 : fixed; // number of decimal places to show\n",
" var b = (value).toPrecision(2).split("e"), // get power\n",
" k = b.length === 1 ? 0 : Math.floor(Math.min(b[1].slice(1), 14) / 3), // floor at decimals, ceiling at trillions\n",
" c = k < 1 ? value.toFixed(0 + fixed) : (value / Math.pow(10, k * 3) ).toFixed(1 + fixed), // divide by power\n",
" d = c < 0 ? c : Math.abs(c), // enforce -0 is 0\n",
" newValue = d,\n",
" suffixe = ['', 'K', 'M', 'B', 'T'][k]; // append power\n",
" return {value, newValue, suffixe};\n",
" }\n",
"\n",
" function formatBytes(bytes, decimals = 2) {\n",
" let newValue,\n",
" suffixe = ""\n",
" if (bytes === 0) return '0 Bytes';\n",
"\n",
" const k = 1024;\n",
" const dm = decimals < 0 ? 0 : decimals;\n",
" const sizes = ['Bytes', 'KiB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n",
"\n",
" const i = Math.floor(Math.log(bytes) / Math.log(k));\n",
" newValue = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));\n",
" suffixe = sizes[i]\n",
" return {bytes, newValue, suffixe};\n",
" }\n",
"\n",
" const valueSuffixe = (newNumber) => `\n",
" <div class="statistic-measurement">\n",
" ${newNumber}\n",
" </div>`\n",
" const valueNumber = (newNumber) => `\n",
" <div class="statistic-number">\n",
" ${newNumber}\n",
" </div>`\n",
"\n",
" const numberWithSuffixe = (number, newNumber, suffixe) =>\n",
" `<div class="tooltip-full-number">\n",
" <div class="statistic-number">\n",
" ${newNumber}\n",
" <div class="statistic-measurement">${suffixe}</div>\n",
" </div>\n",
" <span class="tooltiptext">${number}</span>\n",
" </div>`\n",
"\n",
" Handlebars.registerHelper("observations", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(referenceProfile.properties.observations)\n",
" return numberWithSuffixe(value, valueNumber(newValue), valueNumber(suffixe));\n",
" });\n",
"\n",
" Handlebars.registerHelper("missingCells", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(referenceProfile.properties.missing_cells)\n",
" if (typeof value !== 'undefined' && typeof newValue !== 'undefined' && typeof suffixe !== 'undefined' ) {\n",
" return numberWithSuffixe(value, valueSuffixe(`${newValue}`), suffixe);\n",
" }\n",
" return numberWithSuffixe(0, valueNumber(0), valueNumber(''));\n",
" });\n",
"\n",
" Handlebars.registerHelper("missingCellsPercentage", function (properties) {\n",
" const {value, newValue, suffixe} = abbreviate_number(referenceProfile.properties.missing_percentage)\n",
" if (typeof value !== 'undefined' && typeof newValue !== 'undefined' && typeof suffixe !== 'undefined' ) {\n",
" return numberWithSuffixe(value, valueSuffixe(`(${newValue}%)`), suffixe);\n",
" }\n",
" return numberWithSuffixe(0, valueSuffixe(`(${0}%)`), '');\n",
" });\n",
"\n",
" Handlebars.registerHelper("getProfileTimeStamp", function (properties) {\n",
" return formatLabelDate(+properties.properties.dataTimestamp)\n",
" });\n",
"\n",
" Handlebars.registerHelper("getProfileName", function (properties) {\n",
" return properties.properties.tags.name\n",
" });\n",
"\n",
" let driftCount = 0;\n",
" const diffFromRefTableElement = (driftFromRefNumber, circleColor) => `\n",
" <div class="text-color padding-5 text-align-center justify-content-center">\n",
" <pre class="mb-1"> ${driftFromRefNumber} </pre>\n",
" </div>\n",
" `\n",
"\n",
" const cheqValueTypeNumber = (profile, profileValue) => {\n",
" let validValue;\n",
" if (profile && profileValue !== undefined && typeof profileValue === "number") {\n",
" return true\n",
" } else if (profileValue !== undefined && typeof profileValue !== "number") {\n",
" return false\n",
" }\n",
" }\n",
"\n",
" const cheqDriftFromRef = (profile, driftFromRef) => {\n",
" if (profile && driftFromRef){\n",
" console.log("drift from ref true")\n",
" return true\n",
" }\n",
" else {\n",
" console.log("drift from ref false")\n",
" return false\n",
" }\n",
" }\n",
"\n",
" Handlebars.registerHelper("getpvalue", function (column, key) {\n",
" const columnKey = key.data.key\n",
" const {drift_from_ref} = referenceProfile.columns[columnKey]\n",
" if (cheqDriftFromRef(referenceProfile, drift_from_ref)){\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value)) {\n",
" const driftFromRefNumber = drift_from_ref.primary_value.toPrecision(2)\n",
" return Number(driftFromRefNumber)\n",
"\n",
" }\n",
" }\n",
" else {\n",
" return "-"\n",
" }\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("getDriftCategory", function (column, key) {\n",
" const columnKey = key.data.key\n",
" const {drift_from_ref} = referenceProfile.columns[columnKey]\n",
" const categories = {\n",
" 0: "No evidence of drift",\n",
" 1: "Possible drift",\n",
" 2: "Detected drift"\n",
" }\n",
" if (cheqDriftFromRef(referenceProfile, drift_from_ref)){\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value)) {\n",
" const driftFromRefNumber = drift_from_ref.primary_value.toPrecision(2)\n",
" const driftCategory = countOfDrifts(drift_from_ref, increment=false)\n",
" const circleColor = Object.values(drifts)[driftCategory].colorClass\n",
" return categories[driftCategory]\n",
"\n",
" } else if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value) !== undefined) {\n",
" Object.values(drifts)[0].count++\n",
" return diffFromRefTableElement("undefined", "severe-drift-circle-color")\n",
" }\n",
" }\n",
" else {\n",
" return "Unknown"\n",
" }\n",
"\n",
" return '<p style="color: black">-</p>';\n",
" });\n",
"\n",
"\n",
"\n",
" Handlebars.registerHelper("getDiffFromRef", function (column, key) {\n",
" const columnKey = key.data.key\n",
" const {drift_from_ref} = referenceProfile.columns[columnKey]\n",
" if (drift_from_ref==null){\n",
" return diffFromRefTableElement("-", "")\n",
" }\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value)) {\n",
" const driftFromRefNumber = drift_from_ref.primary_value.toPrecision(2)\n",
" const driftCategory = countOfDrifts(drift_from_ref)\n",
" const circleColor = Object.values(drifts)[driftCategory].colorClass\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.pvalue)){\n",
" stats_string = `using algorithm: ${drift_from_ref.algorithm}\\npvalue is: ${drift_from_ref.pvalue.toPrecision(3)}`\n",
" }\n",
" else {\n",
" stats_string = `using algorithm: ${drift_from_ref.algorithm}\\nstatistic is: ${drift_from_ref.statistic.toPrecision(3)}`\n",
" }\n",
" if (driftCategory==1 || driftCategory == 2){\n",
" driftCount++\n",
" }\n",
" $(document).ready(() => {\n",
" $(".drift-count").html(driftCount)\n",
" $(".all-features").html(Object.keys(referenceProfile.columns).length)\n",
" })\n",
"\n",
" return diffFromRefTableElement(stats_string, circleColor)\n",
"\n",
" }\n",
" return '<p style="color: black">-</p>';\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("getThresholds", function (column, key) {\n",
" const columnKey = key.data.key\n",
" const {drift_from_ref} = referenceProfile.columns[columnKey]\n",
" if (drift_from_ref==null) {\n",
" return "-"\n",
" }\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value)) {\n",
" thresholds_map = Object.entries(drift_from_ref.thresholds)\n",
" let thresholds_string = thresholds_map.map(([key, value]) => `${key}: ${value}`).join('\\n')\n",
" return "thresholds:\\n"+thresholds_string\n",
"\n",
" }\n",
" return '<p style="color: black">-</p>';\n",
" });\n",
"\n",
" $(document).ready(() =>\n",
" Object.values(drifts).map(({count, name, colorClass, range}) =>{\n",
" $("#drift-detection-info-drifts")\n",
" .append(driftCountElement(count, colorClass, name, range))\n",
" })\n",
" )\n",
"\n",
" Handlebars.registerHelper("inferredType", function (column) {\n",
" let infferedType = "";\n",
" console.log(column)\n",
" if (column.isDiscrete) {\n",
" infferedType = "Discrete";\n",
" } else {\n",
" infferedType = "Non-discrete";\n",
" }\n",
" return infferedType;\n",
" });\n",
"\n",
" Handlebars.registerHelper("driftCategory", function (column, key) {\n",
" const columnKey = key.data.key\n",
" const {drift_from_ref} = referenceProfile.columns[columnKey]\n",
" if (drift_from_ref == null){\n",
" return "unknowndrift"\n",
" }\n",
" if (cheqValueTypeNumber(referenceProfile, drift_from_ref.primary_value)) {\n",
" console.log(drift_from_ref.drift_category)\n",
" console.log(Number(drift_from_ref.drift_category)==0)\n",
"\n",
" if (drift_from_ref.drift_category=="NO_DRIFT"){\n",
" console.log("discrete")\n",
" return "nodrift"\n",
" }\n",
" if (drift_from_ref.drift_category=="POSSIBLE_DRIFT"){\n",
" console.log("non-discrete")\n",
" return "possibledrift"\n",
" }\n",
" if (drift_from_ref.drift_category=="DRIFT"){\n",
" console.log("unknown")\n",
" return "drift"\n",
" }\n",
"\n",
" return Number(drift_from_ref.drift_category)\n",
"\n",
" }\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("frequentItems", function (column) {\n",
" frequentItemsElemString = "";\n",
" if (column.isDiscrete) {\n",
" const slicedFrequentItems = column.frequentItems.items.slice(0, 5);\n",
" for (let fi = 0; fi < slicedFrequentItems.length; fi++) {\n",
" frequentItemsElemString +=\n",
" '<span class="wl-table-cell__bedge">' + slicedFrequentItems[fi].jsonValue + "</span>";\n",
" }\n",
" } else {\n",
" frequentItemsElemString += "No data to show";\n",
" }\n",
" return frequentItemsElemString;\n",
" });\n",
"\n",
" Handlebars.registerHelper("totalCount", function (column) {\n",
" if (column.featureStats) {\n",
" return column.featureStats.total_count;\n",
" }\n",
"\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("mean", function (column) {\n",
" if (column.featureStats?.descriptive_statistics) {\n",
" if (isNaN(column.featureStats.descriptive_statistics.mean)){return "-"}\n",
" if (column.featureStats.descriptive_statistics.mean==null){return "-"}\n",
" return fixNumberTo(column.featureStats.descriptive_statistics.mean);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("getGraphHtml",(column,key) => graph(column, key, null));\n",
" Handlebars.registerHelper("getReferenceGraphHtml",(column,key) => graph(column, key, referenceProfile));\n",
"\n",
" Handlebars.registerHelper("getDiscreteTypeCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(this.columns).forEach((feature) => {\n",
" if (feature[1].isDiscrete === true) {\n",
" count++;\n",
" }\n",
" });\n",
" return count.toString();\n",
" });\n",
"\n",
" Handlebars.registerHelper("getNonDiscreteTypeCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(referenceProfile.columns).forEach((feature) => {\n",
" if (feature[1].isDiscrete === false) {\n",
" count++;\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
" Handlebars.registerHelper("getDriftCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(referenceProfile.columns).forEach((feature) => {\n",
" if (feature[1].drift_from_ref != null){\n",
" console.log("drif ref not null")\n",
" if (feature[1].drift_from_ref.drift_category == "DRIFT") {\n",
" count++;\n",
" }\n",
" }\n",
" else{\n",
" console.log("drift ref null")\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
" Handlebars.registerHelper("getPossibleDriftCount", function () {\n",
" let count = 0;\n",
"\n",
" Object.entries(referenceProfile.columns).forEach((feature) => {\n",
" if (feature[1].drift_from_ref != null){\n",
" console.log("drif ref not null")\n",
" if (feature[1].drift_from_ref.drift_category == "POSSIBLE_DRIFT") {\n",
" count++;\n",
" }\n",
" }\n",
" else{\n",
" console.log("drift ref null")\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("getNoDriftCount", function () {\n",
" let count = 0;\n",
" Object.entries(referenceProfile.columns).forEach((feature) => {\n",
" if (feature[1].drift_from_ref != null){\n",
" console.log("drif ref not null")\n",
" if (feature[1].drift_from_ref.drift_category == "NO_DRIFT") {\n",
" count++;\n",
" }\n",
" }\n",
" else{\n",
" console.log("drift ref null")\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
" Handlebars.registerHelper("getUnknownDriftCount", function () {\n",
" let count = 0;\n",
" Object.entries(referenceProfile.columns).forEach((feature) => {\n",
" if (feature[1].drift_from_ref == null){\n",
" console.log("drif ref unknown")\n",
" count++;\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("getUnknownTypeCount", function () {\n",
" let count = 0;\n",
" return count;\n",
" Object.entries(this.columns).forEach((feature) => {\n",
" if (!feature[1].isDiscrete) {\n",
" count++;\n",
" }\n",
" });\n",
" return count;\n",
" });\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"columns": {"fixed acidity": {"histogram": {"start": 4.6, "end": 15.90000159, "width": 0, "counts": [3, 17, 16, 17, 28, 39, 39, 30, 22, 30, 24, 35, 12, 13, 22, 23, 9, 6, 3, 4, 2, 3, 3, 2, 1, 0, 0, 0, 2, 3], "max": 15.9, "min": 4.6, "bins": [4.6, 4.9766667196666665, 5.353333439333333, 5.730000158999999, 6.106666878666666, 6.483333598333333, 6.860000318, 7.236667037666667, 7.613333757333333, 7.990000477000001, 8.366667196666667, 8.743333916333334, 9.120000636, 9.496667355666666, 9.873334075333332, 10.250000795, 10.626667514666668, 11.003334234333334, 11.380000954, 11.756667673666666, 12.133334393333334, 12.510001113000001, 12.886667832666667, 13.263334552333333, 13.640001272000001, 14.016667991666667, 14.393334711333335, 14.770001431, 15.146668150666667, 15.523334870333334, 15.90000159], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 20.588239538027807, "min": 4.6, "max": 15.9, "range": 11.3, "quantile_statistics": {"fifth_percentile": 5.4, "iqr": 2.7, "q1": 6.7, "median": 7.9, "q3": 9.4, "ninety_fifth_percentile": 11.6}, "descriptive_statistics": {"stddev": 2.013014441140909, "coefficient_of_variation": 0.24676558362692394, "sum": 3328.3, "variance": 4.052227140241847, "mean": 8.157598039215687}}}, "volatile acidity": {"histogram": {"start": 0.12, "end": 1.1150001115, "width": 0, "counts": [3, 8, 5, 8, 21, 32, 38, 37, 30, 35, 23, 24, 25, 23, 15, 15, 15, 14, 11, 4, 3, 3, 6, 2, 1, 2, 1, 2, 0, 2], "max": 1.115, "min": 0.12, "bins": [0.12, 0.15316667038333331, 0.18633334076666666, 0.21950001115, 0.2526666815333333, 0.28583335191666664, 0.3190000223, 0.35216669268333334, 0.38533336306666666, 0.41850003345, 0.4516667038333333, 0.48483337421666667, 0.5180000445999999, 0.5511667149833333, 0.5843333853666667, 0.61750005575, 0.6506667261333333, 0.6838333965166666, 0.7170000669, 0.7501667372833333, 0.7833334076666666, 0.81650007805, 0.8496667484333333, 0.8828334188166667, 0.9160000892, 0.9491667595833333, 0.9823334299666666, 1.01550010035, 1.0486667707333335, 1.0818334411166668, 1.1150001115000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.05882840516061, "min": 0.12, "max": 1.115, "range": 0.995, "quantile_statistics": {"fifth_percentile": 0.24, "iqr": 0.23999999999999994, "q1": 0.34, "median": 0.44, "q3": 0.58, "ninety_fifth_percentile": 0.8}, "descriptive_statistics": {"stddev": 0.17791965320497752, "coefficient_of_variation": 0.3771756131540623, "sum": 192.45999999999998, "variance": 0.03165540299657947, "mean": 0.47171568627450977}}}, "citric acid": {"histogram": {"start": 0.0, "end": 0.790000079, "width": 0, "counts": [57, 13, 7, 23, 10, 9, 4, 6, 7, 10, 12, 14, 28, 9, 24, 35, 11, 23, 31, 8, 10, 9, 5, 3, 8, 12, 2, 3, 4, 1], "max": 0.79, "min": 0.0, "bins": [0.0, 0.026333335966666666, 0.05266667193333333, 0.0790000079, 0.10533334386666666, 0.13166667983333333, 0.1580000158, 0.18433335176666665, 0.21066668773333333, 0.2370000237, 0.26333335966666666, 0.2896666956333333, 0.3160000316, 0.34233336756666666, 0.3686667035333333, 0.3950000395, 0.42133337546666666, 0.4476667114333333, 0.4740000474, 0.5003333833666667, 0.5266667193333333, 0.5530000553, 0.5793333912666666, 0.6056667272333334, 0.6320000632, 0.6583333991666667, 0.6846667351333333, 0.7110000711, 0.7373334070666666, 0.7636667430333334, 0.790000079], "n": 398}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 398, "missing": 10, "distinct": 18.090455451204008, "min": 0.0, "max": 0.79, "range": 0.79, "quantile_statistics": {"fifth_percentile": 0.0, "iqr": 0.36, "q1": 0.1, "median": 0.34, "q3": 0.46, "ninety_fifth_percentile": 0.66}, "descriptive_statistics": {"stddev": 0.20859943016152155, "coefficient_of_variation": 0.6620619872750046, "sum": 125.4, "variance": 0.04351372226371151, "mean": 0.3150753768844221}}}, "residual sugar": {"histogram": {"start": 0.9, "end": 12.90000129, "width": 0, "counts": [8, 39, 118, 124, 40, 16, 11, 8, 13, 8, 4, 2, 2, 5, 3, 0, 1, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "max": 12.9, "min": 0.9, "bins": [0.9, 1.300000043, 1.7000000860000002, 2.100000129, 2.500000172, 2.900000215, 3.3000002580000003, 3.700000301, 4.100000344000001, 4.500000387, 4.90000043, 5.300000473000001, 5.700000516000001, 6.100000559000001, 6.500000602000001, 6.900000645, 7.300000688000001, 7.700000731000001, 8.100000774, 8.500000817, 8.90000086, 9.300000903, 9.700000946000001, 10.100000989000002, 10.500001032000002, 10.900001075, 11.300001118, 11.700001161000001, 12.100001204000002, 12.500001247000002, 12.90000129], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 15.931375081243552, "min": 0.9, "max": 12.9, "range": 12.0, "quantile_statistics": {"fifth_percentile": 1.6, "iqr": 0.7000000000000002, "q1": 2.0, "median": 2.3, "q3": 2.7, "ninety_fifth_percentile": 5.15}, "descriptive_statistics": {"stddev": 1.2990000858665753, "coefficient_of_variation": 0.49437249664993493, "sum": 1072.05, "variance": 1.6874012230813702, "mean": 2.6275735294117646}}}, "chlorides": {"histogram": {"start": 0.012, "end": 0.230000023, "width": 0, "counts": [2, 0, 0, 9, 21, 40, 45, 76, 64, 43, 33, 21, 20, 8, 9, 9, 3, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1], "max": 0.23, "min": 0.012, "bins": [0.012, 0.019266667433333332, 0.026533334866666668, 0.0338000023, 0.04106666973333333, 0.04833333716666667, 0.0556000046, 0.06286667203333333, 0.07013333946666667, 0.0774000069, 0.08466667433333333, 0.09193334176666666, 0.0992000092, 0.10646667663333333, 0.11373334406666666, 0.12100001149999999, 0.12826667893333332, 0.13553334636666667, 0.14280001380000001, 0.15006668123333333, 0.15733334866666668, 0.1646000161, 0.17186668353333334, 0.17913335096666666, 0.1864000184, 0.19366668583333332, 0.20093335326666667, 0.2082000207, 0.21546668813333333, 0.22273335556666665, 0.230000023], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 21.813730257594944, "min": 0.012, "max": 0.23, "range": 0.218, "quantile_statistics": {"fifth_percentile": 0.046, "iqr": 0.025999999999999995, "q1": 0.06, "median": 0.071, "q3": 0.086, "ninety_fifth_percentile": 0.12}, "descriptive_statistics": {"stddev": 0.024213181268290383, "coefficient_of_variation": 0.3226526212509791, "sum": 30.618, "variance": 0.0005862781471310883, "mean": 0.07504411764705882}}}, "free sulfur dioxide": {"histogram": {"start": 3.0, "end": 51.0000051, "width": 0, "counts": [31, 79, 21, 21, 36, 16, 27, 26, 29, 22, 3, 13, 12, 11, 10, 5, 6, 4, 8, 6, 4, 8, 2, 1, 3, 0, 1, 0, 1, 2], "max": 51.0, "min": 3.0, "bins": [3.0, 4.60000017, 6.200000340000001, 7.80000051, 9.400000680000002, 11.000000850000001, 12.60000102, 14.200001190000002, 15.800001360000001, 17.40000153, 19.000001700000002, 20.600001870000003, 22.20000204, 23.800002210000002, 25.400002380000004, 27.00000255, 28.600002720000003, 30.200002890000004, 31.80000306, 33.40000323, 35.000003400000004, 36.600003570000005, 38.20000374000001, 39.80000391, 41.40000408, 43.00000425, 44.600004420000005, 46.200004590000006, 47.80000476000001, 49.40000493, 51.0000051], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 11.029412969947018, "min": 3.0, "max": 51.0, "range": 48.0, "quantile_statistics": {"fifth_percentile": 3.0, "iqr": 13.0, "q1": 6.0, "median": 13.0, "q3": 19.0, "ninety_fifth_percentile": 36.0}, "descriptive_statistics": {"stddev": 9.994223698460777, "coefficient_of_variation": 0.6774056431550788, "sum": 6019.5, "variance": 99.884507334875, "mean": 14.753676470588236}}}, "total sulfur dioxide": {"histogram": {"start": 6.0, "end": 289.0000289, "width": 0, "counts": [85, 92, 70, 47, 40, 14, 16, 10, 10, 10, 6, 2, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], "max": 289.0, "min": 6.0, "bins": [6.0, 15.433334296666667, 24.866668593333333, 34.30000289, 43.73333718666667, 53.16667148333333, 62.600005780000004, 72.03334007666666, 81.46667437333333, 90.90000867, 100.33334296666666, 109.76667726333334, 119.20001156000001, 128.63334585666666, 138.06668015333332, 147.50001445, 156.93334874666667, 166.36668304333332, 175.80001734, 185.23335163666667, 194.66668593333333, 204.10002023, 213.53335452666667, 222.96668882333333, 232.40002312000001, 241.83335741666667, 251.26669171333333, 260.70002601, 270.13336030666665, 279.56669460333336, 289.0000289], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 22.303926553943704, "min": 6.0, "max": 289.0, "range": 283.0, "quantile_statistics": {"fifth_percentile": 10.0, "iqr": 29.0, "q1": 17.0, "median": 27.0, "q3": 46.0, "ninety_fifth_percentile": 93.0}, "descriptive_statistics": {"stddev": 31.68721099598923, "coefficient_of_variation": 0.8554477659209692, "sum": 15112.999999999998, "variance": 1004.0793407043408, "mean": 37.041666666666664}}}, "density": {"histogram": {"start": 0.99007, "end": 1.0032001003200002, "width": 0, "counts": [3, 4, 1, 9, 4, 12, 8, 32, 32, 36, 39, 49, 45, 25, 25, 19, 15, 8, 4, 8, 5, 6, 2, 5, 4, 1, 1, 2, 0, 4], "max": 1.0032, "min": 0.99007, "bins": [0.99007, 0.9905076700106666, 0.9909453400213334, 0.991383010032, 0.9918206800426667, 0.9922583500533334, 0.992696020064, 0.9931336900746667, 0.9935713600853334, 0.9940090300960001, 0.9944467001066667, 0.9948843701173334, 0.9953220401280001, 0.9957597101386667, 0.9961973801493335, 0.9966350501600001, 0.9970727201706667, 0.9975103901813335, 0.9979480601920001, 0.9983857302026667, 0.9988234002133335, 0.9992610702240001, 0.9996987402346668, 1.0001364102453334, 1.0005740802560001, 1.0010117502666669, 1.0014494202773334, 1.0018870902880002, 1.002324760298667, 1.0027624303093334, 1.0032001003200002], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 55.88238444546564, "min": 0.99007, "max": 1.0032, "range": 0.013130000000000086, "quantile_statistics": {"fifth_percentile": 0.9922, "iqr": 0.0023400000000000087, "q1": 0.99396, "median": 0.99516, "q3": 0.9963, "ninety_fifth_percentile": 0.9994}, "descriptive_statistics": {"stddev": 0.0021784570136347352, "coefficient_of_variation": 0.002188682344428877, "sum": 406.09385999999995, "variance": 4.745674960254368e-06, "mean": 0.995328088235294}}}, "pH": {"histogram": {"start": 2.89, "end": 4.010000401, "width": 0, "counts": [5, 1, 3, 4, 7, 5, 17, 20, 38, 36, 44, 29, 35, 41, 30, 11, 19, 17, 14, 10, 2, 9, 4, 2, 0, 1, 0, 2, 0, 2], "max": 4.01, "min": 2.89, "bins": [2.89, 2.9273333467000002, 2.9646666934000003, 3.0020000401, 3.0393333868, 3.0766667335, 3.1140000802000003, 3.1513334269, 3.1886667736, 3.2260001203, 3.2633334670000003, 3.3006668137, 3.3380001604, 3.3753335071, 3.4126668538000002, 3.4500002005, 3.4873335472, 3.5246668939, 3.5620002406, 3.5993335873000003, 3.636666934, 3.6740002807, 3.7113336274, 3.7486669741, 3.7860003208000004, 3.8233336675, 3.8606670142, 3.8980003609000002, 3.9353337076, 3.9726670543, 4.010000401], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 18.8725525817653, "min": 2.89, "max": 4.01, "range": 1.1199999999999997, "quantile_statistics": {"fifth_percentile": 3.09, "iqr": 0.2200000000000002, "q1": 3.23, "median": 3.33, "q3": 3.45, "ninety_fifth_percentile": 3.67}, "descriptive_statistics": {"stddev": 0.17649177660211463, "coefficient_of_variation": 0.05273349702213279, "sum": 1365.52, "variance": 0.031149347208170735, "mean": 3.346862745098039}}}, "sulphates": {"histogram": {"start": 0.37, "end": 1.130000113, "width": 0, "counts": [3, 2, 4, 4, 6, 18, 19, 45, 22, 37, 22, 32, 21, 29, 28, 27, 22, 12, 24, 7, 10, 1, 5, 1, 3, 1, 1, 0, 0, 2], "max": 1.13, "min": 0.37, "bins": [0.37, 0.3953333371, 0.4206666742, 0.4460000113, 0.47133334839999996, 0.4966666855, 0.5220000226, 0.5473333597, 0.5726666968, 0.5980000339, 0.623333371, 0.6486667080999999, 0.6740000452, 0.6993333823, 0.7246667194, 0.7500000565, 0.7753333936, 0.8006667306999999, 0.8260000677999999, 0.8513334049, 0.8766667419999999, 0.9020000791, 0.9273334162, 0.9526667533, 0.9780000904, 1.0033334274999999, 1.0286667646, 1.0540001016999998, 1.0793334388, 1.1046667759, 1.130000113], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 14.215688286897349, "min": 0.37, "max": 1.13, "range": 0.7599999999999999, "quantile_statistics": {"fifth_percentile": 0.5, "iqr": 0.19000000000000006, "q1": 0.58, "median": 0.66, "q3": 0.77, "ninety_fifth_percentile": 0.88}, "descriptive_statistics": {"stddev": 0.12701037402923776, "coefficient_of_variation": 0.18776807233831805, "sum": 275.97999999999996, "variance": 0.016131635111046875, "mean": 0.6764215686274508}}}, "alcohol": {"histogram": {"start": 11.066666666666698, "end": 14.900001490000001, "width": 0, "counts": [28, 68, 32, 30, 38, 29, 21, 21, 25, 12, 13, 27, 9, 17, 9, 8, 1, 3, 3, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1], "max": 14.9, "min": 11.066666666666698, "bins": [11.066666666666698, 11.194444494111142, 11.322222321555586, 11.45000014900003, 11.577777976444471, 11.705555803888915, 11.833333631333359, 11.961111458777802, 12.088889286222246, 12.21666711366669, 12.344444941111133, 12.472222768555575, 12.600000596000019, 12.727778423444462, 12.855556250888906, 12.98333407833335, 13.111111905777793, 13.238889733222237, 13.36666756066668, 13.494445388111123, 13.622223215555566, 13.75000104300001, 13.877778870444454, 14.005556697888897, 14.13333452533334, 14.261112352777783, 14.388890180222226, 14.51666800766667, 14.644445835111114, 14.772223662555557, 14.900001490000001], "n": 408}, "frequentItems": null, "drift_from_ref": null, "isDiscrete": false, "featureStats": {"total_count": 408, "missing": 0, "distinct": 7.59803978178436, "min": 11.066666666666698, "max": 14.9, "range": 3.833333333333302, "quantile_statistics": {"fifth_percentile": 11.1, "iqr": 1.0, "q1": 11.4, "median": 11.8, "q3": 12.4, "ninety_fifth_percentile": 13.2}, "descriptive_statistics": {"stddev": 0.6824986635512768, "coefficient_of_variation": 0.05725985668809312, "sum": 4863.083333333334, "variance": 0.4658044257492789, "mean": 11.919321895424838}}}, "quality": {"histogram": null, "frequentItems": [{"value": "bad", "estimate": 267}, {"value": "good", "estimate": 141}], "drift_from_ref": null, "isDiscrete": true, "featureStats": {"total_count": 408, "missing": 0, "distinct": 0.4901960796487877, "min": NaN, "max": NaN, "range": NaN, "quantile_statistics": null, "descriptive_statistics": {"stddev": null, "coefficient_of_variation": null, "sum": null, "variance": null, "mean": null}}}}, "properties": null};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" const $featureSearch = document.getElementById("wl__feature-search");\n",
" const $tableBody = document.getElementById("table-body");\n",
" const $noDrift = document.getElementById("nodrift");\n",
" const $possibleDrift = document.getElementById("possibledrift");\n",
" const $drift = document.getElementById("detecteddrift");\n",
" const $diffFromRef = document.getElementById("diff-from-ref");\n",
" const $unknownDrift = document.getElementById("unknowndrift");\n",
"\n",
" const activeTypes = {\n",
" nodrift: true,\n",
" possibledrift: true,\n",
" drift: true,\n",
" unknowndrift: true,\n",
" };\n",
"\n",
" var driftOrder = {\n",
" original: true,\n",
" descending: false,\n",
" ascending: false,\n",
" };\n",
" var originalColumnOrder;\n",
"\n",
" let searchString = "";\n",
"\n",
" function debounce(func, wait, immediate) {\n",
" let timeout;\n",
"\n",
" return function () {\n",
" const context = this;\n",
" const args = arguments;\n",
" const later = function () {\n",
" timeout = null;\n",
" if (!immediate) func.apply(context, args);\n",
" };\n",
"\n",
" const callNow = immediate && !timeout;\n",
" clearTimeout(timeout);\n",
" timeout = setTimeout(later, wait);\n",
" if (callNow) func.apply(context, args);\n",
" };\n",
" }\n",
"\n",
" function filterNotification() {\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
" const $boxes = $('.wl_filter-options>.form-check>input[name=checkbox]:checked');\n",
" const item = Object.values($boxes).find(function(value) { return $(value)[0] === undefined});\n",
" if (item === undefined) {\n",
" $notifCircleContainer.removeClass("d-none")\n",
" } else {\n",
" $notifCircleContainer.addClass("d-none")\n",
" }\n",
" }\n",
"\n",
"\n",
" function handleSearch() {\n",
" const tableBodyChildren = $tableBody.children;\n",
"\n",
" for (let i = 0; i < tableBodyChildren.length; i++) {\n",
" const type = tableBodyChildren[i].dataset.driftCategory.toLowerCase();\n",
" const name = tableBodyChildren[i].dataset.featureName.toLowerCase();\n",
" if (activeTypes[type] && name.startsWith(searchString)) {\n",
" tableBodyChildren[i].style.display = "";\n",
" } else {\n",
" tableBodyChildren[i].style.display = "none";\n",
" }\n",
" }\n",
" }\n",
"\n",
" $featureSearch.addEventListener(\n",
" "keyup",\n",
" debounce((event) => {\n",
" searchString = event.target.value.toLowerCase();\n",
" handleSearch();\n",
" }, 100),\n",
" );\n",
"\n",
" $noDrift.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["nodrift"] = true;\n",
" } else {\n",
" activeTypes["nodrift"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" $possibleDrift.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["possibledrift"] = true;\n",
" } else {\n",
" activeTypes["possibledrift"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" $unknownDrift.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["unknowndrift"] = true;\n",
" } else {\n",
" activeTypes["unknowndrift"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" $drift.addEventListener("change", (event) => {\n",
" if (event.currentTarget.checked) {\n",
" activeTypes["drift"] = true;\n",
" } else {\n",
" activeTypes["drift"] = false;\n",
" }\n",
" handleSearch();\n",
" filterNotification()\n",
" });\n",
"\n",
" function checkCurrentProfile(item, referenceItem) {\n",
" if (referenceProfile && Object.values(referenceProfile)) {\n",
" return referenceItem\n",
" } else {\n",
" return item\n",
" }\n",
" }\n",
"\n",
" if(checkCurrentProfile(true, false)) {\n",
" $(".svg-container").css("padding-bottom", "27%")\n",
" $("#diff-from-ref").addClass("d-none")\n",
" $(".diff-from-ref-table-cell").addClass("d-none")\n",
" } else {\n",
" $("#diff-from-ref").removeClass("d-none")\n",
" $(".diff-from-ref-table-cell").removeClass("d-none")\n",
" }\n",
" }\n",
"\n",
" function checkedBoxes() {\n",
" const $boxes = $('input[name=checkbox]:checked');\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
"\n",
" if ($boxes.length) {\n",
" $notifCircleContainer.removeClass("d-none")\n",
" }\n",
" }\n",
"\n",
" function openFilter() {\n",
" const $filterOptions = $(".dropdown-container");\n",
" const $notifCircleContainer = $(".notif-circle-container")\n",
" const filterClass = $filterOptions.attr("class");\n",
"\n",
" if (filterClass.indexOf("d-none") > 0) {\n",
" $notifCircleContainer.addClass("d-none")\n",
" $filterOptions.removeClass("d-none");\n",
" $(".filter-icon").addClass("d-none")\n",
" $(".close-filter-icon").removeClass("d-none")\n",
" } else {\n",
" $filterOptions.addClass("d-none");\n",
" $(".close-filter-icon").addClass("d-none")\n",
" $(".filter-icon").removeClass("d-none")\n",
" checkedBoxes()\n",
" }\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=1000px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.summary_drift_report()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"The drift values are calculated in different ways, depending on the existing metrics for each column. Kolmogorov-Smirnov Test is calculated if distribution metrics exists for said column. If not, Chi Square Test is calculated if frequent items, cardinality and count metric exists. If not, then no drift value is associated to the column.\n",
" \n",
"For `alcohol`, there's an alert of severe drift, with calculated p-value of 0.00. That makes sense, since both distributions are mutually exclusive.\n",
"\n",
"We can also conclude some thing just by visually inspecting the distributions. We can clearly see that the \"good-to-bad\" ratio changes significantly between both profiles. That in itself is a good indicator that the alcohol content might be correlated to the wine's quality\n",
"\n",
"The drift value is also relevant for a number of other features. For example, the `density` also is flagged with significant drift. Let's look at this feature in particular."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Histograms and Bar charts"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we have a general idea of both profiles, let's take a look at some of the individual features.\n",
"\n",
"First, let's use the `double_histogram` to check on the `density` feature."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" :root {\n",
" /** Branded colors */\n",
" --brandSecondary900: #4f595b;\n",
" --secondaryLight1000: #313b3d;\n",
" /** Purpose colors */\n",
" --tealBackground: #eaf2f3;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" overflow-y: hidden;\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .desktop-content {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 100%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .circle-color {\n",
" display: inline-block;\n",
" padding: 5px;\n",
" border-radius: 50px;\n",
" }\n",
"\n",
" .colors-for-distingushing-charts {\n",
" padding-right: 10px;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .align-items-flex-end {\n",
" align-items: flex-end;\n",
" }\n",
"\n",
" .chart-box-title {\n",
" width: 88%;\n",
" justify-content: space-between;\n",
" margin: 10px;\n",
" margin-top: 15px;\n",
" bottom: 0;\n",
" }\n",
"\n",
" .chart-box-title p{\n",
" margin-bottom: 0;\n",
" font-family: Asap;\n",
" font-weight: bold;\n",
" font-size: 18px;\n",
" line-height: 16px;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .bar {\n",
" font: 10px sans-serif;\n",
" }\n",
"\n",
" .bar path,\n",
" .bar line {\n",
" fill: none;\n",
" stroke: #000;\n",
" shape-rendering: crispEdges;\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
"{{#each this}} <div class="chart-box" id="chart-box">\n",
" <div class="chart-box-title display-flex">\n",
" <p>{{@key}}</p>\n",
" <div class="display-flex">\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #44C0E7;"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Target</text>\n",
" </div>\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #F5843C"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Reference</text>\n",
" </div>\n",
" </div></div>\n",
" <div class="svg-container">{{{getDoubleHistogramChart this}}}</div>\n",
" </div>\n",
"{{/each}} </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, targetData, referenceData, bottomMargin=30, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", `0 0 ${$(window).width()+100} ${$(window).height()-30}`)\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(targetData, (d) => Math.abs(d.axisY));\n",
" this.minYValue = d3.min(targetData, (d) => Math.abs(d.axisY));\n",
" const mergedReferenceData = referenceData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
" const mergedTargetedData = targetData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
"\n",
" this.charts2 = mergedReferenceData.concat(mergedTargetedData)\n",
" this.charts2 = this.charts2.sort(function(a, b) { return a - b; });\n",
"\n",
" this.targetBinWidth = targetData[1]?.axisX - targetData[0]?.axisX\n",
" this.referenceBinWidth = referenceData[1]?.axisX - referenceData[0]?.axisX\n",
" this.maxTargetXValue = d3.max(targetData, (d) => d.axisX);\n",
"\n",
" this.maxReferenceXValue = d3.max(referenceData, (d) => d.axisX);\n",
"\n",
" this.xScale = d3\n",
" .scaleLinear()\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisX); }),(this.maxTargetXValue+this.targetBinWidth >= this.maxReferenceXValue+this.referenceBinWidth) ? this.maxTargetXValue+this.targetBinWidth:this.maxReferenceXValue+this.referenceBinWidth])\n",
" .range([0, this.CHART_WIDTH ]);\n",
"\n",
" this.svgEl.append("g")\n",
" .attr("transform", "translate("+ this.MARGIN.LEFT +"," + this.SVG_HEIGHT + ")")\n",
" .call(d3.axisBottom(this.xScale));\n",
" this.yScale = d3.scaleLinear()\n",
" .range([this.CHART_HEIGHT , 0])\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisY); }), d3.max(this.charts2, function(d) { return parseFloat(d.axisY); })*1.2])\n",
" .nice();\n",
" }\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column?.histogram) {\n",
" for (let i = 0; i<column.histogram.bins.length; i++) {\n",
" data.push({\n",
" axisY: column.histogram.counts[i] || 0,\n",
" axisX: column.histogram.bins[i] || 0,\n",
" });\n",
" }\n",
" } else {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function verticalLine(column) {\n",
" const line_data = [];\n",
" if (column?.vertical_line) {\n",
" line_data.push({\n",
" axisX: column?.vertical_line || 0,\n",
" });\n",
"\n",
" }\n",
"\n",
" return line_data\n",
" }\n",
"\n",
" function generateDoubleHistogramChart(targetData, referenceData) {\n",
" let histogramData = [],\n",
" overlappedHistogramData = [];\n",
" let yFormat;\n",
"\n",
" histogramData = chartData(targetData)\n",
" overlappedHistogramData = chartData(referenceData)\n",
" lineData = verticalLine(targetData)\n",
"\n",
" const sizes = new GenerateChartParams($(window).height()-80, $(window).width(), histogramData, overlappedHistogramData)\n",
" const {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" maxYValue,\n",
" minYValue,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
"\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
" const yAxis = d3.axisLeft(yScale).ticks(SVG_HEIGHT / 40);\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .call(yAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line")\n",
" .attr("x2", CHART_WIDTH)\n",
" .attr("stroke-opacity", 0.1))\n",
" .call(g => g.append("text")\n",
" .attr("x", -MARGIN.LEFT)\n",
" .attr("y", 10)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "start"))\n",
"\n",
" svgEl.append("line")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .data(lineData)\n",
" .attr("x1", (d) => xScale(d.axisX))\n",
" .attr("y1", MARGIN.TOP + MARGIN.TOP)\n",
" .attr("x2", (d) => xScale(d.axisX))\n",
" .attr("y2", CHART_HEIGHT + MARGIN.TOP)\n",
" .style("stroke-width", 2)\n",
" .style("stroke", "#44C0E7")\n",
" .style("stroke-dasharray", (3,3))\n",
" .style("fill", "none");\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .data(lineData)\n",
" .attr("y", (d) => xScale(d.axisX) + MARGIN.LEFT)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("single value")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform",\n",
" "translate(" + (CHART_WIDTH/2) + " ," +\n",
" (CHART_HEIGHT + MARGIN.TOP + 75) + ")")\n",
" .style("text-anchor", "middle")\n",
" .text("Values")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .attr("y", 0)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("Counts")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" const gChart = svgEl.append("g");\n",
" gChart\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(histogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(histogramData[1]?.axisX)-xScale(histogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[0])\n",
" .style("opacity","0.6")\n",
"\n",
" const gChart1 = svgEl.append("g");\n",
" gChart1\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(overlappedHistogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(overlappedHistogramData[1]?.axisX)-xScale(overlappedHistogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[1])\n",
" .style("opacity","0.6")\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" const profileFromCSVfile = {"density": {"histogram": {"start": 0.99236, "end": 1.003690100369, "width": 0, "counts": [1, 1, 3, 2, 4, 11, 34, 42, 74, 95, 132, 136, 127, 128, 100, 84, 43, 43, 36, 31, 28, 16, 4, 6, 2, 0, 4, 2, 0, 2], "max": 1.00369, "min": 0.99236, "bins": [0.99236, 0.9927376700123001, 0.9931153400246, 0.9934930100369, 0.9938706800492, 0.9942483500615, 0.9946260200738, 0.9950036900861, 0.9953813600984001, 0.9957590301107, 0.996136700123, 0.9965143701353, 0.9968920401476, 0.9972697101599, 0.9976473801722, 0.9980250501845, 0.9984027201968, 0.9987803902091, 0.9991580602214, 0.9995357302337, 0.999913400246, 1.0002910702582999, 1.0006687402706, 1.0010464102829, 1.0014240802952, 1.0018017503075, 1.0021794203198, 1.0025570903321, 1.0029347603443999, 1.0033124303567, 1.003690100369], "n": 1191}}}\n",
"\n",
" Handlebars.registerHelper("getDoubleHistogramChart",(column,key) => {\n",
" const columnKey = key.data.key\n",
" try {\n",
" if (profileFromCSVfile) {\n",
" return generateDoubleHistogramChart (\n",
" column,\n",
" profileFromCSVfile[columnKey]\n",
" )\n",
" }\n",
" } catch (err) {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
" });\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" $(".svg-container").css("height", $(window).height() - 32)\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"density": {"histogram": {"start": 0.99007, "end": 1.0032001003200002, "width": 0, "counts": [3, 4, 1, 9, 4, 12, 8, 32, 32, 36, 39, 49, 45, 25, 25, 19, 15, 8, 4, 8, 5, 6, 2, 5, 4, 1, 1, 2, 0, 4], "max": 1.0032, "min": 0.99007, "bins": [0.99007, 0.9905076700106666, 0.9909453400213334, 0.991383010032, 0.9918206800426667, 0.9922583500533334, 0.992696020064, 0.9931336900746667, 0.9935713600853334, 0.9940090300960001, 0.9944467001066667, 0.9948843701173334, 0.9953220401280001, 0.9957597101386667, 0.9961973801493335, 0.9966350501600001, 0.9970727201706667, 0.9975103901813335, 0.9979480601920001, 0.9983857302026667, 0.9988234002133335, 0.9992610702240001, 0.9996987402346668, 1.0001364102453334, 1.0005740802560001, 1.0010117502666669, 1.0014494202773334, 1.0018870902880002, 1.002324760298667, 1.0027624303093334, 1.0032001003200002], "n": 408}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=300px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.double_histogram(feature_name=\"density\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can visually assess that there seems to be a drift between both distributions indeed. Maybe the alcohol content plays a significant role on the wine's density.\n",
"\n",
"As is the case with the alcohol content, our potential model would see density values in production never before seen in the training test. We can certainly expect performance degradation during production."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also pass a list of feature names to `double_histogram` to generate multiple plots. Let's check the `alcohol` and `chlorides` features. For the alcohol feature, there's a clear separation between the distributions at the value of `11`."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" :root {\n",
" /** Branded colors */\n",
" --brandSecondary900: #4f595b;\n",
" --secondaryLight1000: #313b3d;\n",
" /** Purpose colors */\n",
" --tealBackground: #eaf2f3;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" overflow-y: hidden;\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .desktop-content {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 100%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .circle-color {\n",
" display: inline-block;\n",
" padding: 5px;\n",
" border-radius: 50px;\n",
" }\n",
"\n",
" .colors-for-distingushing-charts {\n",
" padding-right: 10px;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .align-items-flex-end {\n",
" align-items: flex-end;\n",
" }\n",
"\n",
" .chart-box-title {\n",
" width: 88%;\n",
" justify-content: space-between;\n",
" margin: 10px;\n",
" margin-top: 15px;\n",
" bottom: 0;\n",
" }\n",
"\n",
" .chart-box-title p{\n",
" margin-bottom: 0;\n",
" font-family: Asap;\n",
" font-weight: bold;\n",
" font-size: 18px;\n",
" line-height: 16px;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .bar {\n",
" font: 10px sans-serif;\n",
" }\n",
"\n",
" .bar path,\n",
" .bar line {\n",
" fill: none;\n",
" stroke: #000;\n",
" shape-rendering: crispEdges;\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
"{{#each this}} <div class="chart-box" id="chart-box">\n",
" <div class="chart-box-title display-flex">\n",
" <p>{{@key}}</p>\n",
" <div class="display-flex">\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #44C0E7;"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Target</text>\n",
" </div>\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #F5843C"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Reference</text>\n",
" </div>\n",
" </div></div>\n",
" <div class="svg-container">{{{getDoubleHistogramChart this}}}</div>\n",
" </div>\n",
"{{/each}} </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, targetData, referenceData, bottomMargin=30, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", `0 0 ${$(window).width()+100} ${$(window).height()-30}`)\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(targetData, (d) => Math.abs(d.axisY));\n",
" this.minYValue = d3.min(targetData, (d) => Math.abs(d.axisY));\n",
" const mergedReferenceData = referenceData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
" const mergedTargetedData = targetData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
"\n",
" this.charts2 = mergedReferenceData.concat(mergedTargetedData)\n",
" this.charts2 = this.charts2.sort(function(a, b) { return a - b; });\n",
"\n",
" this.targetBinWidth = targetData[1]?.axisX - targetData[0]?.axisX\n",
" this.referenceBinWidth = referenceData[1]?.axisX - referenceData[0]?.axisX\n",
" this.maxTargetXValue = d3.max(targetData, (d) => d.axisX);\n",
"\n",
" this.maxReferenceXValue = d3.max(referenceData, (d) => d.axisX);\n",
"\n",
" this.xScale = d3\n",
" .scaleLinear()\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisX); }),(this.maxTargetXValue+this.targetBinWidth >= this.maxReferenceXValue+this.referenceBinWidth) ? this.maxTargetXValue+this.targetBinWidth:this.maxReferenceXValue+this.referenceBinWidth])\n",
" .range([0, this.CHART_WIDTH ]);\n",
"\n",
" this.svgEl.append("g")\n",
" .attr("transform", "translate("+ this.MARGIN.LEFT +"," + this.SVG_HEIGHT + ")")\n",
" .call(d3.axisBottom(this.xScale));\n",
" this.yScale = d3.scaleLinear()\n",
" .range([this.CHART_HEIGHT , 0])\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisY); }), d3.max(this.charts2, function(d) { return parseFloat(d.axisY); })*1.2])\n",
" .nice();\n",
" }\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column?.histogram) {\n",
" for (let i = 0; i<column.histogram.bins.length; i++) {\n",
" data.push({\n",
" axisY: column.histogram.counts[i] || 0,\n",
" axisX: column.histogram.bins[i] || 0,\n",
" });\n",
" }\n",
" } else {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function verticalLine(column) {\n",
" const line_data = [];\n",
" if (column?.vertical_line) {\n",
" line_data.push({\n",
" axisX: column?.vertical_line || 0,\n",
" });\n",
"\n",
" }\n",
"\n",
" return line_data\n",
" }\n",
"\n",
" function generateDoubleHistogramChart(targetData, referenceData) {\n",
" let histogramData = [],\n",
" overlappedHistogramData = [];\n",
" let yFormat;\n",
"\n",
" histogramData = chartData(targetData)\n",
" overlappedHistogramData = chartData(referenceData)\n",
" lineData = verticalLine(targetData)\n",
"\n",
" const sizes = new GenerateChartParams($(window).height()-80, $(window).width(), histogramData, overlappedHistogramData)\n",
" const {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" maxYValue,\n",
" minYValue,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
"\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
" const yAxis = d3.axisLeft(yScale).ticks(SVG_HEIGHT / 40);\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .call(yAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line")\n",
" .attr("x2", CHART_WIDTH)\n",
" .attr("stroke-opacity", 0.1))\n",
" .call(g => g.append("text")\n",
" .attr("x", -MARGIN.LEFT)\n",
" .attr("y", 10)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "start"))\n",
"\n",
" svgEl.append("line")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .data(lineData)\n",
" .attr("x1", (d) => xScale(d.axisX))\n",
" .attr("y1", MARGIN.TOP + MARGIN.TOP)\n",
" .attr("x2", (d) => xScale(d.axisX))\n",
" .attr("y2", CHART_HEIGHT + MARGIN.TOP)\n",
" .style("stroke-width", 2)\n",
" .style("stroke", "#44C0E7")\n",
" .style("stroke-dasharray", (3,3))\n",
" .style("fill", "none");\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .data(lineData)\n",
" .attr("y", (d) => xScale(d.axisX) + MARGIN.LEFT)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("single value")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform",\n",
" "translate(" + (CHART_WIDTH/2) + " ," +\n",
" (CHART_HEIGHT + MARGIN.TOP + 75) + ")")\n",
" .style("text-anchor", "middle")\n",
" .text("Values")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .attr("y", 0)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("Counts")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" const gChart = svgEl.append("g");\n",
" gChart\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(histogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(histogramData[1]?.axisX)-xScale(histogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[0])\n",
" .style("opacity","0.6")\n",
"\n",
" const gChart1 = svgEl.append("g");\n",
" gChart1\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(overlappedHistogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(overlappedHistogramData[1]?.axisX)-xScale(overlappedHistogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[1])\n",
" .style("opacity","0.6")\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" const profileFromCSVfile = {"alcohol": {"histogram": {"start": 8.4, "end": 11.0000011, "width": 0, "counts": [2, 1, 0, 2, 2, 0, 31, 1, 22, 74, 59, 103, 139, 63, 53, 0, 79, 50, 69, 47, 45, 33, 0, 42, 69, 28, 26, 43, 50, 58], "max": 11.0, "min": 8.4, "bins": [8.4, 8.486666703333334, 8.573333406666666, 8.66000011, 8.746666813333334, 8.833333516666666, 8.92000022, 9.006666923333334, 9.093333626666666, 9.18000033, 9.266667033333334, 9.353333736666666, 9.44000044, 9.526667143333334, 9.613333846666666, 9.70000055, 9.786667253333334, 9.873333956666666, 9.96000066, 10.046667363333334, 10.133334066666666, 10.22000077, 10.306667473333334, 10.393334176666666, 10.48000088, 10.566667583333334, 10.653334286666666, 10.74000099, 10.826667693333334, 10.913334396666666, 11.0000011], "n": 1191}}}\n",
"\n",
" Handlebars.registerHelper("getDoubleHistogramChart",(column,key) => {\n",
" const columnKey = key.data.key\n",
" try {\n",
" if (profileFromCSVfile) {\n",
" return generateDoubleHistogramChart (\n",
" column,\n",
" profileFromCSVfile[columnKey]\n",
" )\n",
" }\n",
" } catch (err) {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
" });\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" $(".svg-container").css("height", $(window).height() - 32)\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"alcohol": {"histogram": {"start": 11.066666666666698, "end": 14.900001490000001, "width": 0, "counts": [28, 68, 32, 30, 38, 29, 21, 21, 25, 12, 13, 27, 9, 17, 9, 8, 1, 3, 3, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1], "max": 14.9, "min": 11.066666666666698, "bins": [11.066666666666698, 11.194444494111142, 11.322222321555586, 11.45000014900003, 11.577777976444471, 11.705555803888915, 11.833333631333359, 11.961111458777802, 12.088889286222246, 12.21666711366669, 12.344444941111133, 12.472222768555575, 12.600000596000019, 12.727778423444462, 12.855556250888906, 12.98333407833335, 13.111111905777793, 13.238889733222237, 13.36666756066668, 13.494445388111123, 13.622223215555566, 13.75000104300001, 13.877778870444454, 14.005556697888897, 14.13333452533334, 14.261112352777783, 14.388890180222226, 14.51666800766667, 14.644445835111114, 14.772223662555557, 14.900001490000001], "n": 408}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=300px\n",
" frameBorder=0></iframe><br><div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" :root {\n",
" /** Branded colors */\n",
" --brandSecondary900: #4f595b;\n",
" --secondaryLight1000: #313b3d;\n",
" /** Purpose colors */\n",
" --tealBackground: #eaf2f3;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" overflow-y: hidden;\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .desktop-content {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 100%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .circle-color {\n",
" display: inline-block;\n",
" padding: 5px;\n",
" border-radius: 50px;\n",
" }\n",
"\n",
" .colors-for-distingushing-charts {\n",
" padding-right: 10px;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .align-items-flex-end {\n",
" align-items: flex-end;\n",
" }\n",
"\n",
" .chart-box-title {\n",
" width: 88%;\n",
" justify-content: space-between;\n",
" margin: 10px;\n",
" margin-top: 15px;\n",
" bottom: 0;\n",
" }\n",
"\n",
" .chart-box-title p{\n",
" margin-bottom: 0;\n",
" font-family: Asap;\n",
" font-weight: bold;\n",
" font-size: 18px;\n",
" line-height: 16px;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .bar {\n",
" font: 10px sans-serif;\n",
" }\n",
"\n",
" .bar path,\n",
" .bar line {\n",
" fill: none;\n",
" stroke: #000;\n",
" shape-rendering: crispEdges;\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
"{{#each this}} <div class="chart-box" id="chart-box">\n",
" <div class="chart-box-title display-flex">\n",
" <p>{{@key}}</p>\n",
" <div class="display-flex">\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #44C0E7;"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Target</text>\n",
" </div>\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #F5843C"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Reference</text>\n",
" </div>\n",
" </div></div>\n",
" <div class="svg-container">{{{getDoubleHistogramChart this}}}</div>\n",
" </div>\n",
"{{/each}} </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, targetData, referenceData, bottomMargin=30, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", `0 0 ${$(window).width()+100} ${$(window).height()-30}`)\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(targetData, (d) => Math.abs(d.axisY));\n",
" this.minYValue = d3.min(targetData, (d) => Math.abs(d.axisY));\n",
" const mergedReferenceData = referenceData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
" const mergedTargetedData = targetData.map(({axisX, axisY}) => {\n",
" return {axisX, axisY}\n",
" })\n",
"\n",
" this.charts2 = mergedReferenceData.concat(mergedTargetedData)\n",
" this.charts2 = this.charts2.sort(function(a, b) { return a - b; });\n",
"\n",
" this.targetBinWidth = targetData[1]?.axisX - targetData[0]?.axisX\n",
" this.referenceBinWidth = referenceData[1]?.axisX - referenceData[0]?.axisX\n",
" this.maxTargetXValue = d3.max(targetData, (d) => d.axisX);\n",
"\n",
" this.maxReferenceXValue = d3.max(referenceData, (d) => d.axisX);\n",
"\n",
" this.xScale = d3\n",
" .scaleLinear()\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisX); }),(this.maxTargetXValue+this.targetBinWidth >= this.maxReferenceXValue+this.referenceBinWidth) ? this.maxTargetXValue+this.targetBinWidth:this.maxReferenceXValue+this.referenceBinWidth])\n",
" .range([0, this.CHART_WIDTH ]);\n",
"\n",
" this.svgEl.append("g")\n",
" .attr("transform", "translate("+ this.MARGIN.LEFT +"," + this.SVG_HEIGHT + ")")\n",
" .call(d3.axisBottom(this.xScale));\n",
" this.yScale = d3.scaleLinear()\n",
" .range([this.CHART_HEIGHT , 0])\n",
" .domain([d3.min(this.charts2, function(d) { return parseFloat(d.axisY); }), d3.max(this.charts2, function(d) { return parseFloat(d.axisY); })*1.2])\n",
" .nice();\n",
" }\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column?.histogram) {\n",
" for (let i = 0; i<column.histogram.bins.length; i++) {\n",
" data.push({\n",
" axisY: column.histogram.counts[i] || 0,\n",
" axisX: column.histogram.bins[i] || 0,\n",
" });\n",
" }\n",
" } else {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function verticalLine(column) {\n",
" const line_data = [];\n",
" if (column?.vertical_line) {\n",
" line_data.push({\n",
" axisX: column?.vertical_line || 0,\n",
" });\n",
"\n",
" }\n",
"\n",
" return line_data\n",
" }\n",
"\n",
" function generateDoubleHistogramChart(targetData, referenceData) {\n",
" let histogramData = [],\n",
" overlappedHistogramData = [];\n",
" let yFormat;\n",
"\n",
" histogramData = chartData(targetData)\n",
" overlappedHistogramData = chartData(referenceData)\n",
" lineData = verticalLine(targetData)\n",
"\n",
" const sizes = new GenerateChartParams($(window).height()-80, $(window).width(), histogramData, overlappedHistogramData)\n",
" const {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" maxYValue,\n",
" minYValue,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
"\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
" const yAxis = d3.axisLeft(yScale).ticks(SVG_HEIGHT / 40);\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .call(yAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line")\n",
" .attr("x2", CHART_WIDTH)\n",
" .attr("stroke-opacity", 0.1))\n",
" .call(g => g.append("text")\n",
" .attr("x", -MARGIN.LEFT)\n",
" .attr("y", 10)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "start"))\n",
"\n",
" svgEl.append("line")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.BOTTOM})`)\n",
" .data(lineData)\n",
" .attr("x1", (d) => xScale(d.axisX))\n",
" .attr("y1", MARGIN.TOP + MARGIN.TOP)\n",
" .attr("x2", (d) => xScale(d.axisX))\n",
" .attr("y2", CHART_HEIGHT + MARGIN.TOP)\n",
" .style("stroke-width", 2)\n",
" .style("stroke", "#44C0E7")\n",
" .style("stroke-dasharray", (3,3))\n",
" .style("fill", "none");\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .data(lineData)\n",
" .attr("y", (d) => xScale(d.axisX) + MARGIN.LEFT)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("single value")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform",\n",
" "translate(" + (CHART_WIDTH/2) + " ," +\n",
" (CHART_HEIGHT + MARGIN.TOP + 75) + ")")\n",
" .style("text-anchor", "middle")\n",
" .text("Values")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .attr("y", 0)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("Counts")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" const gChart = svgEl.append("g");\n",
" gChart\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(histogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(histogramData[1]?.axisX)-xScale(histogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[0])\n",
" .style("opacity","0.6")\n",
"\n",
" const gChart1 = svgEl.append("g");\n",
" gChart1\n",
" .attr("transform", "translate("+ MARGIN.LEFT +",0)")\n",
" .selectAll(".bar")\n",
" .data(overlappedHistogramData)\n",
" .enter()\n",
" .append("rect")\n",
" .style("stroke", "#021826")\n",
" .classed("bar", true)\n",
" .attr("width", function(d) { return xScale(overlappedHistogramData[1]?.axisX)-xScale(overlappedHistogramData[0]?.axisX); })\n",
" .attr("height", (d) => CHART_HEIGHT - yScale(d.axisY))\n",
" .attr("x", 1)\n",
" .attr("transform", function(d) { return "translate(" + xScale(d.axisX) + "," + 0 + ")"; })\n",
" .attr("y", (d) => yScale(d.axisY) + MARGIN.TOP + MARGIN.BOTTOM)\n",
" .attr("fill", rectColors[1])\n",
" .style("opacity","0.6")\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" const profileFromCSVfile = {"chlorides": {"histogram": {"start": 0.038, "end": 0.6110000610999999, "width": 0, "counts": [36, 379, 545, 131, 31, 10, 13, 6, 4, 6, 5, 3, 0, 0, 0, 4, 2, 2, 2, 8, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2], "max": 0.611, "min": 0.038, "bins": [0.038, 0.05710000203666667, 0.07620000407333333, 0.09530000611, 0.11440000814666665, 0.13350001018333332, 0.15260001222, 0.17170001425666664, 0.19080001629333332, 0.20990001833, 0.22900002036666664, 0.24810002240333331, 0.26720002443999996, 0.28630002647666664, 0.30540002851333325, 0.32450003054999993, 0.3436000325866666, 0.3627000346233333, 0.38180003665999995, 0.40090003869666657, 0.42000004073333325, 0.4391000427699999, 0.4582000448066666, 0.47730004684333327, 0.49640004887999994, 0.5155000509166666, 0.5346000529533332, 0.55370005499, 0.5728000570266666, 0.5919000590633333, 0.6110000610999999], "n": 1191}}}\n",
"\n",
" Handlebars.registerHelper("getDoubleHistogramChart",(column,key) => {\n",
" const columnKey = key.data.key\n",
" try {\n",
" if (profileFromCSVfile) {\n",
" return generateDoubleHistogramChart (\n",
" column,\n",
" profileFromCSVfile[columnKey]\n",
" )\n",
" }\n",
" } catch (err) {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
" });\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" $(".svg-container").css("height", $(window).height() - 32)\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"chlorides": {"histogram": {"start": 0.012, "end": 0.230000023, "width": 0, "counts": [2, 0, 0, 9, 21, 40, 45, 76, 64, 43, 33, 21, 20, 8, 9, 9, 3, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1], "max": 0.23, "min": 0.012, "bins": [0.012, 0.019266667433333332, 0.026533334866666668, 0.0338000023, 0.04106666973333333, 0.04833333716666667, 0.0556000046, 0.06286667203333333, 0.07013333946666667, 0.0774000069, 0.08466667433333333, 0.09193334176666666, 0.0992000092, 0.10646667663333333, 0.11373334406666666, 0.12100001149999999, 0.12826667893333332, 0.13553334636666667, 0.14280001380000001, 0.15006668123333333, 0.15733334866666668, 0.1646000161, 0.17186668353333334, 0.17913335096666666, 0.1864000184, 0.19366668583333332, 0.20093335326666667, 0.2082000207, 0.21546668813333333, 0.22273335556666665, 0.230000023], "n": 408}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=300px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.double_histogram(feature_name=[\"alcohol\",\"chlorides\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to the histograms, we can also plot distribution charts for categorical variables, like the `quality` feature."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" :root {\n",
" /** Branded colors */\n",
" --brandSecondary900: #4f595b;\n",
" --secondaryLight1000: #313b3d;\n",
" /** Purpose colors */\n",
" --tealBackground: #eaf2f3;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" overflow-y: hidden;\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .desktop-content {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 100%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .circle-color {\n",
" display: inline-block;\n",
" padding: 5px;\n",
" border-radius: 50px;\n",
" }\n",
"\n",
" .colors-for-distingushing-charts {\n",
" padding-right: 10px;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .flex-direction-column {\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .align-items-flex-end {\n",
" align-items: flex-end;\n",
" }\n",
"\n",
" .chart-box-title {\n",
" width: 98%;\n",
" justify-content: space-between;\n",
" margin: 10px;\n",
" margin-top: 15px;\n",
" bottom: 0;\n",
" }\n",
"\n",
" .chart-box-title p{\n",
" margin-bottom: 0;\n",
" font-family: Asap;\n",
" font-weight: bold;\n",
" font-size: 18px;\n",
" line-height: 16px;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
"{{#each this}} <div class="chart-box" id="chart-box">\n",
" <div class="chart-box-title display-flex">\n",
" <p>{{@key}}</p>\n",
" <div class="display-flex">\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #44C0E7;"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Target</text>\n",
" </div>\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #F5843C"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Reference</text>\n",
" </div>\n",
" </div></div>\n",
" <div class="svg-container">\n",
" {{{getDoubleHistogramChart this}}}\n",
" </div>\n",
" </div>\n",
"{{/each}} </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
"\n",
" const findAndDeleteUndefined = (axisData) => {\n",
" const undefinedAxisIndex = axisData.findIndex((axis) => axis === undefined)\n",
" if (undefinedAxisIndex == -1) {\n",
" return axisData;\n",
" }\n",
"\n",
" const result = [...axisData.slice(0, undefinedAxisIndex), ...axisData.slice(undefinedAxisIndex + 1)]\n",
" return result\n",
" }\n",
"\n",
" const filterAndSortChartData = (overlappedHistogramData, histogramData) => {\n",
" const filteredData = overlappedHistogramData\n",
" .map((d) =>\n",
" histogramData\n",
" .filter((ref) => d.axisX === ref.axisX )[0])\n",
" .sort((a, b) => {\n",
" if (+a.axisY < +b.axisY) {\n",
" return 1;\n",
" }\n",
" if (+a.axisY > +b.axisY) {\n",
" return -1;\n",
" }\n",
"\n",
" return 0;\n",
" })\n",
" .slice(0, 20)\n",
"\n",
" return findAndDeleteUndefined(filteredData)\n",
" }\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, data, referenceData, bottomMargin=30, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", `0 0 ${$(window).width()} ${$(window).height()-30}`)\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max([...data, ...referenceData], (d) => Math.abs(d.axisY));\n",
" this.xScale = d3\n",
" .scaleBand()\n",
" .domain(filterAndSortChartData(data, referenceData).map((sortedCounts) => sortedCounts?.axisX))\n",
" .range([this.MARGIN.LEFT, this.MARGIN.LEFT + this.CHART_WIDTH]);\n",
" this.yScale = d3\n",
" .scaleLinear()\n",
" .domain([0, this.maxYValue * 1.2])\n",
" .range([this.CHART_HEIGHT, 0]);\n",
" }\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column.frequentItems) {\n",
" Object.entries(column.frequentItems).forEach(([key, {value, estimate}], index) => {\n",
" data.push({\n",
" axisY: estimate,\n",
" axisX: value,\n",
" });\n",
" });\n",
" } else {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function generateBarChart(histogramData, overlappedHistogramData) {\n",
" let yFormat,\n",
" xFormat;\n",
" const data = filterAndSortChartData(chartData(histogramData), chartData(overlappedHistogramData)).map((axis, index) => {\n",
" if (axis) {\n",
" const findIndex = chartData(histogramData).findIndex((value) => value.axisX === axis.axisX)\n",
" return {\n",
" group: axis.axisX,\n",
" profile: axis.axisY,\n",
" reference_profile: chartData(histogramData)[findIndex].axisY\n",
" }\n",
" }\n",
" return 0;\n",
" })\n",
"\n",
" const sizes = new GenerateChartParams($(window).height()-60, $(window).width(), chartData(histogramData), chartData(overlappedHistogramData))\n",
" let {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" svgEl,\n",
" xScale,\n",
" yScale\n",
" } = sizes\n",
"\n",
" const rectColors = ["#44C0E7", "#F5843C"]\n",
" const subgroups = ['reference_profile', 'profile']\n",
"\n",
" xScale.padding([0.3])\n",
"\n",
" const xAxis = d3.axisBottom(xScale).ticks(SVG_WIDTH / 80, xFormat).tickSizeOuter(0);\n",
" const yAxis = d3.axisLeft(yScale).ticks(SVG_HEIGHT / 40, yFormat);\n",
" yFormat = yScale.tickFormat(100, yFormat);\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, 0)`)\n",
" .attr("id", "g1")\n",
" .call(yAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line")\n",
" .attr("x2", CHART_WIDTH)\n",
" .attr("stroke-opacity", 0.1))\n",
" .call(g => g.append("text")\n",
" .attr("x", -MARGIN.LEFT)\n",
" .attr("y", 10)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "start"));\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform",\n",
" "translate(" + (CHART_WIDTH/2) + " ," +\n",
" (CHART_HEIGHT + MARGIN.TOP + 40) + ")")\n",
" .style("text-anchor", "middle")\n",
" .text("Values")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .attr("y", 0)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("Counts")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(0,${SVG_HEIGHT - MARGIN.BOTTOM})`)\n",
" .attr("id", "g2")\n",
" .call(xAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line").remove())\n",
" .call(g => g.append("text")\n",
" .attr("x", SVG_WIDTH - MARGIN.RIGHT)\n",
" .attr("y", 27)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "end"));\n",
"\n",
" const xSubgroup = d3.scaleBand()\n",
" .domain(subgroups)\n",
" .range([0, xScale.bandwidth()])\n",
"\n",
" const color = d3.scaleOrdinal()\n",
" .domain(subgroups)\n",
" .range(rectColors)\n",
"\n",
" svgEl.append("g")\n",
" .attr("id", "g3")\n",
" .selectAll("g")\n",
" .data(data)\n",
" .enter()\n",
" .append("g")\n",
" .attr("transform", function(d) { return "translate(" + xScale(d?.group) + ",0)"; })\n",
" .attr("id", "g4")\n",
" .selectAll("rect")\n",
" .data(function(d) { return subgroups.map(function(key) { return {key: key, value: d && d[key]}; }); })\n",
" .enter().append("rect")\n",
" .attr("x", function(d) { return xSubgroup(d.key); })\n",
" .attr("y", function(d) { return yScale(d.value); })\n",
" .attr("width", xSubgroup.bandwidth())\n",
" .attr("height", function(d) { return (CHART_HEIGHT - yScale(d.value)); })\n",
" .attr("fill", function(d) { return color(d.key); })\n",
" .style("opacity", "0.8");\n",
"\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
"\n",
" const profileFromCSVfile = {"quality": {"frequentItems": [{"value": "bad", "estimate": 1115}, {"value": "good", "estimate": 76}]}}\n",
"\n",
" Handlebars.registerHelper("getDoubleHistogramChart",(column,key) => {\n",
" const columnKey = key.data.key\n",
" try {\n",
" if (profileFromCSVfile) {\n",
" return generateBarChart(\n",
" column,\n",
" profileFromCSVfile[columnKey]\n",
" )\n",
" }\n",
" } catch (err) {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
" });\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" $(".svg-container").css("height", $(window).height() - 32)\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"quality": {"frequentItems": [{"value": "bad", "estimate": 267}, {"value": "good", "estimate": 141}]}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=277px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.distribution_chart(feature_name=\"quality\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`distribution_charts` also accepts multiple feature names, but in this case we have a single categorical feature.\n",
"\n",
"We can also look at the difference between distributions:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" :root {\n",
" /** Branded colors */\n",
" --brandSecondary900: #4f595b;\n",
" --secondaryLight1000: #313b3d;\n",
" /** Purpose colors */\n",
" --tealBackground: #eaf2f3;\n",
" }\n",
"\n",
" /* RESET STYLE */\n",
" *,\n",
" *::after,\n",
" *::before {\n",
" margin: 0;\n",
" padding: 0;\n",
" box-sizing: border-box;\n",
" overflow-y: hidden;\n",
" }\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" .desktop-content {\n",
" display: flex;\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .svg-container {\n",
" display: inline-block;\n",
" position: relative;\n",
" width: 100%;\n",
" vertical-align: top;\n",
" overflow: hidden;\n",
" }\n",
"\n",
" .svg-content-responsive {\n",
" display: inline-block;\n",
" position: absolute;\n",
" left: 0;\n",
" }\n",
"\n",
" .circle-color {\n",
" display: inline-block;\n",
" padding: 5px;\n",
" border-radius: 50px;\n",
" }\n",
"\n",
" .colors-for-distingushing-charts {\n",
" padding-right: 10px;\n",
" }\n",
"\n",
" .display-flex{\n",
" display: flex;\n",
" }\n",
"\n",
" .flex-direction-column {\n",
" flex-direction: column;\n",
" }\n",
"\n",
" .align-items-flex-end {\n",
" align-items: flex-end;\n",
" }\n",
"\n",
" .chart-box-title {\n",
" width: 98%;\n",
" justify-content: space-between;\n",
" margin: 10px;\n",
" margin-top: 15px;\n",
" bottom: 0;\n",
" }\n",
"\n",
" .chart-box-title p{\n",
" margin-bottom: 0;\n",
" font-family: Asap;\n",
" font-weight: bold;\n",
" font-size: 18px;\n",
" line-height: 16px;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .bar.positive {\n",
" fill: #44C0E7;\n",
" }\n",
"\n",
" .bar.negative {\n",
" fill: #F5843C;\n",
" }\n",
"\n",
" .error-message {\n",
" display: flex;\n",
" justify-content: center;\n",
" align-items: center;\n",
" color: rgb(255, 114, 71);\n",
" font-size: 30px;\n",
" font-weight: 900;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
"{{#each this}} <div class="chart-box" id="chart-box">\n",
" <div class="chart-box-title display-flex">\n",
" <p>{{@key}}</p>\n",
" <div class="display-flex">\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #44C0E7;"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Target</text>\n",
" </div>\n",
" <div class="colors-for-distingushing-charts">\n",
" <div class="circle-color" style="background: #F5843C"></div>\n",
" <text alignment-baseline="middle" style="font-size: 15px;">Reference</text>\n",
" </div>\n",
" </div></div>\n",
" <div class="svg-container">\n",
" {{{getDoubleHistogramChart this}}}\n",
" </div>\n",
" </div>\n",
"{{/each}} </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
"\n",
" const findAndDeleteUndefined = (axisData) => {\n",
" const undefinedAxisIndex = axisData.findIndex((axis) => axis === undefined)\n",
" if (undefinedAxisIndex > 0) {\n",
" return [...axisData.slice(0, undefinedAxisIndex), ...axisData.slice(undefinedAxisIndex + 1)]\n",
" }\n",
" return axisData\n",
" }\n",
"\n",
" const filterAndSortChartData = (overlappedHistogramData, histogramData) => {\n",
" const filteredData = overlappedHistogramData\n",
" .map((d) =>\n",
" histogramData\n",
" .filter((ref) => d.axisX === ref.axisX )[0])\n",
" .sort((a, b) => {\n",
" if (+a.axisY < +b.axisY) {\n",
" return 1;\n",
" }\n",
" if (+a.axisY > +b.axisY) {\n",
" return -1;\n",
" }\n",
"\n",
" return 0;\n",
" })\n",
" .slice(0, 20)\n",
"\n",
" return findAndDeleteUndefined(filteredData)\n",
" }\n",
"\n",
" class GenerateChartParams {\n",
" constructor(height, width, data, referenceData, bottomMargin=30, topMargin=5) {\n",
" this.MARGIN = {\n",
" TOP: topMargin,\n",
" RIGHT: 5,\n",
" BOTTOM: bottomMargin,\n",
" LEFT: 55,\n",
" };\n",
" this.SVG_WIDTH = width;\n",
" this.SVG_HEIGHT = height;\n",
" this.CHART_WIDTH = this.SVG_WIDTH - this.MARGIN.LEFT - this.MARGIN.RIGHT;\n",
" this.CHART_HEIGHT = this.SVG_HEIGHT - this.MARGIN.TOP - this.MARGIN.BOTTOM;\n",
" this.svgEl = d3.create("svg")\n",
" .attr("preserveAspectRatio", "xMinYMin meet")\n",
" .attr("viewBox", `0 -10 ${$(window).width()} ${$(window).height()-30}`)\n",
" .classed("svg-content-responsive", true)\n",
" this.maxYValue = d3.max(data, (d) => Math.abs(d.axisY));\n",
" this.xScale = d3\n",
" .scaleBand()\n",
" .domain(filterAndSortChartData(data, referenceData).map((sortedCounts) => sortedCounts && sortedCounts.axisX))\n",
" .rangeRound([this.MARGIN.LEFT, this.SVG_WIDTH])\n",
" .padding([0.1]);\n",
" this.yScale = d3\n",
" .scaleLinear()\n",
" .domain([0, this.maxYValue])\n",
" .range([this.CHART_HEIGHT, 0]);\n",
" }\n",
" }\n",
"\n",
" function chartData(column) {\n",
" const data = [];\n",
" if (column.frequentItems) {\n",
" Object.entries(column.frequentItems).forEach(([key, {value, estimate}], index) => {\n",
" data.push({\n",
" axisY: estimate,\n",
" axisX: value,\n",
" });\n",
" });\n",
" } else {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
"\n",
" return data\n",
" }\n",
"\n",
" function generatePositiveNegativeChart(histogramData, overlappedHistogramData) {\n",
" const data = filterAndSortChartData(chartData(histogramData), chartData(overlappedHistogramData)).map((axis, index) => {\n",
" if (axis) {\n",
" const findIndex = chartData(histogramData).findIndex((value) => value.axisX === axis.axisX)\n",
" const difference = axis.axisY - chartData(histogramData)[findIndex].axisY\n",
" return [difference]\n",
" }\n",
" return 0;\n",
" }).flat()\n",
"\n",
" let yFormat,\n",
" xFormat;\n",
"\n",
" const sizes = new GenerateChartParams($(window).height()-60, $(window).width(), chartData(histogramData), chartData(overlappedHistogramData))\n",
" let {\n",
" MARGIN,\n",
" SVG_WIDTH,\n",
" SVG_HEIGHT,\n",
" CHART_WIDTH,\n",
" CHART_HEIGHT,\n",
" xScale,\n",
" svgEl\n",
" } = sizes\n",
"\n",
" const rectColors = ["bar positive", "bar negative"]\n",
" const maxY = Math.abs(d3.max(data));\n",
" const minY = Math.abs(d3.min(data));\n",
" let positiveY = Math.ceil(maxY) % 1 ? maxY + 2*(maxY/(maxY*10)) : maxY + 2*(maxY/(maxY/10)),\n",
" negativeY = Math.ceil(minY) % 1 ? minY + 2*(minY/(minY*10)) : minY + 2*(minY/(minY/10));\n",
"\n",
" const yScale = d3.scaleLinear()\n",
" .range([CHART_HEIGHT,0])\n",
" .domain([-negativeY*1.2, positiveY*1.2 || 0])\n",
" .nice()\n",
"\n",
" const xAxis = d3.axisBottom(xScale).ticks(SVG_WIDTH / 80, xFormat).tickSizeOuter(0);\n",
" const yAxis = d3.axisLeft(yScale).ticks(CHART_HEIGHT / 30, yFormat);\n",
" yFormat = yScale.tickFormat(100, yFormat);\n",
"\n",
" svgEl.append("g")\n",
" .attr("transform", `translate(${MARGIN.LEFT}, 0)`)\n",
" .call(yAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick line")\n",
" .attr("x2", CHART_WIDTH )\n",
" .attr("stroke-opacity", 0.1))\n",
" .call(g => g.append("text")\n",
" .attr("x", - MARGIN.LEFT)\n",
" .attr("y", 10)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "start"));\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform",\n",
" "translate(" + (CHART_WIDTH/2) + " ," +\n",
" (CHART_HEIGHT + MARGIN.TOP + 30) + ")")\n",
" .style("text-anchor", "middle")\n",
" .text("Values")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl.append("text")\n",
" .attr("transform", "rotate(-90)")\n",
" .attr("y", 0)\n",
" .attr("x", 0 - (SVG_HEIGHT / 2))\n",
" .attr("dy", "1em")\n",
" .style("text-anchor", "middle")\n",
" .text("Counts")\n",
" .style("font-size", "15")\n",
" .style("opacity", "0.6")\n",
"\n",
" svgEl\n",
" .append("g")\n",
" .attr("transform", `translate(0,${SVG_HEIGHT - MARGIN.BOTTOM - 7})`)\n",
" .call(xAxis)\n",
" .call(g => g.select(".domain").remove())\n",
" .call(g => g.selectAll(".tick>line").remove())\n",
" .call(g => g.append("text")\n",
" .attr("x", SVG_WIDTH - MARGIN.RIGHT)\n",
" .attr("y", 27)\n",
" .attr("fill", "currentColor")\n",
" .attr("text-anchor", "end"));\n",
"\n",
" svgEl.selectAll(".bar")\n",
" .data(data)\n",
" .enter()\n",
" .append("rect")\n",
" .attr("class", function(d) { return d < 0 ? rectColors[0] : rectColors[1]; })\n",
" .attr("y", function(d) { return yScale(Math.max(0, d)); })\n",
" .attr("x", function(d, i) { return xScale(filterAndSortChartData(chartData(histogramData), chartData(overlappedHistogramData))[i]?.axisX) })\n",
" .attr("height", function(d) { return Math.abs(yScale(d) - yScale(0)); })\n",
" .attr("width", xScale.bandwidth())\n",
" .style("opacity", "0.8")\n",
"\n",
"\n",
" return svgEl._groups[0][0].outerHTML;\n",
" }\n",
"\n",
" const profileFromCSVfile = {"quality": {"frequentItems": [{"value": "bad", "estimate": 1115}, {"value": "good", "estimate": 76}]}}\n",
"\n",
" Handlebars.registerHelper("getDoubleHistogramChart",(column,key) => {\n",
" const columnKey = key.data.key\n",
" try {\n",
" if (profileFromCSVfile) {\n",
" return generatePositiveNegativeChart(\n",
" column,\n",
" profileFromCSVfile[columnKey]\n",
" )\n",
" }\n",
" } catch (err) {\n",
" $(document).ready(() =>\n",
" $(".desktop-content").html(`\n",
" <p style="height: ${$(window).height()}px" class="error-message">\n",
" Something went wrong. Please try again.\n",
" </p>\n",
" `)\n",
" )\n",
" }\n",
" });\n",
" }\n",
"\n",
" function initWebsiteScripts() {\n",
" $(".svg-container").css("height", $(window).height() - 32)\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"quality": {"frequentItems": [{"value": "bad", "estimate": 267}, {"value": "good", "estimate": 141}]}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" initWebsiteScripts();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=277px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.difference_distribution_chart(feature_name=\"quality\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can see that there is 800 or so more \"bads\" in the Reference profile, and 50 or so more \"goods\" on the target profile."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Feature Statistics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With `feature_statistics`, we have access to very useful statistics by passing the feature and profile name.\n",
"\n",
"As with the previous reports (double_histogram and distribution_chart) you can pass a string or a list of strings through `feature_name`. Let's take a look at the summary statistics for some of our features:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" @media screen and (min-width: 1000px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .header-title {\n",
" font-size: 26px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistics-header-title {\n",
" font-size: 20px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistic-number-title {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 14px;\n",
" line-height: 20px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .statistic-number {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 20px;\n",
" line-height: 140%;\n",
" display: flex;\n",
" align-items: center;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .full-summary-statistics-wrap {\n",
" padding: 20px;\n",
" }\n",
"\n",
" .statistics {\n",
" width: 100%;\n",
" }\n",
"\n",
" .statistics-list {\n",
" width: 49% ;\n",
" }\n",
"\n",
" .notif-circle-container{\n",
" position: absolute;\n",
" top: -8px;\n",
" right: -8px;\n",
" padding: 5.3px;\n",
" border-radius: 50%;\n",
" background-color: var(--brandSecondary100);\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .notif-circle {\n",
" position: absolute;\n",
" top: 2px;\n",
" right: 2px;\n",
" height: 16px;\n",
" width: 16px;\n",
" border-radius: 50%;\n",
" font-size: 10px;\n",
" color: #fff;\n",
" background-color: #ECB100;\n",
" }\n",
"\n",
" .border-solid-gray {\n",
" border: 1px solid #CED4DA;\n",
" border-radius: 4px;\n",
" padding: 10px;\n",
" }\n",
"\n",
" .display-flex {\n",
" display: flex;\n",
" }\n",
"\n",
" .justify-content-space-between {\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .align-items-center {\n",
" align-items: center;\n",
" }\n",
"\n",
" .padding-right-30 {\n",
" padding-right: 30px;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
"\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
" <div class="full-summary-statistics-wrap">\n",
" <div class="full-summary-statistics">\n",
" <div class="mb-4">\n",
" <strong class="header-title">{{featureName this}}: Summary Statistics</strong>\n",
" </div>\n",
" <div class="statistics-wrap border-solid-gray display-flex justify-content-center mb-5">\n",
" <div class="statistics display-flex justify-content-space-between">\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Distinct (%)</div>\n",
" <div class="statistic-number">{{distinct this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Missing</div>\n",
" <div class="statistic-number">{{missing this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Mean</div>\n",
" <div class="statistic-number">{{mean this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Minimum</div>\n",
" <div class="statistic-number">{{minimum this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Maximum</div>\n",
" <div class="statistic-number">{{maximum this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between">\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Quantile statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>5-th percentile</div>\n",
" <div>{{fifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q1</div>\n",
" <div>{{q1 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>median</div>\n",
" <div>{{median this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q3</div>\n",
" <div>{{q3 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>95-th percentile</div>\n",
" <div>{{ninetyFifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Range</div>\n",
" <div>{{range this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Interquartile range (IQR)</div>\n",
" <div>{{iqr this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Descriptive statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Standard deviation</div>\n",
" <div>{{stddev this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Coefficient of variation (CV)</div>\n",
" <div>{{coefficientOfVariation this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Sum</div>\n",
" <div>{{getSum this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Variance</div>\n",
" <div>{{variance this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
" // function formatLabelDate(timestamp) {\n",
" // const date = new Date(timestamp);\n",
" // const format = d3.timeFormat("%Y-%m-%d %I:%M:%S %p %Z");\n",
" // return format(date);\n",
" // }\n",
"\n",
" function fixNumberTo(number, decimals = 3) {\n",
" return parseFloat(number).toFixed(decimals);\n",
" }\n",
"\n",
" Handlebars.registerHelper("variance", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.variance === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.variance)){return "-"}\n",
" if (feature.descriptive_statistics.variance==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.variance,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("coefficientOfVariation", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.coefficient_of_variation === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.coefficient_of_variation)){return "-"}\n",
" if (feature.descriptive_statistics.coefficient_of_variation==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.coefficient_of_variation,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("median", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.median === 'number') {\n",
" if (isNaN(feature.quantile_statistics.median)){return "-"}\n",
" if (feature.quantile_statistics.median==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.median);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("fifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("ninetyFifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.ninety_fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.ninety_fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.ninety_fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.ninety_fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("range", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.range && typeof feature.range === 'number') {\n",
" if (isNaN(feature.range)){return "-"}\n",
" if (feature.range==null){return "-"}\n",
" return fixNumberTo(feature.range);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q3", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q3 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q3)){return "-"}\n",
" if (feature.quantile_statistics.q3==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q3);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q1", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q1 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q1)){return "-"}\n",
" if (feature.quantile_statistics.q1==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q1);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("getSum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.sum === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.sum)){return "-"}\n",
" if (feature.descriptive_statistics.sum==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.sum,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("iqr", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.iqr === 'number' ) {\n",
" if (isNaN(feature.quantile_statistics.iqr)){return "-"}\n",
" if (feature.quantile_statistics.iqr==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.iqr);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("distinct", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" const distinct_pct = feature.distinct\n",
"\n",
" return fixNumberTo(distinct_pct,2);\n",
" });\n",
"\n",
" Handlebars.registerHelper("missing", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (feature.missing) {\n",
" return feature.missing;\n",
" }\n",
" return "0";\n",
" });\n",
"\n",
" Handlebars.registerHelper("mean", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.mean === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.mean)){return "-"}\n",
" if (feature.descriptive_statistics.mean==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.mean);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("stddev", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.stddev === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.stddev)){return "-"}\n",
" if (feature.descriptive_statistics.stddev==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.stddev);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("minimum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (typeof feature.min === 'number') {\n",
" if (isNaN(feature.min)){return "-"}\n",
" if (feature.min==null){return "-"}\n",
" return fixNumberTo(feature.min);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("maximum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (typeof feature.max === 'number') {\n",
" if (isNaN(feature.max)){return "-"}\n",
" if (feature.max==null){return "-"}\n",
" return fixNumberTo(feature.max);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" // Handlebars.registerHelper("getProfileTimeStamp", function (column) {\n",
" // return formatLabelDate(+column.properties.dataTimestamp)\n",
" // });\n",
"\n",
" // Handlebars.registerHelper("getProfileName", function (column) {\n",
" // return column.properties.tags.name\n",
" // });\n",
"\n",
" Handlebars.registerHelper("featureName", function (column) {\n",
" const featureName = Object.keys(column)[0]\n",
" return featureName\n",
" });\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"density": {"total_count": 408, "missing": 0, "distinct": 55.88238444546564, "min": 0.99007, "max": 1.0032, "range": 0.013130000000000086, "quantile_statistics": {"fifth_percentile": 0.9922, "iqr": 0.0023400000000000087, "q1": 0.99396, "median": 0.99516, "q3": 0.9963, "ninety_fifth_percentile": 0.9994}, "descriptive_statistics": {"stddev": 0.0021784570136347352, "coefficient_of_variation": 0.002188682344428877, "sum": 406.09385999999995, "variance": 4.745674960254368e-06, "mean": 0.995328088235294}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=650px\n",
" frameBorder=0></iframe><br><div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" @media screen and (min-width: 1000px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .header-title {\n",
" font-size: 26px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistics-header-title {\n",
" font-size: 20px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistic-number-title {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 14px;\n",
" line-height: 20px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .statistic-number {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 20px;\n",
" line-height: 140%;\n",
" display: flex;\n",
" align-items: center;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .full-summary-statistics-wrap {\n",
" padding: 20px;\n",
" }\n",
"\n",
" .statistics {\n",
" width: 100%;\n",
" }\n",
"\n",
" .statistics-list {\n",
" width: 49% ;\n",
" }\n",
"\n",
" .notif-circle-container{\n",
" position: absolute;\n",
" top: -8px;\n",
" right: -8px;\n",
" padding: 5.3px;\n",
" border-radius: 50%;\n",
" background-color: var(--brandSecondary100);\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .notif-circle {\n",
" position: absolute;\n",
" top: 2px;\n",
" right: 2px;\n",
" height: 16px;\n",
" width: 16px;\n",
" border-radius: 50%;\n",
" font-size: 10px;\n",
" color: #fff;\n",
" background-color: #ECB100;\n",
" }\n",
"\n",
" .border-solid-gray {\n",
" border: 1px solid #CED4DA;\n",
" border-radius: 4px;\n",
" padding: 10px;\n",
" }\n",
"\n",
" .display-flex {\n",
" display: flex;\n",
" }\n",
"\n",
" .justify-content-space-between {\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .align-items-center {\n",
" align-items: center;\n",
" }\n",
"\n",
" .padding-right-30 {\n",
" padding-right: 30px;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
"\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
" <div class="full-summary-statistics-wrap">\n",
" <div class="full-summary-statistics">\n",
" <div class="mb-4">\n",
" <strong class="header-title">{{featureName this}}: Summary Statistics</strong>\n",
" </div>\n",
" <div class="statistics-wrap border-solid-gray display-flex justify-content-center mb-5">\n",
" <div class="statistics display-flex justify-content-space-between">\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Distinct (%)</div>\n",
" <div class="statistic-number">{{distinct this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Missing</div>\n",
" <div class="statistic-number">{{missing this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Mean</div>\n",
" <div class="statistic-number">{{mean this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Minimum</div>\n",
" <div class="statistic-number">{{minimum this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Maximum</div>\n",
" <div class="statistic-number">{{maximum this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between">\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Quantile statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>5-th percentile</div>\n",
" <div>{{fifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q1</div>\n",
" <div>{{q1 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>median</div>\n",
" <div>{{median this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q3</div>\n",
" <div>{{q3 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>95-th percentile</div>\n",
" <div>{{ninetyFifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Range</div>\n",
" <div>{{range this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Interquartile range (IQR)</div>\n",
" <div>{{iqr this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Descriptive statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Standard deviation</div>\n",
" <div>{{stddev this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Coefficient of variation (CV)</div>\n",
" <div>{{coefficientOfVariation this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Sum</div>\n",
" <div>{{getSum this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Variance</div>\n",
" <div>{{variance this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
" // function formatLabelDate(timestamp) {\n",
" // const date = new Date(timestamp);\n",
" // const format = d3.timeFormat("%Y-%m-%d %I:%M:%S %p %Z");\n",
" // return format(date);\n",
" // }\n",
"\n",
" function fixNumberTo(number, decimals = 3) {\n",
" return parseFloat(number).toFixed(decimals);\n",
" }\n",
"\n",
" Handlebars.registerHelper("variance", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.variance === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.variance)){return "-"}\n",
" if (feature.descriptive_statistics.variance==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.variance,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("coefficientOfVariation", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.coefficient_of_variation === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.coefficient_of_variation)){return "-"}\n",
" if (feature.descriptive_statistics.coefficient_of_variation==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.coefficient_of_variation,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("median", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.median === 'number') {\n",
" if (isNaN(feature.quantile_statistics.median)){return "-"}\n",
" if (feature.quantile_statistics.median==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.median);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("fifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("ninetyFifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.ninety_fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.ninety_fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.ninety_fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.ninety_fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("range", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.range && typeof feature.range === 'number') {\n",
" if (isNaN(feature.range)){return "-"}\n",
" if (feature.range==null){return "-"}\n",
" return fixNumberTo(feature.range);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q3", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q3 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q3)){return "-"}\n",
" if (feature.quantile_statistics.q3==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q3);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q1", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q1 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q1)){return "-"}\n",
" if (feature.quantile_statistics.q1==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q1);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("getSum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.sum === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.sum)){return "-"}\n",
" if (feature.descriptive_statistics.sum==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.sum,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("iqr", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.iqr === 'number' ) {\n",
" if (isNaN(feature.quantile_statistics.iqr)){return "-"}\n",
" if (feature.quantile_statistics.iqr==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.iqr);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("distinct", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" const distinct_pct = feature.distinct\n",
"\n",
" return fixNumberTo(distinct_pct,2);\n",
" });\n",
"\n",
" Handlebars.registerHelper("missing", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (feature.missing) {\n",
" return feature.missing;\n",
" }\n",
" return "0";\n",
" });\n",
"\n",
" Handlebars.registerHelper("mean", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.mean === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.mean)){return "-"}\n",
" if (feature.descriptive_statistics.mean==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.mean);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("stddev", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.stddev === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.stddev)){return "-"}\n",
" if (feature.descriptive_statistics.stddev==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.stddev);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("minimum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (typeof feature.min === 'number') {\n",
" if (isNaN(feature.min)){return "-"}\n",
" if (feature.min==null){return "-"}\n",
" return fixNumberTo(feature.min);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("maximum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (typeof feature.max === 'number') {\n",
" if (isNaN(feature.max)){return "-"}\n",
" if (feature.max==null){return "-"}\n",
" return fixNumberTo(feature.max);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" // Handlebars.registerHelper("getProfileTimeStamp", function (column) {\n",
" // return formatLabelDate(+column.properties.dataTimestamp)\n",
" // });\n",
"\n",
" // Handlebars.registerHelper("getProfileName", function (column) {\n",
" // return column.properties.tags.name\n",
" // });\n",
"\n",
" Handlebars.registerHelper("featureName", function (column) {\n",
" const featureName = Object.keys(column)[0]\n",
" return featureName\n",
" });\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"alcohol": {"total_count": 408, "missing": 0, "distinct": 7.59803978178436, "min": 11.066666666666698, "max": 14.9, "range": 3.833333333333302, "quantile_statistics": {"fifth_percentile": 11.1, "iqr": 1.0, "q1": 11.4, "median": 11.8, "q3": 12.4, "ninety_fifth_percentile": 13.2}, "descriptive_statistics": {"stddev": 0.6824986635512768, "coefficient_of_variation": 0.05725985668809312, "sum": 4863.083333333334, "variance": 0.4658044257492789, "mean": 11.919321895424838}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=650px\n",
" frameBorder=0></iframe><br><div></div><iframe srcdoc=\"<!DOCTYPE html>\n",
"<html lang="en">\n",
" <head>\n",
" <meta charset="UTF-8" />\n",
" <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n",
" <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n",
" <meta name="description" content="" />\n",
" <meta name="author" content="" />\n",
"\n",
" <title>Profile Visualizer | whylogs</title>\n",
"\n",
" <link rel="icon" href="images/whylabs-favicon.png" type="image/png" sizes="16x16" />\n",
" <link rel="preconnect" href="https://fonts.googleapis.com" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n",
" <link href="https://fonts.googleapis.com/css2?family=Asap:wght@400;500;600;700&display=swap" rel="stylesheet" />\n",
" <link rel="preconnect" href="https://fonts.gstatic.com" />\n",
" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" />\n",
"\n",
" <script\n",
" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"\n",
" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg=="\n",
" crossorigin="anonymous"\n",
" referrerpolicy="no-referrer"\n",
" ></script>\n",
"\n",
" <style type="text/css">\n",
"\n",
" /* Screen on smaller screens */\n",
" .no-responsive {\n",
" display: none;\n",
" position: fixed;\n",
" top: 0;\n",
" left: 0;\n",
" z-index: 1031;\n",
" width: 100vw;\n",
" height: 100vh;\n",
" background-color: var(--tealBackground);\n",
" display: flex;\n",
" align-items: center;\n",
" justify-content: center;\n",
" }\n",
"\n",
" @media screen and (min-width: 1000px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
"\n",
" .no-responsive__content {\n",
" max-width: 600px;\n",
" width: 100%;\n",
" padding: 0 24px;\n",
" }\n",
"\n",
" .no-responsive__title {\n",
" font-size: 96px;\n",
" font-weight: 300;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.167;\n",
" }\n",
"\n",
" .no-responsive__text {\n",
" margin: 0;\n",
" font-size: 16px;\n",
" font-weight: 400;\n",
" color: var(--brandSecondary900);\n",
" line-height: 1.5;\n",
" }\n",
"\n",
" .header-title {\n",
" font-size: 26px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistics-header-title {\n",
" font-size: 20px;\n",
" font-weight: 700;\n",
" color: #444444;\n",
" }\n",
"\n",
" .statistic-number-title {\n",
" font-family: Arial;\n",
" font-weight: normal;\n",
" font-size: 14px;\n",
" line-height: 20px;\n",
" color: #6C757D;\n",
" }\n",
"\n",
" .statistic-number {\n",
" font-family: Arial;\n",
" font-weight: bold;\n",
" font-size: 20px;\n",
" line-height: 140%;\n",
" display: flex;\n",
" align-items: center;\n",
" color: #4F595B;\n",
" }\n",
"\n",
" .full-summary-statistics-wrap {\n",
" padding: 20px;\n",
" }\n",
"\n",
" .statistics {\n",
" width: 100%;\n",
" }\n",
"\n",
" .statistics-list {\n",
" width: 49% ;\n",
" }\n",
"\n",
" .notif-circle-container{\n",
" position: absolute;\n",
" top: -8px;\n",
" right: -8px;\n",
" padding: 5.3px;\n",
" border-radius: 50%;\n",
" background-color: var(--brandSecondary100);\n",
" cursor: pointer;\n",
" }\n",
"\n",
" .notif-circle {\n",
" position: absolute;\n",
" top: 2px;\n",
" right: 2px;\n",
" height: 16px;\n",
" width: 16px;\n",
" border-radius: 50%;\n",
" font-size: 10px;\n",
" color: #fff;\n",
" background-color: #ECB100;\n",
" }\n",
"\n",
" .border-solid-gray {\n",
" border: 1px solid #CED4DA;\n",
" border-radius: 4px;\n",
" padding: 10px;\n",
" }\n",
"\n",
" .display-flex {\n",
" display: flex;\n",
" }\n",
"\n",
" .justify-content-space-between {\n",
" justify-content: space-between;\n",
" }\n",
"\n",
" .justify-content-center {\n",
" justify-content: center;\n",
" }\n",
"\n",
" .align-items-center {\n",
" align-items: center;\n",
" }\n",
"\n",
" .padding-right-30 {\n",
" padding-right: 30px;\n",
" }\n",
"\n",
" @media screen and (min-width: 500px) {\n",
" .desktop-content {\n",
" display: block;\n",
" }\n",
" .no-responsive {\n",
" display: none;\n",
" }\n",
" }\n",
" </style>\n",
" </head>\n",
"\n",
" <body id="generated-html"></body>\n",
"\n",
" <script id="entry-template" type="text/x-handlebars-template">\n",
" \n",
" <div class="desktop-content">\n",
" <div class="full-summary-statistics-wrap">\n",
" <div class="full-summary-statistics">\n",
" <div class="mb-4">\n",
" <strong class="header-title">{{featureName this}}: Summary Statistics</strong>\n",
" </div>\n",
" <div class="statistics-wrap border-solid-gray display-flex justify-content-center mb-5">\n",
" <div class="statistics display-flex justify-content-space-between">\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Distinct (%)</div>\n",
" <div class="statistic-number">{{distinct this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Missing</div>\n",
" <div class="statistic-number">{{missing this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Mean</div>\n",
" <div class="statistic-number">{{mean this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Minimum</div>\n",
" <div class="statistic-number">{{minimum this}}</div>\n",
" </div>\n",
" <div class="statistic-item padding-right-30">\n",
" <div class="statistic-number-title">Maximum</div>\n",
" <div class="statistic-number">{{maximum this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between">\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Quantile statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>5-th percentile</div>\n",
" <div>{{fifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q1</div>\n",
" <div>{{q1 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>median</div>\n",
" <div>{{median this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Q3</div>\n",
" <div>{{q3 this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>95-th percentile</div>\n",
" <div>{{ninetyFifthPercentile this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Range</div>\n",
" <div>{{range this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Interquartile range (IQR)</div>\n",
" <div>{{iqr this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="statistics-list border-solid-gray">\n",
" <div>\n",
" <div class="mb-3">\n",
" <strong class="statistics-header-title">Descriptive statistics</strong>\n",
" </div>\n",
" <div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Standard deviation</div>\n",
" <div>{{stddev this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Coefficient of variation (CV)</div>\n",
" <div>{{coefficientOfVariation this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Sum</div>\n",
" <div>{{getSum this}}</div>\n",
" </div>\n",
" <div class="display-flex justify-content-space-between mb-2">\n",
" <div>Variance</div>\n",
" <div>{{variance this}}</div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" </div>\n",
" <div class="no-responsive">\n",
" <div class="no-responsive__content">\n",
" <h1 class="no-responsive__title">Hold on! :)</h1>\n",
" <p class="no-responsive__text">\n",
" It looks like your current screen size or device is not yet supported by the WhyLabs Sandbox. The Sandbox is\n",
" best experienced on a desktop computer. Please try maximizing this window or switching to another device. We\n",
" are working on adding support for a larger variety of devices.\n",
" </p>\n",
" </div>\n",
" </div>\n",
" \n",
" </script>\n",
"\n",
" <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>\n",
"\n",
" <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>\n",
"\n",
" <script>\n",
" function registerHandlebarHelperFunctions() {\n",
" //helper fun\n",
" // function formatLabelDate(timestamp) {\n",
" // const date = new Date(timestamp);\n",
" // const format = d3.timeFormat("%Y-%m-%d %I:%M:%S %p %Z");\n",
" // return format(date);\n",
" // }\n",
"\n",
" function fixNumberTo(number, decimals = 3) {\n",
" return parseFloat(number).toFixed(decimals);\n",
" }\n",
"\n",
" Handlebars.registerHelper("variance", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.variance === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.variance)){return "-"}\n",
" if (feature.descriptive_statistics.variance==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.variance,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("coefficientOfVariation", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.coefficient_of_variation === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.coefficient_of_variation)){return "-"}\n",
" if (feature.descriptive_statistics.coefficient_of_variation==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.coefficient_of_variation,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("median", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.median === 'number') {\n",
" if (isNaN(feature.quantile_statistics.median)){return "-"}\n",
" if (feature.quantile_statistics.median==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.median);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("fifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("ninetyFifthPercentile", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.ninety_fifth_percentile === 'number') {\n",
" if (isNaN(feature.quantile_statistics.ninety_fifth_percentile)){return "-"}\n",
" if (feature.quantile_statistics.ninety_fifth_percentile==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.ninety_fifth_percentile);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("range", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.range && typeof feature.range === 'number') {\n",
" if (isNaN(feature.range)){return "-"}\n",
" if (feature.range==null){return "-"}\n",
" return fixNumberTo(feature.range);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q3", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q3 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q3)){return "-"}\n",
" if (feature.quantile_statistics.q3==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q3);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("q1", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.q1 === 'number') {\n",
" if (isNaN(feature.quantile_statistics.q1)){return "-"}\n",
" if (feature.quantile_statistics.q1==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.q1);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("getSum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.sum === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.sum)){return "-"}\n",
" if (feature.descriptive_statistics.sum==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.sum,2);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("iqr", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.quantile_statistics && typeof feature.quantile_statistics.iqr === 'number' ) {\n",
" if (isNaN(feature.quantile_statistics.iqr)){return "-"}\n",
" if (feature.quantile_statistics.iqr==null){return "-"}\n",
" return fixNumberTo(feature.quantile_statistics.iqr);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
"\n",
" Handlebars.registerHelper("distinct", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" const distinct_pct = feature.distinct\n",
"\n",
" return fixNumberTo(distinct_pct,2);\n",
" });\n",
"\n",
" Handlebars.registerHelper("missing", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (feature.missing) {\n",
" return feature.missing;\n",
" }\n",
" return "0";\n",
" });\n",
"\n",
" Handlebars.registerHelper("mean", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.mean === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.mean)){return "-"}\n",
" if (feature.descriptive_statistics.mean==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.mean);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("stddev", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (feature.descriptive_statistics && typeof feature.descriptive_statistics.stddev === 'number') {\n",
" if (isNaN(feature.descriptive_statistics.stddev)){return "-"}\n",
" if (feature.descriptive_statistics.stddev==null){return "-"}\n",
" return fixNumberTo(feature.descriptive_statistics.stddev);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("minimum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
" if (typeof feature.min === 'number') {\n",
" if (isNaN(feature.min)){return "-"}\n",
" if (feature.min==null){return "-"}\n",
" return fixNumberTo(feature.min);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" Handlebars.registerHelper("maximum", function (column) {\n",
" const feature = Object.values(column)[0]\n",
"\n",
" if (typeof feature.max === 'number') {\n",
" if (isNaN(feature.max)){return "-"}\n",
" if (feature.max==null){return "-"}\n",
" return fixNumberTo(feature.max);\n",
" }\n",
" return "-";\n",
" });\n",
"\n",
" // Handlebars.registerHelper("getProfileTimeStamp", function (column) {\n",
" // return formatLabelDate(+column.properties.dataTimestamp)\n",
" // });\n",
"\n",
" // Handlebars.registerHelper("getProfileName", function (column) {\n",
" // return column.properties.tags.name\n",
" // });\n",
"\n",
" Handlebars.registerHelper("featureName", function (column) {\n",
" const featureName = Object.keys(column)[0]\n",
" return featureName\n",
" });\n",
" }\n",
"\n",
" function initHandlebarsTemplate() {\n",
" // Replace this context with JSON from .py file\n",
" const context = {"chlorides": {"total_count": 408, "missing": 0, "distinct": 21.813730257594944, "min": 0.012, "max": 0.23, "range": 0.218, "quantile_statistics": {"fifth_percentile": 0.046, "iqr": 0.025999999999999995, "q1": 0.06, "median": 0.071, "q3": 0.086, "ninety_fifth_percentile": 0.12}, "descriptive_statistics": {"stddev": 0.024213181268290383, "coefficient_of_variation": 0.3226526212509791, "sum": 30.618, "variance": 0.0005862781471310883, "mean": 0.07504411764705882}}};\n",
" // Config handlebars and pass data to HBS template\n",
" const source = document.getElementById("entry-template").innerHTML;\n",
" const template = Handlebars.compile(source);\n",
" const html = template(context);\n",
" const target = document.getElementById("generated-html");\n",
" target.innerHTML = html;\n",
" }\n",
"\n",
" // Invoke functions -- keep in mind invokation order\n",
" registerHandlebarHelperFunctions();\n",
" initHandlebarsTemplate();\n",
" </script>\n",
"</html>\n",
"\" width=100% height=650px\n",
" frameBorder=0></iframe>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"visualization.feature_statistics(feature_name=[\"density\",\"alcohol\",\"chlorides\"], profile=\"target\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Looks like we have 72 distinct values for `citric acid`, ranging from 0 to 0.79. We can also see the 10 missing values injected earlier."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Downloading the Visualization Output"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All of the previous visualizations can be downloaded in `HTML` format for further inspection. Just run:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"os.getcwd()\n",
"visualization.write(\n",
" rendered_html=visualization.profile_summary(),\n",
" html_file_name=os.getcwd() + \"/example\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We're downloading the constraints report here, but you can simply replace it for your preferred visualization. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- https://www.kaggle.com/uciml/red-wine-quality-cortez-et-al-2009\n",
"\n",
"- https://www.kaggle.com/vishalyo990/prediction-of-quality-of-wine\n",
"\n",
"- https://medium.com/data-from-the-trenches/a-primer-on-data-drift-18789ef252a6"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.10 ('.venv': poetry)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "5dd5901cadfd4b29c2aaf95ecd29c0c3b10829ad94dcfe59437dbee391154aea"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}