Waffle Chart for Survey Answers
If you are the researcher reporting a survey, the question for each result is plain: out of every hundred people who answered, how many gave each answer? A percentage on a slide makes the reader do that sum in their head. A reader would rather count.
A waffle chart lets them. It draws a hundred squares, each one percent of respondents, and fills them in the proportion each answer holds, so 46 percent agreeing is forty-six squares you can count. The chart above is bound to the grid beneath it, one row per question and answer, so filtering the rows redraws the squares. Filter the Question column to one question and the chart is that question’s hundred.
Why a waffle and not a pie
A pie asks the reader to judge angles, and a slice of 8 percent next to one of 11 percent looks the same. In a waffle the difference is three squares, and small answers stay legible because a square is a square at any size.
The chart over a grid
The chart reads the grid’s rows, so there is nothing to keep in step. Waffle is an opt-in chart type, so you import its module once after the charts module. x names the answer and y is the share. Here is the example in plain JavaScript, with sample rows inline in place of your own.
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/modules/chart-waffle';
const rows = [
{ id: 1, question: 'Easy to get started', answer: 'Agree', share: 46 },
{ id: 2, question: 'Easy to get started', answer: 'Neutral', share: 27 },
{ id: 3, question: 'Easy to get started', answer: 'Disagree', share: 19 },
{ id: 4, question: 'Easy to get started', answer: 'Unsure', share: 8 },
{ id: 5, question: 'Would recommend', answer: 'Agree', share: 63 },
{ id: 6, question: 'Would recommend', answer: 'Neutral', share: 21 },
{ id: 7, question: 'Would recommend', answer: 'Disagree', share: 10 },
{ id: 8, question: 'Would recommend', answer: 'Unsure', share: 6 },
];
const gridEl = document.createElement('div');
const chartEl = document.createElement('div');
gridEl.style.height = '260px';
chartEl.style.height = '420px';
document.body.append(gridEl, chartEl);
const grid = createGrid(gridEl, {
rowKey: 'id',
rows,
columns: [
{ field: 'question', title: 'Question', filter: { type: 'set' } },
{ field: 'answer', title: 'Answer' },
{ field: 'share', title: 'Share (%)', type: 'number' },
],
});
createChart({ grid, container: chartEl, type: 'waffle', x: 'answer', y: 'share',
legend: { values: true }, title: 'Share of respondents by answer' });
The same thing in React
The React adapter is a factory, so you build the component once at module scope. The ref’s grid is the real grid, which is what the chart binds to.
import React, { useEffect, useRef } from 'react';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createLatticeGrid } from '@toclocoinc/lattice-grid/modules/react';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/modules/chart-waffle';
const LatticeGrid = createLatticeGrid({ React, createGrid });
const rows = [
{ id: 1, question: 'Easy to get started', answer: 'Agree', share: 46 },
{ id: 2, question: 'Easy to get started', answer: 'Neutral', share: 27 },
{ id: 3, question: 'Easy to get started', answer: 'Disagree', share: 19 },
{ id: 4, question: 'Easy to get started', answer: 'Unsure', share: 8 },
{ id: 5, question: 'Would recommend', answer: 'Agree', share: 63 },
{ id: 6, question: 'Would recommend', answer: 'Neutral', share: 21 },
{ id: 7, question: 'Would recommend', answer: 'Disagree', share: 10 },
{ id: 8, question: 'Would recommend', answer: 'Unsure', share: 6 },
];
const columns = [
{ field: 'question', title: 'Question', filter: { type: 'set' } },
{ field: 'answer', title: 'Answer' },
{ field: 'share', title: 'Share (%)', type: 'number' },
];
export function SurveyWaffle() {
const gridRef = useRef(null);
const chartRef = useRef(null);
useEffect(() => {
const chart = createChart({ grid: gridRef.current.grid, container: chartRef.current,
type: 'waffle', x: 'answer', y: 'share', legend: { values: true } });
return () => chart.destroy();
}, []);
return (<>
<LatticeGrid ref={gridRef} rowKey="id" columns={columns} rows={rows} style={{ height: 220 }} />
<div ref={chartRef} style={{ height: 380 }} />
</>);
}
The options used
These are the rows of the charts reference for the options in the listings above.
| Option | Type | What it does |
|---|---|---|
grid |
Grid | Required. The grid to read. |
container |
Element or string | Required. Where to draw. |
type |
string | 'waffle' here, one of the chart types the charts module draws. |
x |
string | The category column. Here the answer. |
y |
string | The measure column that sizes each part. Here the share of respondents. |
legend |
boolean or object | Shows the legend. values: true appends each entry’s own total after its name. |
title |
string | Drawn above the plot. |
Reading the squares
Filter the Question column to one question and count. In the example, Would recommend fills most of its hundred with agreement, while Good value splits closer to even between agree, neutral and disagree. With no filter the chart pools every question’s rows, so filter to one question when you want a single hundred. Because each question’s shares add to 100, the squares match the percentages exactly.
Next
See the waffle beside the other part-to-whole charts in the part-to-whole family demo, read the full list of chart types on the charts page, or follow the how-to for a chart that follows the grid.