Skip to content

Preserving Categorical Data Order

This example demonstrates how to preserve the order of categorical data when creating charts. By default, Observable Plot sorts categorical values alphabetically, but you can override this behavior using explicit domain specification.

Problem: Alphabetical Sorting

Workflow Status Example (Wrong Order)

Without specifying a domain, the categories will be sorted alphabetically:

View Source
data:
  source: [
    {"status": "todo", "count": 12, "priority": "normal"},
    {"status": "in-progress", "count": 8, "priority": "normal"},
    {"status": "done", "count": 25, "priority": "normal"}
  ]
type: bar
x: status
y: count
title: "Task Status (Alphabetically Sorted - Wrong Order)"
fill: steelblue
height: 300

Result: Categories appear as "done", "in-progress", "todo" ❌

This breaks the natural workflow progression!


Solution: Explicit Domain

Workflow Status Example (Correct Order)

Specify the domain to preserve the intended order:

View Source
data:
  source: [
    {"status": "todo", "count": 12, "priority": "normal"},
    {"status": "in-progress", "count": 8, "priority": "normal"},
    {"status": "done", "count": 25, "priority": "normal"}
  ]
type: bar
x: status
y: count
scales:
  x:
    domain: ["todo", "in-progress", "done"]
title: "Task Status (Correct Workflow Order)"
fill: orange
height: 300

Result: Categories appear as "todo", "in-progress", "done" ✓

Perfect! The workflow progression makes sense.


Common Use Cases

Time Periods (Quarters)

View Source
data:
  source: [
    {"quarter": "Q1", "revenue": 125000},
    {"quarter": "Q2", "revenue": 145000},
    {"quarter": "Q3", "revenue": 135000},
    {"quarter": "Q4", "revenue": 165000}
  ]
type: bar
x: quarter
y: revenue
scales:
  x:
    domain: ["Q1", "Q2", "Q3", "Q4"]
title: "Quarterly Revenue (Correct Temporal Order)"
fill: teal
height: 300

Priority Levels

View Source
data:
  source: [
    {"priority": "P0", "count": 3},
    {"priority": "P1", "count": 8},
    {"priority": "P2", "count": 15},
    {"priority": "P3", "count": 22}
  ]
type: bar
x: priority
y: count
scales:
  x:
    domain: ["P0", "P1", "P2", "P3"]
title: "Issues by Priority Level"
fill: crimson
height: 300

Ordinal Rankings

View Source
data:
  source: [
    {"severity": "low", "incidents": 45},
    {"severity": "medium", "incidents": 28},
    {"severity": "high", "incidents": 12},
    {"severity": "critical", "incidents": 3}
  ]
type: bar
x: severity
y: incidents
scales:
  x:
    domain: ["low", "medium", "high", "critical"]
title: "Security Incidents by Severity"
fill: purple
height: 300

Multi-Mark Charts

For multi-mark charts, set the domain at the top-level scales:

View Source
data:
  source: [
    {"month": "Jan", "target": 100, "actual": 95},
    {"month": "Feb", "target": 110, "actual": 108},
    {"month": "Mar", "target": 105, "actual": 112},
    {"month": "Apr", "target": 115, "actual": 118}
  ]
scales:
  x:
    domain: ["Jan", "Feb", "Mar", "Apr"]
marks:
  - type: bar
    x: month
    y: actual
    fill: lightblue
  - type: line
    x: month
    y: target
    stroke: red
    strokeWidth: 2
    marker: dot
title: "Monthly Performance: Target vs Actual"
height: 350

Advanced: Using Object-Based Axis Configuration

You can also set the domain directly in the axis configuration:

View Source
data:
  source: [
    {"stage": "backlog", "items": 42},
    {"stage": "planning", "items": 15},
    {"stage": "development", "items": 23},
    {"stage": "testing", "items": 8},
    {"stage": "deployed", "items": 67}
  ]
type: bar
x:
  field: stage
  type: band
  domain: ["backlog", "planning", "development", "testing", "deployed"]
  label: "Development Stage"
y: items
title: "Items by Development Stage"
fill: mediumseagreen
height: 350

Line Charts with Categorical X-Axis

The same principle applies to line charts:

View Source
data:
  source: [
    {"phase": "alpha", "users": 50},
    {"phase": "beta", "users": 250},
    {"phase": "rc", "users": 800},
    {"phase": "ga", "users": 5000}
  ]
type: line
x: phase
y: users
scales:
  x:
    domain: ["alpha", "beta", "rc", "ga"]
title: "User Growth Through Release Phases"
stroke: darkblue
strokeWidth: 3
marker: dot
height: 350

Key Takeaways

  1. Default Behavior: Observable Plot sorts categorical data alphabetically
  2. Use scales.x.domain or scales.y.domain to preserve your intended order
  3. Common scenarios:
    • Workflow stages (todo → in-progress → done)
    • Time periods (Q1 → Q2 → Q3 → Q4)
    • Rankings (low → medium → high)
    • Release phases (alpha → beta → rc → ga)
  4. Multi-mark charts: Set domain at top-level scales configuration
  5. Object-based axes: Set domain directly in axis configuration

Released under the MIT License. Built by Boundary Lab.