A radial beeswarm or dot plot.
Given this 100 most popular movies on IMDb:
... we can render the following radar:
Install via npm
:
npm install @gramex/radar@1
Use locally as an ES module:
<script type="module">
import { radar } from "./node_modules/@gramex/radar/dist/radar.js";
</script>
Use locally as a script:
<script src="./node_modules/@gramex/radar/dist/radar.min.js"></script>
<script>
gramex.radar(...)
</script>
Use via CDN as an ES Module:
<script type="module">
import { radar } from "https://cdn.jsdelivr.net/npm/@gramex/radar@1";
</script>
Use via CDN as a script:
<script src="https://cdn.jsdelivr.net/npm/@gramex/radar@1/dist/radar.min.js"></script>
<script>
gramex.radar(...)
</script>
The data is a list of rows containing an ordered "level" category (going outwards) and an unordered "spoke" category . Each row is an object with keys for each column. For example:
const data = [
{ level: "low", spoke: "blue" },
{ level: "low", spoke: "orange" },
{ level: "low", spoke: "green" },
{ level: "medium", spoke: "blue" },
{ level: "medium", spoke: "orange" },
{ level: "medium", spoke: "green" },
{ level: "high", spoke: "blue" },
{ level: "high", spoke: "orange" },
{ level: "high", spoke: "green" },
];
Calling radar(el, { data })
will render the radar. You need to style the output.
Use level
and spoke
parameters to customize the columns used for levels and spokes. For example, this
employees data has:
team | band |
---|---|
Analytics | Band 1 |
Consulting | Band 2 |
Design | Band 3 |
Engineering | Band 4 |
... | ... |
We use the level
and spoke
parameters to specify the columns:
const graph = radar("#radar", {
data,
level: (d) => d.band.replace(/Band /, ""),
spoke: (d) => d.team,
...
});
The radar automatically adjusts the
See how to specify level and spoke columns
Radar auto-extracts levels and spokes from data. Use levels
and spokes
to:
- Show only specific levels and spokes (even if they don't exist in the data)
- Re-order levels and spokes
For example:
const graph = radar("#radar", {
data,
// Show only the top 3 levels, in descending order
levels: ["6", "5", "4"],
// Show these levels in order, even though Legal doesn't exist
spokes: ["Design", "Consulting", "Engineering", "Legal"],
...
});
You can specificy levels
and spokes
dynamically with a function Map(key, count) => [...keys]
. For example:
const graph = radar("#radar", {
data,
// Sort levels in reverse (e.g. "6", "5", "4", ... "1")
levels: (counter) => [...counter.keys()].sort(d3.descending),
// Sort spokes in descending order of count, i.e. largest first
spokes: (counter) => d3.sort([...counter.keys()], d => -counter.get(d)),
...
});
See how to customize levels and spokes
Radar distributes levels and spokes evenly within the container circle. Use levelScale
and spokeScale
to:
- Create spacing between levels or spokes
- Give more space to specific levels or spokes
For example:
// Start and end radius in SVG units (pixels)
const levelRange = [[0, 60], [66, 120], [125, 140], [145, 160], [165, 180], [185, 200]];
// Start and end angle in degrees
const spokeRange = [[180, 210], [210, 240], [240, 255], [255, 310], [310, 330], [330, 360]]
const graph = radar("#radar", {
data,
levelScale: (level, index) => levelRange[index],
spokeScale: (spoke, index) => spokeRange[index],
...
});
You can also specify the center of the radar using cx
and cy
. It defaults to width / 2
and height / 2
.
const graph = radar("#radar", {
data,
cx: 100,
cy: 100,
...
});
Use graph.r
to get the maximum non-overlapping radius of the nodes.
See how to resize levels and spokes
The returned object has the following D3 selections:
-
cells
: Arc paths for each level, spoke segment. (Used to style the background of the radar). The data includes:-
level
: The level -
spoke
: The spoke -
levelIndex
: The index of the level (0, 1, 2, ...) -
spokeIndex
: The index of the spoke (0, 1, 2, ...) -
r0
: The inner radius of the spoke -
r1
: The outer radius of the spoke -
a0
: The start angle of the spoke in radians -
a1
: The end angle of the spoke in radians -
n
: Number of data points in this cell -
data
: All data elements for this cell (i.e. level and spoke combination). Absent if n == 0
-
-
nodes
: Circles for each data element. The data includes:-
x
: The x-coordinate of the node -
y
: The y-coordinate of the node
-
-
levelLabels
: Text labels for each level. The data includes:-
level
: The level name / label -
levelIndex
: The index of the level (0, 1, 2, ...) -
r0
: The inner radius of the spoke -
r1
: The outer radius of the spoke
-
-
spokeLabels
: Text labels for each spoke. The data includes:-
spoke
: The spoke name / label -
spokeIndex
: The index of the spoke (0, 1, 2, ...) -
a0
: The start angle of the spoke in radians -
a1
: The end angle of the spoke in radians
-
You can style these using D3 selections. For example:
// Color empty background cells pink, and filled cells white
graph.cells.attr("fill", (d) => (d.n ? "white" : "pink"));
// Color nodes with see-through black. Give them a bit more than the maximum non-overlapping size
graph.nodes.attr("fill", "rgba(0,0,0,0.2)").attr("r", graph.r * 1.5);
// Make labels bigger and red
graph.levelLabels.attr("font-size", 12).attr("fill", "red");
graph.spokeLabels.attr("font-size", 12).attr("fill", "red");
You can use Bootstrap tooltips.
- Add a
data-bs-toggle="tooltip" title="..."
attribute to each feature usingupdate
- Call
new bootstrap.Tooltip(element, {selector: '[data-bs-toggle="tooltip"]'})
to initialize tooltips
If you already have D3 loaded, or want to use a specific version / instance of D3, pass it to radar(el, { d3 })
:
- 1.0.0: 24 Mar 2024. Initial release
- Anand S s.anand@gramener.com