Polar charts¶

Polar charts are a common variation of circular graphs. They are used when relationships between data points can be better visualized in terms of radiuses and angles.

In a polar chart, a series is represented by a trace connecting points in the polar coordinate system. Each data point is determined by the distance from the pole (the radial coordinate) and the angle from the origin direction (the angular coordinate).

To create polar charts with Taipy, you must set the type property of the chart control to "scatterpolar", and set the r and theta properties to the data you want to display.

Key properties¶

Name Value Notes
type `scatterpolar`
theta angular values
options dictionary `subplot` is used to set the name of a trace so it can be used in the layout property.
`fill` can be used to fill traces.
layout dictionary `showlegend` can be set to False to hide the legend.
`polar*` properties can specify specific layout parameters for a specific trace (see options.subplot).

Examples¶

Simple polar chart¶

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

A typical use case for polar charts is when you have a parametric equation to represent, based on an angular value.

Here is an example of such a chart. We are creating a parametric curve that will result in a nice-looking polar chart:

``````# One data point for each degree
theta = range(0, 360)

# Parametric equation that draws a shape (source Wolfram Mathworld)
def draw_heart(angle):
sa = math.sin(a)
return 2-2*sa+sa*(math.sqrt(math.fabs(math.cos(a)))/(sa+1.4))

data = {
# Create the heart shape
"r": [draw_heart(angle) for angle in theta],
"theta": theta
}
``````

The data object is built with a linear range of values for theta and a computed series of r values.

We can use this object in the definition of the chart we want to display:

Definition

``````<|{data}|chart|type=scatterpolar|mode=lines|>
``````
``````<taipy:chart type="scatterpolar" mode="lines">{data}</taipy:chart>
``````
``````import taipy.gui.builder as tgb
...
tgb.chart("{data}", type="scatterpolar", mode="lines")
``````

We don't need to explicitly set the r and theta properties because data has two data sets, with the respective names.

Note that we set mode to "lines" to keep only the lines of the trace and no markers on every data point.

Here is the result:

Areas¶

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

Filling an area in a polar chart is only a matter of setting the fill property of the value assigned to the options property of the chart control.

Here is an example that uses the same data set (in data) as in the previous example:

``````options = {
"fill": "toself"
}

layout = {
# Hide the legend
"showlegend": False,
"polar": {
# Hide the angular axis
"angularaxis": {
"visible": False
},
"visible": False
}
}
}
``````

The options object forces the trace to be filled.
layout is configured to remove all axis information as well as the legend.

The chart control definition is:

Definition

``````<|{data}|chart|type=scatterpolar|mode=none|layout={layout}|options={options}|>
``````
``````<taipy:chart type="scatterpolar" mode="none" layout="{layout}" options="{options}">{data}</taipy:chart>
``````
``````import taipy.gui.builder as tgb
...
tgb.chart("{data}", type="scatterpolar", mode="none", layout="{layout}", options="{options}")
``````

Here is the resulting rendering:

Multiple traces¶

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

Several traces can be displayed in the same polar chart. The r indexed property can refer to different data.

Here is how to create several traces:

``````# One data point for each degree
theta = range(0, 360)

# Create a rose-like shaped radius-array
def create_rose(n_petals):
return [math.cos(math.radians(n_petals*angle)) for angle in theta]

data = {
"theta": theta,
"r1": create_rose(2),
"r2": create_rose(3),
"r3": create_rose(4)
}

# We want three traces in the same chart
r = ["r1", "r2", "r3"]

layout = {
# Hide the legend
"showlegend": False,
"polar": {
# Hide the angular axis
"angularaxis": {
"visible": False
},
"visible": False
}
}
}
``````

Three traces are used, stored in the array r.

The layout object ensures that only the traces will be rendered.

Here is the chart control definition:

Definition

``````<|{data}|chart|type=scatterpolar|mode=lines|r={r}|theta=theta|layout={layout}|>
``````
``````<taipy:chart type="scatterpolar" mode="lines" r="{r}" theta="theta" layout="{layout}">{data}</taipy:chart>
``````
``````import taipy.gui.builder as tgb
...
tgb.chart("{data}", type="scatterpolar", mode="lines", r="{r}", theta="theta", layout="{layout}")
``````

Here is what the chart looks like:

Specifying the angular range¶

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

It may be necessary not to display the entire 360° of the chart but instead select a range of angles to represent: a sector.

Here is some code that does that:

``````# Sample small plot definition
trace = {
"r": [1, 2, 3, 4, 1],
"theta": [0, 40, 80, 120, 160],
}

# The same data is used in both traces
data = [trace, trace]

# Naming the subplot is mandatory to get them both in
# the same chart
options = [
{
"subplot": "polar",
},
{
"subplot": "polar2"
}
]

layout = {
# Hide the legend
"showlegend": False,
# Restrict the angular values for second trace
"polar2": {
"sector": [30, 130]
},
}
``````

We use two traces: the first uses the complete circle, the second uses only a sector, as defined in the layout object.
Note that for the layout to be correctly applied, you need to name the subplots (as done in the options object) with names that start with "polar", then reference these names in the layout object.

The chart control definition can now be written:

Definition

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

Here are our two traces, gathered in the same chart:

Angular axis settings¶

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

The angular axis can be tuned to set its origin wherever needed. It can also be oriented, and the angular values can appear in different units.

Here is a demonstration of these capabilities:

``````# Create a star shape
data = {
"r": [3, 1] * 5 + [3],
"theta": list(range(0, 360, 36)) + [0]
}

options=[
# First plot is filled with a yellow-ish color
{
"subplot": "polar",
"fill": "toself",
"fillcolor": "#E4FF87"
},
# Second plot is filled with a blue-ish color
{
"subplot": "polar2",
"fill": "toself",
"fillcolor": "#709BFF"
}
]

layout = {
"polar": {
# This actually is the default value
"angularaxis": {
"direction": "counterclockwise",
},
},
"polar2": {
"angularaxis": {
# Rotate the axis 180° (0 is on the left)
"rotation": 180,
# Orient the axis clockwise
"direction": "clockwise",
# Show the angles as radians
},
},
# Hide the legend
"showlegend": False,
}
``````

The two traces have very different settings for their angular axis.

The chart control definition is straightforward:

Definition

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

The two traces appear in the chart, mirroring one another:

Setting tick texts¶

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

You can also customize what the tick texts are: what text is at what location on the axis.

Here is an example that uses a chart control to show the current time in a way that looks like a wall clock:

``````def generate_hand_shapes():
# Retrieve and store the current, local time
time_now = datetime.now()
hours = time_now.hour
minutes = time_now.minute
seconds = time_now.second

# Compute the angle that represents the hours
hours_angle = 360 * ((hours % 12) / 12 + (minutes % 60) / 60 / 60 + (seconds % 60) / 60 / 60 / 60)
# Short and thick hand for the hours
hours_hand = {
"r": [0, 4, 5, 4, 0],
"a": [0, hours_angle-7, hours_angle, hours_angle+7, 0]
}

# Compute the angle that represents the minutes
minutes_angle = 360 * ((minutes % 60) / 60 + (seconds % 60) / 60 / 60)
# Longer and slighly thinner hand for the minutes
minutes_hand = {
"r": [0, 6, 8, 6, 0],
"a": [0, minutes_angle-4, minutes_angle, minutes_angle+4, 0]
}

# Compute the angle that represents the seconds
seconds_angle = 360 * (seconds % 60) / 60
# Even longer and thinner hand for the seconds
seconds_hand = {
"r": [0, 8, 10, 8, 0],
"a": [0, seconds_angle-2, seconds_angle, seconds_angle+2, 0]
}
# Build and return the whole data set
return [
hours_hand,
minutes_hand,
seconds_hand
]

# Initialize the data set with the current time
data = generate_hand_shapes()

layout = {
"polar": {
"angularaxis": {
"rotation": 90,
"direction": "clockwise",
# One tick every 30 degrees
"tickvals": list(numpy.arange(0., 360., 30)),
# Text value for every tick
"ticktext": [
"XII", "I", "II", "III", "IV", "V",
"VI", "VII", "VIII", "IX", "X", "XI"
]
},
"angle": 90,
"visible": False,
"range": [0, 12]
}
},
"showlegend": False
}

# Options to be used for all three traces
base_opts = {"fill": "toself"}
# Specific for hours
hours_opts = dict(base_opts)
hours_opts["fillcolor"] = "#FF0000"
# Specific for minutes
minutes_opts = dict(base_opts)
minutes_opts["fillcolor"] = "#00FF00"
# Specific for seconds
seconds_opts = dict(base_opts)
seconds_opts["fillcolor"] = "#0000FF"

# Store all the chart control properties in a single object
properties = {
# Don't show data point markers
"mode": "lines",
# Data for the hours
"theta[1]": "0/a",
"r[1]": "0/r",
# Data for the minutes
"theta[2]": "1/a",
"r[2]": "1/r",
# Data for the seconds
"theta[3]": "2/a",
"r[3]": "2/r",
# Options for the three traces
"options[1]": hours_opts,
"options[2]": minutes_opts,
"options[3]": seconds_opts,
"line": {"color": "black"},
"layout": layout
}

# Update time on every refresh
def on_navigate(state, page):
state.data = generate_hand_shapes()
return page
``````

All the properties for the chart control are stored in the properties dictionary, so the chart control definition can be concise.

Note how we use the on_navigate() callback function to refresh the data so that every refresh of the display will compute and display the current time.

Here is the very simple chart control definition:

Definition

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

The chart control will display the current local time on every refresh: