Skip to content

Advanced topics

Taipy exposes advanced features that the Plotly library provides. This section demonstrates some of those features, when they are needed and how to use them in your Taipy application.

Multiple traces with different dataset sizes

You can download the entire source code used in this section from the GitHub repository.

The Adding a trace example explained how to create multiple traces on the same chart.
However, a strong limitation was that all series have to have the same size.
This limitation is due to the technical nature of a Pandas DataFrame, which must have all its columns holding the same number of items.

To allow for plotting series of different sizes, we set the data property of the chart control to an array of dictionaries (converted to Pandas DataFrames internally): each dictionary that holds different series that must all be the same size to be used in the chart control, but different dictionaries in the data array may have different sizes.
The data column names must then be prefixed by their index in the data array (starting at index 0) followed by a slash: "1/value" indicates the key (or the column) called "value" in the second element of the array set to the data property of the chart control.

Here is an example that demonstrates this. We want to plot two different data sets that represent the same function (x squared) on two different intervals.

  • The first trace is very coarse, with a data point at every other x unit between -10 and 10.
  • The second trace is far more detailed, with ten data points between each x unit, on the interval [-4, 4].

Here is the code that does just that:

# The first data set uses the x interval [-10..10],
# with one point at every other unit
x1_range = [x*2 for x in range(-5, 6)]

# The second data set uses the x interval [-4..4],
# with ten point between every unit
x2_range = [x/10 for x in range(-40, 41)]

# Definition of the two data sets
data = [
    # Coarse data set
    {
        "x": x1_range,
        "Coarse": [x*x for x in x1_range]
    },
    # Fine data set
    {
        "x": x2_range,
        "Fine": [x*x for x in x2_range]
    }
]

As you can see, the array data has two elements:

  • At index 0: A dictionary that has two properties: "x" is set to the array [-10, -8, ..., 8, 10], which is the x range for the coarse plot, and "y" is the series of all these values, squared.
    Both these arrays hold 11 elements.
  • At index 1: A dictionary that has two properties: "x" is set to the array [-4.0, -3.9, ..., 3.9, 4.0], which is the x range for the fine plot, and "y" is the series of all these values, squared, just like in the first element.
    Both these arrays hold 81 elements.

To use these series in the chart control, the x and y properties must indicate which trace they refer to and be set with the indexed data set syntax as shown below:

Definition

<|{data}|chart|x[1]=0/x|y[1]=0/Coarse|x[2]=1/x|y[2]=1/Fine|>
<taipy:chart x[1]="0/x" y[1]="0/Coarse" x[2]="1/x" y[2]="1/Fine">{data}</taipy:chart>
import taipy.gui.builder as tgb
...
tgb.chart("{data}", x__1="0/x", y__1="0/Coarse", x__2="1/x", y__2="1/Fine")

Trace number 1 (the Coarse trace) is defined by x[1] and y[1], and uses the series data[0]["x"] and data[0]["Coarse"];
Trace number 2 (the Fine trace) is defined by x[2] and y[2], and uses the series data[1]["x"] and data[1]["Fine"].

Here is what the resulting plot looks like:

Unbalanced data sets

Adding annotations

You can download the entire source code used in this section from the GitHub repository.

You can add text annotations on top of a chart using the annotations property of the dictionary set to the layout property of the chart control.
These annotations can be connected to an arrow that points to an exact location in the chart coordinates system, and have many customization capabilities. You may look at the documentation on the annotations documentation page on Plotly's website.

Here is an example demonstrating how you can add annotations to your chart controls:

# Function to plot: x^3/3 - x
def f(x):
    return x*x*x/3-x

# x values: [-2.2, ..., 2.2]
x = [(x-10)/4.5 for x in range(0, 21)]

data = {
    "x": x,
    # y: [f(-2.2), ..., f(2.2)]
    "y": [f(x) for x in x]
}

layout = {
    # Chart title
    "title": "Local extrema",
    "annotations": [
        # Annotation for local maximum (x = -1)
        {
            "text": "Local <b>max</b>",
            "font": {
                "size": 20
            },
            "x": -1,
            "y": f(-1)
        },
        # Annotation for local minimum (x = 1)
        {
            "text": "Local <b>min</b>",
            "font": {
                "size": 20
            },
            "x": 1,
            "y": f(1),
            "xanchor": "left"
        }
    ]
}

The important part is the definition of the layout object, the rest of the code being very standard.
You can see how we create a dictionary for every annotation we want to add, including the text information, location, font setting, etc.

Adding the annotations to the chart control is a matter of setting the layout property of the chart control to that object:

Definition

<|{data}|chart|layout={layout}|>
<taipy:chart layout="{layout}">{data}</taipy:chart>
import taipy.gui.builder as tgb
...
tgb.chart("{data}", layout="{layout}")

Here is how the annotated chart control shows on the page:

Text annotations

Adding shapes

You can download the entire source code used in this section from the GitHub repository.

Shapes can be rendered on top of a chart at defined locations.
To create shapes, you must add shape descriptors to the shapes property of the dictionary set to the layout property of the chart control.
The documentation for shape descriptors can be found in the shapes documentation page on Plotly's website.

Here is some code that creates shapes on top of a chart control:

# Function to plot: x^3/3-x
def f(x):
    return x*x*x/3-x

# x values: [-2.2, ..., 2.2]
x = [(x-10)/4.5 for x in range(0, 21)]

data = {
    "x": x,
    # y: [f(-2.2), ..., f(2.2)]
    "y": [f(x) for x in x]
}

shape_size = 0.1

layout = {
    "shapes": [
        # Shape for local maximum (x = -1)
        {
            "x0": -1-shape_size,
            "y0": f(-1)-2*shape_size,
            "x1": -1+shape_size,
            "y1": f(-1)+2*shape_size,
            "fillcolor": "green",
            "opacity": 0.5
        },
        # Shape for local minimum (x = 1)
        {
            "x0": 1-shape_size,
            "y0": f(1)-2*shape_size,
            "x1": 1+shape_size,
            "y1": f(1)+2*shape_size,
            "fillcolor": "red",
            "opacity": 0.5
        }
    ]
}

The part you should be looking at is the setting of the property shapes in the layout dictionary: each shape is defined as a dictionary that holds all the parameters that configure how and where to render it.
Note how we indicate the size of the shape, using the shape_size variable.

Here is the definition of the chart control:

Definition

<|{data}|chart|layout={layout}|>
<taipy:chart layout="{layout}">{data}</taipy:chart>
import taipy.gui.builder as tgb
...
tgb.chart("{data}", layout="{layout}")

And here is what the resulting chart looks like:

Shapes on chart

Tracking selection

You can download the entire source code used in this section from the GitHub repository.

The chart control has the selected property that can be used to track data point selection in the chart control.
This property is dynamic and indexed; the bound variable holds an array of point indexes. It is updated when the user selects points on the chart.

Here is some code that calculates the mean of the values of the selected points of a chart control:

# x = [0..20]
x = list(range(0, 21))

data = {
    "x": x,
    # A list of random values within [1, 10]
    "y": [random.uniform(1, 10) for _ in x]
}

layout = {
    # Force the Box select tool
    "dragmode": "select",
    # Remove all margins around the plot
    "margin": {"l":0,"r":0,"b":0,"t":0}
}

config = {
    # Hide Plotly's mode bar
    "displayModeBar": False
}

selected_indices = []

mean_value = 0.

def on_change(state, var, val):
    if var == "selected_indices":
        state.mean_value = numpy.mean([data["y"][idx] for idx in val]) if len(val) else 0

The part you should be looking at is the use of the default callback property on_change: it detects the changes in selected_indices and calculates the mean, which updates the text in the title.

We also create two objects (layout and config) used to remove the mode bar above the chart and force the Box select tool.

Here is the definition of the chart and text controls:

Definition

## Mean of <|{len(selected_indices)}|raw|> selected points: <|{mean_value}|format=%.2f|raw|>

<|{data}|chart|selected={selected_indices}|layout={layout}|plot_config={config}|>
<h2>Mean of
  <taipy:text raw="true">{len(selected_indices)}</taipy:text>
  selected points:
  <taipy:text format="%.2f" raw="true">{mean_value}</taipy:text>
  </h2>

<taipy:chart selected="{selected_indices}" layout="{layout}"
    plot_config="{config}">{data}</taipy:chart>
import taipy.gui.builder as tgb
...
tgb.chart("{data}", selected="{selected_indices}", layout="{layout}", plot_config="{config}")

And here is what the resulting page looks like:

Data points selection

Using the Ploty Python library

You can download the entire source code used in this section from the GitHub repository.

The chart control of Taipy GUI lets you create graphs that can be more or less complex using the control's properties. There are situations, though, when it would be more straightforward to code the chart using the Plotly Open Source Graphing Library for Python or even the high-level Ploty Express API.

An obvious use case where you would want to do that is when you were able to find a code sample based on Plotly Python that looks very similar to what you want to achieve with Taipy GUI, and you don't want to rewrite the sample using the chart's properties. It would be really easier if a copy-paste of the sample code suffice.
The figure property is available to do precisely that: create the figure (an instance of plotly.graph_objects.Figure) and set it as the figure property value. The resulting chart will be exactly what you expect.

Note that thanks for figure being a dynamic property, you can use Python to generate a new graph and have your page update automatically.

Here is an example of injecting a figure defined using the Plotly Python API in a Taipy GUI page.
We want to create three (violin plots)[https://plotly.com/python/violin/] representing three different probability distributions available in the (Numpy)[https://numpy.org/] package. A single figure will be created, to which we will add three traces: one for each probability distribution we want to represent.

Here is the code that does just that:

import numpy as np
import plotly.graph_objects as go

from taipy.gui import Gui

# Create the Plotly figure object
figure = go.Figure()

# Add trace for Normal Distribution
figure.add_trace(go.Violin(name="Normal",
                           y=np.random.normal(loc=0, scale=1, size=1000),
                           box_visible=True, meanline_visible=True))
# Add trace for Exponential Distribution
figure.add_trace(go.Violin(name="Exponential",
                           y=np.random.exponential(scale=1, size=1000),
                           box_visible=True, meanline_visible=True))
# Add trace for Uniform Distribution
figure.add_trace(go.Violin(name="Uniform",
                           y=np.random.uniform(low=0, high=1, size=1000),
                           box_visible=True, meanline_visible=True))

# Updating layout for better visualization
figure.update_layout(title="Different Probability Distributions")

To have the chart control directly use this Plotly figure, all you must do is set the figure property to the object you have just created:

Definition

<|chart|figure={figure}|>
<taipy:chart figure="{figure}"/>
import taipy.gui.builder as tgb
...
tgb.chart(figure="{figure}")

When the page is displayed, the Plotly magic operates:

Plotly Python figure