def whatevers_bar_chart(self, whatever: str="total") -> Figure: """ Stacked bar chart of total active, deaths and recovered values """ columns = ["active", "deaths", "recovered"] dates = self.main_df.reset_index()["Date"] color = { "active": "#ffb347", # Pastel Orange "deaths": "#ff6961", # Pastel Red "recovered": "#77dd78", # Pastel Green } engine = inflect.engine() fig = Figure() for column in columns: name = "{} {}".format(whatever.capitalize(), column.capitalize()) ys = self.main_df[name].sum(level=1) if whatever == "daily": ys = ys[1:] fig.add_trace(Bar(name=name, x=dates, y=ys, marker={"color": color[column]})) fig.update_layout(barmode="stack", title_text=engine.plural(whatever).capitalize()) fig.update_traces(marker_line_width=0) return fig
def format_well_overview_figure(figure: go.Figure, charttype: str, settings: List[str], sumvec: str) -> go.Figure: """This function formate the well overview figure. The reason for keeping this function outside the figure class is that we can update the figure formatting without generating a new WellOverviewFigure object and reloading the data. It can be applied directly to the current state of the figure dict if only formatting settings are changed. See in the well_overview_callbacks how it is used. """ if charttype == "pie": figure.update_traces( texttemplate=("%{label}<br>%{value:.2s}" if "show_prod_text" in settings else "%{label}")) elif charttype == "bar": figure.update_layout( barmode=("overlay" if "overlay_bars" in settings else "group")) figure.update_traces( textposition=("auto" if "show_prod_text" in settings else "none")) # These are valid for all chart types figure.update_layout(template=( "plotly_white" if "white_background" in settings else "plotly")) phase = {"WOPT": "Oil", "WGPT": "Gas", "WWPT": "Water"}[sumvec] figure.update( layout_title_text=f"Cumulative Well {phase} Production (Sm3)", layout_showlegend=("legend" in settings), ) return figure
def add_custom_hovercard(fig: go.Figure) -> None: """Add a custom hovercard to the figure based on the first element of customdata, without the title to the right. :param fig: Plotly figure. :type fig: go.Figure """ # Per https://stackoverflow.com/a/69430974/1840471. fig.update_traces(hovertemplate="%{customdata[0]}<extra></extra>")
def add_to_plot( fig: go.Figure, y: List[float], y_labels: Optional[List[str]] = None, y_err: Optional[List[float]] = None, practical_maximum: Optional[float] = None, label: Optional[str] = None, index: int = 0): color = QUALITATIVE_COLORS[index % len(QUALITATIVE_COLORS)] rgb_ints = [str(int(x.strip(" "))) for x in color[4:][:-1].split(",")] new_color = f"rgba({','.join(rgb_ints + ['0.2'])})" custom_data: Optional[List[Any]] = None hover_template = None if y_labels: if y_err: custom_data: List[Any] = list(zip(y_labels, y_err)) hover_template = '<b>%{y:.3f} +- %{customdata[1]:.3f}</b><br>%{customdata[0]}' else: custom_data = y_labels hover_template = '<b>%{y:.3f}</b><br>%{customdata}' # Error range x = list(range(1, len(y) + 1)) if y_err: y_upper = [m + e for m, e in zip(y, y_err)] y_lower = [m - e for m, e in zip(y, y_err)] fig.add_trace(go.Scatter( x=x + x[::-1], y=y_upper + y_lower[::-1], fill='toself', fillcolor=new_color, line_color='rgba(255,255,255,0)', showlegend=False, name=label, hoverinfo='skip', legendgroup=label, )) # Mean line fig.add_trace(go.Scatter( x=x, y=y, customdata=custom_data, hovertemplate=hover_template, line_color=color, showlegend=True, name=label, legendgroup=label, )) fig.update_traces(mode='lines') # Make serif fig.update_layout(font=dict( family="serif", )) return fig
def get_cmp_indicator(cmps: List[float]) -> Figure: # get last and first values last_cmp = 0.0 first_cmp = 0.0 if len(cmps) > 0: last_cmp = cmps[-1] first_cmp = cmps[0] # create indicator fig = Figure( Indicator(mode="number+delta", value=last_cmp, delta={ 'reference': first_cmp, 'relative': True, 'valueformat': '.2%' })) fig.update_traces(delta_font={'size': 14}, number_font_size=18, number_valueformat=',.2f') fig.update_layout(height=80, width=150) return fig
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() # Only thing I figured is - I could do this s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if s1.name else "series_1" ) # Not what is desired - need a line s2 = series_2.sort_index() fig.add_scatter( x=s2.index, y=s2, mode="lines", name=s2.name if s2.name else "series_2" ) # Not what is desired - need a line fig.update_layout(**layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def multi_plotly_timeseries_plot( dataframe: pd.DataFrame, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() columns = list(dataframe.keys()) to_plot_df = dataframe.sort_index() # Only thing I figured is - I could do this for key in columns: fig.add_scatter(x=to_plot_df.index, y=to_plot_df[key], mode="lines", name=key) # Not what is desired - need a line fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def update_traces(figure: go.Figure, **kwargs: Any) -> go.Figure: data_frame = kwargs["data_frame"] facet_col = kwargs.get("facet_col") return (figure.update_traces( marker_size=max((20 - (1.5 * data_frame[facet_col].nunique())), 5) if facet_col is not None else 20, selector=lambda t: t["type"] in ["scatter", "scattergl"], ).update_traces(textposition="inside", selector=dict(type="pie")).for_each_trace( lambda t: set_marker_color(t)).for_each_trace( lambda t: t.update(xbins_size=(t["x"].max() - t[ "x"].min()) / kwargs.get("nbins", 15)) if is_numeric_dtype(t["x"]) else None, selector=dict(type="histogram"), ))
def substituted_data_plot( raw_values: pd.Series, substitutions: pd.Series, message_series: pd.Series = None, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() s1 = raw_values.sort_index() s1 = s1.loc[~s1.index.duplicated(keep="first")] s2 = substitutions.sort_index() s2 = s2.loc[~s2.index.duplicated(keep="first")] completely_handled_series, replaced_originals, replacements, new_values, deleted = handle_substitutions( s1, s2) fig.add_scatter( x=completely_handled_series.index, y=completely_handled_series, mode="markers+lines", name=raw_values.name + "_substituted" if raw_values.name else "raw_values_substituted", line_color="blue", opacity=0.6, ) # Not what is desired - need a line fig.add_scatter( x=replaced_originals.index, y=replaced_originals, mode="markers", name="replaced raw values", line_color="orange", marker=dict(size=10, opacity=0.6), ) # Not what is desired - need a line fig.add_scatter( x=deleted.index, y=deleted, mode="markers", name="ignored raw values", line_color="red", marker=dict(symbol="x", size=10, opacity=0.6), ) # Not what is desired - need a line fig.add_scatter( x=new_values.index, y=new_values, mode="markers", name="added values", line_color="green", marker=dict(symbol="cross", size=10, opacity=0.6), ) # Not what is desired - need a line fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, limit_violation_timestamp: str, limit: float, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() # Only thing I figured is - I could do this s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if s1.name else "series_1" ) # Not what is desired - need a line s2 = series_2.sort_index() fig.add_scatter( x=s2.index, y=s2, mode="lines", name=s2.name if s2.name else "series_2" ) # Not what is desired - need a line fig.update_layout(**layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? vline_y_min = np.min([series_1.min(), series_2.min(), limit]) vline_y_max = np.max([series_1.max(), series_2.max(), limit]) hline_x_min = np.min([series_1.index.min(), series_2.index.min()]) hline_x_max = np.max([series_1.index.max(), series_2.index.max()]) fig.update_layout( shapes=( [ dict( type="line", yref="y", y0=vline_y_min, y1=vline_y_max, xref="x", x0=limit_violation_timestamp, x1=limit_violation_timestamp, ) ] if limit_violation_timestamp is not None else [] ) + [ dict( type="line", yref="y", y0=limit, y1=limit, xref="x", x0=hline_x_min, x1=hline_x_max, line={"color": "red", "width": 1}, ) ] ) fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def plot(self, Xt, sample=0, homology_dimensions=None, plotly_params=None): """Plot a sample from a collection of Betti curves arranged as in the output of :meth:`transform`. Include homology in multiple dimensions. Parameters ---------- Xt : ndarray of shape (n_samples, n_homology_dimensions, n_bins) Collection of Betti curves, such as returned by :meth:`transform`. sample : int, optional, default: ``0`` Index of the sample in `Xt` to be plotted. homology_dimensions : list, tuple or None, optional, default: ``None`` Which homology dimensions to include in the plot. ``None`` means plotting all dimensions present in :attr:`homology_dimensions_`. plotly_params : dict or None, optional, default: ``None`` Custom parameters to configure the plotly figure. Allowed keys are ``"traces"`` and ``"layout"``, and the corresponding values should be dictionaries containing keyword arguments as would be fed to the :meth:`update_traces` and :meth:`update_layout` methods of :class:`plotly.graph_objects.Figure`. Returns ------- fig : :class:`plotly.graph_objects.Figure` object Plotly figure. """ check_is_fitted(self) homology_dimensions_mapping = _make_homology_dimensions_mapping( homology_dimensions, self.homology_dimensions_) layout_axes_common = { "type": "linear", "ticks": "outside", "showline": True, "zeroline": True, "linewidth": 1, "linecolor": "black", "mirror": False, "showexponent": "all", "exponentformat": "e" } layout = { "xaxis1": { "title": "Filtration parameter", "side": "bottom", "anchor": "y1", **layout_axes_common }, "yaxis1": { "title": "Betti number", "side": "left", "anchor": "x1", **layout_axes_common }, "plot_bgcolor": "white", "title": f"Betti curves from diagram {sample}" } fig = Figure(layout=layout) for ix, dim in homology_dimensions_mapping: fig.add_trace( Scatter(x=self.samplings_[dim], y=Xt[sample][ix], mode="lines", showlegend=True, name=f"H{dim}")) # Update traces and layout according to user input if plotly_params: fig.update_traces(plotly_params.get("traces", None)) fig.update_layout(plotly_params.get("layout", None)) return fig
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, freq, freq_factor, max_shifts_past, max_shifts_future, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 480, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ if max_shifts_past < 0 or max_shifts_future < 0: raise ValueError("Shift maxima values must be greater equal zero.") fig = Figure() s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if (s1.name and s1.name != "measurement") else "series", ) s2 = series_2.sort_index() for step in np.arange(-max_shifts_past, max_shifts_future, 1): shifted = s2.copy() shifted.index = shifted.index.shift(periods=step * freq_factor, freq=freq) fig.add_scatter( x=shifted.index, y=shifted, mode="lines", name=shifted.name if (shifted.name and shifted.name != "measurement") else "series_shiftable", visible=False, ) fig.data[max_shifts_past + 1].visible = True # start somewhere steps = [] for i in range(len(fig.data)): step = dict( method="update", args=[ { "visible": [False] * (len(fig.data)) }, { "title": f"Shifted by x times frequency {freq_factor}{freq}: " + str(i - max_shifts_past - 1) }, ], # layout attribute ) step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible" step["args"][0]["visible"][0] = True step["label"] = str(i - max_shifts_past - 1) steps.append(step) sliders = [ dict( active=max_shifts_past, currentvalue={ "prefix": f"Shifted by x times frequency {freq_factor}{freq}: " }, pad={ "t": 80, "b": 10 }, steps=steps[1:], ) ] fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=40, t=30, pad=10)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) fig.update_layout(sliders=sliders) return fig
def plot_dimensions_graph( fig: go.Figure, y: List[float], y_labels: Optional[List[str]] = None, y_err: Optional[List[float]] = None, practical_maximum: Optional[float] = None, label: Optional[str] = None): color = QUALITATIVE_COLORS[0] rgb_ints = [str(int(x.strip(" "))) for x in color[4:][:-1].split(",")] new_color = f"rgba({','.join(rgb_ints + ['0.2'])})" # Error range x = list(range(1, len(y) + 1)) if y_err: y_upper = [m + e for m, e in zip(y, y_err)] y_lower = [m - e for m, e in zip(y, y_err)] fig.add_trace(go.Scatter( x=x + x[::-1], y=y_upper + y_lower[::-1], fill='toself', fillcolor=new_color, line_color='rgba(255,255,255,0)', showlegend=False, name=label, hoverinfo='skip', legendgroup=label, )) custom_data: Optional[List[Any]] = None hover_template = None if y_labels: if y_err: custom_data = list(zip(y_labels, y_err)) hover_template = '<b>%{y:.3f} +- %{customdata[1]:.3f}</b><br>%{customdata[0]}' else: custom_data = y_labels hover_template = '<b>%{y:.3f}</b><br>%{customdata}' # Mean line fig.add_trace(go.Scatter( x=x, y=y, customdata=custom_data, hovertemplate=hover_template, line_color=color, showlegend=True, name=label, legendgroup=label, )) fig.update_traces(mode='lines') # Max MI if practical_maximum: fig.add_shape( # Line Horizontal type="line", x0=min(x), y0=practical_maximum, x1=max(x), y1=practical_maximum, line=dict( color="LightSeaGreen", dash="dash", ), ) fig.update_layout(margin=dict(l=20, r=20, t=20, b=20)) # noqa fig.update_xaxes(title_text="Dimensions Selected", tickvals=x) # Make serif fig.update_layout(font=dict( family="serif", )) return fig