Where session 6 sits
Session 5 scored every minute of the 60 Lumen Glow streams and produced the segment table: CTA minutes sell at 11.57 orders per thousand viewer-minutes, dead air at 0.71. That table is descriptive. It says which minutes sold, not what made them sell, and a CTA minute in a room of 1,500 and a CTA minute in a room of 300 are in it side by side. Tonight separates the room from the pitch, with two regressions and a tree model, and turns what survives into the playbook the leader track hands to hosts.
★ What you walk out with tonight. Two regressions that disagree about the giveaway and are both right: pooled across streams the giveaway opener is worth plus 5,576 dollars of GMV, within a stream a giveaway minute costs 2.31 points of click-to-order rate. A LightGBM 4.7.0 model with SHAP 0.52.0, held out by stream, whose ranking sides with the second regression. And a playbook, written to data/derived/playbook.csv, whose every line carries its within-stream effect and its p-value.
LiveTwo answers to one question3 min▶
"What do the top streams share?" sounds like one question. Run it against the summary table and you get the traffic answer: the top ten by GMV are all human hosts, nine of the ten opened with a giveaway, and giveaway streams average 10,364 views against 7,546. That is real. The platform's boost rewards a giveaway opener, x1.93 against x1.25, and the room fills.
Run it against the minute table, comparing every stream with itself, and you get the selling answer: inside a stream, the minutes where the giveaway is live convert 2.31 points worse than the same stream's other minutes. Also real. The room is bigger and browsing for a free serum, and it clicks less on the card that costs 29 dollars.
The pooled regression sees streams, so it sees traffic. The within-stream regression sees minutes of one stream, so the traffic is held fixed and only the pitch is left to vary. Neither can answer the other's question, and the mistake is not picking the wrong one, it is not knowing there are two.
Lumen Glow's brand lead came to the data team with the top-ten list and a plan to make every stream open with a giveaway. The pooled number backed her: plus 5,576 dollars a stream. The within-stream number said the free serum was pulling a bigger, colder room. Both went on the same slide, on separate lines: a giveaway is a traffic decision, priced in views, and it is paid for in conversion. She kept the giveaway on the two weekend slots where the boost mattered most and dropped it elsewhere.
Self-studyThe vocabulary tonight leans on4 min▶
- Pooled regression - one OLS over all rows as if they were exchangeable. Across streams it compares stream A to stream B; across minutes it compares minute 43 of stream A to minute 12 of stream B.
- Fixed effects - a separate intercept per stream, so each stream is compared only with itself. Everything constant inside a stream (host, hour, weekday, the traffic draw, the giveaway opener) is absorbed and cannot confound.
- Demeaning - the arithmetic that implements fixed effects: subtract each stream's mean from every column, then run OLS without an intercept. The stream dummies never have to be built.
- Clustered standard errors - minutes of one stream are not independent draws; they share a room, a host, a mood. Clustering by stream tells the standard error to expect that, so the p-values are honest about 60 streams rather than pretending to 7,200 free observations.
- Confound - a variable that moves both the treatment and the outcome. Here, the platform's boost moves both "giveaway" and "GMV", which is why the pooled coefficient is a traffic number wearing a giveaway costume.
- SHAP - a per-row, per-feature share of a model's prediction, summing to the prediction. Mean absolute SHAP over rows is a feature ranking.
- Held-out stream - a stream the model never trained on. Holding out rows instead lets the model see minute 42 and be asked about minute 43 of the same stream, which is not a test.
Pooled versus within
9 min liveSame data, same giveaway column, two lenses. The first lens looks across the 60 streams and finds a large positive coefficient with a p-value below one in ten million. The second looks inside each stream and finds a modest negative one. The diagram puts them side by side so you never again read one without asking for the other.
LiveThe naive read and the pooled OLS3 min▶
Start where everyone starts, with the summary table sorted by GMV. Nine of the top ten opened with a giveaway, against 32 of 60 overall. Giveaway streams average 16,904 dollars of GMV against 15,265, and 10,364 views against 7,546. The eye says "giveaway wins", and a regression that treats streams as the unit says the same thing with a p-value attached.
The pooled model is gmv ~ giveawayOpen + C(hostType) + C(hour) on the 60-row summary. Controlling host type and hour is the responsible-looking move; it removes the AI-host penalty and the evening effect. The giveaway coefficient survives it: plus 5,576 dollars, p below 1e-7. That is not a fluke and it is not wrong. It is the platform's boost, x1.93 against x1.25, showing up as GMV, because GMV is traffic times selling and the giveaway bought traffic.
Look at the same summary table's other columns and the seam is already visible. Giveaway streams have a lower CTOR, 11.16 against 13.10, a lower GPM, 1,547 against 1,983, and a shorter watch, 8.67 against 9.38 minutes. More people, converting less. The pooled GMV regression cannot see that because GMV adds the two together.
LiveWithin-stream demeaning by hand3 min▶
Fixed effects sounds like a technique; it is a subtraction. For every column in the model, subtract that stream's mean from every one of its minutes. Then run OLS on the demeaned columns with no intercept. Whatever was constant within a stream is now zero everywhere, so the host, the hour, the weekday, the traffic draw and the giveaway opener all vanish from the comparison. What is left is minute 43 against minute 12 of the same stream. Standard errors are clustered by stream, because 120 minutes of one room are not 120 independent draws.
The outcome is CTOR in points, orders over clicks, and the regressors are the segment indicators, the three visual columns, and whether the giveaway is live in that minute.
| Regressor | CTOR, points | p | Read |
|---|---|---|---|
| CTA minute | +6.50 | below .001 | The ask moves the order |
| Price minute | +3.78 | below .001 | Saying the number sells |
| Short urgency (run 1-2) | +4.93 | below .001 | Two minutes of scarcity work |
| Stretched urgency (run 3+) | -1.84 | .0001 | Minute three of "last chance" costs |
| Q&A minute | +0.96 | .012 | Small, positive, real |
| Hook minute | -3.18 | below .001 | Openers do not sell; they are not meant to |
| Filler minute | -2.64 | below .001 | Nothing to buy into |
| Dead air | -4.72 | .0013 | The worst minute you can have |
| Giveaway live | -2.31 | .010 | A bigger, colder room |
| Product in hand | +0.04 | .81 | No effect on the order |
| Close-up ratio | +0.46 | .65 | No effect on the order |
| Price overlay | -0.13 | .58 | No effect on the order |
Three rows say nothing, and that is the finding of the night, not a disappointment. Product in hand, close-up ratio and a price on screen do not move CTOR. Run the identical regression with CTR as the outcome and the same three rows come alive.
| Regressor | CTR, points | p |
|---|---|---|
| Product in hand | +1.38 | below .001 |
| Close-up ratio | +1.01 | below .001 |
| Price overlay | +0.97 | below .001 |
| CTA minute | +3.28 | below .001 |
| Demo minute | +2.54 | below .001 |
| Price minute | +1.55 | below .001 |
| Q&A minute | -0.97 | below .001 |
| Giveaway live | -0.05 | .71 |
LiveReading the two tables together3 min▶
Put the CTR table over the CTOR table and a clean split appears. What you show moves the click. Product in hand, a close-up, a price card on screen: each adds about a point of CTR and none of them touches CTOR. The viewer taps the card because they saw the thing. What you say moves the order. CTA, price, a short burst of urgency: each adds points of CTOR, and the CTA adds points of both. The viewer buys because they were asked, told the number, and given a reason to do it now.
The Q&A row is the one worth pausing on. It lowers CTR by a point (the host is looking at comments, not the product) and raises CTOR by a point (the person whose question got answered converts). A per-viewer read from session 5 says Q&A minutes sell 3.68 orders per thousand viewer-minutes, below the mean of 5.80, so on its own it is a weak selling minute. Session 8's bench is where "answer the room every ten minutes" gets priced properly, and it comes out at plus 4.4 percent GMV with the longest watch of any single cue.
You do not have to demean by hand. linearmodels 7.0 does the same arithmetic in one line: PanelOLS(y, X, entity_effects=True) on a MultiIndex of (stream_id, t), then fit(cov_type="clustered", cluster_entity=True). Its coefficients match the hand version to the third decimal. Tonight's build-along does it by hand once, so you know exactly what the one line is doing and can explain it to the person who asks.
Maya's stream 17, Wednesday 21:00 with a giveaway opener, took 29,799 dollars on 16,404 views at a CTOR of 11.58. Priya's stream 16, Monday 18:00 with no giveaway, took 11,217 dollars on 4,858 views at 14.01. Ranked by GMV, Maya's stream is the one to copy. Ranked by GPM, 1,817 against 2,309, Priya's is. The pooled regression is ranking Maya's traffic. The within regression is measuring what each host did with each minute, and by that measure the Monday stream is the better pitch.
Self-studyWhere DoWhy and EconML pick up4 min▶
The within-stream regression answers "on average, inside a stream, what does a CTA minute do to CTOR". It does not answer "for which hosts, in which rooms, does it do the most". That is a heterogeneous-effects question, and two libraries in the panel are built for it.
- DoWhy 0.14 makes the causal claim explicit before estimating it:
CausalModel(data, treatment, outcome, graph), thenidentify_effect(),estimate_effect(), and arefute_estimate()step that adds a random common cause or a placebo treatment and checks the estimate survives. The graph is where you write down that the boost moves both the giveaway and the room, which is exactly what the pooled regression could not say. - EconML 0.17.0 estimates conditional effects:
LinearDMLorCausalForestDMLwith the minute's context (room size, minute of show, host) as the effect modifiers. The output is a per-minute estimate of what a CTA is worth, which is what a coach needs at minute 43 and a pooled average cannot give.
Both need the identification argument that fixed effects gave you tonight: within a stream, what varies is the pitch. The method ladder for that argument, from DAGs through backdoor adjustment to doubly robust estimators, is a course of its own: learn causal inference with phoebe.
A model that agrees
8 min liveA regression with hand-chosen columns is one witness. A gradient-boosted model that was never told which columns mattered is a second, and if its ranking lands on the same columns the within-stream table found, the playbook rests on two legs. If it lands somewhere else, you have learned something before a host did.
LiveLightGBM, split by stream and not by row3 min▶
The model is LightGBM 4.7.0's LGBMRegressor on per-minute orders, with 24 features: the traffic columns (room, impressions), the content columns (the three visual scores, scene cuts, words per minute, the urgency run counter, the eight segment indicators), the giveaway state, the clock (hour, minute of show), the engagement counts, and whether the host is an AI avatar. The settings are modest on purpose: n_estimators=400, learning_rate=0.03, num_leaves=15, min_child_samples=40, subsample=0.8, subsample_freq=1, colsample_bytree=0.8, random_state=20260924. Fifteen leaves and forty minimum samples per leaf keep it from memorising individual minutes.
The split is the part that matters. Twelve streams, chosen by a seeded shuffle, are held out whole, every minute of them unseen by the model. Splitting by row would let the model train on minute 42 of stream 9 and be tested on minute 43 of the same stream, and adjacent minutes of one room are near copies of each other. That test scores brilliantly and tells you nothing about a stream you have not seen. On the 12 held-out streams the model's MAE is 1.43 orders per minute against 2.56 for predicting the mean; the model's error is a little over half the baseline's.
Say the baseline every time you say the score. "MAE 1.43" is a number. "MAE 1.43 against 2.56 for the mean" is a claim. The same rule returns in session 7 with the nowcast, where a naive baseline is the only thing standing between a forecast and a guess.
LiveSHAP and what the ranking says3 min▶
SHAP 0.52.0's TreeExplainer is exact for tree ensembles: for every held-out minute and every feature it returns the share of that minute's prediction the feature is responsible for, and the shares sum to the prediction. Take the absolute value, average over the held-out minutes, sort, and you have the ranking in the diagram.
Impressions is first at 1.058 and room is fifth at 0.414. That is the traffic, and it should dominate a model of order counts: a minute in a room of 1,500 has more orders than a minute in a room of 300 whatever the host does. The model is not being clever there. The interesting part is everything between and after: is_cta at 0.684, close_up_ratio at 0.463, product_in_hand at 0.417, fomo_run at 0.310, is_hook at 0.261, then price_overlay and is_price at 0.19. Those are, almost column for column, the rows of the two within-stream tables that had a p-value below .05. The model found them without being told, from a different objective (orders, not a rate) with a different method (trees, not demeaned OLS).
Note what is not in the top ten: giveaway_live and giveaway_open. In a model that already has the room and impressions as features, the giveaway has nothing left to explain. Its whole effect on orders was the traffic it bought, which the model reads directly from the room. That is the pooled-versus-within lesson again, from a third angle.
is_ai is tenth at 0.153. The host being an avatar matters, and it matters less than what the host is doing in the minute. On TikTok Shop the point is moot: the Requirements for High-Quality Videos and LIVEs, effective 2026-09-17, require real-time verbal interaction and do not permit AI-generated voices on a promotional LIVE, so the AI-host streams in this data describe a trade that is not on offer there.
LiveThe playbook, with a number on every line2 min▶
A playbook line a host will follow needs a verb, a moment and a number. The number comes from the within-stream table, because that is the one that compares a host with herself; the model is the corroborating witness, not the source. Here is the table the leader track's sessions 3 and 4 hand out, as the build-along writes it.
| Lever | Moves | Within-stream effect | p |
|---|---|---|---|
| Say: the ask (CTA) | CTOR | +6.50 pts | below .001 |
| Say: the price | CTOR | +3.78 pts | below .001 |
| Say: two minutes of urgency, then stop | CTOR | +4.93 pts (run 1-2) | below .001 |
| Never: stretch the urgency past two | CTOR | -1.84 pts (run 3+) | .0001 |
| Show: product in hand | CTR | +1.38 pts | below .001 |
| Show: a close-up | CTR | +1.01 pts | below .001 |
| Show: the price on screen | CTR | +0.97 pts | below .001 |
| Answer the room | CTOR | +0.96 pts | .012 |
| Giveaway: a traffic decision | CTOR while live | -2.31 pts | .010 |
Short urgency on this data is minutes one and two of a run, with CTOR at 15.12 and 15.44 for those two minutes and 8.30 from the third. The playbook says two minutes because that is where the number turns, and because the TikTok Shop Content Policy (effective 2026-09-17) and the UK DMCC Act both treat a limited-time claim that is not genuine as a violation; a countdown that runs six minutes is not a countdown. The giveaway line is not a "never". It is worth 5,576 dollars of GMV through traffic on the pooled read and costs 2.31 points of CTOR while it is live, and whoever owns the traffic budget makes that call with both numbers in front of them.
Self-studyWhat the model cannot tell you3 min▶
A SHAP value is a share of a prediction. If the model had learned that CTA minutes tend to come after price minutes, and price minutes tend to be high-order minutes, the CTA feature would carry a large SHAP value for reasons that have nothing to do with the CTA. SHAP faithfully reports what the model used; it makes no promise that what the model used is what caused the orders. The within-stream regression, with the segment indicators entered together and the stream fixed, is the design that lets you read a coefficient as "holding the rest of the minute's content and the stream fixed, this segment does this". That is why the playbook cites the regression and merely mentions the model.
Two more things the model cannot say. It cannot price a cue the host has never done, because a tree has no leaf for it; the bench in session 8 is where a policy is actually applied. And it cannot separate "the host did a CTA" from "the host did a CTA at the right moment", because the moment is exactly the thing session 7's nowcast exists to find. The model tells you the CTA column matters. It does not tell you when.
The two regressions, the model, and the playbook file
★ 20 min · everyone buildsFive steps. The pooled OLS in one statsmodels formula, the demeaning function you write yourself, the same fit with CTR as the outcome, LightGBM with the stream-level split and SHAP, and a playbook CSV with the evidence beside every line. statsmodels 0.15.0, LightGBM 4.7.0, SHAP 0.52.0 (Python 3.12 or later).
The pooled OLS on the summary table. Sixty rows, one per stream. Control host type and hour so the giveaway coefficient is not carrying the AI penalty or the evening effect, and watch it stay large anyway.
Demean by stream, then OLS with no intercept and clustered errors. The function is the whole of fixed effects. The outcome is CTOR in points; minutes with zero clicks have no CTOR and are dropped, as analyze.py does.
The CTR version. Same function, same regressors, the click rate as the outcome. The three visual rows that were flat on CTOR come alive here.
LightGBM with a stream-level split, then SHAP. Shuffle the stream ids with the seeded generator, hold twelve out whole, fit, score against the mean-only baseline, and rank features by mean absolute SHAP over the held-out minutes.
Write the playbook with its evidence. One row per lever, which metric it moves, the within-stream coefficient and its p-value. The model is not in the file; it is the reason you trust the file.
If your hand-demeaned coefficients and PanelOLS disagree, the dropped rows are the reason. The zero-click minutes with no CTOR have to be dropped before the demeaning, not after, or the stream means are computed on rows the regression never sees. analyze.py drops them first; so should you.
Before session 7
30 min- Run the pooled minute-level regression,
cvr100on the same X with no demeaning, clustered by stream. Put its coefficients beside the within-stream ones and write one sentence per row where the two differ by more than a point. Every one of those rows is a confound with a name. - Add
time_effects=Trueto the PanelOLS call, so minute-of-show is absorbed as well as stream. Which coefficients move? The hook and the CTA both happen at predictable minutes, so this is a real test of whether the segment or the clock is doing the work. - Re-run the LightGBM split with a different seed three times. Record the MAE and the top five SHAP features each time. Report the ranking as "in the top five on every draw" and "in the top five on some draws"; a ranking quoted from one draw is a single sample.
- Take stream 17 (Maya, giveaway) and stream 16 (Priya, no giveaway). For each, compute the mean of every X column, and write the two rows of the playbook each host follows least. That is the coaching note the leader track's session 5 would hand them.
Three questions
The pooled regression says a giveaway opener is worth plus 5,576 dollars of GMV. The within-stream regression says a giveaway minute costs 2.31 points of CTOR. Which is wrong?
Both are right about different questions. GMV is traffic times selling. The giveaway buys traffic (boost x1.93 against x1.25, views 10,364 against 7,546) and costs selling (CTOR 11.16 against 13.10). The pooled model sees the product; the within model sees the second factor alone.
Why is the LightGBM test set twelve whole streams rather than a random 20 percent of minute rows?
The unit the model will be used on is a stream nobody has seen. The test has to be that unit. Held out by stream, MAE is 1.43 against a mean-only 2.56; held out by row it would look far better and mean far less.
The mean absolute SHAP value for is_cta is 0.684, second only to impressions. What does that number tell you?
SHAP attributes a prediction to features. It is faithful to the model, not to the world. The playbook cites the within-stream coefficient (+6.50 points of CTOR, p below .001) as the evidence and the SHAP ranking as the confirmation that a second method found the same columns.