While attempting to create a radar chart with both matplotlib and plotly, I have been trying to figure out a way to calculate the area of each dataset as represented on the chart. This would help me holistically evaluate a dataset’s effectiveness compared to the variables I have assigned by assigning a “score,” which would be equivalent to the area of the radar chart.
For example, in the figure below there are two datasets plotted on the radar chart, each representing a different object being evaluated based on the criteria/characteristics in the axes. I want to calculate the total area being covered by the polygon to assign each a “total score” that would serve as another metric.
Does anyone know how to do this?
Advertisement
Answer
- have used multiple-trace-radar-chart sample as indicated by your image
- start by extracting data back out of figure into a dataframe
- calculate theta in radians so basic trig can be used
- use basic trig to calculate x and y co-ordinates of points
JavaScript
x
7
1
# convert theta to be in radians
2
df["theta_n"] = pd.factorize(df["theta"])[0]
3
df["theta_radian"] = (df["theta_n"] / (df["theta_n"].max() + 1)) * 2 * np.pi
4
# work out x,y co-ordinates
5
df["x"] = np.cos(df["theta_radian"]) * df["r"]
6
df["y"] = np.sin(df["theta_radian"]) * df["r"]
7
r | theta | trace | theta_n | theta_radian | x | y | |
---|---|---|---|---|---|---|---|
0 | 1 | processing cost | Product A | 0 | 0 | 1 | 0 |
1 | 5 | mechanical properties | Product A | 1 | 1.25664 | 1.54508 | 4.75528 |
2 | 2 | chemical stability | Product A | 2 | 2.51327 | -1.61803 | 1.17557 |
3 | 2 | thermal stability | Product A | 3 | 3.76991 | -1.61803 | -1.17557 |
4 | 3 | device integration | Product A | 4 | 5.02655 | 0.927051 | -2.85317 |
0 | 4 | processing cost | Product B | 0 | 0 | 4 | 0 |
1 | 3 | mechanical properties | Product B | 1 | 1.25664 | 0.927051 | 2.85317 |
2 | 2.5 | chemical stability | Product B | 2 | 2.51327 | -2.02254 | 1.46946 |
3 | 1 | thermal stability | Product B | 3 | 3.76991 | -0.809017 | -0.587785 |
4 | 2 | device integration | Product B | 4 | 5.02655 | 0.618034 | -1.90211 |
- now if you have knowledge of shapely it’s simple to construct polygons from these points. From this polygon the
area
JavaScript
1
4
1
df_a = df.groupby("trace").apply(
2
lambda d: shapely.geometry.MultiPoint(list(zip(d["x"], d["y"]))).convex_hull.area
3
)
4
trace | 0 |
---|---|
Product A | 13.919 |
Product B | 15.2169 |
full MWE
JavaScript
1
49
49
1
import numpy as np
2
import pandas as pd
3
import shapely.geometry
4
import plotly.graph_objects as go
5
6
categories = ['processing cost','mechanical properties','chemical stability',
7
'thermal stability', 'device integration'] # fmt: skip
8
9
fig = go.Figure()
10
11
fig.add_trace(
12
go.Scatterpolar(
13
r=[1, 5, 2, 2, 3], theta=categories, fill="toself", name="Product A"
14
)
15
)
16
fig.add_trace(
17
go.Scatterpolar(
18
r=[4, 3, 2.5, 1, 2], theta=categories, fill="toself", name="Product B"
19
)
20
)
21
22
fig.update_layout(
23
polar=dict(radialaxis=dict(visible=True, range=[0, 5])),
24
# showlegend=False
25
)
26
27
# get data back out of figure
28
df = pd.concat(
29
[
30
pd.DataFrame({"r": t.r, "theta": t.theta, "trace": np.full(len(t.r), t.name)})
31
for t in fig.data
32
]
33
)
34
# convert theta to be in radians
35
df["theta_n"] = pd.factorize(df["theta"])[0]
36
df["theta_radian"] = (df["theta_n"] / (df["theta_n"].max() + 1)) * 2 * np.pi
37
# work out x,y co-ordinates
38
df["x"] = np.cos(df["theta_radian"]) * df["r"]
39
df["y"] = np.sin(df["theta_radian"]) * df["r"]
40
41
# now generate a polygon from co-ordinates using shapely
42
# then it's a simple case of getting the area of the polygon
43
df_a = df.groupby("trace").apply(
44
lambda d: shapely.geometry.MultiPoint(list(zip(d["x"], d["y"]))).convex_hull.area
45
)
46
47
# let's use the areas in the name of the traces
48
fig.for_each_trace(lambda t: t.update(name=f"{t.name} {df_a.loc[t.name]:.1f}"))
49