Radar Charts with create_radar()¶
create_radar() produces a multi-group radar (spider) chart that compares several metrics simultaneously across different segments of your workforce. The key steps inside the function are:
Person-level aggregation — averages (or medians) each metric per person per group.
Group-level aggregation — summarises person-level values to one row per group.
Privacy filtering — drops groups below
mingroupunique persons.Optional indexing — rescales values so groups are easy to compare visually.
Three exported functions cover the full workflow:
Function |
What it does |
|---|---|
|
Computes and indexes the group-level table |
|
Renders the radar chart from a pre-computed table |
|
End-to-end wrapper (calc + viz) |
[ ]:
import warnings
warnings.filterwarnings('ignore')
import vivainsights as vi
pq_data = vi.load_pq_data()
Basic usage¶
Pass a data frame, a list of metric columns, and a grouping column (hrvar). By default the chart indexes each metric so that the overall population mean equals 100.
[ ]:
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=5,
)
Each polygon represents one group. A value of 100 on any axis means that group sits exactly at the population average for that metric; values above 100 indicate above-average activity and values below 100 indicate below-average activity.
Returning the underlying table¶
Set return_type="table" to get the indexed values as a data frame instead:
[ ]:
radar_tbl = vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=5,
return_type="table",
)
radar_tbl
Indexing modes¶
The index_mode parameter controls how metric values are rescaled before plotting.
"total" (default)¶
Each metric is divided by the overall population mean and multiplied by 100. A group at 100 matches the overall average.
[ ]:
vi.create_radar(
data=pq_data,
metrics=[
"Collaboration_hours",
"Email_hours",
"Meeting_and_call_hours",
"After_hours_collaboration_hours",
],
hrvar="Organization",
mingroup=5,
index_mode="total",
)
"ref_group" — benchmark against one group¶
Set index_ref_group to the name of the group that should equal 100 on every axis. All other groups are expressed relative to it.
[ ]:
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=5,
index_mode="ref_group",
index_ref_group="Finance",
)
"minmax" — relative spread within observed groups¶
Each metric is scaled so the lowest group maps to 0 and the highest to 100. This maximises visual contrast and is useful when absolute levels matter less than which group is highest or lowest.
[ ]:
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=5,
index_mode="minmax",
)
"none" — raw group values¶
No rescaling is applied. The axes carry the original units. Since metrics may be on very different scales this mode is most useful when inspecting the table rather than the chart.
[ ]:
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=5,
index_mode="none",
return_type="table",
)
Aggregation method¶
The default two-step aggregation uses the mean at both the person level and the group level. Switch to agg="median" for robustness against outliers.
[ ]:
vi.create_radar(
data=pq_data,
metrics=[
"Collaboration_hours",
"Email_hours",
"Meeting_and_call_hours",
"After_hours_collaboration_hours",
],
hrvar="Organization",
mingroup=5,
agg="median",
)
Grouping by a different HR variable¶
Any character column can serve as the grouping variable. Here we compare collaboration profiles by LevelDesignation:
[ ]:
vi.create_radar(
data=pq_data,
metrics=[
"Collaboration_hours",
"Email_hours",
"Meeting_and_call_hours",
"Internal_network_size",
],
hrvar="LevelDesignation",
mingroup=5,
)
Privacy filtering with mingroup¶
Groups with fewer than mingroup unique persons are silently dropped before plotting. Raise the threshold to be more conservative:
[ ]:
# Only show groups with 20 or more unique persons
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"],
hrvar="Organization",
mingroup=20,
return_type="table",
)
Handling missing values¶
By default (dropna=False) the function retains rows with NaN metric values and lets the aggregation handle them. Set dropna=True to drop any row containing a NaN in any of the requested metrics before aggregation — this is equivalent to a complete-cases analysis.
[ ]:
vi.create_radar(
data=pq_data,
metrics=["Collaboration_hours", "Email_hours"],
hrvar="Organization",
mingroup=5,
dropna=True,
return_type="table",
)
Using return_type to retrieve the table or figure¶
create_radar() accepts a return_type argument:
return_type="table"— returns the indexed group-level DataFrame so you can inspect or export the numbers.return_type="plot"(default) — returns the matplotlib Figure, which you can customise further before displaying.
There is no need to call create_radar_calc or create_radar_viz directly in typical usage.
[ ]:
METRICS = ["Collaboration_hours", "Email_hours", "Meeting_and_call_hours"]
table = vi.create_radar(
data=pq_data,
metrics=METRICS,
hrvar="Organization",
mingroup=5,
index_mode="total",
return_type="table",
)
print("Indexed group-level table:")
table
[ ]:
vi.create_radar(
data=pq_data,
metrics=METRICS,
hrvar="Organization",
mingroup=5,
index_mode="total",
title="Behavioral Profiles by Organisation (Indexed)",
)