{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Radar Charts with `create_radar()`\n", "\n", "`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:\n", "\n", "1. **Person-level aggregation** — averages (or medians) each metric per person per group.\n", "2. **Group-level aggregation** — summarises person-level values to one row per group.\n", "3. **Privacy filtering** — drops groups below `mingroup` unique persons.\n", "4. **Optional indexing** — rescales values so groups are easy to compare visually.\n", "\n", "Three exported functions cover the full workflow:\n", "\n", "| Function | What it does |\n", "|---|---|\n", "| `create_radar_calc()` | Computes and indexes the group-level table |\n", "| `create_radar_viz()` | Renders the radar chart from a pre-computed table |\n", "| `create_radar()` | End-to-end wrapper (calc + viz) |" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "import vivainsights as vi\n", "\n", "pq_data = vi.load_pq_data()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic usage\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "### Returning the underlying table\n", "\n", "Set `return_type=\"table\"` to get the indexed values as a data frame instead:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "radar_tbl = vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " return_type=\"table\",\n", ")\n", "\n", "radar_tbl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Indexing modes\n", "\n", "The `index_mode` parameter controls how metric values are rescaled before plotting.\n", "\n", "### `\"total\"` (default)\n", "\n", "Each metric is divided by the overall population mean and multiplied by 100. A group at 100 matches the overall average." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\n", " \"Collaboration_hours\",\n", " \"Email_hours\",\n", " \"Meeting_and_call_hours\",\n", " \"After_hours_collaboration_hours\",\n", " ],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " index_mode=\"total\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `\"ref_group\"` — benchmark against one group\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " index_mode=\"ref_group\",\n", " index_ref_group=\"Finance\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `\"minmax\"` — relative spread within observed groups\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " index_mode=\"minmax\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `\"none\"` — raw group values\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " index_mode=\"none\",\n", " return_type=\"table\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Aggregation method\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\n", " \"Collaboration_hours\",\n", " \"Email_hours\",\n", " \"Meeting_and_call_hours\",\n", " \"After_hours_collaboration_hours\",\n", " ],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " agg=\"median\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Grouping by a different HR variable\n", "\n", "Any character column can serve as the grouping variable. Here we compare collaboration profiles by `LevelDesignation`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\n", " \"Collaboration_hours\",\n", " \"Email_hours\",\n", " \"Meeting_and_call_hours\",\n", " \"Internal_network_size\",\n", " ],\n", " hrvar=\"LevelDesignation\",\n", " mingroup=5,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Privacy filtering with `mingroup`\n", "\n", "Groups with fewer than `mingroup` unique persons are silently dropped before plotting. Raise the threshold to be more conservative:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Only show groups with 20 or more unique persons\n", "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=20,\n", " return_type=\"table\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Handling missing values\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n", " data=pq_data,\n", " metrics=[\"Collaboration_hours\", \"Email_hours\"],\n", " hrvar=\"Organization\",\n", " mingroup=5,\n", " dropna=True,\n", " return_type=\"table\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n\n## Using `return_type` to retrieve the table or figure\n\n`create_radar()` accepts a `return_type` argument:\n\n- `return_type=\"table\"` — returns the indexed group-level DataFrame so you can inspect or export the numbers.\n- `return_type=\"plot\"` (default) — returns the matplotlib Figure, which you can customise further before displaying.\n\nThere is no need to call `create_radar_calc` or `create_radar_viz` directly in typical usage." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "METRICS = [\"Collaboration_hours\", \"Email_hours\", \"Meeting_and_call_hours\"]\n\ntable = vi.create_radar(\n data=pq_data,\n metrics=METRICS,\n hrvar=\"Organization\",\n mingroup=5,\n index_mode=\"total\",\n return_type=\"table\",\n)\n\nprint(\"Indexed group-level table:\")\ntable" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vi.create_radar(\n data=pq_data,\n metrics=METRICS,\n hrvar=\"Organization\",\n mingroup=5,\n index_mode=\"total\",\n title=\"Behavioral Profiles by Organisation (Indexed)\",\n)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.13.0" }, "nbsphinx": { "execute": "never" } }, "nbformat": 4, "nbformat_minor": 5 }