cswork / libraryOpen practice workspace

Interview guides · Gametime United

Gametime United Data Scientist Interview Questions & Guide 2026

Modeling drills for a Gametime United data scientist interview: baselines, regression interpretation, class imbalance, model defense.

The 60-second brief

The role of Data Scientist at Gametime United is pivotal in transforming complex data into actionable insights that drive strategic decisions. As a Data Scientist, you will utilize statistical analysis, machine learning, and data visualization techniques to enhance user experience and optimize operational efficiency. Your work will directly influence product development, marketing strategies, and customer engagement, making this position not only critical but also immensely rewarding.

SQL is seldom the hardest round and is often the one that eliminates people. The working bar is usually window functions, correct deduplication, and joins that do not silently fan out rows, rather than obscure syntax.

Focus areas

  • Design matchmaking tests that survive interference
  • Reconstruct balances from a signed ledger
  • Diagnose churn by progression stage, not calendar day

Overview

In this role, you will collaborate with cross-functional teams, including product managers, engineers, and marketing specialists, to tackle complex problems across various domains, especially in marketing analytics for mobile platforms. You will be expected to analyze user behavior, forecast trends, and contribute to the overall mission of making event experiences seamless and enjoyable for users. This is an exciting opportunity to impact the business at scale, where your insights will shape the future of the company.

The Gametime United interview process

Gametime United candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Screening Call

Data Scientist covers at least four different jobs: experimentation, product analytics, causal work on observational data, and applied modelling that ships into a system. A screening call is the cheapest place to find out which of them is being hired for, and doing that diagnosis openly reads as senior rather than fussy. Ask what the last few pieces of work on the team actually were, and roughly how a week splits between querying, modelling and stakeholder time. Then say which parts of that you have done and which you have not. Claiming the whole range is the fastest way to be caught one round later.

What to demonstrate

  • Whether you can distinguish the flavours of the role and locate your own experience inside one of them honestly
  • Whether you name what you have not done instead of stretching to cover every line of the posting
  • Whether your hard constraints (notice period, location, work authorisation, level) surface now rather than at offer stage

How to prepare

  • Map the last two years of your time into rough percentages across query writing, experiment design, modelling and stakeholder work, so a question about scope has a real answer
  • Mark every responsibility in the posting as done, adjacent or new, and prepare one sentence for each adjacent item naming the closest thing you have actually built
  • Decide which logistics are non-negotiable before the call so you can state them in one sentence rather than negotiating live

Technical Assessment

Before anything else, this round is a reading test. You are given a small schema and a question phrased in business language, and most of the difficulty sits in the gap between them. Who counts as an active user, does a refunded order still count as an order, is that date column an event time or a load time. Weak answers start typing immediately and compute something precise about the wrong population. Strong ones pin the definition in one sentence, name the column that encodes it, then write the query. On a timed assessment with nobody to tell, write the definition in a comment anyway.

What to demonstrate

  • Whether an ambiguous term becomes a specific column and filter before any computation happens
  • Whether you read the schema for keys and cardinality rather than only for column names
  • Whether the result answers the question at the grain it was asked at, per user or per session or per day

How to prepare

  • Take three metrics you already use and write down the exact filter and exact grain behind each, then practise stating one of them in a single sentence out loud
  • On a schema you have never seen, spend the first minute writing what one row of each table means and which key it is unique on, then predict which joins can duplicate rows
  • Rehearse a version where the definition changes halfway through, and edit the query you have instead of starting over

Team Interviews

An extra round usually exists because something is still open after the standard loop: a skill the earlier interviews did not sample, a level decision, or two interviewers who disagreed. It is rarely a rerun of what you already did well. Ask the recruiter who you are meeting, what function they sit in, and how long the session runs. That is an ordinary scheduling question, and the answer changes what you should prepare. What separates a strong candidate here is treating the round as a fresh evaluation with its own bar, rather than assuming earlier performance carries you through or sinks you.

What to demonstrate

  • Whether you can answer well on ground the earlier rounds did not cover, without leaning on what you already said to someone else
  • Consistency of the facts in your stories: the same sample size, timeframe, team size and scope of your own role as in earlier conversations
  • How you handle an unfamiliar format live, including whether you ask what kind of answer is wanted before producing one

How to prepare

  • Ask the recruiter for the interviewer's function, the length, and whether to expect a coding surface, a discussion, or a presentation. Preparing for a 30 minute conversation with a partner team is not the same work as preparing for a 60 minute technical block.
  • Write out what each earlier round actually covered, then list the two or three areas nobody probed. That gap is the most likely subject of the extra round.
  • Re-read the numbers in the project stories you have already told, so a second telling does not quietly contradict the first.

Where candidates lose points

Treating the account as the person, and the calendar week as neutral time.

Reinstalls, multi-platform play and shared accounts split or merge humans relative to player_id, which inflates install counts and deflates retention denominators in ways that correlate with platform and campaign. Meanwhile daily actives spike on release day and decay over the following week, so a week-over-week read aliases the content calendar. Anchor comparisons to time since release or to matched days in the release cycle, and report identity-sensitive metrics with an explicit statement of which identity key was used.

Comparing retention or spend across progression stages reached.

Reaching level 20 requires having survived long enough to reach level 20, so grouping by max level attained conditions on the outcome you are trying to explain. Every correlate of playing longer then looks causal, and 'players who join a guild retain better' is the canonical version of this error. The defensible framing fixes a cohort at a common age or a common exposure point and measures forward from there, or models the hazard with progression as a time-varying covariate.

Dropping rows with missing values without naming the mechanism

Say whether the values are missing at random, missing by a known process, or missing in a way that depends on the outcome, and handle them accordingly. Deleting incomplete rows silently redefines the population whenever missingness correlates with what you are measuring.

Comparing periods without accounting for seasonality or day-of-week

Compare whole weeks against whole weeks and check whether the same swing appeared in prior cycles or prior years before attributing it to anything you changed. Weekday and weekend populations often differ enough that a Tuesday-to-Saturday comparison is meaningless.

Questions & practice

Implement a function in Python to calculate the mean and standard deviation of a list of numbers.

Medium · statistics and probability

Approach

  1. Quantify uncertainty explicitly rather than reporting a point estimate alone.
  2. Sanity-check the answer against a simple bound or a simulated case.
  3. Say what the estimate is of, and over what population it generalises.

Follow-up

  • Which assumption here is most likely to be violated in practice?
  • How would you explain this result to someone who does not know statistics?

Can you describe a machine learning project you've worked on and its impact?

Medium · machine learning and modelling

Approach

  1. Check what information would not exist at prediction time, and exclude it.
  2. Frame the prediction: the label, the moment of prediction, and the action it triggers.
  3. Set a baseline first, so any model has something honest to beat.

Follow-up

  • How would you choose the decision threshold, and who owns that choice?
  • Where could label leakage enter this setup?

Sink to faucet ratio per currency from a signed ledger

Easy · ledger accounting · groupby · metric definition

You are given ledger, a pandas DataFrame with one row per signed currency movement: ledger_id, player_id, occurred_at (UTC timestamp), currency_code, delta_amount (signed int64), flow_type in {faucet, sink, transfer, correction}, source_system, is_reversal (bool). Implement sink_to_faucet(ledger) returning one row per (currency_code, week_start) with faucet_units, sink_units and ratio = sink_units / faucet_units. Sink units are reported as positive magnitudes. Rows with flow_type in {transfer, correction} and rows with is_reversal true are excluded from both sides. A currency-week with zero faucet volume must return a null ratio rather than raise or return infinity.

Approach

  1. Filter first, aggregate second: build one boolean mask for flow_type in ('faucet','sink') and is_reversal == False, apply it once, so the exclusion cannot be applied to one side of the ratio and forgotten on the other.
  2. Derive week_start by flooring occurred_at to the Monday of its ISO week: occurred_at.dt.normalize() minus pd.to_timedelta(occurred_at.dt.weekday, unit='D'). Avoid dt.isocalendar().week on its own, which drops the year and collapses week 1 of two different years into one bucket.
  3. Split the aggregate by sign rather than by abs(): faucet_units = sum of delta_amount where flow_type == 'faucet', sink_units = sum of -delta_amount where flow_type == 'sink'. Taking abs() of the whole column hides a faucet row carrying a negative delta, which is a real data bug you want surfaced.
  4. Pivot to one row per (currency_code, week_start) and divide under a guard so faucet_units == 0 yields NaN, not inf; downstream charts silently drop NaN and silently rescale on inf.
  5. Run the same aggregate once with the is_reversal filter removed and diff the two results. Any currency whose ratio moves materially is telling you that clawbacks or customer-service corrections are being read as genuine economy flow.

Worked solution (20 min)

  1. Apply the single inclusion mask and assert that the filtered frame contains only the two flow types you intend.
  2. Add the week_start column by weekday subtraction and confirm every value is a Monday at 00:00 UTC.
  3. Aggregate faucet and sink magnitudes separately with a groupby on (currency_code, week_start), then join the two results on that key with an outer join so a currency-week with sinks but no faucets survives.
  4. Divide under a where() guard and return the frame sorted by currency_code then week_start.

Expected result: A frame keyed by (currency_code, week_start) carrying faucet_units, sink_units and ratio, where ratio is NaN rather than inf for any week with zero faucet volume, and rows never disappear because one side of the ratio was empty.

Verify it yourself

  • Recompute one currency-week by hand from a filtered slice and confirm both aggregates match to the unit.
  • The row count equals the number of (currency_code, week_start) pairs present after filtering, with no week silently dropped by an inner join.
  • Running with and without the reversal exclusion changes the answer only for currencies that actually carry is_reversal rows.

Follow-up

  • The definition drops the reversal row but leaves the original entry it undid inside the aggregate. Defend that choice or change it, and say which direction the ratio moves either way.
  • The soft currency has held a ratio near 0.8 for six weeks. What do you look at next, and what evidence would let you say the economy is nevertheless fine?
  • How would you report this when a single live-ops event contributed 60 percent of the week's faucet volume?

Write a SQL query to find the top 10 most purchased items in the last month.

Medium · sql and data manipulation

Approach

  1. Say which table is the grain you start from, and join outward from it.
  2. Compute rates by summing numerator and denominator separately, never by averaging rates.
  3. State the window function and its partition and ordering out loud before writing it.

Follow-up

  • How does the query change if the join becomes one-to-many?
  • What breaks if events arrive late or out of order?

Stitch crash-fragmented sessions into play blocks with gaps and islands

Medium · window functions · gaps and islands · sessionisation

fct_session has session_id, player_id, session_start_ts, session_end_ts (NULL when the client crashed), duration_seconds (NULL alongside it), platform and ended_reason. A crash splits one sitting into two rows seconds apart. Group consecutive sessions for the same player and platform into play blocks, starting a new block when the gap from the previous end to the next start is 120 seconds or more. For NULL ends, impute session_start_ts plus that player's median duration_seconds on the same platform. Return one row per block.

Approach

  1. Build the imputed end in its own CTE: COALESCE(session_end_ts, session_start_ts + the median duration as an interval). Compute the median with PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration_seconds) grouped by player_id and platform over the non-NULL rows, then join it back. Players with no non-NULL session have no median, so decide explicitly whether to drop them and report how many.
  2. Get the previous end with LAG(imputed_end) OVER (PARTITION BY player_id, platform ORDER BY session_start_ts, session_id). The session_id tiebreak matters because two sessions can share a start timestamp on a device that relaunched instantly.
  3. Flag a block boundary where prev_end IS NULL (the first session in the partition) OR session_start_ts - prev_end >= interval '120 seconds', and cast that flag to an integer.
  4. Turn flags into block ids with SUM(flag) OVER (PARTITION BY player_id, platform ORDER BY session_start_ts, session_id ROWS UNBOUNDED PRECEDING). This is the gaps-and-islands step. The ROWS frame here is explicit, not load-bearing: session_id is unique, so every row is its own peer group and the default RANGE frame would produce the same block ids. What actually keeps two sittings apart is the session_id tiebreak, which is why it has to appear in all three window clauses.
  5. Drop that tiebreak and the ordering stops being total, which breaks the step in one of two ways depending on the frame. Under the default RANGE, sessions sharing a start timestamp are peers, so each receives the tie group's closing cumulative total, they share a block id, and two genuinely separate sittings merge. Under ROWS they get distinct ids, but the order inside the tie is unspecified, so which session opens the block, and the LAG that fed the boundary flag, can differ between runs on identical data.
  6. Aggregate by (player_id, platform, block_id) for MIN(session_start_ts), MAX(imputed_end), COUNT(*), the summed in-session seconds and BOOL_OR(ended_reason = 'crash') to mark blocks stitched across a crash.
  7. State the direction of the imputation bias: if the imputed end lands earlier than the true end, the gap is overstated and the query under-stitches, so the crash-stitch rate under this rule is a lower bound.

Worked solution (35 min)

  1. Compute per-(player_id, platform) median duration_seconds over rows where it is NOT NULL, and count how many players have no median at all.
  2. Build the imputed_end CTE and confirm no NULL ends remain among the rows you kept.
  3. Add LAG, the boundary flag and the cumulative-sum block id in one CTE, all three carrying the same PARTITION BY (player_id, platform) and the same ORDER BY (session_start_ts, session_id). If the three orderings disagree, the flags and the ids are computed over different sequences and the block boundaries land on the wrong rows.
  4. Aggregate to block grain and spot-check a player known to have crashed: their two adjacent rows should share one block id.
  5. Recompute with the threshold at 30 and at 600 seconds and watch the block count move. A threshold that changes nothing means crashes are not in fact producing adjacent rows.

Expected result: One row per (player_id, platform, block_id) with block_start, block_end, sessions_in_block, played_seconds and crash_stitched. Most blocks marked crash_stitched carry at least two sessions, though a crash that ended play for the night is legitimately a single-session block.

Verify it yourself

  • SUM(sessions_in_block) must equal the count of input rows retained after imputation. A mismatch means a row fell out of a partition.
  • No two blocks for the same (player_id, platform) may overlap in time; verify with a LAG on block_start against the previous block_end.
  • Raising the gap threshold must monotonically decrease or hold the block count, never increase it.
  • Re-run the block id ordered by session_start_ts alone under the default RANGE: a player with two sessions sharing a start timestamp should lose a block to the merge. If the block count is unchanged, your sample has no tied starts and the tiebreak is untested.

Follow-up

  • The 120-second threshold is an assertion. How would you pick it from the distribution of inter-session gaps instead?
  • Under your imputation, does the block count over-count or under-count real sittings, and which direction would you rather err in for a crash-impact readout?
  • A player is on two platforms at once. Should that be one block or two, and what does your partition key currently decide for you?

How do you prioritize tasks when managing multiple projects?

Medium · product sense and metrics

Approach

  1. Fix the population and the time window before naming any metric.
  2. Decompose the metric into the rates that drive it, and say which one you would check first.
  3. Restate the decision this analysis has to support, and who acts on the answer.

Follow-up

  • Which segment would you cut first, and what would that rule out?
  • How would you detect that the metric is being gamed rather than genuinely improving?

How would you approach optimizing a marketing campaign based on past performance data?

Medium · product sense and metrics

Approach

  1. Restate the decision this analysis has to support, and who acts on the answer.
  2. Name one primary metric, then the guardrail that stops it being gamed.
  3. State what result would change your recommendation, so the answer is falsifiable.

Follow-up

  • Which segment would you cut first, and what would that rule out?
  • What would you do if the primary metric and the guardrail moved in opposite directions?

Given a dataset of user interactions, how would you identify trends in user behavior?

Medium · product sense and metrics

Approach

  1. Decompose the metric into the rates that drive it, and say which one you would check first.
  2. Name one primary metric, then the guardrail that stops it being gamed.
  3. Restate the decision this analysis has to support, and who acts on the answer.

Follow-up

  • What would you do if the primary metric and the guardrail moved in opposite directions?
  • Which segment would you cut first, and what would that rule out?

How would you design a system to track user engagement across multiple platforms?

Medium · product sense and metrics

Approach

  1. Name one primary metric, then the guardrail that stops it being gamed.
  2. Fix the population and the time window before naming any metric.
  3. State what result would change your recommendation, so the answer is falsifiable.

Follow-up

  • What would you do if the primary metric and the guardrail moved in opposite directions?
  • Which segment would you cut first, and what would that rule out?

What is the purpose of A/B testing, and how do you implement it?

Medium · a/b testing and experimentation

Approach

  1. Decide the analysis before seeing data, including how long it runs and when you look.
  2. Say whether units interfere with each other, and switch design if they do.
  3. State the primary metric and the minimum effect worth shipping, then size the test.

Follow-up

  • What would you do if you could not randomise at all?
  • What would you conclude if the result is positive but the test is underpowered?

Explain how you would ensure data integrity and security in your data processing workflows.

Medium

Approach

  1. Work from the decision backwards to the evidence you would need.
  2. State your assumptions explicitly before working the problem.
  3. Clarify what is being asked and what a complete answer would contain.

Follow-up

  • How would you know your answer was wrong?
  • What assumption would you test first?

Build the scorecard for a season pass without overstating young cohorts

Medium · cohort maturity · refunds · decomposition · net revenue

A season pass (item_class = 'battle_pass') sells for real money and pays premium currency back across its tiers. Two weeks after launch, a deck reports gross bookings per install for the launch-week cohort against last quarter's cohort and claims a 40% lift. Using fct_iap_transaction (price_usd_gross, platform_fee_usd, price_usd_net, status, refund_ts, purchase_ts), fct_currency_ledger and dim_player, rewrite the scorecard: give the primary metric with its maturity rule, two guardrails, and the reason the reported lift is not evidence.

Approach

  1. Kill the comparison first. A two-week-old cohort has two weeks of purchases and almost no refund exposure, while last quarter's cohort has a full quarter of both. Gross bookings also ignores platform_fee_usd, which is a percentage haircut that differs by store_channel. The two numbers are not one quantity measured twice, so their ratio means nothing.
  2. Primary: net revenue per install at a fixed cohort age of 90 days, computed only for install cohorts that have reached that age. Fixing the age fixes refund exposure as well: a reversed purchase leaves status = 'completed' whenever the reversal lands, so a cohort's net revenue keeps falling for weeks after purchase and only equal-age reads compare equal amounts of that decay. The launch cohort has no primary number yet, and saying so plainly is the answer rather than a dodge.
  3. Give the team something honest to read at two weeks: net revenue per install at 14 days of cohort age, compared only against the 14-day value of earlier cohorts. Fixed-age against fixed-age, never as-of-today against as-of-today.
  4. Guardrail one: refund and chargeback rate at 60-day maturity on pass purchases, restricted to purchase-date cohorts at least 60 days old. This is precisely the exposure the two-week read is structurally blind to.
  5. Guardrail two: premium-currency faucet volume with source_system = 'pass_tier', reported next to the premium sink-to-faucet ratio. A pass that pays back most of its price in currency is partly a discount on future purchases, and that cost lands in the economy rather than on the pass's own revenue line.
  6. Decompose so any movement is attributable: net revenue per install equals paying conversion multiplied by net revenue per payer. State which factor moved before recommending anything, because the two imply opposite follow-ups.

Worked solution (30 min)

  1. Write the net revenue definition once, in full: sum of price_usd_net over rows with purchase_ts inside the cohort window and status = 'completed'. There is no second refund term. status is current state, so a reversed purchase has already left that filter, and subtracting it as well deducts the refund twice. If your warehouse instead freezes status at purchase time, the reversal term keyed on refund_ts is required, so confirm which column carries the current state before writing the SQL.
  2. Write the maturity rule: fixed cohort age, with younger cohorts excluded rather than extrapolated or annualised.
  3. Build the fixed-age comparison table, every cohort at 14, 60 and 90 days, so a young cohort shows a blank cell rather than a misleading one.
  4. Add both guardrails with their windows and their cohort restrictions.
  5. Write the decomposition identity and the rule for which factor you report first.

Expected result: Primary: net revenue per install at a fixed 90-day cohort age, reported only for matured cohorts, with the launch cohort deliberately blank; an interim 14-day-at-14-days comparison; guardrails of 60-day refund and chargeback rate and premium faucet volume from pass tiers paired with the premium sink-to-faucet ratio; and an explicit statement that the 40% lift compares two maturities and two fee treatments.

Verify it yourself

  • Your scorecard has an empty cell for the launch cohort's primary metric, and that blank is the correct output rather than a gap to fill.
  • Net appears wherever revenue appears, and no metric in your answer uses price_usd_gross.
  • Each refund reduces net revenue once. Filtering to status = 'completed' and then subtracting reversed rows applies the same deduction twice and understates net revenue by the full refunded amount.
  • At least one guardrail sits in the currency ledger, because the pass ships a faucet as well as a price.

Follow-up

  • The pass pays back 900 premium currency against a 1,000-currency equivalent price. What does that do to next quarter's currency-pack revenue, and which metric in your scorecard would show it first?
  • Last quarter's cohort is now 100 days old. Which of its numbers can you compare against the launch cohort today, and which cannot be compared at all?

Recent install cohorts look richer than mature ones

Medium · cohort maturity · refund recognition · revenue definition

Net revenue per install at cohort day 30 reads about 6% higher for install cohorts aged 30 to 45 days than for cohorts aged 120 days or more. Revenue is refund-adjusted using refunds recognised to date. Tables: dim_player (player_id, install_ts, acquisition_channel) and fct_iap_transaction (player_id, purchase_ts, price_usd_net, status, status_changed_at, refund_ts). Marketing wants to credit a creative refresh. Decompose the 6% and say what you would change about the chart.

Approach

  1. Separate the two clocks the chart conflates. Purchase accrual is capped at cohort day 30 by the metric definition, so it is the same for every cohort. Refund recognition is not capped: a refund lands weeks after its purchase, so a young cohort has observed only part of the refunds its day-30 purchases will eventually attract. The metric is therefore not comparable across cohort ages even though the accrual window is fixed.
  2. Measure the recognition curve from cohorts old enough to be complete. Using purchases at least 60 days old, compute the share of eventual 60-day refund and chargeback value that had been recognised by each day since purchase. That curve is the instrument for everything that follows.
  3. Apply the curve to size the bias. If r is the eventual share of net value refunded within 60 days and f is the share of that already recognised for a given cohort, measured net over matured net is (1 minus r times f) divided by (1 minus r). Compute f as a weighted average over the cohort's purchase-age distribution, not at the cohort's own age, because a cohort aged 40 days contains purchases from day 0 to day 30.
  4. Subtract the maturity term and decompose what is left by acquisition_channel using the standard mix and within-segment split, since a creative refresh usually moves channel mix as well as channel performance.
  5. Confirm nothing else changed in the pipeline over the comparison span: check store_channel availability, whether any price book or FX conversion changed, and whether platform_fee_usd rates moved, since all three alter price_usd_net without any player behaving differently.
  6. Fix the chart rather than the number: restrict it to cohorts that have passed a fixed maturity, or hold refunds at a fixed observation lag so every cohort is measured with the same refund window.

Follow-up

  • How long must a cohort be to appear on this chart, and how do you answer the executive who wants a read on last week's cohort?
  • Would you rather fix the maturity by dropping young cohorts or by using the recognition curve to project them, and what does each choice cost you?
  • If the creative refresh genuinely raised revenue per install, how would that look different from what you found here?

Suggested practice checklist

For someone who has spent the last year in notebooks, dashboards or modelling work and has not written raw SQL under time pressure. The first four days rebuild query fluency against a fixture you control and can verify by hand; the last three attach that fluency to the rest of the loop.

Day 1 — Build a fixture you can check answers against

  • Create a local Postgres or SQLite database with four tables (users, sessions, events, orders) holding roughly 200 rows you generated yourself, so you know the contents well enough to predict every result.
  • Deliberately seed the cases that break queries: a user with no sessions, a session with no events, two orders sharing a timestamp, a NULL in one join key, and one duplicated user row.
  • Before writing any SQL, hand-compute five answers on paper (how many users placed at least one order, median orders per ordering user, and three others) and save them as the ground truth for the week.

Deliverable: A one-command seed script plus a text file of five hand-computed answers to grade every later query against.

Day 2 — Joins, filters and NULL semantics

  • Answer "which users have no orders" three ways (LEFT JOIN with IS NULL, NOT EXISTS, NOT IN) and confirm that the NOT IN version returns zero rows once the subquery contains a NULL, because the comparison is never TRUE.
  • Reproduce the LEFT JOIN that silently collapses to an inner join by putting a right-table predicate in WHERE, then fix it by moving the predicate into the ON clause, and record both row counts.
  • Create a fan-out bug on purpose by joining orders to order_items and summing the order total, then correct it with a pre-aggregated subquery and explain in one line which table changed the grain.

Deliverable: One annotated .sql file holding the three join traps, each with the wrong result and the corrected result side by side.

Day 3 — Window functions and frames

  • Write three window queries against the fixture: a running order total per user, the rank of each order within its user by value, and the day gap to that user's previous order, then check each against the day-one ground truth.
  • Run ROW_NUMBER, RANK and DENSE_RANK over a column containing ties, print all three side by side, and write one sentence on when each is the correct choice.
  • Switch one query from the default frame (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which is what you get when ORDER BY is present and no frame is written) to ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and explain why the output differs only when the ORDER BY column has duplicates.

Deliverable: Three verified window queries plus a short note explaining the RANGE versus ROWS difference in your own words.

Day 4 — The four analytical query patterns

  • Write a monthly retention grid: first order month per user, then months-since-first as the column, and verify that month zero equals the cohort size exactly.
  • Sessionize the events table under a 30-minute inactivity rule using LAG plus a cumulative sum over a new-session flag.
  • Build a four-step funnel that counts distinct users rather than events at each step, and state the rule you applied to a user who reaches step three without ever logging step two.

Deliverable: One file with the retention, sessionization and funnel patterns, each carrying a one-line note on the assumption it bakes in.

Day 5 — Write SQL the way you will have to write it live

  • Set a 12-minute timer and solve three medium prompts in a plain editor with no execution and no autocomplete, then run them and tally syntax errors separately from logic errors.
  • Narrate one solution aloud while writing it, stating the grain of each intermediate result (one row per user, one row per user-day) before you type its body.
  • Rewrite your slowest solution as a CTE chain where every CTE name states its grain, and time yourself re-solving it from blank.

Deliverable: A recording of one narrated solution plus an error tally that separates syntax from logic.

Day 6 — One day for everything that is not SQL

  • Write the preconditions of the two-sample t-test from memory, then check them: independent observations, and a difference in means whose sampling distribution is approximately normal, which at large sample sizes follows from the central limit theorem rather than from normality of the raw values.
  • Write the difference between an odds ratio from logistic regression and a relative risk, and state the condition under which the two are close (low outcome prevalence).
  • Prepare a 90-second answer to "how would you know this model is any good" that names the metric, the baseline you would beat, and the cost of the errors you care about.

Deliverable: One page of notes covering test preconditions, the odds-ratio caveat and the model-quality answer.

Day 7 — Full loop rehearsal

  • Run a 45-minute mock with someone willing to interrupt: 20 minutes of SQL, 15 minutes defining a metric, 10 minutes on a past project.
  • Re-solve from blank the two queries you were slowest on this week and compare the times against day five.
  • Write a five-line answer to "walk me through a project" that puts a number in the first sentence and names the decision the work changed.

Deliverable: Mock feedback notes plus a timed project narrative you can deliver without reading it.

Rigour against a deadline

Nearly every data role forces a trade between the analysis you want and the one that fits the decision window. Prepare a case where you deliberately shipped something less rigorous, named the weakness to the person relying on it, and said what would change your answer. The naming is the part interviewers listen for.

Describe your experience working in cross-functional teams.

Medium · behavioural and stakeholder questions

Approach

  1. State the situation in two sentences and spend the rest on your reasoning.
  2. Close with what you would do differently, concretely.
  3. Pick a story where you drove the decision, not one where you observed it.

Follow-up

  • How did you know the outcome was caused by your change?
  • What did you decide not to do, and why?

Explain a wide revenue interval to a non-technical executive

Easy · uncertainty communication · heavy tails · bootstrap · decision framing

A 14-day test of a new currency-pack price is done. Net ARPDAU is up 4.1% with a bootstrap 95% interval of -1.8% to +10.3%. Paying conversion is up 0.6 percentage points with a much tighter interval. In both arms the top 1% of payers hold 42% of net revenue. An executive with no statistics background asks whether this is a win, yes or no, and has ten minutes. Give the explanation, the recommendation, and the claim you refuse to make.

Approach

  1. Answer the decision first, in one sentence, then explain. An executive who waits ninety seconds for a recommendation stops listening to the reasoning.
  2. Convert the interval into stakes in their units. Translate -1.8% to +10.3% of net ARPDAU into a monthly or annual dollar range at current active player-days, and say which end you would budget against.
  3. Explain the width with a fact they already believe rather than with sampling theory: 42% of net revenue sits on 1% of accounts, so a handful of large purchases move the average. That is a property of the business, not a defect of the test, and it is why a conversion number of the same length is far tighter.
  4. Offer the conversion result as the part of the decomposition that does have enough sample, and be explicit that it is a different claim: more people paid, and the size of the average payment is where the uncertainty lives.
  5. Price more data honestly. The interval narrows roughly with the square root of exposure, so halving its width needs about four times the runtime. Give the calendar date that implies and say whether the decision is worth waiting for.
  6. Name the claim you will not make, so nobody reports the point estimate as fact after you leave the room.

Follow-up

  • The executive suggests capping the top payers and rerunning the numbers to tighten the interval. What do you say?
  • Bootstrap intervals can undercover on a very heavy tail. How would you check whether yours is trustworthy at this sample size?
  • If the interval had been -0.4% to +1.2%, would your recommendation change, and why?

State your own impact on a number you did not own

Hard · impact measurement · attribution · counterfactuals · self-assessment

You are writing a one-page performance case that a committee with no context will read. Over two quarters, net revenue per active player-day rose 11%. In that period you designed the pricing experiment behind one shipped change, found and fixed a ledger bug that had been double-counting a faucet in reporting, and argued a team out of a store redesign that a later test showed would have been flat. Write the impact claim you can defend, including the part of the 11% you will not take credit for.

Approach

  1. Refuse the aggregate as your claim, explicitly and early. The 11% contains seasonality, the release calendar, acquisition mix shift and other teams' shipped work. Claiming it invites exactly one question that then collapses the entire page.
  2. Claim only estimates that carry their own counterfactual. The pricing experiment has a randomised estimate, so quote the interval rather than the point, and quote the effect on net revenue per active player-day so it is on the same scale as the 11% without being confused for it.
  3. Price the ledger bug by what it corrupted rather than by revenue, because a reporting fix creates no revenue. Say how far the sink-to-faucet ratio was wrong, for how many weeks, and name the specific decision that was being made on the wrong number.
  4. Handle the prevented work as the weakest of the three claims and label it as such. State the engineering weeks not spent and cite the later test as the vindication, while being explicit that an avoided cost rests on a counterfactual and is worth less as evidence than a measured lift.
  5. Include one thing you got wrong in the same period, with what you changed. A page with no misses reads as unaudited, and a committee that finds the miss themselves discounts everything else.
  6. Make every claim checkable in under a minute: experiment name, date range, readout document, and the person who can confirm it.

Follow-up

  • A reviewer says the pricing lift would have happened anyway because a competitor changed prices that quarter. How do you respond?
  • How would you have measured the ledger bug's impact if no decision had yet been made on the wrong number?
  • What is the strongest evidence you could have collected at the time that you did not?