From 59c0f2d718a04e6bef6d5d88f163638a7e0b66e1 Mon Sep 17 00:00:00 2001 From: Patil Date: Tue, 7 Jun 2022 16:22:47 +0200 Subject: [PATCH] feat: plotly express --- popmon/visualization/histogram_section.py | 12 +-- popmon/visualization/utils.py | 111 ++++++++-------------- 2 files changed, 48 insertions(+), 75 deletions(-) diff --git a/popmon/visualization/histogram_section.py b/popmon/visualization/histogram_section.py index b893cd27..94d15689 100644 --- a/popmon/visualization/histogram_section.py +++ b/popmon/visualization/histogram_section.py @@ -141,11 +141,11 @@ def transform(self, data_obj: dict, sections: Optional[list] = None): (feature, dates[i], hists[i], hist_names, self.top_n) for i in range(last_n) ] - # plots = parallel(_plot_histograms, args) + plots = parallel(_plot_histograms, args) - # # filter out potential empty plots - # plots = [e for e in plots if len(e["plot"])] - # plots = sorted(plots, key=lambda plot: plot["name"]) + # filter out potential empty plots + plots = [e for e in plots if len(e["plot"])] + plots = sorted(plots, key=lambda plot: plot["name"]) # filter out potential empty heatmap plots, then prepend them to the sorted histograms hplots = [] @@ -153,7 +153,7 @@ def transform(self, data_obj: dict, sections: Optional[list] = None): if isinstance(h, dict): if len(h["plot"]): hplots.append(h) - plots = hplots #+ plots + plots = hplots + plots features_w_metrics.append({"name": feature, "plots": plots}) @@ -193,7 +193,7 @@ def _plot_histograms(feature, date, hc_list, hist_names, top_n, max_nbins=1000): # make plot. note: slow! if hc_list[0].n_dim == 1: - if all(h.size == 0 for h in hc_list): + if all(bool(h) is False for h in hc_list): # triviality checks, skip all histograms empty return {"name": date, "description": get_stat_description(date), "plot": ""} diff --git a/popmon/visualization/utils.py b/popmon/visualization/utils.py index df2e19f4..1d0fc726 100644 --- a/popmon/visualization/utils.py +++ b/popmon/visualization/utils.py @@ -21,15 +21,14 @@ import logging import math from io import BytesIO, StringIO -from re import X from typing import List import numpy as np import pandas as pd -import pybase64 -from matplotlib import pyplot as plt import plotly.express as px import plotly.graph_objects as go +import pybase64 +from matplotlib import pyplot as plt import popmon.config from popmon.resources import templates_env @@ -94,45 +93,14 @@ def plot_bars_b64(data, labels=None, bounds=None, ylim=False, skip_empty=True): logger.debug("skipping plot with empty data.") return "" - # fig, ax = plt.subplots() - - index = np.arange(n) - width = (index[1] - index[0]) * 0.9 if n >= 2 else 1.0 -<<<<<<< HEAD - # ax.bar(index, data, width=width, align="center") - - + # plot bar fig = go.Figure([go.Bar(x=labels, y=data)]) - #handle high cardinality ? - # if labels: - # granularity = math.ceil(len(labels) / 50) - # for i in range(len(labels)): - # if i % granularity != 0: - # # plotly skips duplicate x-axis labels, so work around to have a x-axis label each time : https://github.com/plotly/plotly.js/issues/1516 417 - # labels[i] = " " - - fig.update_layout(xaxis_tickangle=-90) fig.update_xaxes(tickvals=labels, ticktext=labels) fig.update_yaxes(ticks="outside") - -======= - ax.bar(index, data, width=width, align="center") - if labels is not None: - ax.set_xticks(index) - ax.set_xticklabels(labels, fontdict={"rotation": "vertical"}) - granularity = math.ceil(len(labels) / 50) - [ - l.set_visible(False) - for (i, l) in enumerate(ax.xaxis.get_ticklabels()) - if i % granularity != 0 - ] - ->>>>>>> develop # plot boundaries - try: all_nan = (np.isnan(data)).all() max_value = np.nanmax(data) if not all_nan else np.nan @@ -162,19 +130,20 @@ def plot_bars_b64(data, labels=None, bounds=None, ylim=False, skip_empty=True): fig.add_hline(y=min_r[0], line_color="red") if y_max > y_min: - fig.update_yaxes(range = [y_min,y_max]) - + fig.update_yaxes(range=[y_min, y_max]) + elif ylim: spread = (max_value - min_value) / 20 y_min = min_value - spread y_max = max_value + spread if y_max > y_min: - fig.update_yaxes(range = [y_min,y_max]) + fig.update_yaxes(range=[y_min, y_max]) except Exception: logger.debug("unable to plot boundaries") return fig.to_json() + def render_traffic_lights_table(feature, data, metrics: List[str], labels: List[str]): colors = {} color_map = ["green", "yellow", "red"] @@ -382,7 +351,8 @@ def plot_overlay_1d_histogram_b64( if len(hists) != len(hist_names): raise ValueError("length of hist and hist_names are different") - fig, ax = plt.subplots(figsize=(9, 7)) + # fig, ax = plt.subplots(figsize=(9, 7)) + fig = go.Figure() alpha = 1.0 / len(hists) for i, hist in enumerate(hists): @@ -428,20 +398,21 @@ def plot_overlay_1d_histogram_b64( width = np.diff(bin_edges) # plot histogram - ax.bar( - bin_edges[:-1], - bin_values, - width=width, - alpha=alpha, - label=hist_names[i], + fig.add_trace( + go.Bar( + x=bin_edges[1:], + y=bin_values, + showlegend=True, + opacity=alpha, + name=hist_names[i], + ) ) # set x-axis properties if xlim: - ax.set_xlim(xlim) + fig.update_xaxes(range=xlim) else: - ax.set_xlim(min(bin_edges), max(bin_edges)) - ax.tick_params(axis="x", labelsize=12, labelrotation=90 if is_ts else 0) + fig.update_xaxes(range=[min(bin_edges), max(bin_edges)]) # plot categories else: @@ -453,10 +424,6 @@ def plot_overlay_1d_histogram_b64( len(labels), len(values), x_label ) - # plot histogram - tick_pos = np.arange(len(labels)) + 0.5 - ax.bar(tick_pos, values, width=0.8, alpha=alpha, label=hist_names[i]) - # set x-axis properties def xtick(lab): """Get x-tick.""" @@ -465,18 +432,23 @@ def xtick(lab): lab = lab[:17] + "..." return lab - ax.set_xlim((0.0, float(len(labels)))) - ax.set_xticks(tick_pos) - ax.set_xticklabels([xtick(lab) for lab in labels], fontsize=12, rotation=90) + # plot histogram + fig.add_trace( + go.Bar( + x=[xtick(lab) for lab in labels], + y=values, + showlegend=True, + opacity=alpha, + name=hist_names[i], + ) + ) # set common histogram properties - ax.set_xlabel(x_label, fontsize=14) - ax.set_ylabel(str(y_label) if y_label is not None else "Bin count", fontsize=14) - ax.tick_params(axis="y", labelsize=12) - ax.grid() - ax.legend() + fig.update_layout(barmode="overlay") + fig.update_yaxes(title=str(y_label) if y_label is not None else "Bin count") + fig.update_xaxes(title=x_label) - return plt_to_str(fig) + return fig.to_json() def plot_heatmap_b64( @@ -518,7 +490,7 @@ def plot_heatmap_b64( if len(hist_name) == 0: raise ValueError("length of heatmap names is zero") - #fig = plt.figure(figsize=(40, 20)) + # fig = plt.figure(figsize=(40, 20)) assert hist_values is not None and len( hist_values @@ -559,13 +531,14 @@ def xtick(lab): return lab # plot histogram - tick_pos_x = np.arange(len(date)) - tick_pos_y = np.arange(len(labels)) - fig = px.imshow(values, labels=dict(x="Time Bins", y=x_label, color="Productivity"), - x=date, - y=[xtick(lab) for lab in labels], - color_continuous_scale=cmap, - text_auto='.2f') + fig = px.imshow( + values, + labels={"x": "Time Bins", "y": x_label}, + x=date, + y=[xtick(lab) for lab in labels], + color_continuous_scale=cmap, + text_auto=".2f", + ) fig.update_xaxes(tickvals=date, ticktext=date, tickangle=-90) fig.update_yaxes(ticks="outside")