Exemple #1
0
    def _add_tier_annotations(fig: go.Figure) -> go.Figure:
        """Adds tier annotations to the figure."""
        fig.add_traces([
            go.Scatter(x=[x_, x_],
                       y=[-2, 40],
                       line_color="gray",
                       line_dash="dash") for x_ in [1.5, 1, 0.5]
        ])

        annos = [
            (1.75, 10, "S TIER"),
            (1.25, 10, "A TIER"),
            (0.75, 10, "B TIER"),
            (0.25, 10, "C TIER"),
        ]
        for anno in annos:
            annotation = dict(
                x=anno[0],
                y=anno[1],
                text="<b>%s</b>" % anno[2],
                showarrow=
                True,  # with True there is no arrow, but text is centered
                xanchor="left",
                yanchor="middle",
                borderpad=0,
                font=dict(color="black", size=50, family="MonacoRegular"),
                opacity=0.7,
                textangle=90,
            )
            fig.add_annotation(annotation)
        return fig
Exemple #2
0
    def _save(self, data: go.Figure) -> None:
        save_path = get_filepath_str(self._get_save_path(), self._protocol)

        with self._fs.open(save_path, **self._fs_open_args_save) as fs_file:
            data.write_json(fs_file, **self._save_args)

        self._invalidate_cache()
Exemple #3
0
    def plot(self, fig: go.Figure) -> go.Figure:
        width = 2
        color1 = 'rgba(46, 134, 193, 0.5)'
        color2 = 'rgba(40, 180, 99, 0.5)'
        color3 = 'rgba(136, 78, 160, 0.5)'
        position = (1, 1)
        macd_, signal, hist = self.result

        #fig.add_trace(go.Scatter(x=self.data['date'],
        #                         y=macd_,
        #                         mode='lines',
        #                         name='macd',
        #                         line=dict(color=color1, width=width)
        #                         ),
        #              row=position[0], col=position[1]
        #              )
        fig.add_trace(go.Scatter(x=self.data['date'],
                                 y=signal,
                                 mode='lines',
                                 name='macd_signal',
                                 line=dict(color=color2, width=width)),
                      row=position[0],
                      col=position[1])
        fig.add_trace(go.Bar(
            x=self.data['date'],
            y=hist,
            marker_color=color3,
            name='macd_hist',
        ),
                      row=position[0],
                      col=position[1])

        return fig
def run_and_plot_single_fit_comparison(fig: go.Figure,
                                       data: Data1D) -> go.Figure:
    """
    Runs both
    Args:
        fig ():
        data ():

    Returns:

    """
    i_sense_fit = fit_single_transition(data,
                                        fit_with='i_sense',
                                        initial_params=None)
    nrg_params = get_initial_params(data, 'nrg')
    nrg_params['g'].value = 0.001
    nrg_params['g'].vary = False
    nrg_params['occ_lin'].vary = False
    nrg_fit = fit_single_transition(data,
                                    fit_with='nrg',
                                    initial_params=nrg_params)
    for fit, name in zip([i_sense_fit, nrg_fit], ['i_sense', 'NRG']):
        fig.add_trace(
            p1d.trace(x=data.x,
                      data=fit.eval_fit(x=data.x),
                      name=name,
                      mode='lines'))
    print(i_sense_fit)
    print(nrg_fit)
    return fig
    def show_plot(fig: go.Figure) -> None:
        """Save plot to the specified file."""

        # Show only if SHOW_PLOT is True
        if SHOW_PLOT:
            fig.update_layout(template=_plot_layout_template)
            fig.show()
Exemple #6
0
    def plot_traces(self,
                    fig: go.Figure,
                    data: pd.DataFrame,
                    x: str,
                    y: str,
                    hue: str,
                    row: int,
                    col: int,
                    mode: str = 'lines') -> go.Figure:
        """ The goal is create a similar behavior as plotly express or seaborn.
            This function will take x, y, and hue column names and use them to layer
            the correct scatter plots together.

        :param fig: ploty fig where this plot (or these traces) will be places
        :param data: dataframe containing all the columns listed in x, y, and hue
        :param x: column name of x-axis
        :param y: column name of y-axis
        :param hue: column name of category column
        :param row: the row number inside the fig where the plot will be located
        :param col: the column number inside the fig where the plot
        :param mode: plotly.graph_objects.Scatter mode value
        :return: plot fig with plot added s
        """
        for n, category in enumerate(data[hue].unique()):
            temp = data[data[hue] == category]
            chart = go.Scatter(x=temp[x],
                               y=temp[y],
                               mode='lines',
                               name=category,
                               marker_color=px.colors.sequential.Plasma[n])
            fig.add_trace(chart, row=row, col=col)
        return fig
Exemple #7
0
def update_layout(df: pd.DataFrame, fig: go.Figure) -> None:
    fig.update_layout(
        xaxis={
            'tickvals': df['date'],
            'tickformat': DATE_FORMAT,
            'tickangle': 45,
            'type': 'date',
            'ticks': 'outside',
            'ticklen': 5,
            'tickwidth': 2,
            'showline': True,
            'linewidth': 2,
            'linecolor': 'black',
            'tickfont': {
                'size': 14
            },
        },
        yaxis={
            'showgrid': True,
            'gridwidth': 0.5,
            'gridcolor': GRID_COLOR,
            'ticks': 'outside',
            'ticklen': 5,
            'tickwidth': 2,
            'showline': True,
            'linewidth': 2,
            'linecolor': 'black',
            #'dtick': 20,
            'tickfont': {
                'size': 14
            },
        },
        plot_bgcolor="white",
    )
    def __add_const_trend_trace(self, trend_name: str, trend: PlotlyTrendLine, fig: pgo.Figure):
        all_series = {**self.series, **self.averages}
        if trend.related_series_id not in all_series:
            raise InvalidTrendLine(f"Missing related series: {trend.related_series_id}")
        if trend.constant is None:
            raise InvalidTrendLine(f"Constant must be specified to draw const trendline")

        series = all_series[trend.related_series_id]
        xs_range = series.data[series.x_field]
        xs = np.linspace(xs_range.min(), xs_range.max(), 10)
        ys = np.full(10, trend.constant)

        trace = pgo.Scatter(
            x=xs,
            y=ys,
            name=trend_name,
            mode='lines',
            line={
                'dash': 'dash',
                'color': 'red',
                'width': 0.4,
            }
        )

        fig.add_trace(trace)
    def init_figure(self, analyzed_interval, predictions, model):
        """
        Initializes the plotly Figure and puts predictions argument 
        as candlestick data into the chart
        Parameters:
        analyzed_interval : np.ndarray - dates and prices 
        predictions : np.ndarray - hardmax indices of model's predictions 
        model : keras.Model
        Returns:
        figure : plotly.Figure 
        """
        candlestick = Candlestick(x=analyzed_interval[:, 0],
                                  open=analyzed_interval[:, 1],
                                  high=analyzed_interval[:, 2],
                                  low=analyzed_interval[:, 3],
                                  close=analyzed_interval[:, 4])

        figure = Figure(data=[candlestick])

        model.categories.sort()
        steps = ', '.join(['+' + str(c) + '%' for c in model.categories])
        title = ('Predictions of ' + model.name + ' for the next ' +
                 str(model.prediction_interval) +
                 ' days based on the previous ' + str(model.input_interval) +
                 ' days. Steps: ' + steps)
        figure.update_layout(title=title, yaxis_title='Price')

        return figure
Exemple #10
0
    def add_textbox(fig: go.Figure,
                    text: str,
                    position: Union[str, Tuple[float, float]],
                    fontsize=10) -> None:
        """
        Adds <text> to figure in a text box.
        Args:
            fig (): Figure to add text box to
            text (): Text to add
            position (): Absolute position on figure to add to (e.g. (0.5,0.9) for center top, or 'CT' for center top, or 'T' for center top)

        Returns:
            None: Modifies figure passed in
        """
        if isinstance(position, str):
            position = get_position_from_string(position)
        text = text.replace('\n', '<br>')
        fig.add_annotation(text=text,
                           xref='paper',
                           yref='paper',
                           x=position[0],
                           y=position[1],
                           showarrow=False,
                           bordercolor='#111111',
                           borderpad=3,
                           borderwidth=1,
                           opacity=0.8,
                           bgcolor='#F5F5F5',
                           font=dict(size=fontsize))
Exemple #11
0
def update_axes(fig: Figure, temp_kwargs: Dict[str, Any]) -> None:
    """
    Separeated this portion of the code because it is clumsy. It changes
    the axes looks.
    """
    # Layout and title parameters https://plotly.com/python/figure-labels/
    fig.update_layout(scene=dict(
        xaxis=dict(
            title_text=temp_kwargs['x_label'],
            showline=True,
            linewidth=2,
            linecolor='black',
            ticks="outside",
            mirror=True,
            backgroundcolor='#9467bd',
        ),
        yaxis=dict(
            title_text=temp_kwargs['y_label'],
            showline=True,
            linewidth=2,
            linecolor='black',
            ticks="outside",
            mirror=True,
            backgroundcolor='#9467bd',
        ),
        zaxis=dict(
            title_text=temp_kwargs['z_label'],
            showline=True,
            linewidth=2,
            linecolor='black',
            ticks="outside",
            mirror=True,
            backgroundcolor='#9467bd',  #change the color of axis
        )))
def save_figure(fig: go.Figure, dformat: str, output_path: str):
    if dformat == 'html':
        fig.write_html(output_path)
    elif dformat in SUPPORTED_FORMATS:
        fig.write_image(output_path, format=dformat)
    else:
        raise NotImplemented(f'Format {dformat} is not supported')
Exemple #13
0
    def show(self, *args, **kwargs) -> None:
        """Show the figure."""
        from vectorbt.settings import plotting

        fig_kwargs = dict(width=self.layout.width, height=self.layout.height)
        show_kwargs = merge_dicts(fig_kwargs, plotting['show'], kwargs)
        _Figure.show(self, *args, **show_kwargs)
Exemple #14
0
def wrap_plotly_fig(fig: go.Figure, width: str = '100%', height: str = '100%'):
    if 'px' in width:
        fig = fig.to_html(include_plotlyjs=False, full_html=False, default_height=height, default_width=width)
        return div(raw(fig), style=f'width: {width}')
    else:
        fig = fig.to_html(include_plotlyjs=False, full_html=False, default_height=height, default_width='100%')
        return div(raw(fig), style=f'width: {width}')
Exemple #15
0
def add_area_trace(
    fig: go.Figure,
    x_series: list,
    y_series: list,
    name: str,
    color: str,
    row: int,
    showlegend: bool = True,
    linewidth: float = 0.2,
) -> go.Figure:
    """Adding single area trace to subplot"""
    fig.add_trace(
        go.Scatter(
            x=x_series,
            y=y_series,
            hoverinfo="text+x+y",
            hoveron="fills",
            mode="lines",
            # fill="tonexty",
            # fillcolor=color,
            line=dict(width=linewidth, color=color),
            name=name,
            text=name,
            stackgroup="one",
            legendgroup="Ctrl Modes",
            showlegend=showlegend,
        ),
        row=row,
        col=1,
    )
def main():
    # Data File Name and file path
    filename = 'us-counties.csv'
    filepath = os.path.join('./data/raw', filename)

    # Import data into DataFrame and rename columns
    df = read_csv(filepath, dtype={'fips': str})
    df.columns = ['date', 'county', 'state', 'zip', 'cases', 'deaths']

    print(df.dtypes)
    print(f'Number of total cases:  {df.cases.sum()}')
    print(f'Number of total deaths:  {df.deaths.sum()}')

    # Extract Year, Month and Date and place in separate columns
    df['year'] = DatetimeIndex(df.date, yearfirst=True).year
    df['month'] = DatetimeIndex(df.date, yearfirst=True).month_name()

    # States and Counties
    states = df.state.unique()
    counties = df.county.unique()

    # Sum of Grouped Dats
    grp_dates = df.groupby(['date', 'state'])['cases', 'deaths'].sum()
    print(grp_dates.sum())

    # Plot Scatter plot of Cases vs Deaths
    renderers.default = 'browser'

    fig = Figure(data=Scatter(x=df.cases, y=df.deaths, mode='markers'))

    fig.show()

    return
 def __add_trace(self, series: PlotSeries, fig: pgo.Figure):
     show_error_y = self.properties.show_error_bars and \
                    series.y_err_field is not None and \
                    series.y_err_field in series.data.columns
     trace_mode = {
         PlotSeriesKind.SCATTER: 'markers',
         PlotSeriesKind.SCATTERLINE: 'markers+lines',
         PlotSeriesKind.LINE: 'lines',
     }[self.properties.default_series_kind]
     trace = pgo.Scatter(
         name=series.series_info.series_name,
         mode=trace_mode,
         x=series.data[series.x_field],
         y=series.data[series.y_field],
         error_y=None if not show_error_y else {
             'type': 'data',
             'visible': series.y_err_field is not None,
             'array': series.data[series.y_err_field],
             'color': PlotUtils.series_color_hex(series.series_info.series_id),
             'thickness': 0.5,
             'width': 1,
         },
         marker={
             'symbol': 'circle',
         },
         line={
             'color': PlotUtils.series_color_hex(series.series_info.series_id),
         }
     )
     fig.add_trace(trace)
Exemple #18
0
def generate_line_plots(
    y_matrix: np.ndarray,
    lap_time_matrix: np.ndarray,
    figure: go.Figure,
) -> None:
    """
    Given a matrix of lap times and a matrix to plot on the y-axis, this function will
    add line plots to the figure. Each line could represent, e.g., the distance to
    the first driver or the position of the driver

    Parameters
    ----------
    y_matrix
        Each row of this matrix will become a separate line in the figure
    lap_time_matrix
        A matrix with the lap times for each driver
    figure
        The figure to which the lines should be added

    Returns
    -------

    """
    for driver_idx in range(lap_time_matrix.shape[0]):
        line_plot = go.Scatter(
            x=list(range(lap_time_matrix.shape[1])),
            y=y_matrix[driver_idx, :],
            name=f'Driver {driver_idx}',
            marker_size=1,
        )

        figure.add_trace(line_plot)
    def __add_ml_linear_trend_trace(self, trend_name: str, trend: PlotlyTrendLine, fig: pgo.Figure):
        all_series = {**self.series, **self.averages}
        if trend.related_series_id not in all_series:
            raise InvalidTrendLine(f"Missing related series: {trend.related_series_id}")

        series = all_series[trend.related_series_id]
        xs = np.array(series.data[series.x_field]).reshape(-1, 1)
        ys = np.array(series.data[series.y_field]).reshape(-1, 1)
        model = LinearRegression()

        model.fit(xs, ys)
        xs_predict = np.linspace(xs.min(), xs.max(), 100).reshape(-1, 1)
        ys_predict = model.predict(xs_predict)

        trace = pgo.Scatter(
            x=xs_predict.reshape(1, -1)[0],
            y=ys_predict.reshape(1, -1)[0],
            name=trend_name,
            mode='lines',
            line={
                'dash': 'dash',
                'color': 'red',
            }
        )

        fig.add_trace(trace)
Exemple #20
0
    def _fig_layout(fig: go.Figure) -> go.Figure:
        """ Modify the figure to match the desired style

        :param fig: figure to be formatted
        :return: formatted figure
        """
        fig.update_layout(xaxis=dict(
            showline=True,
            showgrid=False,
            showticklabels=True,
            linecolor='rgb(204, 204, 204)',
            linewidth=2,
            ticks='outside',
            tickfont=dict(
                family='Arial',
                size=12,
                color='rgb(82, 82, 82)',
            ),
        ),
                          yaxis=dict(
                              showgrid=False,
                              zeroline=False,
                              showline=False,
                              showticklabels=False,
                          ),
                          autosize=True,
                          showlegend=False,
                          margin=dict(
                              autoexpand=False,
                              l=20,
                              r=20,
                              t=110,
                          ),
                          plot_bgcolor='white')
        return fig
Exemple #21
0
    def plot_3d(self):
        z = self.get_point_clouds()

        fig = Figure(data=Scatter3d(x=z[:,0].ravel(), y=z[:,1].ravel(), z=z[:,2].ravel(),
                                        mode='markers',
                                        marker=dict(size=1)        
                                        )
                        )

        x_range = [floor(z[:,0].min()), ceil(z[:,0].max())]
        y_range = [floor(z[:,1].min()), ceil(z[:,1].max())]
        z_range = [floor(z[:,2].min()), ceil(z[:,2].max())]

        x_len = x_range[1] - x_range[0]
        y_len = y_range[1] - y_range[0]
        z_len = z_range[1] - z_range[0]
        base = min([x_len, y_len, z_len])
        
        fig.update_layout(
            scene = dict(
                aspectmode='manual', aspectratio=dict(x=x_len/base, y=y_len/base, z=z_len/base),
                xaxis = dict(range=x_range),
                yaxis = dict(range=y_range),
                zaxis = dict(range=z_range)
            )
        )

        return fig
Exemple #22
0
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
Exemple #23
0
def add_periodic_vertical_lines(fig: go.Figure,
                                start: Union[str, Timestamp],
                                end: Union[str, Timestamp],
                                freq: str,
                                y1: float,
                                y0: float = 0) -> go.Figure:
    """
    add vertical lines to plotly figures that repeat at a certain frequency ie every sunday
    Args:
        fig: plotly figure
        start: first date of period
        end: last date of the period
        freq: pandas time series frequency directive ie W-THU
        y1: max value of line
        y0: minimum value of line

    Returns:
        fig: the plotly figures with lines added as a trace
    """
    dates_dow = pd.date_range(start, end, freq=freq)
    for day in dates_dow:
        fig.add_shape(
            type='line',
            x0=day,
            y0=y0,
            x1=day,
            y1=y1,
            line=dict(width=1, dash='dot'),
        )
    return fig
Exemple #24
0
def update_layout(fig: go.Figure) -> None:
    fig.update_layout(
        xaxis={
            'tick0': 0,
            'dtick': 20,
            'ticks': 'outside',
            'ticklen': 5,
            'tickwidth': 2,
            'tickangle': 45,
            'tickfont': {'size': 14},
            'showline': True, 
            'linewidth': 2, 
            'linecolor': 'black',
        },
        yaxis={
            'showgrid': True,
            'gridwidth': 0.5,
            'gridcolor': GRID_COLOR,
            'ticks': 'outside',
            'ticklen': 5,
            'tickwidth': 2,
            'tickfont': {'size': 14},
            'showline': True, 
            'linewidth': 2, 
            'linecolor': 'black',
            'tick0': 0,
            'dtick': 1,
        },
        plot_bgcolor="white",
        legend={'orientation': 'h', 'x': 0, 'y': 1.2},
    )
Exemple #25
0
def highlight_lines(fig: go.Figure, country: str) -> go.Figure:
    """Highlight lines of the infection plots"""
    if index := [i for i, c in enumerate(fig.data) if c["name"] == country]:
        fig.data[index[0]]["line"]["color"] = styles.highlight.color
        fig.data[index[0]]["line"]["width"] = styles.highlight.line_width
        fig.data[index[0]]["opacity"] = styles.highlight.opacity
        return fig
 def _update_plot_layout(initial_layout: dict, update_layout: dict) -> Dict:
     if initial_layout is None:
         raise PreventUpdate
     fig = Figure({"layout": initial_layout})
     if update_layout is not None:
         fig.update_layout(update_layout)
     return fig["layout"]
def configure_upset_plot_axes(fig: go.Figure, set_intersections: pd.DataFrame,
                              truncated: bool, title: str) -> go.Figure:
    """
    Format and organise plot axes for upset plotly figure
    :param fig: a go.Figure containing the plotly upsetplot
    :param set_intersections: pandas DataFrame with all set intersections
    :param truncated: boolean if the number of intersections has been truncated
                      or not (i.e. if num intersections > MAX_UPSET_INTERSECTIONS)
    :param title: str containing the plot title
    :return: A go.Figure of the plotly upsetplot with tidied/formatted
             axes legends
    """

    if truncated:
        plot_label = f"{title} UpSet Plot<br>(Truncated to {MAX_UPSET_INTERSECTIONS} Most "\
                      "Common Intersections)"
    else:
        plot_label = f"{title} UpSet Plot<br>(All Intersections)"

    fig.update_layout(
        dict(
            title=plot_label,

            # tidy axes for cardinality plot
            xaxis1=dict(showticklabels=False, fixedrange=True),
            yaxis1=dict(title="Set Count"),

            # grid
            xaxis2=dict(showticklabels=False,
                        fixedrange=True,
                        range=[-0.5,
                               len(set_intersections.columns) - 0.5]),
            yaxis2=dict(showticklabels=False,
                        fixedrange=True,
                        range=[-0.5, len(set_intersections.index) + 0.5]),

            # membership
            xaxis3=dict(showticklabels=False,
                        fixedrange=True,
                        range=[-0.5,
                               len(set_intersections.columns) - 0.5]),
            yaxis3=dict(
                tickmode='array',
                tickvals=list(range(len(set_intersections.index))),
                ticktext=set_intersections.index,
                title=f"{title}",
                fixedrange=True,
                range=[-0.5, len(set_intersections.index) + 0.5],
                tickfont=dict(size=10),
                automargin=True,
            ),

            # category count
            xaxis4=dict(title=f"Unique Count of<br>{title}"),
            yaxis4=dict(showticklabels=False,
                        fixedrange=True,
                        range=[-0.5, len(set_intersections.index) + 0.5])))
    return fig
Exemple #28
0
def _add_camera_trace(
    fig: go.Figure,
    cameras: CamerasBase,
    trace_name: str,
    subplot_idx: int,
    ncols: int,
    camera_scale: float,
):
    """
    Adds a trace rendering a Cameras object to the passed in figure, with
    a given name and in a specific subplot.

    Args:
        fig: plotly figure to add the trace within.
        cameras: the Cameras object to render. It can be batched.
        trace_name: name to label the trace with.
        subplot_idx: identifies the subplot, with 0 being the top left.
        ncols: the number of sublpots per row.
        camera_scale: the size of the wireframe used to render the Cameras object.
    """
    cam_wires = get_camera_wireframe(camera_scale).to(cameras.device)
    cam_trans = cameras.get_world_to_view_transform().inverse()
    cam_wires_trans = cam_trans.transform_points(cam_wires).detach().cpu()
    # if batch size is 1, unsqueeze to add dimension
    if len(cam_wires_trans.shape) < 3:
        cam_wires_trans = cam_wires_trans.unsqueeze(0)

    nan_tensor = torch.Tensor([[float("NaN")] * 3])
    all_cam_wires = cam_wires_trans[0]
    for wire in cam_wires_trans[1:]:
        # We combine camera points into a single tensor to plot them in a
        # single trace. The NaNs are inserted between sets of camera
        # points so that the lines drawn by Plotly are not drawn between
        # points that belong to different cameras.
        all_cam_wires = torch.cat((all_cam_wires, nan_tensor, wire))
    x, y, z = all_cam_wires.detach().cpu().numpy().T.astype(float)

    row, col = subplot_idx // ncols + 1, subplot_idx % ncols + 1
    fig.add_trace(
        go.Scatter3d(  # pyre-ignore [16]
            x=x,
            y=y,
            z=z,
            marker={"size": 1},
            name=trace_name),
        row=row,
        col=col,
    )

    # Access the current subplot's scene configuration
    plot_scene = "scene" + str(subplot_idx + 1)
    current_layout = fig["layout"][plot_scene]

    # flatten for bounds calculations
    flattened_wires = cam_wires_trans.flatten(0, 1)
    verts_center = flattened_wires.mean(0)
    max_expand = (flattened_wires.max(0)[0] - flattened_wires.min(0)[0]).max()
    _update_axes_bounds(verts_center, max_expand, current_layout)
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 set_layout_size(fig: go.Figure, width: int, height: int,
    hsplit: int = 1, vsplit: int = 1) -> None:
    fig.update_layout(
        dict(
            autosize=False,
            width=width / hsplit,
            height=height / vsplit,
        )
    )