Module 3 · Lecture 15

Atlas Charts & the Ride-Analytics Problem

Thirty-seven percent of bookings never carry a passenger. Today we build the dashboard that finds out why.

⏱ 90 minutes 🎯 CLO4 📍 Prerequisites: Lecture 14 (aggregation pipelines) 🔧 Lab: MongoDB Atlas Charts

Contents

  1. Learning Objectives
  2. Session Timeline
  3. Section 1: The Brief — A Real Business Problem
  4. Section 2: Why Atlas Charts Rather Than Excel
  5. Section 3: Charts' Mental Model — Sources, Encodings, Filters
  6. Section 4: Building the First Four Charts
  7. Section 5: Choosing the Right Chart Type
  8. Section 6: Three Traps That Produce Confident Nonsense
  9. Section 7: Hands-On — Build the Dashboard
  10. Key Takeaways

Learning Objectives

Session Timeline

TimeSegmentWhat Happens
0–10 minThe briefThe ops director's question, and what 37.71% actually costs
10–20 minWhy ChartsLive data vs extract; where Charts sits against Excel, Tableau and Power BI
20–32 minThe mental modelData sources, encoding channels, and how they map to aggregation stages
32–50 minBuild along — charts 1 & 2Status mix and revenue by vehicle type
50–62 minBuild along — charts 3 & 4Demand by hour, and the cancellation-reason breakdown
62–72 minChart choiceMatching chart type to question; the four that are almost always wrong
72–84 minTrapsSmall samples, missing fields, truncated axes — with live examples from this data
84–90 minWrapAssemble the dashboard; set up for the Lecture 16 deep-dive

Section 1: The Brief — A Real Business Problem

For four lectures you have been running queries against bdma.rides because they were on a worksheet. Today the worksheet is replaced by a person.

The brief. You are three weeks into a role at a ride-hailing operator in Delhi NCR. The operations director stops you after a meeting and says: “Our completion rate is sitting around sixty percent and finance thinks we're leaving money on the table. Nobody can tell me where it's going. You have the data. I have a board meeting on Friday — give me somewhere to start.”

That is a real brief, and it is typical in exactly the way that matters: it names a symptom, not a question. Before you open any tool, the first job is to convert it into something answerable.

From a complaint to a question

“Where is the money going?” cannot be plotted. Decompose it until each piece can be:

QuestionShape of the answerChart that answers it
How do bookings actually end?A breakdown of one categorical fieldDonut or bar
Which failures are most common?A ranked list within one categoryHorizontal bar
Does failure depend on time of day?A measure across an ordered dimensionColumn + line combo
Which products carry the value?A ranked measure by categoryHorizontal bar
Where does it happen?A ranked list with a secondary rateTable or bar

Each row of that table becomes one chart. Nothing else gets built. This discipline — no chart without a question written down first — is the single most useful habit in this module, and it is also the first thing the project rubric checks.

Sizing the problem before you start

Begin by establishing the magnitude, because it determines how much effort the problem deserves. From the aggregations you wrote in Lecture 14:

MeasureValueWhy it matters
Total bookings (2024)15,000The denominator for everything below
Completed9,343 (62.29%)Only these earn revenue
Failed5,657 (37.71%)The problem, quantified
Revenue realised₹22,02,449What the operation actually earned
Average completed fare₹235.73The multiplier for lost bookings
Estimated lost revenue₹13,33,5395,657 × ₹235.73 — the headline number

Be honest about what “lost revenue” means. ₹13,33,539 assumes every failed booking would have completed at the average fare. It would not have. Some customers rebooked successfully within minutes and are already counted in the 9,343. Some trips would have been short and cheap. The number is an upper bound, and you must say so out loud — because the first thing a competent CFO will do is attack it. A defended upper bound survives that attack; an undefended point estimate does not.

Even halved, the figure is roughly ₹6.7 lakh against ₹22 lakh of realised revenue. That is large enough to justify the work. Establishing this in the first five minutes is what separates an analysis from a hobby.

Section 2: Why Atlas Charts Rather Than Excel

You could export a CSV and build this in Excel in an afternoon. Many analysts would. It is worth being precise about what you lose when you do.

DimensionCSV → ExcelAtlas Charts
FreshnessFrozen at export. Someone must remember to redo it.Queries the live collection on each load
Nested dataroute.pickup must be flattened by hand firstDot notation works natively
Size ceilingAbout a million rows, and painful well before thatAggregation runs server-side; the browser receives results only
ReproducibilityManual steps live in one person's memoryThe chart is the specification
SharingEmail a file; now there are nine versionsOne URL, access controlled
Ad-hoc explorationExcellent — genuinely better than ChartsFine, but more structured

The honest summary. Excel is better for exploring something once. Charts is better for anything that must stay true next month. The failure mode we are designing against is not “Excel is bad” — it is the quarterly report that silently reflects data from March.

Where Charts sits in the wider market

Atlas Charts is not competing with Tableau or Power BI on features, and you should not present it as though it were. It wins on exactly one axis: it is already connected to your data.

For a team already on Atlas that needs an operational dashboard this week, Charts is usually the right call. For a finance function blending ERP, CRM and warehouse data, it is not. Saying which situation you are in is the analysis; the tool follows.

Section 3: Charts' Mental Model — Sources, Encodings, Filters

Atlas Charts has three concepts. Once they click, the interface stops being a maze.

1. The data source

A data source is a collection, a view, or a collection with a pipeline attached. It is defined once in Charts → Data Sources and reused by every chart that needs it.

The important option is the one people miss: a data source can carry its own aggregation pipeline. Everything you learned in Lecture 14 is available here. If a chart needs a derived field — booking hour, say — you do not fight the chart builder. You attach a pipeline to the source and the field simply exists:

[
  { $addFields: {
      hour: { $toInt: { $substr: ["$booked_at", 11, 2] } },
      failed: { $cond: [{ $eq: ["$status", "Completed"] }, 0, 1] }
  } }
]

Two lines, and every chart built on that source can now encode by hour or average the failure flag. Recall from Lecture 12 that booked_at is stored as an ISO string in this dataset, which is why we slice characters 11–12 rather than calling $hour. If you prefer the date operators, convert first with { $toDate: "$booked_at" }.

Push work into the source, not the chart. A derived field defined once in the source stays consistent across every chart on the dashboard. The same expression pasted into six charts will diverge the first time someone edits five of them.

2. Encoding channels

A chart in Charts is built by dragging fields onto channels. Each channel is a visual property, and each one maps directly onto something you already know how to write by hand:

ChannelVisual roleAggregation equivalent
X / CategoryWhat the bars or slices areThe _id of a $group
Y / ValueHow tall or large each one isThe accumulator — $sum, $avg, $count
Series / ColourSplits each category into sub-barsA compound _id with two keys
FilterWhich documents are considered$match, placed first
Sort & limitRanking and top-N$sort then $limit

This equivalence is the point of teaching aggregation before visualisation. Charts is a graphical front end to a pipeline. When a chart shows a number you did not expect, you debug it by asking what pipeline would produce this? — and then you go and run that pipeline in mongosh to check.

3. Filters

Filters exist at three levels, and knowing which one you used matters more than it sounds:

An undisclosed filter is how dashboards lie. A chart titled “Average Booking Value” that quietly filters to status: "Completed" is not wrong — ₹235.73 is the correct average for completed rides — but a reader who assumes it covers all 15,000 bookings has been misled. Put the filter in the title: “Average Booking Value (Completed Rides Only)”. This is marked in both Assignment 3 and the project rubric.

Section 4: Building the First Four Charts

Build these along with the lecturer. Each one states its question first, then its configuration, then the answer it gives.

Chart 1 — How do bookings actually end?

1

Type: Donut. Label: status. Arc: COUNT.

Five categories, parts of a whole, and the whole is meaningful. This is one of the few cases where a donut genuinely beats a bar chart — the reader's question is “what share?”, not “which is bigger?”.

The result, which should match your Lecture 14 pipeline exactly:

StatusBookingsShare
Completed9,34362.29%
Cancelled by Driver2,69817.99%
No Driver Found1,0366.91%
Cancelled by Customer1,0226.81%
Incomplete9016.01%

What it tells you immediately: driver cancellations are 17.99% — more than two and a half times customer cancellations at 6.81%. Add “No Driver Found” at 6.91% and roughly 25% of all bookings fail on the supply side. That single comparison reframes the brief: this is not customers changing their minds, it is the operator failing to deliver a car. Every subsequent chart should be chosen to test that hypothesis.

Chart 2 — Which products carry the value?

2

Type: Horizontal bar. Y: vehicle_type. X: SUM of booking_value. Sort: descending.

Horizontal, not vertical, because the category labels are words — “Premier Sedan” reads cleanly on a horizontal axis and becomes a rotated mess on a vertical one.

Vehicle typeRevenueBookingsAvg fareCompletion
Premier Sedan₹5,36,8551,954₹442.2262.13%
Go Sedan₹4,91,4732,854₹278.7761.77%
Uber XL₹4,31,9271,231₹558.7762.79%

The insight hiding in the ordering: Premier Sedan earns the most revenue from only 1,954 bookings, while Go Sedan takes 2,854 bookings to earn less. Uber XL earns ₹4.3 lakh from 1,231 bookings. Ranking by volume and ranking by revenue give different answers — and a failed Uber XL booking costs ₹558.77 against ₹278.77 for a Go Sedan. If you can only fix cancellations for one product, the arithmetic says start where each failure is most expensive, not where failures are most numerous.

A dashboard that only ever counts things will mislead you. Count answers “how often”. Value answers “how much”. They rank differently, and the decision almost always follows value.

Chart 3 — Does failure depend on the time of day?

3

Type: Column chart with a line series. X: hour (the field derived in the source pipeline). Y (columns): COUNT. Y (line): MEAN of failed, formatted as a percentage.

Two measures on different scales against a shared ordered dimension. Volume as columns, rate as a line, and the line gets its own axis.

HourBookingsFailure rateReading
18:001,37337.14%Busiest hour of the day
19:001,246—Evening peak continues
09:001,172—Morning commute
08:001,076—Morning commute
17:001,06239.92%Worst failure rate of any high-volume hour

This is the finding of the session. The busiest hour is 18:00, and the instinct of every operations team is to throw resources at the peak. But 17:00 — one hour earlier, still carrying 1,062 bookings — fails at 39.92% against the peak's 37.14%. Supply is thinnest just before demand peaks, not during it. Drivers have not yet repositioned for the evening rush.

Volume is not risk. The hour with the most bookings is not the hour with the most problems. A dashboard that plots only volume would have sent the operations director to 18:00 and the intervention would have underperformed. This is precisely the kind of finding that justifies building the dashboard at all.

Chart 4 — Which failures are most common?

4

Type: Horizontal bar. Y: cancellation.reason. X: COUNT. Filter: cancellation.by = "driver".

Note the dot notation on a nested field — no flattening step, which is exactly the advantage Charts has over a CSV export.

Driver cancellation reasonCountShare
Customer related issue72927.02%
More than permitted people in there66624.68%
Personal & Car related issues65224.17%
The customer was coughing/sick65124.13%

A genuinely awkward result. Four reasons, all between 24% and 27%. There is no dominant cause. This is worth sitting with, because the temptation is to pick the top bar and call it the answer — but a 27% leader among four near-equal options is not a finding, it is a flat distribution.

The honest read: there is no single fix available. Either you attack all four, or you accept that the reason codes themselves are too coarse to act on and recommend better instrumentation. “The data cannot answer this; here is what we would need to collect” is a legitimate and often valuable recommendation. We develop it properly in Lecture 16.

Section 5: Choosing the Right Chart Type

Chart choice is not aesthetic. Each type encodes a specific relationship, and using the wrong one makes the reader work to undo your encoding.

The questionUseBecause
Which is biggest?Bar (horizontal if labels are words)Length on a common baseline is the most accurately judged visual channel
What share of the whole?Donut — with at most 5–6 slicesAngle reads share directly; beyond six slices it becomes unreadable
How does it move over time?LineSlope encodes rate of change, which the eye reads instantly
Two measures, one dimension?Combo column + line, dual axisKeeps different units on their own scales
Exact values matter?TableWhen the reader will quote the number, do not make them estimate it
Where, geographically?MapOnly when position itself carries meaning
Density across two dimensions?HeatmapHour × day-of-week demand is the canonical case

Four choices that are almost always wrong

Section 6: Three Traps That Produce Confident Nonsense

Each of these is live in the course dataset right now. They are not hypothetical.

Trap 1 — The small-sample rate

Sort the hourly failure rates from Chart 3 by rate instead of by hour, and the top of the list is not 17:00. It is 03:00, at 43.48%.

03:00 carries 46 bookings. Twenty of them failed. One more cancellation moves that rate by more than two percentage points. It is noise wearing the costume of a finding — and a dashboard that ranks purely by rate will send the operations director to staff the night shift.

The fix is a volume floor, applied in the source pipeline after the grouping stage:

{ $group: {
    _id: "$hour",
    bookings: { $sum: 1 },
    failed:   { $sum: { $cond: [{ $eq: ["$status", "Completed"] }, 0, 1] } }
} },
{ $match: { bookings: { $gte: 1000 } } },
{ $addFields: {
    failure_rate: { $round: [{ $multiply: [{ $divide: ["$failed", "$bookings"] }, 100] }, 2] }
} },
{ $sort: { failure_rate: -1 } }

Now 17:00 tops the list legitimately. The threshold of 1,000 is a judgement call, not a law — but you must state it and be able to defend it. “We excluded hours below 1,000 bookings because rates below that are unstable” is a defensible sentence. Silently excluding them is not.

Trap 2 — Averaging over absent fields

Recall from Lecture 12 that booking_value is absent on cancelled and no-driver-found bookings. MongoDB's $avg skips missing fields silently — it does not treat them as zero, and it does not warn you.

What you builtWhat it computesResult
$avg over all 15,000 documentsMean over the ~10,244 that have the fieldLooks like a fleet average, is not
$avg after $match: { status: "Completed" }Mean over exactly 9,343 completed rides₹235.73 — defensible

Both produce a plausible-looking number. Only the second has a definition you can state in one sentence. Filter explicitly to the population you mean, and name that population in the chart title.

Trap 3 — The truncated axis

Completion rates by vehicle type run from 61.77% to 62.79% — a spread of one percentage point across the fleet. Plot that on an axis from 61% to 63% and Uber XL's bar towers over Go Sedan's. The chart is technically accurate and completely misleading.

Start value axes at zero. The honest conclusion here is that completion rate barely varies by vehicle type, which is itself informative: the failure problem is not product-specific, so a product-specific fix will not work. A truncated axis would have hidden that conclusion behind a dramatic-looking bar chart.

The common thread. All three traps produce charts that are defensible line by line and wrong as a whole. The defence is the same in each case: state the population, state the threshold, start at zero, and sanity-check every chart against a pipeline you ran by hand in mongosh.

Section 7: Hands-On — Build the Dashboard

1

Open Charts. In Atlas, select your project and click Charts in the left sidebar. On first use it provisions automatically — no separate signup.

2

Add the data source. Go to Data Sources → Add Data Source, pick your cluster, and select bdma.rides. Confirm it reports 15,000 documents. If it does not, re-run the mongoimport from Lecture 12 before continuing.

3

Attach the pipeline. Open the source's options, choose Pipeline, and paste the $addFields stage from Section 3. Save. The fields hour and failed now appear in the field list of every chart built on this source.

4

Create the dashboard. Dashboards → Add Dashboard. Name it Ride Operations — Failure Analysis. A name that states the subject beats “Dashboard 1” the moment a second dashboard exists.

5

Build the four charts from Section 4, in order. After each one, verify the number against the corresponding mongosh pipeline from Lecture 14. If the chart and the shell disagree, the chart has a filter you forgot about — find it before you build the next chart.

6

Title every chart with its question. Not “Status” but “How do bookings end? (All 15,000 bookings, 2024)”. The title carries the question and the population; the chart carries the answer.

7

Arrange for reading order. Top-left is read first, so put the status donut there — it frames everything else. Hourly failure goes top-right. Revenue and cancellation reasons go below. Readers scan in a Z; lay the argument out along it.

8

Compare against the reference. Open the course dashboard alongside yours. Every number should match exactly — the dataset is seeded, so there is no room for “close enough”. A mismatch is a bug in your filter, and finding it now is the whole point of the exercise.

Key Takeaways

✓ What You Should Be Able to Say After This Session

  1. No chart without a written question. The question determines the chart type, the filter and the title. A chart built because the data was available is decoration.
  2. Charts is a graphical front end to the aggregation framework. Category is $group._id, value is the accumulator, filter is $match. Debug a wrong chart by writing the pipeline it implies and running it.
  3. Push derived fields into the data source pipeline. Defined once, consistent everywhere, and the chart builder stops fighting you.
  4. Volume is not risk. 18:00 is the busiest hour at 1,373 bookings; 17:00 fails more often at 39.92%. Plotting only volume would have aimed the fix at the wrong hour.
  5. Count and value rank differently. Go Sedan has the most bookings; Premier Sedan earns the most revenue. The decision follows value.
  6. Always name the population and the threshold. A 43.48% failure rate on 46 bookings is noise; an undisclosed status: "Completed" filter turns a correct average into a misleading one.

Build it yourself before Lecture 16. The Atlas Charts build guide gives you the exact aggregation behind every panel on the reference dashboard, the chart type to pick, and which field belongs on which encoding channel. Work through it with your own cluster — the lab in this session assumes you arrive with the dashboard already standing up.

→ Next: Lecture 16 — Dashboard Storytelling & the Recommendation

Four charts and a finding are not yet an answer. Next session we take the 17:00 insight apart, work out what the flat cancellation-reason distribution really means, and turn the dashboard into the one-page recommendation the operations director asked for on Friday. Bring the dashboard you built today — we edit it live.