A live-build companion deck for a Tableau tutoring session. It pivots three wide Jira date columns into a single long axis of Milestone Type and Milestone Date, then builds the FIXED-LOD running-percentage calculation that stops a self-normalizing Actual series from claiming 100% completion before it is true. It covers TODAY()-boundary and dashed-Projected styling, including the fallback for versions before 2023.2, and finishes with a data-hygiene audit of a real export - a column shift, three typo dates, and an orphan Parent Epic - that students must catch before trusting a single number.
Subject: Business Analytics · 113 slides · applied lesson
Open the interactive version of this deck · Homework for this lesson
Title
Business Analytics · Live Build Session
Planned, Actual, and Projected on one chart — connect, relate, pivot, and calculate your way to a single burn-up line, plus the data-hygiene issues and version limitations you'll actually hit.
Objectives
This session builds one chart from a real (messy) Jira export: Planned + Actual + Projected on a single burn-up line. By the end you can:
Concretely: everything above happens against one real (messy) Jira export — the same file you'll have open today. You'll build across 7 parts, roughly 60–75 minutes end to end, with two deliberate return trips to an audit mindset: once on the raw data (Part 2), once on the finished chart (Part 7).
Warm-up
Discussion prompt
Before we open Jira Burndown in Tableau: Planned, Actual & Projected on One Chart: without looking back, what was the main idea of The Data Analyst's Toolkit — First Session (Sports Business), and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
Kickoff session for a student building a portfolio of end-to-end sports-business-operations analytics projects for internship interviews (31 slides). Introduces the five-tool stack — SQL, Python, Power BI, Excel, and GitHub — with the honest strengths and limits of each, where each fits in a project, and how they hand off to form one pipeline (extract → analyze → present → publish).
Concept
Before wiring anything up, look at where you're headed. The reference workbook's finished chart has three lines on one axis, colored by Milestone Type.
| Series | Look | Behavior |
|---|---|---|
| Planned | Gray-blue, solid | Rises to 100% (on the cleaned file) |
| Actual | Green, solid | Plateaus at ~41% — the true resolved share |
| Projected | Light green, dashed | Sparse — only ~11 of 44 features have a forecast |
A vertical TODAY() line splits history from forecast. One chart tells the entire project story: what was planned, what's actually done, and what's expected next — and every piece of it comes from a real, occasionally messy, Jira export.
The takeaway: every line you're about to build is a promise about honesty — solid means it already happened, dashed means it's a guess, and the gap between Planned's 100% and Actual's 41% is the entire point of the chart.
Comparison
Comparison matrix
From Show, don't tell: the finished chart: refill the Look column from what you know. The rest of the table is as it appeared.
| Series | Look | Behavior |
|---|---|---|
| Planned | Gray-blue, solid | Rises to 100% (on the cleaned file) |
| Actual | Green, solid | Plateaus at ~41% — the true resolved share |
| Projected | Light green, dashed | Sparse — only ~11 of 44 features have a forecast |
Section
Segment 1
Concept
In this part: wire up the two Jira tables, see why a relationship (not a join) keeps every count honest, and build the validation sheet you'll lean on for the rest of the session. About 5 minutes in a live session — the shortest part, but everything downstream depends on getting it right.
Concept
Before any pivot or calculation, Tableau needs to know how these two tables relate to each other — get this wrong and every count downstream is suspect. That's the job of this first part.
The export is two Excel tabs: Epics (Parents) — 5 rows — and Features (Children) — 44 rows — linked by Features.Parent Epic = Epics.Key.
relationship — A logical-layer link between two tables that keeps each table at its own grain — one row per epic, one row per feature — until a sheet actually needs to combine them. Contrast with a physical join, which flattens the tables into one.
In plain words: Epics and Features are separate lists that happen to reference each other; a relationship is just Tableau remembering that reference without merging the lists into one.
Counterexample
Discussion prompt
Before any pivot or calculation, Tableau needs to know how these two tables relate to each other — get this wrong and every count downstream is suspect. That's the job of this first part.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
The export is two Excel tabs: Epics (Parents) — 5 rows — and Features (Children) — 44 rows — linked by Features.Parent Epic = Epics.Key.
Intuition
Picture Epics and Features as two separate stacks of index cards. A relationship is a note saying 'this card in stack B belongs to that card in stack A' — both stacks stay intact until you ask a question that needs both.
A physical join, by contrast, actually staples every matching pair of cards together into one new stack. At 49 total rows here it's invisible on a chart of sums — but every Feature row would duplicate for its Epic, and any Epic-level SUM() would silently double-count.
Another way to picture it: a relationship is like looking up a contact's phone number only when you need to call them. A join is like photocopying that phone number onto every page of a 44-page document — accurate today, but now there are 44 copies to keep in sync, and 44 chances to double-count if you ever SUM() that page.
Estimation
Predict first
Click-path: Connect → Microsoft Excel → the workbook. Drag Epics (Parents) onto the canvas, then Features (Children) beside it — Tableau auto-detects Features.Parent Epic = Epics.Key as a relationship in the logical layer.
Commit before you compute: what does Connect and relate the tables come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Confirm the relationship in the canvas reads Features.Parent Epic = Epics.Key
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. This is the logical (relationships) layer, not a physical join — worth pointing at explicitly so the distinction from the next trap slide is concrete, not abstract.
Worked example
Click-path: Connect → Microsoft Excel → the workbook. Drag Epics (Parents) onto the canvas, then Features (Children) beside it — Tableau auto-detects Features.Parent Epic = Epics.Key as a relationship in the logical layer.
Confirm the relationship in the canvas reads Features.Parent Epic = Epics.Key
Why: This is the logical (relationships) layer, not a physical join — worth pointing at explicitly so the distinction from the next trap slide is concrete, not abstract.
On screen right now: the Data Source canvas shows two boxes — Epics (Parents) and Features (Children) — connected by a thin line with a small relationship icon, not a solid join line. Clicking that connecting line pops up the editor showing Features.Parent Epic = Epics.Key.
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Confirm the relationship in the canvas reads Features.Parent Epic = Epics.Key
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
Click-path: Connect → Microsoft Excel → the workbook. Drag Epics (Parents) onto the canvas, then Features (Children) beside it — Tableau auto-detects Features.Parent Epic = Epics.Key as a relationship in the logical layer.
Missing information
Discussion prompt
That's 42 features under the 5 epics. The remaining 2 — DPN-060 and DPN-061 — don't show up under any epic filter yet. Hold that thought; it's Part 2's second teaching moment.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
A validation sheet before any chart answers one question: does every epic show the feature count you expect?
Worked example
New worksheet → Epic Key on Rows, COUNTD([Key]) (Features) on Text
Why: A validation sheet before any chart answers one question: does every epic show the feature count you expect?
| Epic | Feature Count | Feature Keys (sample) |
|---|---|---|
| DPN-001 | 9 | DPN-006 … DPN-014 |
| DPN-002 | 11 | DPN-015 … DPN-025 |
| DPN-003 | 9 | DPN-026 … DPN-037 (with gaps) |
| DPN-004 | 10 | DPN-038 … DPN-047 |
| DPN-005 | 3 | DPN-057, DPN-058, DPN-059 |
That's 42 features under the 5 epics. The remaining 2 — DPN-060 and DPN-061 — don't show up under any epic filter yet. Hold that thought; it's Part 2's second teaching moment.
On screen right now: Rows shows five epic keys (DPN-001…DPN-005), and Text shows five numbers that add up to 42 visible plus 2 hidden orphans — 44 total. If the sheet shows one blended total instead of five separate rows, Epic Key isn't on Rows yet.
Trade off
Comparison matrix
From Build the validation sheet: every row here is a choice with a cost. Fill the Feature Count column, then say which row you would actually pick and what you give up for it.
| Epic | Feature Count | Feature Keys (sample) |
|---|---|---|
| DPN-001 | 9 | DPN-006 … DPN-014 |
| DPN-002 | 11 | DPN-015 … DPN-025 |
| DPN-003 | 9 | DPN-026 … DPN-037 (with gaps) |
| DPN-004 | 10 | DPN-038 … DPN-047 |
| DPN-005 | 3 | DPN-057, DPN-058, DPN-059 |
Anomaly
Predict first
A student writes this, and it looks reasonable:
Why this is tempting: joins are the more familiar, SQL-style idea, and “just join them” sounds simpler than learning a Tableau-specific concept. At 49 total rows the mistake is also invisible on screen — nothing looks wrong until someone builds a SUM().
It is wrong. Say what breaks — and say it before you turn the page.
Correct: A physical join flattens the tables: each Feature row is stapled to its Epic row, so an Epic now repeats once per child Feature.
Use the logical (relationships) layer Tableau builds by default when you drag two tables onto the canvas without joining them.
Why: A physical join flattens the tables: each Feature row is stapled to its Epic row, so an Epic now repeats once per child Feature. SUM(anything) on the Epics side (budget, headcount) multiplies by however many features that epic has.
Trap
Why this is tempting: joins are the more familiar, SQL-style idea, and “just join them” sounds simpler than learning a Tableau-specific concept. At 49 total rows the mistake is also invisible on screen — nothing looks wrong until someone builds a SUM().
"A join is more standard, and it's faster — just join the tables."
Data Source canvas: drag Epics + Features onto the SAME physical layer
-> creates a physical join: Features.Parent Epic = Epics.KeyEvery Epic-level SUM() now silently multiplies
Why: A physical join flattens the tables: each Feature row is stapled to its Epic row, so an Epic now repeats once per child Feature. SUM(anything) on the Epics side (budget, headcount) multiplies by however many features that epic has.
Use the logical (relationships) layer Tableau builds by default when you drag two tables onto the canvas without joining them.
Data Source canvas: drag Epics, then Features NEXT to it (not onto it)
-> Tableau infers a relationship: Features.Parent Epic = Epics.KeyEach table keeps its own grain
Why: Epics stays 1 row per epic; Features stays 1 row per feature. A sheet only combines them at the moment a viz actually asks for both — no duplication, no silent multiplication.
The takeaway: relate first, and only reach for a physical join when you have a specific reason — for example, you truly want one flattened row per Feature-and-its-Epic-fields.
Section
Segments 2–3
Concept
In this part: re-point the data source at the RAW file and watch a real count drop from 44 to 29, then track down why — a 15-row column shift, three typo dates, and two orphan foreign keys. About 20 minutes combined (10 for the validation-sheet discovery, 10 for the deep-dive) — the longest part of the session, because everything you build afterward is only as trustworthy as this audit.
Concept
This next stretch is the longest part of the session for a reason: everything you build on top of bad data is bad, so real time goes here first, making sure the data is actually what you think it is.
A validation sheet isn't busywork — it's the tripwire that catches a broken import before it reaches a chart. "Does this count make sense?" is the single most useful question in this whole session.
Rule of thumb: if a total looks smaller than it should, don't assume the project shrank. Assume the data did.
The takeaway: a validation sheet's job isn't to look tidy — it's to be the one place you'd actually notice if a count silently changed.
Analogy
Discussion prompt
Explain The validation sheet's real job by analogy to something with no Business Analytics in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Rule of thumb: if a total looks smaller than it should, don't assume the project shrank. Assume the data did.
Pattern
Predict first
The table runs: Planned (Due Date) | 44 / 44 features | 29 / 44 features · Projected | 11 / 44 features | 11 / 44 features
In The count that shouldn't drop, given the rows so far: what is the next one — the row where Series is Actual (Resolved)?
Correct: Actual (Resolved) | 18 / 44 features | 18 / 44 features
| Series | Cleaned file | Raw file |
|---|---|---|
| Planned (Due Date) | 44 / 44 features | 29 / 44 features |
| Projected | 11 / 44 features | 11 / 44 features |
| Actual (Resolved) | 18 / 44 features | 18 / 44 features |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. Same workbook, same calcs, only the underlying file changes — isolating whether a problem is the chart or the data.
Worked example
Setup: the reference workbook is built against a cleaned copy of the export. Now re-point that same data source at the untouched raw file instead and refresh.
Re-point the data source at Tableau_DemoNewProject.xlsx (raw) and refresh
Why: Same workbook, same calcs, only the underlying file changes — isolating whether a problem is the chart or the data.
| Series | Cleaned file | Raw file |
|---|---|---|
| Planned (Due Date) | 44 / 44 features | 29 / 44 features |
| Projected | 11 / 44 features | 11 / 44 features |
| Actual (Resolved) | 18 / 44 features | 18 / 44 features |
Planned drops from 44 to 29 — a loss of 15 features (~34% of the dataset). Projected and Actual don't move; their sparsity comes from a different, unrelated cause covered later. Ask: why did Planned alone just lose 15 features?
On screen right now: after refreshing to the raw file, the Planned count drops from 44 to 29 while Actual and Projected hold steady at 18 and 11 — that visible, single-series drop is the whole “aha” moment this segment is built around.
Intuition
Tableau's pivot doesn't read column headers at pivot time — it reads whatever sits in the columns you selected when you built the pivot. If a hand-edit shifts values one column left, the pivot faithfully reshapes the wrong cells: empty ones.
There's no error message, no red flag. The pivot just quietly produces nulls where dates should be — which is exactly the 15-feature gap you just found.
In plain words: Tableau trusts you drew the map correctly. It reads column H expecting a Due Date; if a hand-edit put that date in column G instead, Tableau doesn't go looking for it — it just reports column H as empty.
Worked example
Open the Features sheet in Excel. Scroll to row 31 (DPN-038, the first row of Build 3A) and compare it with row 2 (DPN-006, a healthy Build-0 row).
| Row | Key | Col F | Col G | Col H (Due) | Col I (Projected) | Col J (Resolved) |
|---|---|---|---|---|---|---|
| 2 (healthy) | DPN-006 | Resolution string | Start date | has date | has date/empty | has date/empty |
| 31 (shifted) | DPN-038 | 2026-07-01 (46204) | 2026-09-30 (46295) | [empty] | [empty] | [empty] |
F and G hold dates that belong in G and H
Why: Rows 31–45 — Build 3A (DPN-038–047), Build 3B (DPN-057–059), and orphans DPN-060/061 — 15 rows total — were hand-edited with every value shifted one column left. Columns H, I, J, the three pivot columns, are empty for all fifteen.
Impact: these 15 rows contribute zero rows to Planned, Projected, and Actual after the pivot — that's the 44→29 drop, plus part of the Projected/Actual sparsity you'll see later.
On screen right now: scrolling to Excel row 31, columns F and G are filled with Excel serial numbers (46204, 46295) while H, I, and J sit empty — compare that instantly to row 2, where F holds a text string and G through J all hold real values.
Trap
Why this is tempting: 29 out of 44 sounds like the ordinary kind of incompleteness you'd expect from an active project — nothing about the number 29 itself looks broken, so it's easy to accept it at face value and move on to building the chart.
"29 out of 44 planned dates — maybe some features just don't have a due date yet. That's normal for an active project."
Accept the 29 and move on
Why: This treats a structural defect as ordinary sparsity. It isn't: every one of the 15 missing rows DOES have a due date — it's just sitting one column to the left of where it belongs.
Spot-check a handful of rows across the range before trusting a count.
Click a few cells in rows 31–45 and read the column letter off the header
Why: Two minutes of clicking shows the real cause: dates ARE present, just shifted left. That distinction changes the fix — correct the source columns, not the story you tell about the data.
Concept
You just found a structural problem — a whole column shifted. This next stretch is about a quieter kind of problem: the values themselves being simply wrong, even though every column is in the right place.
A shifted column is a structural defect. A typo date is a value defect — and Tableau validates neither. Both flow straight into your chart unless you catch them first.
This export has three: one plain decade typo, one typo compounded with the column shift, and one date that just doesn't add up next to its neighbors.
Explain it
Discussion prompt
Explain Typos slip through silently too to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
A shifted column is a structural defect. A typo date is a value defect — and Tableau validates neither. Both flow straight into your chart unless you catch them first.
Worked example
DPN-013 (Build 0, epic DPN-001) is Closed / Resolution: Done — a legitimately finished feature.
| Field | Value |
|---|---|
| Start Date | 2025-08-29 |
| Due Date (Planned) | 2025-09-20 |
| Resolved (Actual) | 2036-01-30 |
Filter the chart to DPN-013 and look at where its Resolved point lands
Why: The x-axis suddenly has to stretch out to 2036 to fit one point, compressing every other feature's real history into a narrow band on the left.
Likely correct value: 2026-01-30 — a plausible four-month slip past a September 2025 due date, not an eleven-year one.
On screen right now: with the chart filtered to just DPN-013, the Milestone Date axis suddenly stretches out to include the year 2036, squeezing every other feature's real 2025–2027 activity into a thin band on the left edge.
Worked example
DPN-042 (Build 3A, epic DPN-004, row 35) is already one of the 15 column-shifted rows — its H/I/J are empty, so it contributes nothing to any series yet. But look at where its values landed.
| Column | Value | What it actually is |
|---|---|---|
| F | 2026-10-04 (46299) | The Start Date, shifted left |
| G | 2016-12-09 (42713) | The Due Date, shifted left AND a decade typo |
Two defects stack on one row
Why: Fixing only the column shift isn't enough — once G moves back to H, it would still read 2016 instead of 2026. The intended Due Date is 2026-12-09: correct the position first, then the year.
On screen right now: DPN-042 doesn't appear anywhere on the chart yet — it's invisible, because its H/I/J pivot columns are empty. The only way to see the problem at all is to go back into the Excel source and read columns F and G directly.
Worked example
DPN-021 (Build 1, epic DPN-002) isn't shifted or an obvious typo, but its three dates don't tell a consistent story.
| Field | Value |
|---|---|
| Start Date | 2025-10-15 |
| Due Date (Planned) | 2026-12-01 |
| Resolved (Actual) | 2026-02-15 |
Compare Resolved to Due: the feature finished 2026-02-15 — ten months BEFORE its own 2026-12-01 due date
Why: Finishing early is possible, but a 14-month start-to-due window followed by a 4-month actual delivery is unusual enough to flag. Likely correct Due Date: 2025-12-01, a ~14-week target consistent with the Feb 2026 finish.
On screen right now: nothing looks visibly wrong on the chart itself — DPN-021 plots as an ordinary point. This is a spreadsheet-only catch, which is exactly why a coherence check has to be a deliberate step, not something you wait to stumble on visually.
Comparison
Comparison matrix
From DPN-021 — a coherence check: refill the Value column from what you know. The rest of the table is as it appeared.
| Field | Value |
|---|---|
| Start Date | 2025-10-15 |
| Due Date (Planned) | 2026-12-01 |
| Resolved (Actual) | 2026-02-15 |
Anomaly
Predict first
A student writes this, and it looks reasonable:
Why this is tempting: most modern software validates input somewhere — a form won't let you submit an invalid email, a database can enforce constraints. It's a reasonable, common assumption that a mature tool like Tableau does something similar for dates. It doesn't.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Nothing happens. Tableau parses 2036-01-30 and 2016-12-09 exactly as written — they're valid dates, just wrong ones.
Treat Tableau as a presentation layer with zero validation gates, and audit upstream.
Why: Nothing happens. Tableau parses 2036-01-30 and 2016-12-09 exactly as written — they're valid dates, just wrong ones. A spreadsheet enforces no 'Resolved ≤ Due' rule.
Trap
Why this is tempting: most modern software validates input somewhere — a form won't let you submit an invalid email, a database can enforce constraints. It's a reasonable, common assumption that a mature tool like Tableau does something similar for dates. It doesn't.
"Surely a decade-off date or a resolved-before-due feature throws an error somewhere."
Load the raw file and expect a warning
Why: Nothing happens. Tableau parses 2036-01-30 and 2016-12-09 exactly as written — they're valid dates, just wrong ones. A spreadsheet enforces no 'Resolved ≤ Due' rule.
Treat Tableau as a presentation layer with zero validation gates, and audit upstream.
Spot-check date ranges and date logic before building, in Excel or with a simple validation sheet
Why: The fix belongs in the Jira export process (or a pre-import script), not in Tableau. Tableau will faithfully chart whatever you hand it, typos included.
Concept
One more category of silent failure, and then Part 2 is done: what happens when a foreign key points at something that simply isn't there.
DPN-060 and DPN-061 both list Parent Epic = DPN-006. Check the Epics sheet: it only contains DPN-001 through DPN-005. DPN-006 is a Feature, not an Epic.
orphan row — A row whose foreign key value doesn't match any row on the other side of a relationship. The relationship match simply fails — the row isn't deleted, it just never joins into any epic-scoped view.
Definition probe
Sort into buckets
Every line below is part of the definition of relationship or of orphan row — one or the other, never both. Put each where it belongs.
Worked example
Filter the validation sheet to Epic DPN-001. You see exactly 9 features — correct, matching the table from Part 1.
Ask: shouldn't DPN-060/061 also appear, since DPN-006 (their parent) is itself part of DPN-001?
Why: Jira's data model here only supports Epic → Feature, one level. DPN-060/061 point to a Feature acting as a pseudo-parent, which the Epics relationship can't traverse.
Inspect Parent Epic for DPN-060/061 → DPN-006 → look it up in the Epics sheet → not found
Why: Features.Parent Epic = Epics.Key has no match for DPN-006 on the Epics side, so DPN-060/061 are silently excluded from every epic-filtered view — no error, just two missing rows.
On screen right now: filtering the validation sheet to DPN-001 shows exactly 9 rows — correct on its face — and nothing on screen hints that two more features (DPN-060/061) exist anywhere. Their absence IS the lesson; there's no error banner to notice.
Blank canvas
Draw it
Draw what Find the missing two just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Trap
Why this is tempting: deleting the broken rows makes the chart look clean immediately, and under time pressure it feels like “cleanup” rather than data loss — the goal in the moment is just a working chart on screen.
"DPN-060/061 are broken and DPN-038–047 are all empty after the pivot — cut them from the source file so the chart looks clean."
Delete the offending rows from the Excel source
Why: This destroys real project data — 13 legitimate Build-3A/3B/orphan features — to hide a formatting problem, and leaves no record that anything was ever wrong.
Audit, document, and fix the positions and values — never the row count.
Un-shift Build 3A/3B back into H/I/J, correct the three typo dates, and flag DPN-060/061's Parent Epic for a Jira-side fix
Why: The cleaned copy keeps every feature; only the broken cells change. The raw file stays untouched as the before/after teaching artifact.
Break the constraint
Discussion prompt
The rule this trap just fixed:
The cleaned copy keeps every feature; only the broken cells change. The raw file stays untouched as the before/after teaching artifact.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
This destroys real project data — 13 legitimate Build-3A/3B/orphan features — to hide a formatting problem, and leaves no record that anything was ever wrong.
Section
Segment 4
Concept
In this part: reshape three wide date columns into one long Milestone Type / Milestone Date pair, so a single axis can hold all three series. About 5 minutes — short and mechanical, and the one step that makes everything after it possible.
Concept
The data is clean now (or you know exactly what's still broken). Before the chart can show three lines, the data itself has to change shape — that's this part.
Right now Due Date, Planned Completion, and Resolved are three separate columns. Tableau can only put ONE date field on a continuous axis — you'd need a dual axis (caps at two) or Measure Values (visually noisy) to force three.
tidy data — Data shaped so each row is one observation and each column is one variable. Here, one 'feature' isn't one observation — one (feature, milestone type) pair is.
In plain words: tidy data means one row = one observation. Right now one feature spans three columns of dates; after the pivot, one feature spans three ROWS, one per milestone — which is what lets a single axis show all three.
Intuition
Picture the three date columns as three separate lanes on a highway. A pivot merges them into a single lane with a sign on each car saying which lane it came from — one axis (Milestone Date), one label (Milestone Type) a color encoding can read directly.
Every feature that had three date columns now becomes three rows: one row for its Planned date, one for its Projected date, one for its Actual date.
Another way to say it: a wide layout is like a sign-in sheet with three separate columns for “Monday visit,” “Tuesday visit,” and “Wednesday visit.” A tidy, pivoted version has one column for “visit date” and one column for “which day” — every visit gets its own row, sign-in-sheet style, no matter how many days someone came in.
Sorting
Sort into buckets
These are the pieces of Jira Burndown in Tableau: Planned, Actual & Projected on One Chart, out of order. Put each one back under the part of the lesson it belongs to.
Estimation
Predict first
Click-path: open the Features table's physical layer. Select Due Date (Planned), Planned Completion (Projected), and Resolved (Actual) (Ctrl/Cmd-click all three) → right-click → Pivot.
Commit before you compute: what does Build the pivot come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Alias the three Milestone Type values to Planned / Projected / Actual
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The raw pivot values are the original column headers verbatim — alias them to the short labels the legend and tooltips will show.
Worked example
Click-path: open the Features table's physical layer. Select Due Date (Planned), Planned Completion (Projected), and Resolved (Actual) (Ctrl/Cmd-click all three) → right-click → Pivot.
Rename the two new fields: Pivot Field Names -> Milestone Type, Pivot Field Values -> Milestone Date
Why: The generated names are meaningless placeholders; renaming makes every downstream shelf and calc readable.
Alias the three Milestone Type values to Planned / Projected / Actual
Why: The raw pivot values are the original column headers verbatim — alias them to the short labels the legend and tooltips will show.
| Key | Milestone Type | Milestone Date |
|---|---|---|
| DPN-006 | Planned | 2025-XX-XX |
| DPN-006 | Projected | (null or date) |
| DPN-006 | Actual | (null or date) |
On screen right now: the Features physical table used to show one row per feature with three separate date columns; after Pivot, the same table shows three rows per feature sharing two new columns — Milestone Type and Milestone Date — with the original three date columns folded into them (reshaped, not deleted).
Pattern
Step through it
Step through Build the pivot one row at a time. What is driving the change, and what would the row after the last one be?
Concept
One immediate side effect of the pivot needs addressing before you build anything on top of it: some of those new rows will have a blank date. That's expected — how you handle it matters.
An unresolved feature has no Actual date; a Build-0 feature might have no Projected date. After the pivot those rows still exist — with Milestone Date = null — because "not done yet" and "no forecast issued" are real, true facts about the project.
Handling rule: exclude nulls per-visualization (a Milestone Date filter on the sheet), never delete them from the source. Deleting would erase the fact that a feature exists and just hasn't hit that milestone.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Why this is tempting: dragging three familiar date fields straight onto Columns feels more direct than learning a new reshaping step — the same instinct as the join-instead-of-relate trap back in Part 1: skip the unfamiliar step, use what you already know.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Tableau accepts only one continuous date per axis without a dual axis or Measure Values — forcing three onto two axis slots (or blending them into Measure Names/Values) produces overlapping, hard-to-label lines.
Pivot first, so there is only ever ONE date field to place.
Why: Tableau accepts only one continuous date per axis without a dual axis or Measure Values — forcing three onto two axis slots (or blending them into Measure Names/Values) produces overlapping, hard-to-label lines.
Trap
Why this is tempting: dragging three familiar date fields straight onto Columns feels more direct than learning a new reshaping step — the same instinct as the join-instead-of-relate trap back in Part 1: skip the unfamiliar step, use what you already know.
"Why pivot? Just put Due Date, Planned Completion, and Resolved on the same chart directly."
Try to drag all three date fields onto Columns
Why: Tableau accepts only one continuous date per axis without a dual axis or Measure Values — forcing three onto two axis slots (or blending them into Measure Names/Values) produces overlapping, hard-to-label lines.
Pivot first, so there is only ever ONE date field to place.
One Milestone Date on Columns, Milestone Type on Color
Why: The reshape does the work up front so the chart-building step is trivial: one axis, one color legend, no dual-axis synchronization to fight.
Section
Segments 5–6
Concept
In this part: build a raw running count with COUNTD, then turn it into a percentage using a FIXED-LOD denominator that refuses to let any one series grade its own homework. About 11 minutes (3 for the raw count, 8 for the calculation and its table-calc scope) — this is the conceptual heart of the whole session.
Concept
Every percentage is a fraction wearing a costume. Before writing the % formula, build its numerator out in the open so you can see exactly what's being counted.
Drag Milestone Date (continuous) to Columns and COUNTD([Key]) to Rows, Milestone Type to Color. You now see three lines of raw cumulative feature counts, rising left to right.
Why COUNTD, not COUNT(rows): the pivot just tripled every feature into three rows. COUNT([Number of Records]) would count each feature three times over; COUNTD([Key]) counts each distinct feature key exactly once per date it appears.
Worked example
On the raw file, the running COUNTD per series should climb toward:
| Series | Raw count (of 44) |
|---|---|
| Planned | 29 |
| Actual | 18 |
| Projected | 11 |
If any series shows a number near 44×3 = 132, the wrong aggregation is in play
Why: That would mean COUNT(rows) slipped in somewhere instead of COUNTD([Key]) — a dead giveaway of the pivot-triplication trap.
On screen right now: three lines climb from left to right, topping out at three different heights: Planned's line reaches highest (29), Actual reaches partway (18), and Projected reaches lowest (11) — this is the raw file, before any of Part 2's fixes are applied.
Pattern
Step through it
Step through The raw counts you should see one row at a time. What is driving the change, and what would the row after the last one be?
Anomaly
Predict first
A student writes this, and it looks reasonable:
Why this is tempting: COUNT is the everyday word for “how many,” and before the pivot, COUNT(rows) and COUNTD(feature) would have given the same answer — the trap only exists because the pivot quietly tripled the row count out from under that old assumption.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: The pivot turned 44 features into 132 rows.
Count distinct feature keys, not rows.
Why: The pivot turned 44 features into 132 rows. A plain row count triples every series' total, and any percentage built on it is meaningless.
Trap
Why this is tempting: COUNT is the everyday word for “how many,” and before the pivot, COUNT(rows) and COUNTD(feature) would have given the same answer — the trap only exists because the pivot quietly tripled the row count out from under that old assumption.
"Just count the rows — that's what COUNT does."
COUNT([Milestone Date]) -- or SUM([Number of Records])Every feature counts up to three times
Why: The pivot turned 44 features into 132 rows. A plain row count triples every series' total, and any percentage built on it is meaningless.
Count distinct feature keys, not rows.
Use COUNTD([Key]) wherever the denominator or a raw count is needed
Why: COUNTD collapses the pivot's triplication back down to one count per feature, regardless of how many milestone rows that feature now has.
Concept
Before the formula itself, here's what each piece needs to do in plain English: RUNNING_SUM just means ‘keep a running tally as we move left to right along the dates.’ COUNT means ‘how many features hit this exact date.’ FIXED means ‘count something once, over the whole table, no matter what date or series we happen to be looking at.’ Put together: a running tally of features-per-date, divided by one constant total.
Create a calculated field named % Complete (running):
RUNNING_SUM(COUNT([Milestone Date])) / ATTR({FIXED : COUNTD([Key])})COUNT([Milestone Date]) counts features per date, skipping nulls automatically. RUNNING_SUM(...) accumulates that count left to right along the date axis. ATTR({FIXED : COUNTD([Key])}) is a constant: the total distinct feature count (44), computed once, ignoring series or nulls.
Intuition
Imagine dividing the Actual series by its OWN non-null total instead — 18 resolved features divided by 18 (its own count). That reaches 100%. But 18 out of the project's real 44 features is only 41% complete. The series would be lying about the whole project by only ever grading itself.
The FIXED LOD sidesteps this entirely: the denominator is the same 44 for every series, every date, no matter how many of that series' own rows are null.
The takeaway: “percent complete” only means something if everyone agrees what 100% is. A FIXED denominator forces every series to agree: 100% is always all 44 features, not just the ones that happen to have data in that particular series.
Pattern
Predict first
The table runs: Date checked, Type unchecked (correct) | Three independent rising lines · Date + Type both checked | Lines compete for one sum; no partition
In Configure the table calculation, given the rows so far: what is the next one — the row where Compute Using setting is Type checked, Date unchecked?
Correct: Type checked, Date unchecked | Each series jumps once, then flat-lines
| Compute Using setting | Visual result |
|---|---|
| Date checked, Type unchecked (correct) | Three independent rising lines |
| Date + Type both checked | Lines compete for one sum; no partition |
| Type checked, Date unchecked | Each series jumps once, then flat-lines |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. This is the running axis: the sum accumulates left to right as dates increase.
Worked example
Right-click the % Complete (running) pill on Rows → Edit Table Calculation → Compute Using → Specific Dimensions.
Check Milestone Date
Why: This is the running axis: the sum accumulates left to right as dates increase.
Uncheck Milestone Type
Why: Unchecking it makes the sum restart (partition) for each series — so Planned, Actual, and Projected each run their own independent accumulation instead of blending into one merged total.
| Compute Using setting | Visual result |
|---|---|
| Date checked, Type unchecked (correct) | Three independent rising lines |
| Date + Type both checked | Lines compete for one sum; no partition |
| Type checked, Date unchecked | Each series jumps once, then flat-lines |
On screen right now: three separate lines, each starting near 0% at its own earliest date and climbing independently. If instead you see one merged line, or three lines that all jump straight to 100%, Compute Using is set wrong — recheck the settings table two beats back.
Trap
Why this is tempting: it feels more “accurate” to only count features that could possibly have an Actual date — dividing by the whole project can look like padding the denominator with irrelevant rows. The catch: “irrelevant to Actual” is exactly the 26 unresolved features the chart exists to show you.
"The denominator should just be however many features actually have a Resolved date — why divide by the whole project?"
RUNNING_SUM(COUNT([Milestone Date])) / ATTR({FIXED : COUNTD(IF NOT ISNULL([Resolved]) THEN [Key] END)})Actual climbs to a false 100%
Why: 18 resolved out of 18-with-a-date is 100% — but that hides the 26 features still unresolved. The chart would claim the project is finished when it's really at 18/44 ≈ 41%.
Keep the denominator FIXED over ALL distinct features, every series, no exceptions.
ATTR({FIXED : COUNTD([Key])})Actual correctly plateaus at ~41% (18/44)
Why: The true resolved share is visible precisely because unresolved features stay in the denominator even though they never contribute to the numerator.
Section
Segments 7–8
Concept
In this part: give the three series meaningful colors, mark today's date with a reference line, and encode Projected as a dashed line (with a fallback for older Tableau versions). About 7 minutes (2 for color, 5 for the TODAY() line and line pattern) — the math is already done, so this is where the chart starts telling its story without you narrating it.
Concept
The math is done; the rest of this part is about making the chart tell its story to someone who's only glancing at it for three seconds.
Milestone Type on Color already separates the three lines. But which colors say what you mean matters: a chart reader should feel confidence differences at a glance, not just count distinct hues.
| Series | Suggested color | What it signals |
|---|---|---|
| Planned | Neutral gray-blue (#6B8DAE) | The baseline everyone's working toward |
| Actual | Saturated green (#2E7D32) | Confirmed fact — this happened |
| Projected | Light tint (#A5D6A7) | A forecast — tentative, not yet real |
Trade off
Comparison matrix
From Color must encode meaning: every row here is a choice with a cost. Fill the What it signals column, then say which row you would actually pick and what you give up for it.
| Series | Suggested color | What it signals |
|---|---|---|
| Planned | Neutral gray-blue (#6B8DAE) | The baseline everyone's working toward |
| Actual | Saturated green (#2E7D32) | Confirmed fact — this happened |
| Projected | Light tint (#A5D6A7) | A forecast — tentative, not yet real |
Worked example
Right-click the Milestone Type pill on Color -> Edit Colors
Why: Opens the per-value color picker for the three aliased labels (Planned / Actual / Projected).
Set Planned = #6B8DAE, Actual = #2E7D32, Projected = #A5D6A7
Why: Saturation now carries meaning: the darkest, most confident color is reserved for what's already happened.
On screen right now: the legend reads Planned / Actual / Projected in gray-blue, dark green, and pale green — glance at the chart and the darkest line is immediately the one you can trust most (Actual), which is the whole design intent.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Why this is tempting: bright, saturated colors are the default instinct for “making a chart pop,” and nothing about picking red/blue/yellow feels wrong in isolation — the problem only shows up once a viewer tries to guess which line is the confirmed fact and which is the guess.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Bright, fully-saturated hues all look equally 'real' to a viewer.
Reserve saturation for certainty; use a light tint for anything forecasted.
Why: Bright, fully-saturated hues all look equally 'real' to a viewer. Nothing distinguishes a confirmed Actual point from a Projected guess except the legend text — which most viewers never read.
Trap
Why this is tempting: bright, saturated colors are the default instinct for “making a chart pop,” and nothing about picking red/blue/yellow feels wrong in isolation — the problem only shows up once a viewer tries to guess which line is the confirmed fact and which is the guess.
"Just pick three colors that pop — red, blue, yellow."
All three lines read as equally certain
Why: Bright, fully-saturated hues all look equally 'real' to a viewer. Nothing distinguishes a confirmed Actual point from a Projected guess except the legend text — which most viewers never read.
Reserve saturation for certainty; use a light tint for anything forecasted.
Keep Projected visibly lighter than Planned and Actual
Why: The eye reads a light tint as tentative before it even reads the legend. Encoding does work the legend alone can't.
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
Features.Parent Epic = Epics.Key.; Rule of thumb: if a total looks smaller than it should, don't assume the project shrank. Assume the data did.Concept
One more encoding decision before line style: where does “now” sit on this chart, and how does a viewer know it without reading a single label?
A reference line at TODAY() is the one visual element that tells a viewer, without reading anything, where fact ends and forecast begins.
Click-path: right-click the Milestone Date axis → Add Reference Line → Scope: Entire Table, Line: Constant, Value: TODAY(), Label: "Today", Style: solid gray.
Worked example
Build the constant reference line at TODAY()
Why: Everything left of the line is confirmed history (Planned/Actual points that already happened); everything right of it is forecast territory (remaining Planned dates and all Projected points).
Optionally shade the region right of TODAY() with a reference band
Why: A light background tint reinforces the same past/future split without relying on the line alone.
On screen right now: a single vertical gray line now crosses the chart at today's date, with a small “Today” label — every Planned/Actual point to its left is confirmed history; everything to its right, including the whole Projected series, is forecast territory.
Concept
Color already tells Planned/Actual/Projected apart, so why add a second signal? Because not every viewer reads a legend carefully, and dashed-vs-solid still works even for someone who can't quite tell your two greens apart.
Color already separates the three series, but a dashed Projected line adds a second, redundant signal: "this is a guess, not a fact" — useful for anyone who can't easily distinguish the two greens.
Tableau 2023.2 added a Path-shelf Line Pattern for exactly this: per-series solid vs. dashed rendering on one chart, no workaround required.
Worked example
Before the formula: this calc doesn't compute anything numeric — it's just a label-switch. Read it as ‘if this row's series is Projected, call it Dashed; otherwise call it Solid,’ which the Path shelf then turns into an actual stroke style.
Create a calculated field Line Pattern:
IF [Milestone Type] = "Projected" THEN "Dashed" ELSE "Solid" ENDDrag Line Pattern to the Path shelf (Marks card → right-click → Path if not visible)
Why: The Path shelf controls how a mark connects to its neighbors along the line; assigning it a dimension lets each value get its own stroke style.
Edit Path -> assign "Solid" and "Dashed" to the two values
Why: Planned and Actual render as solid strokes (fact); Projected renders dashed (forecast) — visually redundant with color, which is exactly the point.
On screen right now: Planned and Actual render as solid strokes; Projected renders as a dashed stroke — the same visual vocabulary as a blueprint's dashed “proposed” lines versus its solid “built” ones.
Concept
Before moving on, one honest pause: the step you just did doesn't exist on every Tableau install. Worth knowing before you promise it to a student or colleague.
Limitation callout: the Path-shelf Line Pattern feature doesn't exist before Tableau 2023.2. On an older install, the Path shelf simply won't offer dashed/solid styling — there's no error, the option is just absent.
Check the student's Tableau version before promising this step. If it's older, the fallback below still gets a visually distinct Projected line — just with more setup.
Explain it
Discussion prompt
Explain Limitation: Line Pattern needs Tableau 2023.2+ to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
Before moving on, one honest pause: the step you just did doesn't exist on every Tableau install. Worth knowing before you promise it to a student or colleague.
Worked example
Pre-2023.2, build two calculated fields that split the SAME measure at the boundary, so they visually overlap and appear to connect:
// % Complete (Historical)
IF [Milestone Date] <= TODAY() THEN [% Complete (running)] END
// % Complete (Forecast)
IF [Milestone Date] >= TODAY() THEN [% Complete (running)] ENDUse <= and >= (not < and >) on the two calcs
Why: The overlapping comparison means both calcs return a value exactly on TODAY() itself — the one point where the two segments meet — so the lines touch instead of leaving a visible gap.
Style each calc's line independently (solid for Historical, dashed for Forecast)
Why: Because they're two separate measures now, Tableau lets you format each one's line style without needing the Path-shelf feature at all.
On screen right now (pre-2023.2): two measures instead of one, each formatted with its own line style, meeting at a single shared point directly on the TODAY() line — from a normal viewing distance the join is invisible and the chart reads as one continuous, correctly-styled line.
Error analysis
Annotate
Walk the callouts on Fallback: split-at-TODAY() calcs. Each one is a place this is easy to get subtly wrong.
Section
Segment 9 + limitations
Concept
In this part: see exactly why Tableau's built-in Forecast feature can't touch a table-calc measure, then spend about 5 minutes on formatting — percentage axis, date axis, tooltips, legend order — the small polish that turns a working chart into a readable one.
Concept
Here's a second honest limitation, worth knowing before a student asks you to “just have Tableau predict the rest.”
Limitation callout: Tableau's built-in Forecast (exponential smoothing) cannot run on a table-calculation measure. % Complete (running) is a table calc, so Forecast is disabled for it — greyed out in the Analytics pane, no explanation given.
This is exactly why the session uses the export's OWN Planned Completion (Projected) column instead of asking Tableau to forecast anything. It's arguably better data anyway: it's the team's own revised estimate, not a generic statistical extrapolation.
Missing information
Discussion prompt
If a real statistical forecast is ever needed, it has to be computed outside Tableau (Python, R) and imported as a column — Tableau's Forecast feature is a presentation tool here, not a data-science one.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
The Forecast option is greyed out / does nothing, because the measure is a table calc.
Worked example
Drag Analytics -> Forecast onto the sheet with % Complete (running) in view
Why: The Forecast option is greyed out / does nothing, because the measure is a table calc.
Confirm the Projected series is already doing this job
Why: Milestone Type = Projected, sourced from the export's own Planned Completion column, plots exactly where a forecast would — without fighting Tableau's Forecast restriction at all.
If a real statistical forecast is ever needed, it has to be computed outside Tableau (Python, R) and imported as a column — Tableau's Forecast feature is a presentation tool here, not a data-science one.
On screen right now: the Analytics pane's Forecast option sits greyed out with no tooltip explaining why — nothing in the UI tells you it's because the measure is a table calc, which is exactly why this is worth demonstrating live rather than just describing.
Blank canvas
Draw it
Draw what See the limitation, then use the real fix just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Concept
The chart is functionally complete. What's left is making sure nobody has to do mental math or guess a date format just to read it.
A misformatted axis actively confuses a reader: an Excel serial number instead of a date, or a raw 0.41 instead of 41%, forces them to do translation work the chart should have done for them.
Four formatting moves finish the chart: percentage axis, date axis title, tooltip contents, and legend order.
Analogy
Discussion prompt
Explain Formatting is clarity, not decoration by analogy to something with no Business Analytics in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
The chart is functionally complete. What's left is making sure nobody has to do mental math or guess a date format just to read it.
Ranking
Put in order
Put the moves of Finish the formatting into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Shows "41%" instead of "0.41" or a false-precision "41.11%" the raw counts don't justify.
Worked example
Right-click the % Complete axis -> Format -> Numbers -> Percentage, 0 decimals
Why: Shows "41%" instead of "0.41" or a false-precision "41.11%" the raw counts don't justify.
Right-click the Milestone Date axis -> Edit Axis -> Automatic date format, title "Milestone Date"
Why: Confirms the axis reads as calendar dates, not Excel serial numbers.
Right-click Marks -> Tooltip -> show Milestone Type, Milestone Date, % Complete (running), and COUNTD([Key])
Why: A hover should answer 'which series, which date, what percent, how many features' without the viewer clicking anything else.
Reorder the legend to Planned, Actual, Projected
Why: Matches the reading order of the lesson itself: baseline, then what happened, then what's forecast.
On screen right now: the % axis reads in clean percentages (like 41%, not 0.41), the date axis reads as calendar dates (not a five-digit Excel serial number), and hovering any point pops up a tooltip naming the series, date, percent, and feature count together.
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Reorder the legend to Planned, Actual, Projected
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
On screen right now: the % axis reads in clean percentages (like 41%, not 0.41), the date axis reads as calendar dates (not a five-digit Excel serial number), and hovering any point pops up a tooltip naming the series, date, percent, and feature count together.
Section
Segment 10
Concept
In this part: spot-check the finished chart's numbers against the audit from Part 2, walk the eight-step recipe that generalizes to any burndown, and answer five check questions before the recap. About 5 minutes for the validation pass itself, plus the pattern and checks.
Concept
One last discipline before you call this done: check the finished chart's numbers the same skeptical way you checked the raw data back in Part 2.
A chart that LOOKS reasonable isn't the same as a chart that IS correct. Before calling the build done, spot-check every series against numbers you already know from Parts 2 and 4.
This is the same audit mindset from Part 2, applied one more time — to the finished chart instead of the raw data.
Counterexample
Discussion prompt
One last discipline before you call this done: check the finished chart's numbers the same skeptical way you checked the raw data back in Part 2.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
A chart that LOOKS reasonable isn't the same as a chart that IS correct. Before calling the build done, spot-check every series against numbers you already know from Parts 2 and 4.
Estimation
Predict first
On screen right now: hover the rightmost point of each line — Planned should read 100% on the cleaned file (66% on raw), Actual should read 41% on both files, and Projected should read 25% on both files. Any other number means a step upstream needs revisiting.
Commit before you compute: what does The full spot-check come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: If Actual ever reaches 100%, the FIXED-LOD denominator was swapped for a series-specific one
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. 41% is the true resolved share; only a self-normalizing denominator (Part 4's trap) would push it higher.
Worked example
| Series | Raw file | Cleaned file |
|---|---|---|
| Planned | 29/44 -> 66% | 44/44 -> 100% |
| Actual | 18/44 -> 41% | 18/44 -> 41% (unchanged) |
| Projected | 11/44 -> 25% | 11/44 -> 25% (unchanged) |
If Planned reaches only 66% on the cleaned file, the column shift wasn't actually fixed
Why: 100% on Planned is the tell that all 44 due dates are now in column H where they belong.
If Actual ever reaches 100%, the FIXED-LOD denominator was swapped for a series-specific one
Why: 41% is the true resolved share; only a self-normalizing denominator (Part 4's trap) would push it higher.
On screen right now: hover the rightmost point of each line — Planned should read 100% on the cleaned file (66% on raw), Actual should read 41% on both files, and Projected should read 25% on both files. Any other number means a step upstream needs revisiting.
Comparison
Comparison matrix
From The full spot-check: refill the Cleaned file column from what you know. The rest of the table is as it appeared.
| Series | Raw file | Cleaned file |
|---|---|---|
| Planned | 29/44 -> 66% | 44/44 -> 100% |
| Actual | 18/44 -> 41% | 18/44 -> 41% (unchanged) |
| Projected | 11/44 -> 25% | 11/44 -> 25% (unchanged) |
Constraint
Discussion prompt
Run The end-to-end recipe with this step confiscated:
Count distinct keys (COUNTD), never raw rows, once the pivot has triplicated them.
Is it still possible? If it is, say what takes its place and what it costs you. If it is not, say exactly what that step was providing that nothing else does.
Hint: A step you can drop for free was never load-bearing. If you cannot drop it, name the thing that goes wrong the moment it is gone.
Answer:
Pattern
Every multi-series burndown you build this way follows the same eight moves. The topic changes; the shape doesn't:
The takeaway: none of these eight moves are Tableau trivia — they're the same eight questions (“how do these tables relate,” “is the data actually what I think,” “can one axis hold this,” “what's my real denominator,” “does the encoding tell the truth”) you'd ask of any burndown, in any tool, built from any messy export.
Edge cases
Discussion prompt
The end-to-end recipe works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".
Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.
Answer:
Every multi-series burndown you build this way follows the same eight moves. The topic changes; the shape doesn't:
Elimination
Eliminate the wrong options
Epics has 5 rows; Features has 44. If they're combined with a physical JOIN instead of a relationship, what happens to a SUM() built on an Epics-level field (like a budget column)?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: A physical join flattens the two tables into one, repeating each Epic row once per matching Feature row. A SUM() on an Epics-only field then adds that same value in as many times as the epic has features — a silent multiplication with no warning.
Check
Recall what happens when two tables share the SAME physical layer instead of a relationship.
Check your understanding
Epics has 5 rows; Features has 44. If they're combined with a physical JOIN instead of a relationship, what happens to a SUM() built on an Epics-level field (like a budget column)?
Answer: A
Why: A physical join flattens the two tables into one, repeating each Epic row once per matching Feature row. A SUM() on an Epics-only field then adds that same value in as many times as the epic has features — a silent multiplication with no warning.
Prediction
Predict first
On the raw file, the Actual series's running % Complete formula uses ATTR({FIXED : COUNTD([Key])}) as its denominator. Why does the Actual line correctly stop at ~41% instead of 100%?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: Because the denominator is fixed at all 44 features, but only 18 have a non-null Resolved date
Why: The FIXED LOD denominator counts all 44 distinct features regardless of series or nulls. The numerator (RUNNING_SUM of COUNT) only grows when a Resolved date exists — 18 of 44 — so the ratio tops out at 18/44 ≈ 41%, correctly reflecting that 26 features are still unresolved.
Check
Think through what each denominator actually measures before you answer.
Check your understanding
On the raw file, the Actual series's running % Complete formula uses ATTR({FIXED : COUNTD([Key])}) as its denominator. Why does the Actual line correctly stop at ~41% instead of 100%?
Answer: A
Why: The FIXED LOD denominator counts all 44 distinct features regardless of series or nulls. The numerator (RUNNING_SUM of COUNT) only grows when a Resolved date exists — 18 of 44 — so the ratio tops out at 18/44 ≈ 41%, correctly reflecting that 26 features are still unresolved.
Commit first
Predict first
The Planned series reaches 29/44 (~66%) on the raw file but 44/44 (100%) on the cleaned file. What single fix explains the entire jump?
Commit to an answer, then rate it — certain, fairly sure, or guessing — and write the rating down before you turn the page.
Correct: Un-shifting the 15 column-shifted rows (Build 3A, Build 3B, and the two orphans) so their Due Date values land back in column H
Why: The column shift (Issue 0) is what emptied H/I/J for exactly those 15 rows. Moving each value back one column restores all 15 Due Dates, taking Planned from 29/44 to the full 44/44 — the other fixes (typos, orphan parent) don't touch the Due Date column at all.
The rating matters as much as the answer: confident-and-wrong is the combination that survives revision, because nothing about it feels like it needs revisiting.
Check
Recall Part 2's numbers before answering.
Check your understanding
The Planned series reaches 29/44 (~66%) on the raw file but 44/44 (100%) on the cleaned file. What single fix explains the entire jump?
Answer: A
Why: The column shift (Issue 0) is what emptied H/I/J for exactly those 15 rows. Moving each value back one column restores all 15 Due Dates, taking Planned from 29/44 to the full 44/44 — the other fixes (typos, orphan parent) don't touch the Due Date column at all.
Prediction
Predict first
A colleague suggests changing the % Complete denominator to ATTR({FIXED : COUNTD(IF NOT ISNULL([Resolved]) THEN [Key] END)}) for the Actual series specifically. What breaks?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: Actual climbs to a false 100%, because it now divides resolved features by only the resolved features themselves
Why: That denominator counts only features with a non-null Resolved date — exactly the numerator's own population. 18 resolved / 18 resolved = 100%, hiding that 26 of 44 features are still unresolved. The whole point of the FIXED-LOD-over-ALL-features denominator is to prevent a series from grading only itself.
Check
This is the trap from Part 4, restated as a choice.
Check your understanding
A colleague suggests changing the % Complete denominator to ATTR({FIXED : COUNTD(IF NOT ISNULL([Resolved]) THEN [Key] END)}) for the Actual series specifically. What breaks?
Answer: A
Why: That denominator counts only features with a non-null Resolved date — exactly the numerator's own population. 18 resolved / 18 resolved = 100%, hiding that 26 of 44 features are still unresolved. The whole point of the FIXED-LOD-over-ALL-features denominator is to prevent a series from grading only itself.
Elimination
Eliminate the wrong options
A student on Tableau 2022.1 wants a dashed Projected line. What should you tell them?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: The Path-shelf Line Pattern feature is 2023.2+ only, but the split-at-TODAY() calc pair — using <= and >= so the segments touch at TODAY() — reproduces a visually distinct, independently-stylable line on any Tableau version. It's the documented fallback for exactly this limitation.
Check
Consider what actually changes when the Tableau version is older.
Check your understanding
A student on Tableau 2022.1 wants a dashed Projected line. What should you tell them?
Answer: A
Why: The Path-shelf Line Pattern feature is 2023.2+ only, but the split-at-TODAY() calc pair — using <= and >= so the segments touch at TODAY() — reproduces a visually distinct, independently-stylable line on any Tableau version. It's the documented fallback for exactly this limitation.
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — Part 1 — Connect & Relate · Part 2 — Audit Before You Build · Part 3 — Pivot to Long Format · Part 4 — The Running % Complete Calc · Part 5 — Style: Color, TODAY(), Line Pattern · Part 6 — Forecast Limitation & Formatting. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
You can now build — and defend — a one-chart Planned/Actual/Projected burndown from a real, messy Jira export:
| Step | What it prevents |
|---|---|
| Relationship, not join | Row duplication on Epic-level aggregates |
| Validate before pivoting | Trusting a count that's silently wrong |
| Pivot to long format | Needing a dual axis or Measure Values |
| COUNTD + FIXED LOD | Triplicated counts and a self-normalizing 100% |
| Color + TODAY() + Line Pattern | A chart where fact and forecast look identical |
Two Tableau limits worth remembering: Forecast doesn't run on table calcs, and per-series line styling needs 2023.2+ (or the split-at-TODAY() fallback). Bring your own export next time — the audit steps here (column shift, typo dates, orphan keys) generalize to almost any Jira data you'll pull.
Plain words, one more time: build the relationship, audit before you trust a count, reshape wide into long, count what's real, calculate against one honest denominator, and make the encoding say what's true.
Want this taught 1-on-1? Alexander tutors Business Analytics — $55/session, free consultation.