learn-ship-ecommerce-enabler-with-phoebe / Analyst session 5 of 8
Ship It: Social Commerce Enablers · Analyst session 5 of 8

Stop, Hold, Love, Buy as code

Session 4 gave Pasar Live one clock and one minute table. Tonight the minute table becomes a scorecard, and the scorecard has to survive a harder test than a single brand's dashboard ever faces: many brands' streams, two kinds of host, and a brand manager asking why her host ranks seventh. You write every stage as a numerator over a named denominator, meet the two averages that disagree by almost a factor of two on the same eight rows, and learn why orders rank the rooms while the North Star ranks the hosts. Then you build the whole scorecard in pandas and again in SQL, and the two agree to the second decimal.

🟠 Advanced Analysts Python, pandas, SQL 45 min
0-3 · Welcome 3-19 · Denominators, averages, rankings 19-41 · Build-along: the scorecard 41-45 · Q&A
Part 0

Where this sits in the analyst track

Sessions 1 to 3 built the estate, the conformed orders and the identity gate. Session 4 wrote the per-minute event spec for Pasar Live as a contract between eight teams. Tonight is the first session that turns that contract into a number somebody will be judged on, which is exactly when definitions start to matter. All Pasar Live data on this page is constructed: the marketplace, the channel, the hosts and the eight streams are invented to teach the shape of the problem, and your numbers will differ. The platform's own four stages, and every livestream method that reads a room minute by minute (survival curves, change-points, fixed effects, SHAP, host clustering, difference-in-differences), are taught in the live commerce analytics course, starting from the stream as data. This page takes the enabler's side: many brands' streams on one scorecard, two host types, and a comparison fair enough to sell.

🎫 Clear all three quiz questions to stamp your passport Live = presented in the 45 minutes Self-study = the full depth, after class ★ Build-along = run the code yourself

★ What you walk out with tonight. A stage dictionary where every ratio names its numerator and its denominator in one place, a pooled host scorecard in pandas and in SQL that match, and the sentence that ends most scorecard arguments before they start: "this is the ratio of sums, so it answers the channel's question, not the typical stream's".

Part 1 · covers the stage metrics and their denominators

Five ratios, five denominators

6 min live

A stage metric is two counts and a slash. The slash is where the arguments live, because the denominator decides whose work is being graded. Pasar Live's naming, constructed for this story, writes five ratios plus the North Star, and each one says out loud what it divides by. Underneath there are only four distinct counts: Hold, Love and the North Star all divide by room entries.

FIVE RATIOS, EACH WITH ITS DENOMINATOR constructed · Pasar Live streams S1 and S5 · your numbers will differ S1 · platform host · a big room S5 · brand host · a small room impressions 120,000 entries 9,600 completers 1,150 replays 290 card impr 8,200 clicks 410 orders 62 live card impressions the feed showed the room Stop 8.00% entries / impressions Hold 11.98% completers / entries Love 3.02% replays / entries own denominator cards shown in room click rate 5.00% clicks / card impr to order 15.12% orders / clicks North Star · 62 / 9,600 entries = 0.65% impressions 40,000 entries 1,600 completers 520 replays 140 card impr 1,500 clicks 190 orders 44 live card impressions the feed showed the room Stop 4.00% entries / impressions Hold 32.50% completers / entries Love 8.75% replays / entries own denominator cards shown in room click rate 12.67% clicks / card impr to order 23.16% orders / clicks North Star · 44 / 1,600 entries = 2.75% S1 has six times S5's entries and 18 more orders. Per person in the room, S5 converts 4.2 times better.
🔍 Click to zoom - the three curved arrows all start at entries: change that one count's definition and three numbers move
LiveThe stage definitions, each with its denominator3 min▶
StageNumerator / denominatorThe question it answersWhose work it grades
Stoproom entries / live card impressionsDid the cover pull people in from the feed?Traffic ops, the brand's set-up, the face on the card
Holdcompleters / room entriesDid the room keep the people it got?The host, minute by minute
Lovereplays / room entriesDid anyone come back or watch the replay?The host and the content team
Buy, click rateproduct card clicks / product card impressionsWhen a card was shown, was it tapped?The host's ask, the assortment
Buy, click to orderorders / product card clicksOnce tapped, did it sell?Price, stock, the product page
North Starorders / room entriesOf the people in the room, how many bought?The whole room, with reach taken out

Buy is two ratios because a card can fail in two places: nobody taps it, or people tap it and do not buy. Its first denominator is not entries. Product card impressions fire every time a card is shown in the room, so a host who shows the card often gets more chances to be tapped, and the click rate grades what happened on each showing. Add to cart sits in the minute table too, and stays a diagnostic rather than a stage: it tells you where between the click and the order the sale died.

LiveHold's constructed rule, and why it has to be one rule2 min▶

A completer, on Pasar Live, is a viewer who stays past minute 10. The rule is constructed for this story, and so is every rule like it on a real channel: somebody picked the minute. Hold then divides completers by room entries, every person who walked in, including the ones who left in the first ten seconds. That is the point of the ratio. Divide by the viewers still present at minute 10 instead and Hold becomes close to 100 percent by construction, because you have counted only the survivors.

The enabler's twist is that Serai scores many brands' streams on one sheet, and their streams do not look alike. A 30-minute flash sale and a three-hour brand night both meet the same minute-10 line, and that is fine as long as it is the same line for every stream and the sheet says so. The moment one brand team counts "stayed past 5 minutes" because its streams are short, Hold stops being comparable across brands and nobody can see it. Write the rule once, in the stage dictionary, and let every report import it.

Self-studyWhy not one denominator for everything3 min▶

A single "orders per live card impression" would be tidy and would grade the wrong people. It folds Stop into the result, so the host with the famous face wins before saying a word. That number is fair for the traffic team, who own the impression, and unfair for a brand's product expert, who owns the room.

Each stage divides by the count the previous team handed over. Traffic hands over impressions, the card turns them into entries, the host turns entries into completers and clicks, the product page turns clicks into orders. A denominator is a handover. Choosing one is choosing which team's output you start counting from, which is why the stage dictionary is a cross-team document before it is code.

Real world

Two brand teams, two Holds, one stream shape (constructed). In Serai's first month on Pasar Live, one brand team divided completers by room entries and another divided them by the viewers present at minute 10. The second team's host looked like the best on the channel for three weeks. Nothing in the dashboard was wrong except the slash, and the fix was not a better chart but one line in a shared dictionary. The case is constructed; the failure is the most common one in any multi-team funnel.

Part 2 · covers the averaging trap

Ratio of sums or mean of ratios

6 min live

Ask "what is Pasar Live's conversion" of the constructed eight-stream table and you can get two honest answers from the same rows. Add up all orders and divide by all entries: 437 / 45,720 = 0.96 percent. Or compute each stream's conversion and average the eight: 1.71 percent. Neither is a mistake. Printing either one without saying which is.

EIGHT ROOMS, SIZED BY ENTRIES constructed · circle area = room entries · label = entries, then conversion S1 S2 S3 S4 S5 S6 S7 S8 9,600 0.65% 8,330 0.70% 11,880 0.60% 9,020 0.60% 1,600 2.75% 1,400 2.93% 2,130 2.82% 1,760 2.67% platform hosts · 38,830 entries brand hosts · 6,890 orders per room entry ratio of sums 0.96% 437 / 45,720 · the channel mean of ratios 1.71% 8 rates / 8 · the typical stream 0% 1% 2% 3% four platform rooms, 0.60 to 0.70% four brand rooms, 2.67 to 2.93% Big rooms hold 85 percent of entries and pull the pooled number down. The mean gives each room one vote.
🔍 Click to zoom - no stream converts at 0.96 or 1.71 percent: both are summaries of a mix, and the mix is two host types
LiveTwo numbers, two questions3 min▶
  • Ratio of sums, 0.96 percent, answers the channel's question: of every person who walked into a Pasar Live room Serai ran, how many ordered? It weights every stream by its entries, so the four platform rooms, holding 38,830 of the 45,720 entries, set most of the answer. Finance, the brand's quarterly review and anything that multiplies by traffic want this one.
  • Mean of ratios, 1.71 percent, answers the typical stream's question: if you pick one of these eight streams, how does it tend to convert? Every stream gets one vote, whatever its size. A host coach planning next week, or a brand asking "what should a normal stream of ours do", wants this one.

The rule is short: say which, every time. Put "pooled" or "average of streams" in the column name, not in a footnote. The two can differ by almost a factor of two on the same eight rows, and the reader cannot tell from the number which one you meant.

LivePool within host type, and the gap disappears2 min▶

Split the eight streams by host type and compute both averages inside each group. In the build-along below, the code prints the same value both ways for each type: platform 0.63 percent either way, brand 2.79 percent either way. Inside a host type the rooms are similar in size and in conversion, so the weighting hardly matters. The whole gap between 0.96 and 1.71 came from mixing two populations that behave differently.

That is the practical rule for a scorecard: pool with the ratio of sums, and pool within the groups that are meant to be compared, host type first. A channel-wide number is still worth printing for the channel's own question. It is not a benchmark for any host.

Self-studyWhere each average misleads3 min▶

The ratio of sums can be captured by one room. Add a single platform stream with ten times S3's traffic to the table and the channel's conversion would sit almost exactly on that one stream's rate, whatever the other eight did. When one row dominates the denominator, a pooled number is mostly a report on that row.

The mean of ratios has the opposite weakness: a tiny room gets the same vote as a huge one. A hypothetical test stream with 40 entries and 3 orders would convert at 7.5 percent and pull the mean up on its own. The usual defence is a minimum room size for a stream to count in the mean, and that threshold is one more definition to write down once. Both failure modes are why the stage dictionary carries the aggregation rule next to the ratio. Breaking a metric into its parts to see which one moved is taught in the metric decomposition course.

Real world

Every headline number is a choice of numerator and denominator. Sea Limited's annual report for fiscal 2019 defines Shopee's GMV as the value of orders on the marketplace and says its calculation "includes shipping and other charges"; an order counts "regardless of whether the transaction is settled or if the item is returned". That is a listed company writing its choices down in public, which is the whole lesson of this Part at the scale of a stage ratio: the definition is part of the number. Sea Limited, Annual Report on Form 20-F for the fiscal year 2019, "Conventions" definitions. How an enabler's own GMV should be defined is taught in the ecommerce metrics course, and session 7 writes it into the registry.

Part 3 · covers host comparison across brands

GMV ranks the rooms, the North Star ranks the hosts

4 min live

Sort the eight constructed streams by orders and the platform hosts win: 245 orders against 192. Sort them by the North Star and the brand hosts win: 2.79 percent against 0.63, 4.4 times better per person in the room. Both sorts are correct arithmetic. Only one of them is about the host.

SAME EIGHT STREAMS, TWO RANKINGS constructed · white = platform host · pink = brand host · your numbers will differ RANKED BY ORDERS how a GMV-only table ranks them RANKED BY NORTH STAR orders per room entry 1 · S3 platform 71 2 · S1 platform 62 3 · S7 brand 60 4 · S2 platform 58 5 · S4 platform 54 6 · S8 brand 47 7 · S5 brand 44 8 · S6 brand 41 1 · S6 brand 2.93% 2 · S7 brand 2.82% 3 · S5 brand 2.75% 4 · S8 brand 2.67% 5 · S2 platform 0.70% 6 · S1 platform 0.65% 7 · S4 platform 0.60% 8 · S3 platform 0.60% The biggest room tops the orders table and finishes last per entry. All four brand streams move up. Pooled: platform 245 orders at 0.63%, brand 192 at 2.79%. No prices here, so orders stand in for GMV.
🔍 Click to zoom - every line that crosses is a host whose coaching conversation changes when the denominator does
LiveWhy orders rank the room, not the host2 min▶

Orders are entries times conversion. Entries come mostly from work the host did not do: the traffic push, the slot, the brand's following, the face on the card. S3 had the biggest room of the eight, 11,880 entries, and sold the most, 71 orders, while converting at 0.60 percent, the lowest on the table (S3 and S4 both print 0.60; S3 is lower in the third decimal). A GMV-only table books S3's host into prime slots, which makes the room bigger, which makes the GMV bigger. The loop feeds itself and never asks whether the host sold.

The North Star divides the room out. It is not "better" than orders; it answers a different question. Orders answer "which room made money", and the brand's finance team is right to ask it. The North Star answers "who turns a room into buyers", and that is the question a host scorecard exists for.

LiveFair comparison across brands: within first, across only with a label1 min▶

Serai sees what no single brand sees: many brands' streams on one channel. That is the value and the trap. A beauty brand's host and a kitchenware brand's host face different baskets, prices and buyers; a platform host and a brand host face different rooms. The practice for a fair host comparison:

  1. Compare within host type and within brand first. Rank a brand's hosts against each other, then platform hosts against platform hosts.
  2. Across brands, stay within a category, and pool with the ratio of sums inside it.
  3. Never rank across host type, brand or category without saying so on the chart. "Across host types" is a legitimate view for the channel; it is not a verdict on a person.

Holding brand and category fixed properly, rather than by splitting the table, is a fixed-effects model and belongs to the live commerce analytics course linked at the top of the page.

Self-studyThe scorecard as the thing Serai sells3 min▶

A brand running its own streams sees its own rooms. Serai, running streams for 12 brands on Pasar Live (constructed), can tell a brand what a normal Hold looks like for a brand host in its category, which stage its host is losing, and which minute the room drained. That per-host scorecard, pooled within host type and category, with every stage's denominator on the sheet, is a service line, not a report. It is the "knowing" that the leader track prices.

It only sells if it survives the brand manager's first question: "why does my host rank seventh?" The answer has to be one sentence about a named ratio and a named comparison group, for example "your host's Hold is below the pooled Hold for brand hosts in your category, and here is the minute the room drained", and never "the model says so". Every scorecard row should carry its comparison group as a column. Benchmarks across brands also carry an obligation: show a brand only pooled figures from others, never another brand's own rows.

Real world

The swap request (constructed). After a quarter of GMV-only weekly reports, one of Serai's brands asked to replace its own host with a platform host, because the platform hosts' streams "sold more". The analyst's reply was the two columns in the figure: the platform streams sold more because they had about 5.6 times the entries (38,830 against 6,890), and the brand's host converted the people in her room 4.4 times better. The brand kept its host and bought a traffic push for her slot instead. The story is constructed; the arithmetic is the eight-stream table above.

Build-along

The scorecard in pandas and SQL

★ 22 min · everyone builds

Eight rows, ten columns, constructed. Build the stream table, write the stage dictionary once, pool by host type with the ratio of sums, print both averages, rank the streams two ways, then run the same pooled scorecard in SQL through duckdb. Every output below is what the code printed when run on pandas 2.1 and duckdb 1.5; the data is constructed, so on your own channel your numbers will differ.

SUM ALL THE WAY UP, DIVIDE ONCE AT THE TOP Events one row per fire entry, card impr, click, add to cart, order Minute table stream_id + minute + sku counts per minute built in session 4 Stream table one row per stream 8 rows, 10 columns tonight starts here Host scorecard sum by host type, then divide, once 0.63% · 2.79% sum per minute sum per stream sum per host type wrong way: average the 8 rates = 1.71%, not the channel Counts add; rates do not. Keep every level as counts and let the scorecard be the only place a slash appears.
🔍 Click to zoom - the stage dictionary is applied once, at the grain you report, never averaged on the way up

Build the stream table from the constructed eight-stream table. In production this is the minute table from session 4, summed by stream. Keep it as counts: every column is something that fired and can be added.

★ Step 1 · eight streams, constructedimport pandas as pd cols = ["stream", "host", "impr", "entries", "completers", "replays", "card_impr", "clicks", "atc", "orders"] rows = [ ("S1", "platform", 120_000, 9_600, 1_150, 290, 8_200, 410, 150, 62), ("S2", "platform", 98_000, 8_330, 1_080, 250, 7_100, 390, 140, 58), ("S3", "platform", 135_000, 11_880, 1_310, 300, 10_100, 480, 170, 71), ("S4", "platform", 110_000, 9_020, 990, 260, 7_600, 370, 128, 54), ("S5", "brand", 40_000, 1_600, 520, 140, 1_500, 190, 88, 44), ("S6", "brand", 36_000, 1_400, 480, 120, 1_300, 170, 80, 41), ("S7", "brand", 52_000, 2_130, 700, 190, 2_000, 260, 118, 60), ("S8", "brand", 45_000, 1_760, 590, 150, 1_650, 205, 96, 47), ] streams = pd.DataFrame(rows, columns=cols) print(streams.shape) print(streams.groupby("host")[["entries", "orders"]].sum()) # (8, 10) # entries orders # host # brand 6890 192 # platform 38830 245

Write the stage dictionary once, then compute every stage per stream. Each stage is a name, a numerator and a denominator, in one place. The function divides; nothing else in the codebase is allowed to.

★ Step 2 · the stage dictionarySTAGES = { # name: (numerator, denominator) "stop": ("entries", "impr"), "hold": ("completers", "entries"), "love": ("replays", "entries"), "click_rate": ("clicks", "card_impr"), "click_to_order": ("orders", "clicks"), "north_star": ("orders", "entries"), } def rates(df): out = df[[c for c in ("stream", "host") if c in df]].copy() for name, (num, den) in STAGES.items(): out[name] = (df[num] / df[den] * 100).round(2) return out per_stream = rates(streams) print(per_stream.to_string(index=False)) # stream host stop hold love click_rate click_to_order north_star # S1 platform 8.00 11.98 3.02 5.00 15.12 0.65 # S2 platform 8.50 12.97 3.00 5.49 14.87 0.70 # S3 platform 8.80 11.03 2.53 4.75 14.79 0.60 # S4 platform 8.20 10.98 2.88 4.87 14.59 0.60 # S5 brand 4.00 32.50 8.75 12.67 23.16 2.75 # S6 brand 3.89 34.29 8.57 13.08 24.12 2.93 # S7 brand 4.10 32.86 8.92 13.00 23.08 2.82 # S8 brand 3.91 33.52 8.52 12.42 22.93 2.67

Pool by host type with the ratio of sums. Sum the counts first, divide second, and reuse the same function: the pooled scorecard cannot drift from the per-stream one because they share the dictionary.

★ Step 3 · the pooled host scorecardcounts = [c for c in streams.columns if c not in ("stream", "host")] pooled = streams.groupby("host")[counts].sum() # sum first ... print(rates(pooled).to_string()) # ... divide second # stop hold love click_rate click_to_order north_star # host # brand 3.98 33.24 8.71 12.79 23.27 2.79 # platform 8.39 11.67 2.83 5.00 14.85 0.63

Print both averages, and label them. The channel's conversion and the typical stream's conversion, side by side, then both again inside each host type. The last table is the argument for pooling within host type.

★ Step 4 · two averages, two questionscr = streams["orders"] / streams["entries"] ratio_of_sums = streams["orders"].sum() / streams["entries"].sum() mean_of_ratios = cr.mean() print(f"channel conversion (ratio of sums): {streams['orders'].sum()} / " f"{streams['entries'].sum()} = {ratio_of_sums:.2%}") print(f"typical stream (mean of ratios): {mean_of_ratios:.2%}") print(dict(zip(streams["stream"], (cr * 100).round(2)))) by_host = streams.assign(cr=cr).groupby("host").agg( orders=("orders", "sum"), entries=("entries", "sum"), mean_of_ratios=("cr", "mean")) by_host["ratio_of_sums"] = by_host["orders"] / by_host["entries"] print((by_host[["ratio_of_sums", "mean_of_ratios"]] * 100).round(2)) # channel conversion (ratio of sums): 437 / 45720 = 0.96% # typical stream (mean of ratios): 1.71% # {'S1': 0.65, 'S2': 0.7, 'S3': 0.6, 'S4': 0.6, 'S5': 2.75, 'S6': 2.93, 'S7': 2.82, 'S8': 2.67} # ratio_of_sums mean_of_ratios # host # brand 2.79 2.79 # platform 0.63 0.63

Rank the streams by orders and by the North Star. Rank on the unrounded rate, so S3 and S4, which both print 0.60, still sort correctly.

★ Step 5 · two rankingsboard = per_stream[["stream", "host", "north_star"]].assign(orders=streams["orders"]) board["rank_orders"] = board["orders"].rank(ascending=False, method="first").astype(int) board["rank_ns"] = (streams["orders"] / streams["entries"]).rank( ascending=False, method="first").astype(int) print(board.sort_values("rank_orders").to_string(index=False)) # stream host north_star orders rank_orders rank_ns # S3 platform 0.60 71 1 8 # S1 platform 0.65 62 2 6 # S7 brand 2.82 60 3 2 # S2 platform 0.70 58 4 5 # S4 platform 0.60 54 5 7 # S8 brand 2.67 47 6 4 # S5 brand 2.75 44 7 3 # S6 brand 2.93 41 8 1

Run the same pooled scorecard in SQL. Most brand teams will read this through a warehouse view, not a notebook. In SQL the ratio of sums is SUM(x) / SUM(y) and the mean of ratios is AVG(x / y): one bracket apart, which is why the column name has to say which. pip install duckdb if you do not have it; with sqlite3 from the standard library, load the table with streams.to_sql("streams", con) and the same query runs unchanged.

★ Step 6 · the same scorecard in SQLimport duckdb con = duckdb.connect() con.register("streams", streams) print(con.sql(""" SELECT host, CAST(SUM(orders) AS INT) AS orders, ROUND(100.0 * SUM(entries) / SUM(impr), 2) AS stop, ROUND(100.0 * SUM(completers) / SUM(entries), 2) AS hold, ROUND(100.0 * SUM(replays) / SUM(entries), 2) AS love, ROUND(100.0 * SUM(clicks) / SUM(card_impr), 2) AS click_rate, ROUND(100.0 * SUM(orders) / SUM(clicks), 2) AS click_to_order, ROUND(100.0 * SUM(orders) / SUM(entries), 2) AS north_star FROM streams GROUP BY host ORDER BY north_star DESC """).fetchdf().to_string(index=False)) print(con.sql(""" SELECT ROUND(100.0 * SUM(orders) / SUM(entries), 2) AS ratio_of_sums, ROUND(AVG(100.0 * orders / entries), 2) AS mean_of_ratios FROM streams """).fetchall()) # host orders stop hold love click_rate click_to_order north_star # brand 192 3.98 33.24 8.71 12.79 23.27 2.79 # platform 245 8.39 11.67 2.83 5.00 14.85 0.63 # [(0.96, 1.71)]

Keep the stage dictionary in the estate registry. Session 1's registry declared tables and grains; the stage dictionary is its first metric entry, and session 7 turns both into a registry with tests. If the SQL view and the notebook each carry their own copy of Hold, they will disagree the week somebody changes the minute-10 rule in one of them.

Homework

Before session 6

30 min
  1. Find three rate metrics on a dashboard you use weekly. For each, write the numerator, the denominator, and whether the total row is a ratio of sums or a mean of ratios. If the dashboard does not say, find out from the query.
  2. Add a ninth, constructed stream to the table: a platform host with 30,000 entries converting at 0.50 percent. Re-run steps 3 and 4 and write one sentence on what happened to each average, and why.
  3. Add a brand and a category column to the stream table (invent them) and change step 3 to pool by host type within category. Print a scorecard where every row carries its comparison group.
  4. Write the one-sentence answer you would give a brand manager whose host ranks last on the North Star but first on orders. Name the ratio and the comparison group.
Check yourself

Three questions

Clear all three and Analyst session 5 gets stamped in your passport.

1 · On the constructed eight streams, conversion is 0.96 percent as a ratio of sums and 1.71 percent as a mean of the eight rates. Which question does each answer?

The ratio of sums divides all orders by all entries, so the four big platform rooms dominate it. The mean of ratios gives every stream one vote. Both are correct arithmetic for different questions, and within each host type they agree, which is why the scorecard pools within host type and names the method.

2 · Why does a GMV-only table mis-rank Pasar Live's hosts?

S3 had the biggest room, 11,880 entries, and the most orders, 71, while converting at 0.60 percent, last of eight. Orders rank the rooms; orders per room entry rank the hosts. The other options may be true of some GMV definition, but none of them explains the ranking.

3 · Pasar Live's Hold counts completers, viewers who stay past minute 10. What does it divide by?

Hold grades whether the room kept the people it got, so its denominator is everyone the room got. Dividing by the viewers present at minute 10 counts only survivors and pushes Hold towards 100 percent by construction.

Sources covered

What this session teaches, and where it came from

✓The stage metrics: Stop, Hold, Love, Buy and the North StarPasar Live's constructed naming and definitions, including the minute-10 completer rule; the platform's own version is taught in the live commerce analytics course, linked
✓The averaging trap: ratio of sums against mean of ratiosComputed on the constructed eight-stream table; every printed value from code run in this build (pandas 2.1, duckdb 1.5)
◐Fair host comparison across brands and host typesPractice, not a cited study: compare within host type and brand first, label any comparison across; the modelling is the live commerce course's
✓A headline metric's definition is part of the numberSea Limited, Annual Report on Form 20-F for the fiscal year 2019, "Conventions" definitions

Every fact on this page, and its verification tier, is recorded in the course's source map.

Analyst session 5 cheat sheet · pin this

A slash is a handoverEvery stage names its numerator and denominator; the denominator decides whose work is graded.
Hold divides by entriesCompleters (past minute 10, constructed) over everyone who walked in, never over the survivors.
Say which averageRatio of sums is the channel (0.96%); mean of ratios is the typical stream (1.71%).
Pool within groupsInside a host type the two averages agree; the gap was the mix.
Orders rank roomsThe North Star ranks hosts. Compare within host type and brand; label anything across.
Divide onceCounts all the way up, one stage dictionary, applied at the grain you report, in pandas and SQL alike.