Diverging Stacked Bar Charts
This example demonstrates how to create stacked bar charts for Likert-scale survey data using vertical bars. The original Observable Plot example (https://observablehq.com/@observablehq/plot-diverging-stacked-bar) uses horizontal bars with custom offset functions.
Note: These examples use vertical stacked bars (type: bar with stack: y) because Observable Plot's horizontal bar stacking (barX) requires different data structures that don't work well with pre-aggregated count data. For survey visualizations, vertical stacking provides equivalent functionality and follows the proven pattern from DataGlass's working examples.
Limitations and Workarounds
Features from the original Observable Plot example NOT currently supported in DataGlass:
Custom offset functions - The original uses a custom "Likert" offset that centers neutral responses around the midpoint. DataGlass only supports built-in offsets:
expand,center,normalize,wiggle,silhouette.groupZ transform with custom reducers - The original uses
Plot.groupZ({x: "count"}, {...})to automatically aggregate and count responses. DataGlass requires pre-aggregated data with explicit counts.Custom tick formatters - The original uses
x: {tickFormat: Math.abs}to show absolute values on both sides of zero. DataGlass uses standard tick formatting.
Workarounds available in DataGlass:
- Pre-aggregate your data to count responses per category
- Use
offset: "center"oroffset: "normalize"for symmetric/proportion effects - Use
offset: "expand"for 100% normalized stacks (same asnormalize) - Use faceting (
fy:) to show multiple survey questions as small multiples - Apply diverging color scales with manual color ranges to emphasize positive vs negative sentiment
Important Note: Observable Plot's barX automatically stacks bars horizontally when you provide a fill channel that creates groups. The offset parameter controls how the stacks are positioned.
Basic Survey Data Structure
For Likert-scale surveys, data should have:
- A question identifier (e.g., "Q1", "Q2")
- A response category ("Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree")
- A count of responses in each category
Example 1: Simple Vertical Stacked Bar (Pre-aggregated Data)
This shows a single survey question with response counts stacked vertically.
View Source
data:
source: [
{"question": "Q1", "response": "Strongly Disagree", "count": 12},
{"question": "Q1", "response": "Disagree", "count": 18},
{"question": "Q1", "response": "Neutral", "count": 15},
{"question": "Q1", "response": "Agree", "count": 32},
{"question": "Q1", "response": "Strongly Agree", "count": 23}
]
engine: plot
title: Employee Satisfaction Survey (Q1)
width: 800
height: 500
marks:
- type: bar
configuration:
x: question
y: count
fill: response
stack: y
tip: true
scales:
color:
type: ordinal
domain: ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"]
range: ["#d73027", "#fc8d59", "#fee090", "#91bfdb", "#4575b4"]
marginLeft: 80
marginRight: 50Example 2: Normalized Vertical Stacked Bar (100%)
Shows proportions to compare response distributions.
View Source
data:
source: [
{"question": "Q1: Job Satisfaction", "response": "Strongly Disagree", "count": 12},
{"question": "Q1: Job Satisfaction", "response": "Disagree", "count": 18},
{"question": "Q1: Job Satisfaction", "response": "Neutral", "count": 15},
{"question": "Q1: Job Satisfaction", "response": "Agree", "count": 32},
{"question": "Q1: Job Satisfaction", "response": "Strongly Agree", "count": 23},
{"question": "Q2: Work-Life Balance", "response": "Strongly Disagree", "count": 8},
{"question": "Q2: Work-Life Balance", "response": "Disagree", "count": 22},
{"question": "Q2: Work-Life Balance", "response": "Neutral", "count": 20},
{"question": "Q2: Work-Life Balance", "response": "Agree", "count": 28},
{"question": "Q2: Work-Life Balance", "response": "Strongly Agree", "count": 22},
{"question": "Q3: Career Growth", "response": "Strongly Disagree", "count": 15},
{"question": "Q3: Career Growth", "response": "Disagree", "count": 25},
{"question": "Q3: Career Growth", "response": "Neutral", "count": 18},
{"question": "Q3: Career Growth", "response": "Agree", "count": 24},
{"question": "Q3: Career Growth", "response": "Strongly Agree", "count": 18}
]
engine: plot
title: Employee Survey Responses (Normalized)
width: 800
height: 500
marks:
- type: bar
configuration:
x: question
y: count
fill: response
stack: y
offset: normalize
tip: true
scales:
color:
type: ordinal
domain: ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"]
range: ["#d73027", "#fc8d59", "#fee090", "#91bfdb", "#4575b4"]
y:
label: Proportion of Responses
marginLeft: 180
marginRight: 50
marginBottom: 80Example 3: Faceted Survey Questions
Shows multiple questions using small multiples with chart-level faceting.
View Source
engine: plot
title: Employee Engagement Survey Results
width: 800
height: 750
facet:
y: question
scales:
color:
type: ordinal
domain: ["Strongly Disagree", "Disagree", "Neutral", "Agree", "Strongly Agree"]
range: ["#d73027", "#fc8d59", "#fee090", "#91bfdb", "#4575b4"]
legend: true
x:
label: Number of Responses
marks:
- type: barX
configuration:
x: count
y: response
fill: response
tip: true
offset: normalize
marginLeft: 50
marginRight: 50
data:
source: [
{"question": "Job Satisfaction", "response": "Strongly Disagree", "count": 12},
{"question": "Job Satisfaction", "response": "Disagree", "count": 18},
{"question": "Job Satisfaction", "response": "Neutral", "count": 15},
{"question": "Job Satisfaction", "response": "Agree", "count": 32},
{"question": "Job Satisfaction", "response": "Strongly Agree", "count": 23},
{"question": "Work-Life Balance", "response": "Strongly Disagree", "count": 8},
{"question": "Work-Life Balance", "response": "Disagree", "count": 22},
{"question": "Work-Life Balance", "response": "Neutral", "count": 20},
{"question": "Work-Life Balance", "response": "Agree", "count": 28},
{"question": "Work-Life Balance", "response": "Strongly Agree", "count": 22},
{"question": "Management Quality", "response": "Strongly Disagree", "count": 10},
{"question": "Management Quality", "response": "Disagree", "count": 15},
{"question": "Management Quality", "response": "Neutral", "count": 22},
{"question": "Management Quality", "response": "Agree", "count": 30},
{"question": "Management Quality", "response": "Strongly Agree", "count": 23},
{"question": "Career Growth", "response": "Strongly Disagree", "count": 15},
{"question": "Career Growth", "response": "Disagree", "count": 25},
{"question": "Career Growth", "response": "Neutral", "count": 18},
{"question": "Career Growth", "response": "Agree", "count": 24},
{"question": "Career Growth", "response": "Strongly Agree", "count": 18},
{"question": "Company Culture", "response": "Strongly Disagree", "count": 6},
{"question": "Company Culture", "response": "Disagree", "count": 12},
{"question": "Company Culture", "response": "Neutral", "count": 16},
{"question": "Company Culture", "response": "Agree", "count": 35},
{"question": "Company Culture", "response": "Strongly Agree", "count": 31}
]Example 4: Customer Satisfaction Vertical Bars
This example uses vertical stacked bars with diverging colors to emphasize positive vs negative sentiment.
View Source
data:
source: [
{"category": "Product Quality", "response": "Very Dissatisfied", "count": 8},
{"category": "Product Quality", "response": "Dissatisfied", "count": 12},
{"category": "Product Quality", "response": "Neutral", "count": 15},
{"category": "Product Quality", "response": "Satisfied", "count": 42},
{"category": "Product Quality", "response": "Very Satisfied", "count": 23},
{"category": "Customer Service", "response": "Very Dissatisfied", "count": 15},
{"category": "Customer Service", "response": "Dissatisfied", "count": 20},
{"category": "Customer Service", "response": "Neutral", "count": 18},
{"category": "Customer Service", "response": "Satisfied", "count": 28},
{"category": "Customer Service", "response": "Very Satisfied", "count": 19},
{"category": "Value for Money", "response": "Very Dissatisfied", "count": 12},
{"category": "Value for Money", "response": "Dissatisfied", "count": 18},
{"category": "Value for Money", "response": "Neutral", "count": 22},
{"category": "Value for Money", "response": "Satisfied", "count": 32},
{"category": "Value for Money", "response": "Very Satisfied", "count": 16},
{"category": "Delivery Speed", "response": "Very Dissatisfied", "count": 5},
{"category": "Delivery Speed", "response": "Dissatisfied", "count": 10},
{"category": "Delivery Speed", "response": "Neutral", "count": 12},
{"category": "Delivery Speed", "response": "Satisfied", "count": 38},
{"category": "Delivery Speed", "response": "Very Satisfied", "count": 35}
]
engine: plot
title: Customer Satisfaction Survey
width: 800
height: 500
marks:
- type: bar
configuration:
x: category
y: count
fill: response
stack: y
offset: normalize
tip: true
scales:
color:
type: ordinal
domain: ["Very Dissatisfied", "Dissatisfied", "Neutral", "Satisfied", "Very Satisfied"]
range: ["#ca0020", "#f4a582", "#f7f7f7", "#92c5de", "#0571b0"]
legend: true
y:
label: Response Distribution
marginLeft: 150
marginRight: 50
marginBottom: 80Example 5: Product Feedback Vertical Bars with Centered Offset
Using vertical stacked bars with the center offset to create a more symmetric visualization.
View Source
data:
source: [
{"feature": "Ease of Use", "rating": "Poor", "count": 5},
{"feature": "Ease of Use", "rating": "Below Average", "count": 8},
{"feature": "Ease of Use", "rating": "Average", "count": 12},
{"feature": "Ease of Use", "rating": "Good", "count": 35},
{"feature": "Ease of Use", "rating": "Excellent", "count": 40},
{"feature": "Performance", "rating": "Poor", "count": 12},
{"feature": "Performance", "rating": "Below Average", "count": 15},
{"feature": "Performance", "rating": "Average", "count": 18},
{"feature": "Performance", "rating": "Good", "count": 30},
{"feature": "Performance", "rating": "Excellent", "count": 25},
{"feature": "Design", "rating": "Poor", "count": 3},
{"feature": "Design", "rating": "Below Average", "count": 7},
{"feature": "Design", "rating": "Average", "count": 15},
{"feature": "Design", "rating": "Good", "count": 38},
{"feature": "Design", "rating": "Excellent", "count": 37},
{"feature": "Documentation", "rating": "Poor", "count": 18},
{"feature": "Documentation", "rating": "Below Average", "count": 22},
{"feature": "Documentation", "rating": "Average", "count": 20},
{"feature": "Documentation", "rating": "Good", "count": 25},
{"feature": "Documentation", "rating": "Excellent", "count": 15}
]
engine: plot
title: Product Feature Ratings (Centered)
width: 800
height: 500
marks:
- type: bar
configuration:
x: feature
y: count
fill: rating
stack: y
offset: center
tip: true
scales:
color:
type: ordinal
domain: ["Poor", "Below Average", "Average", "Good", "Excellent"]
range: ["#b2182b", "#ef8a62", "#fddbc7", "#67a9cf", "#2166ac"]
legend: true
y:
label: Number of Responses
marginLeft: 150
marginRight: 50
marginBottom: 100Tips for Effective Diverging Bar Charts
- Pre-aggregate your data - Count responses for each category before visualization
- Use consistent ordering - Always maintain the same order for Likert scales (negative to positive)
- Choose appropriate color schemes:
- Red-Blue for temperature/sentiment (red = negative, blue = positive)
- Red-Green for agree/disagree (avoid if color-blind accessibility is important)
- Neutral colors in the middle (gray, yellow, white)
- Consider normalization - Use
offset: "normalize"when comparing questions with different response counts - Use faceting for multiple questions - The
fy:parameter creates small multiples for easier comparison - Add reference lines - Include a rule mark at the neutral point if needed
- Provide context - Include the number of total responses in titles or annotations
Related Examples
- See
stacked-areas.mdfor examples of stack transforms with areas - See
diverging-color-examples.mdfor diverging color scale patterns - See
complete-mark-showcase.mdfor more bar chart variations
Future Enhancements
When DataGlass adds support for custom offset functions, we'll be able to implement the exact Likert-style centering from the original Observable Plot example. For now, these workarounds provide effective alternatives for visualizing survey data.