cswork / libraryOpen practice workspace

Interview guides · easyJet

easyJet Data Scientist Interview Questions & Guide 2026

easyJet data scientist prep on data pitfalls: selection bias, target leakage, and checking an outlier's provenance before you drop it.

The 60-second brief

As a Data Scientist at easyJet, you sit at the heart of one of Europe’s most recognizable travel brands. Operating a fleet that connects 38 countries, easyJet relies heavily on data to maintain its competitive edge as a low-cost carrier. Your work directly influences operational efficiency, on-time performance, and customer satisfaction, turning vast, complex datasets into actionable strategies that keep millions of passengers moving across the continent.

A large share of questions open as "how would you measure X", where the real work is choosing the metric, fixing its denominator, and defining the population it applies to. Any computation comes last and is frequently not required at all.

Focus areas

  • Compute occupancy from sellable nights, never property averages
  • Cohort bookings on stay date, not booking date
  • Model demand against availability, not observed bookings

Overview

This role is not purely academic; it is deeply embedded in the Operations Data & Intelligence team. You will be responsible for the full lifecycle of data science projects, from initial stakeholder engagement to the deployment of predictive and prescriptive models. Whether you are optimizing flight scheduling, enhancing fuel efficiency, or predicting operational bottlenecks, your contributions have a tangible impact on the company’s bottom line and the daily experience of easyJet's 18,000 colleagues.

The role requires a balance of technical rigor and business acumen. You must be comfortable explaining complex machine learning outputs to non-technical operational stakeholders while maintaining the high standards of a FTSE-250 data organization.

The easyJet interview process

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

Initial Screening

A screening call is a matching exercise run by someone who will not evaluate your statistics. They are checking that the work described on your resume is work you personally did, and that its scope matches the level the role is written for. Logistics get settled in the same half hour so nobody spends an interviewer's afternoon on a mismatch. The answer that fails is the one narrated in the plural. If every sentence is 'we built' and 'the team decided', there is nothing specific to write down about you. Name the piece that was yours, the decision you made inside it, and what changed after.

What to demonstrate

  • Whether the ownership implied by your resume survives one round of follow-up about who actually did which part
  • Whether your described scope (data size, stakeholders, what shipped) matches the seniority the role is written at
  • Whether timeline, location and compensation expectations make the rest of the loop worth scheduling

How to prepare

  • Rewrite your top three resume bullets in the first person singular, each with the decision you made and what moved afterwards, then say them out loud once so the 'we' does not return under pressure
  • Attach one number to each project: the baseline, the change, and the window it was measured over. Where impact was never measured, say that plainly rather than inventing a figure
  • Settle your compensation range before the call and give it as a range with a reason behind it, such as current total comp or a competing timeline, instead of deflecting the question twice

Deep-Dive Assessment

A handful of shapes account for most of what gets asked in this format: a ranking or deduplication inside groups, a running or rolling total, a period-over-period comparison, and a cohort tracked forward over time. Recognising the shape quickly is most of the speed here; deriving it from scratch while a clock runs is where the time goes. Know that a window function keeps every row while a GROUP BY collapses them, and know which one the question needs. If the exercise is in Python instead of SQL, the same shapes arrive as groupby with transform, shift and merge, and the same grain mistakes are available.

What to demonstrate

  • Whether you reach the right construct without a detour, such as ROW_NUMBER over a partition to deduplicate instead of a self-join against a MAX subquery
  • Whether you know what your window frame actually is, since adding ORDER BY inside OVER changes the default frame and silently changes a running total
  • Whether the thing runs. A near-miss that throws an error scores below a plainer query that returns the right rows.

How to prepare

  • Write each of the four shapes once from memory against a small schema and keep the working version somewhere you will reread it: dedupe with ROW_NUMBER, a running total, a month-over-month change with LAG, and a retention table
  • Compute one running total twice on data with tied timestamps, once on the default frame and once with ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and look at where the two disagree
  • If Python is on the table, rebuild the dedupe and the running total with groupby and cumsum, then assert the two implementations return identical rows

Onsite Assessment

Where a loop ends with a senior leader, that conversation is rarely another skills test. The technical signal already exists by then, so the questions tend to open up: what you would look at first, where a metric you have heard about could mislead, what you would push back on. The decision being made is scope, which in practice means level and how much you would be trusted to own unsupervised. Treating it as a formality is the usual mistake. An open question late in the day is still being scored, and a vague answer reads as someone who has not run anything themselves.

What to demonstrate

  • Whether your view of the business has anything specific behind it, given that you are working only from what is public and are expected to say so
  • Whether the scope of work you describe owning matches the scope of the role, instead of sitting a level below it
  • Whether you can disagree with something concrete and stay useful about it, rather than agreeing with everything said in the room
  • Whether your questions are ones only this person could answer, as opposed to ones the recruiter already covered

How to prepare

  • Build one view you could defend for two minutes using only public information: what the funnel probably looks like, which metric likely drives decisions, and where that metric could mislead. Being wrong for a stated reason survives this round; having no view does not
  • Write down the largest piece of work you have owned from question to decision, who else touched it, and what you decided alone, then check that it reads at the level you are interviewing for
  • Prepare one thing you would want changed if you joined and phrase it as a question rather than a verdict, so it opens a conversation instead of closing one

Where candidates lose points

Reading cancellation, completion or repeat rates on cohorts that have not matured

A cohort of bookings made last week for stays six months out cannot have cancelled at the check-in gate yet, so its cancellation rate is mechanically near zero and its completion rate mechanically near zero as well, in opposite directions. Comparing that cohort with a mature one is not a noisy comparison, it is a guaranteed wrong one, and the bias always makes the recent period look different in a way that invites a false story about a recent change. Because lead time is heavily right-skewed, the mean lead time is a bad maturity threshold; use the cohort's 95th percentile, or report a hazard at a fixed age (cancelled within k days of booking) with k capped at the youngest cohort's elapsed age. The same applies to repeat rate, where the honest answer is often that the cohort in question is not readable for another nine months.

Attributing a blended rate change to behaviour when the mix moved underneath it

Occupancy, average daily rate, take rate and conversion are all ratios computed over a heterogeneous pool, and their blended values move whenever the composition of markets, property classes, lead times, party sizes or contract types changes, with nothing changing inside any segment. This is Simpson's paradox with a seasonal engine driving it: a shift toward short-lead leisure demand lowers blended ADR and raises blended conversion at the same time, and both movements are mix. Year-over-year comparison does not rescue you by itself, because the calendar does not repeat: day-of-week alignment drifts and moving holidays shift by weeks, so the same ISO week is not the same demand week. Decompose every rate change into a within-segment component and a mix component before writing a sentence about cause, and align year-over-year comparisons on the local market calendar using market_holiday_flag rather than on the date.

Reporting a p-value with no effect size or interval

Give the estimated difference with a confidence interval in the units the business cares about, then say whether that whole interval is worth acting on. A p-value only addresses whether you can rule out exactly zero; it says nothing about magnitude.

Naming a model class before naming the deployment constraints

Set out the latency budget, the label delay, the retraining cadence, the interpretability requirement and the number of labelled examples, then pick the model that fits them. A boosted-tree answer to a problem where each decision must be explained to the affected user is a well-executed answer to the wrong question.

Questions & practice

How do you approach feature engineering for time-series data related to flight schedules?

Medium · machine learning and modelling

Approach

  1. Frame the prediction: the label, the moment of prediction, and the action it triggers.
  2. Set a baseline first, so any model has something honest to beat.
  3. Say how the offline result would be validated online before it is trusted.

Follow-up

  • What would you monitor after launch to know the model is still valid?
  • Where could label leakage enter this setup?

Describe a time you utilized a machine learning model to solve a complex operational bottleneck.

Medium · machine learning and modelling

Approach

  1. Check what information would not exist at prediction time, and exclude it.
  2. Pick an evaluation metric that matches the cost of each error type, not a default.
  3. Set a baseline first, so any model has something honest to beat.

Follow-up

  • Where could label leakage enter this setup?
  • What would you monitor after launch to know the model is still valid?

Decompose a blended ADR move into mix and rate

Hard · decomposition · mix effects · pandas · metric diagnosis

You have stayed-night data for two periods with destination_market_id, property_class, rate_plan, night count and unit_revenue_usd. Blended ADR fell 4.20 USD. Decompose that change into a within-segment rate component, a mix component and their interaction, so the three sum exactly to the blended delta. Segments exist in one period only, so an inner join is not acceptable. Return a per-segment table plus the three totals and the five segments with the largest absolute contribution. State the convention you use for a segment's missing base-period rate and what it does to the split.

Approach

  1. Write the identity before any code. Blended ADR is sum over segments of r_i * w_i where r_i is segment ADR and w_i is the segment share of stayed nights. Then ADR1 - ADR0 = sum of (delta r * w0) + sum of (r0 * delta w) + sum of (delta r * delta w), and that expands algebraically back to r1w1 - r0w0, so it is exact rather than approximate.
  2. Aggregate to the segment grain by re-summing revenue and night counts, then divide once. Averaging pre-computed per-night rates inside a segment reintroduces the same mean-of-ratios error the decomposition exists to expose.
  3. Outer-join the two periods so entering and exiting segments survive as missing values, then handle them deliberately: for an entering segment w0 is 0, so its within term is zero for any choice of r0, and the total is unaffected. The r0 convention only moves value between the mix and interaction terms. Setting r0 to the period-0 blended ADR reads as 'a new segment is average until proven otherwise', which puts its deviation from average in the interaction term.
  4. Assert the reconciliation to 1e-9 USD before reporting anything. If it fails, the cause is almost always a segment lost to an inner join or weights that do not sum to 1 within a period.
  5. Rank segments by the absolute sum of their three terms and report the top five with the terms shown separately, because a segment can have a large mix term and a near-zero rate term, and only the second of those is a pricing decision anyone made.

Worked solution (40 min)

  1. Aggregate each period to (destination_market_id, property_class, rate_plan): total revenue, total stayed nights; r = revenue/nights, w = nights/period total nights.
  2. Outer-join period 0 and period 1 on the segment key; fill missing w with 0 and missing r with the corresponding period's blended ADR, recording which rows were filled.
  3. Compute within = (r1 - r0) * w0, mix = r0 * (w1 - w0), interaction = (r1 - r0) * (w1 - w0).
  4. Assert the three column sums add to blended ADR1 minus blended ADR0 within 1e-9, and that w0 and w1 each sum to 1.
  5. Rank by abs(within + mix + interaction), return the top five with all three terms shown, and write one sentence naming which component carries the move.

Expected result: The three totals reconcile to the -4.20 USD blended delta to within 1e-9. A typical pattern for this shape of move is roughly -1.10 within segment, -3.30 mix and +0.20 interaction, supporting the statement that rates inside segments barely moved and the blended fall is a shift of stayed nights toward budget class and short lead times.

Verify it yourself

  • within + mix + interaction equals ADR1 - ADR0 exactly, to floating tolerance
  • Forcing w1 = w0 zeroes both the mix and interaction totals, leaving within equal to the whole delta
  • Forcing r1 = r0 zeroes both the within and interaction totals
  • Every entering segment has a within term of exactly 0, and changing the r0 convention leaves the reconciled total unchanged while moving value between mix and interaction

Follow-up

  • The mix term dominates. Name two operational causes that would produce exactly that pattern, and the query you would run to tell them apart.
  • Your comparison is the same ISO week a year apart. What breaks when a moving holiday falls inside one of them, and how do you align instead?
  • How would you present this so the decision-maker does not walk away believing prices were cut?

Twelve-month repeat-trip rate on mature first-stay cohorts

Medium · cohorts · retention · window-functions

fct_booking has traveller_id, booking_id, check_in_date, check_out_date and booking_status with values 'confirmed', 'amended', 'cancelled', 'no_show', 'in_stay' and 'stayed'. Compute the twelve-month repeat-trip rate: for travellers whose first stayed booking checked out in a given calendar month, the share with a second stayed booking whose check_out_date falls within 365 days of the first. Report cohort month, cohort size, repeat travellers and the rate. Publish only cohorts that have had a full 365 days to mature.

Approach

  1. Restrict to booking_status = 'stayed' before anything else. 'confirmed' and 'in_stay' bookings have not been delivered, and 'amended' must be checked against the lifecycle before you assume it is live.
  2. Number each traveller's stays with ROW_NUMBER() OVER (PARTITION BY traveller_id ORDER BY check_out_date, booking_id). The booking_id tiebreak makes the result deterministic when two stays end on the same date, which happens whenever a party books two units.
  3. Pivot the first two stays with conditional aggregation, MIN(CASE WHEN rn = 1 THEN check_out_date END) and the same for rn = 2, or self-join rn = 1 to rn = 2. Keep the second stay even when it lands outside 365 days, so the denominator is unaffected by the numerator condition.
  4. Decide and state whether a second booking ending the same day as the first counts as a repeat trip; requiring the second stay's check_in_date to be after the first stay's check_out_date is the defensible default and excludes same-trip extra rooms.
  5. Cohort on DATE_TRUNC('month', first check_out_date) and suppress with an explicit filter any cohort whose month end is less than 365 days before the data as-of date.

Follow-up

  • Your July cohort reads six points below January. Before calling that a regression, what do you check?
  • How does the answer change if you cohort on first booking creation instead of first completed stay, and which question does each version answer?
  • A traveller's first stay was fully refunded after check-out. Does it still open a cohort, and what does your choice do to the denominator?

Intent-to-booking conversion within a seven-day attribution window

Medium · funnel · conversion · attribution-window

fct_search carries trip_intent_key, searched_at_utc, device_type, lead_time_days, results_returned_count and is_bot_flagged; fct_booking carries booking_id, trip_intent_key, booked_at_utc and booking_status. For intents whose first qualifying search falls in one month, compute the intent-to-booking rate: an intent converts if any booking shares its trip_intent_key and was booked within seven days of that first search, counting bookings that later cancel. Qualifying searches are is_bot_flagged = FALSE with results_returned_count > 0. Report numerator, denominator and rate by device_type and lead-time bucket.

Approach

  1. Filter to qualifying searches first, then collapse to one row per trip_intent_key using MIN(searched_at_utc), and state the convention plainly: an intent whose earliest search returned nothing is cohorted on its first search that did return results.
  2. Carry device_type and lead_time_days from that first qualifying search, picked with ROW_NUMBER or FIRST_VALUE. One intent routinely spans mobile and desktop, and attributing it to the device of the converting search inflates whichever device closes.
  3. Aggregate bookings to one row per trip_intent_key with MIN(booked_at_utc) before joining. Joining raw bookings makes an intent with three bookings count three times in the numerator and, once grouped, in the denominator too.
  4. LEFT JOIN the booking aggregate onto the intent set and flag conversion where booked_at_utc falls within [first_search_at, first_search_at + 7 days]. Keep cancelled and no-show bookings in: this metric measures demand conversion, not delivery.
  5. Group by device and bucket, summing the flag and counting intents, and compute the rate as the ratio of those two sums rather than as an average of per-cell rates.

Worked solution (30 min)

  1. Count rows, distinct trip_intent_key and distinct visitor_id over the search window, so searches-per-intent is a number you can quote rather than assume.
  2. Build the intent CTE with first-qualifying-search attributes and verify its row count equals the distinct intent count.
  3. Build the booking CTE at one row per trip_intent_key and compare it with the raw booking count to see how many intents carry more than one booking.
  4. LEFT JOIN, apply the seven-day predicate, and confirm the joined row count still equals the intent count.
  5. Aggregate by device and bucket, then re-sum the cells and check the total against a directly computed overall figure.

Expected result: One row per (device_type, lead_time_bucket) with converted_intents, qualified_intents and the rate; the overall rate re-summed from the cells differs from the unweighted mean of the cell rates, and that difference is the mix.

Verify it yourself

  • The post-join intent count equals the pre-join intent count; any increase is fan-out from the booking side.
  • Every converted intent has booked_at_utc at or after its first_search_at.
  • Bookings with a NULL trip_intent_key never appear in the numerator; count them separately and report the share rather than losing them silently.

Follow-up

  • Mobile converts at half the desktop rate. How much of that gap is mix, and what decomposition would you run to show it?
  • How do you hold the seven-day window open without restating last week's published number every morning?
  • A ranking change cuts searches per intent by 20% and leaves bookings flat. What happens to this metric, and what happens to a search-denominated version of it?

How do you prioritize competing data science projects when resources are limited?

Medium · product sense and metrics

Approach

  1. Name one primary metric, then the guardrail that stops it being gamed.
  2. Decompose the metric into the rates that drive it, and say which one you would check first.
  3. Fix the population and the time window before naming any metric.

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?

Explain the trade-offs between optimization and simulation techniques in an operations context.

Medium · product sense and metrics

Approach

  1. Name one primary metric, then the guardrail that stops it being gamed.
  2. Decompose the metric into the rates that drive it, and say which one you would check first.
  3. Fix the population and the time window before naming any metric.

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?

Cut variance with CUPED and stratification on a seasonal outcome

Medium · cuped · variance-reduction · stratification

A 14-day test on completed stay-nights per exposed traveller is under-powered: the outcome is zero for 91% of travellers and heavy-tailed above that. You have 8 weeks of pre-period history. Pre-period completed stay-nights correlate with the in-test outcome at rho = 0.45, but only 38% of exposed travellers existed before the test started. Write the CUPED estimator, give the variance reduction it buys on paper and the reduction you will actually realise, say how you treat the 62% with no history, and name what you would stratify on.

Approach

  1. Write the estimator: Y_adj = Y - theta * (X - Xbar) with theta = Cov(Y, X) / Var(X) estimated pooled across arms, and the treatment effect taken as the difference in mean Y_adj. Because X is measured before assignment, using both arms to estimate theta does not bias the contrast.
  2. Give the variance identity Var(Y_adj) = Var(Y) * (1 - rho^2) and convert it into an MDE multiplier of sqrt(1 - rho^2), which is the number the sizing conversation actually needs.
  3. Handle the coverage problem without changing the population. Set X to its group mean for travellers with no pre-period so their adjustment term is exactly zero; the estimator stays unbiased and they simply contribute no reduction. Dropping them would redefine the test population as returning travellers only.
  4. Re-state the realised reduction over the full population rather than the headline one: if the two groups have comparable outcome variance, the reduction is roughly the covered share times rho^2, which is a much smaller number than the paper figure and is the one to put in the plan.
  5. Stratify on pre-assignment variables that predict the outcome: the has-history indicator itself, destination_market_id bucket, lead-time bucket, device_type and loyalty_tier. Use post-stratification with fixed population weights and keep strata coarse enough that every stratum holds both arms.
  6. Guard the heavy tail with a winsorisation percentile chosen and written down before unblinding, and report capped and uncapped estimates side by side.

Worked solution (30 min)

  1. Compute the paper reduction: rho^2 = 0.45^2 = 0.2025, so 20.25% of the variance, and an MDE multiplier of sqrt(0.7975) = 0.893, a 10.7% smaller MDE.
  2. Compute the realised reduction: 0.38 * 0.2025 = 0.077 under equal within-group outcome variance, an MDE multiplier of sqrt(0.923) = 0.961, so 3.9% smaller.
  3. Note the direction of the residual error: returning travellers usually carry the larger outcome variance, so 7.7% is a conservative floor rather than a point estimate.
  4. Define the strata grid and confirm every cell has both arms and enough mass to estimate a within-stratum difference, collapsing sparse cells upward before unblinding.
  5. Fix theta, the strata, the winsorisation percentile and the readout date in the plan document before the test starts.

Expected result: Y_adj = Y - theta(X - Xbar) with theta = Cov(Y,X)/Var(X); 20.25% variance reduction on the covered 38%, about 7.7% across the whole population, which is an MDE roughly 3.9% smaller. Travellers with no pre-period keep X at the group mean and are retained.

Verify it yourself

  • Run the estimator on a pre-period A/A: the CUPED point estimate must sit at zero like the unadjusted one while the variance falls. If the mean moves, theta is being estimated on post-treatment data.
  • Confirm rho was computed on the outcome exactly as the test defines it, same window and same maturity rule, not on a longer or more mature window.
  • Confirm the adjusted and unadjusted point estimates agree to well within one standard error; CUPED changes precision, not the estimate.

Follow-up

  • The outcome is completed stay-nights but the test ran 14 days and mean lead time is 38 days. What are you actually measuring, and what does that do to rho?
  • When does CUPED bias the estimate rather than only tighten it?
  • Would you rather spend the effort on CUPED or on switching the primary metric to a booking-axis proxy? Argue it.

Cancellation rate improved the week a status label changed

Medium · definition-change · cancellation · changepoint · reclassification

Traveller-initiated cancellation rate, defined as fct_booking rows with booking_status IN ('cancelled','no_show') AND cancellation_reason = 'traveller' over bookings created in the same lead-time bucket, fell from 22% to 17% in one week and has stayed there. A payments change shipped that week. You have fct_booking (booking_id, booked_at_utc, booking_status, cancelled_at_utc, cancellation_reason, lead_time_days, rate_plan, payment_timing). Determine whether traveller behaviour changed, and produce a series that is comparable across the changepoint. Use no table outside fct_booking.

Approach

  1. Recompute the rate with the reason filter removed. If total cancellations per cohort are flat while the traveller-reason slice fell, no behaviour moved and rows were relabelled; that single comparison is worth more than any amount of segment hunting.
  2. Plot the cancellation_reason distribution by week. A reclassification shows as one reason losing very close to what another gains, on one date, with no ramp, which is a shape genuine behaviour almost never produces.
  3. Locate the changepoint on cancelled_at_utc, not on booked_at_utc. A relabel applies to cancellations processed after the deploy, including cancellations of bookings made months earlier, so the booking-date view smears the step across many cohorts and hides it.
  4. Establish whether the relabel was retroactive. If historical rows were rewritten in place, the original series no longer exists in this table, and the only honest statement is which history is reconstructed and which is original.
  5. Hold cohort maturity fixed throughout. Compare hazard at a fixed age, the share cancelled within k days of booking with k no greater than the youngest cohort's elapsed age, so the post-change weeks are not simply younger than the pre-change ones.
  6. Rebuild a comparable definition: publish the all-reason rate while the split is unreliable, or publish the new split forward with the changepoint annotated on every chart that crosses it.

Follow-up

  • Which definition do you publish going forward, and what do you do with the eight quarters of history behind it?
  • Payment-failure cancellations are now separable from traveller ones. What decision does that actually enable, and which team owns it?
  • How would you detect the next relabel automatically, without knowing in advance which column it lands in?

Suggested practice checklist

For a candidate whose interviews will centre on A/B testing, metric movement and causal claims. Design comes before arithmetic, arithmetic before analysis, and the week ends by rehearsing the readout rather than the derivation.

Day 1 — Design one test end to end on paper

  • Take a single feature change and write the full design: randomization unit, the exact point of exposure, the primary metric with its grain, guardrails, allocation, planned duration, and the decision rule committed before any data exists.
  • Write why the randomization unit must sit at or above the level where treatment can spill over, and give one case where user-level randomization is still contaminated (shared accounts or devices, or two participants in the same marketplace).
  • State in advance what you will do if the primary metric is flat while a secondary metric is significant.

Deliverable: A one-page test design with a decision rule written before launch.

Day 2 — Power arithmetic until it is automatic

  • Compute required sample size per arm for a binary metric with the normal approximation, n is approximately 2 times (z for alpha/2 plus z for power) squared times p(1 minus p) divided by delta squared, for baselines of 2, 10 and 40 percent at a 5 percent relative lift, and note that for a fixed relative lift the requirement falls as the baseline rises because delta grows proportionally with p.
  • Redo the calculation for a continuous metric using variance in place of p(1 minus p), and show why a heavy-tailed quantity such as revenue per user needs either far more traffic or a capped version with a stated cap.
  • Convert one of the results into weeks given a weekly eligible traffic figure, then list the two honest ways to shorten it (accept a larger detectable effect, or reduce variance) and write why quietly lowering the power target is a decision to miss more real wins, not a speedup.

Deliverable: A small script or sheet that maps baseline, minimum detectable effect, alpha and power to sample size and weeks, cross-checked against a published calculator.

Day 3 — Variance and the unit-of-analysis problem

  • Take a ratio metric whose denominator is not the randomization unit (clicks per session, randomized by user) and compute the standard error twice, once naively at session level and once by the delta method or a user-level bootstrap, then record how much the naive version understates it.
  • Implement CUPED on simulated data: choose a pre-period covariate X measured before assignment, estimate theta as Cov(Y, X) divided by Var(X), and analyse Y minus theta times (X minus its mean) in place of Y. Confirm the variance of the adjusted outcome equals the raw variance multiplied by one minus the squared correlation between Y and X, so a correlation of 0.45 removes about 20 percent of the variance and not 80.
  • Now run that simulation a few hundred times and confirm the adjusted effect estimate is unbiased for the same effect rather than numerically identical to the raw one. Within any single run the two differ, sometimes by a large fraction of the true effect, because the two arms' pre-period covariate means never coincide exactly in a finite sample; they agree in expectation, which is the property that matters and the one to state out loud.

Deliverable: A notebook showing the adjusted estimator with a measurably smaller variance than the raw one, plus a repeated-simulation table showing the two estimators agreeing on average while differing run by run.

Day 4 — Validity threats you can actually test for

  • Run a sample ratio mismatch check as a chi-square goodness-of-fit test against the intended allocation, and write the three causes you would chase first (assignment logged before exposure, an arm-specific redirect or load failure, bot filtering applied asymmetrically).
  • Simulate peeking: generate A/A data, test daily at alpha 0.05 across 14 looks, record the inflated false positive rate, then apply an alpha-spending boundary or commit to a fixed horizon and confirm the rate returns to nominal.
  • Write how you would separate a novelty effect from a durable lift using the treatment effect plotted against days since first exposure, and what shape would change your recommendation.

Deliverable: One table showing the peeking false positive rate before and after correction, plus a written SRM triage list.

Day 5 — When randomization is not available

  • Write the identifying assumption for difference-in-differences (parallel trends in the absence of treatment), then plot pre-period trends for two candidate control groups and justify rejecting one of them.
  • Design a switchback test for a change where user-level randomization would leak across participants, choosing a time-block length against the carryover you expect and saying how you would detect carryover in the data.
  • List what an interrupted time series or a synthetic control buys you and the one thing neither can rule out: an unobserved shock that coincides with the launch.

Deliverable: A one-page memo recommending a single quasi-experimental design and naming its weakest assumption explicitly.

Day 6 — The readout query

  • Write the assignment-to-exposure join that returns exactly one row per unit per experiment, and handle units appearing in both arms by excluding and counting them rather than silently keeping one.
  • Compute the per-arm metric, its variance and the relative lift with a confidence interval in SQL, then reproduce the identical numbers in a notebook as a cross-check.
  • Add a segment breakdown and write the sentence that keeps it from being p-hacking: segments declared in advance, everything else reported as exploratory and corrected for multiplicity.

Deliverable: A single query that outputs the full readout table, matched to a notebook recomputation.

Day 7 — Present it to someone who will not read the appendix

  • Give a 10-minute readout of a real or simulated experiment in the order decision, number, uncertainty, caveat.
  • Have your listener ask "can we ship it" in the case where the primary is flat and a guardrail moved, and answer with a recommendation rather than a request for more data.
  • Rewrite your opening line so the recommendation lands before any methodology.

Deliverable: A one-page readout whose first line is the recommendation.

Getting an analysis acted on

Work that nobody used is a common and unflattering pattern in data careers, and interviewers probe for it. Have a story about an analysis that changed a decision, and be specific about how you got it in front of the person who could act. Also have one about work that went nowhere, with your reading of why.

Give an example of how you have mentored a junior data scientist or improved team technical standards.

Medium · behavioural and stakeholder questions

Approach

  1. Quantify the outcome, including what you would not claim credit for.
  2. State the situation in two sentences and spend the rest on your reasoning.
  3. Pick a story where you drove the decision, not one where you observed it.

Follow-up

  • What would you do differently if you ran that project again?
  • What did you decide not to do, and why?

How do you handle data ingestion and preprocessing for large, messy datasets?

Medium · behavioural and stakeholder questions

Approach

  1. State the situation in two sentences and spend the rest on your reasoning.
  2. Name the disagreement or constraint, and how you resolved it with evidence.
  3. Close with what you would do differently, concretely.

Follow-up

  • How did you know the outcome was caused by your change?
  • What would you do differently if you ran that project again?

Retract an occupancy comparison after the decision shipped

Hard · error disclosure · occupancy denominators · accountability

Three weeks ago you published a market occupancy comparison that led to marketing spend being moved out of one market. Reviewing the query, you find the two markets used different denominators: sellable nights from fct_rate_availability_snapshot (units_sold_to_date + units_available) for one, and capacity_units from dim_supply_unit for the other. The second market is mostly individual_host supply with large units_blocked, so its occupancy was understated. The spend move is already live. Prepare the correction: who you tell, in what order, and what the note says.

Approach

  1. Quantify the error before telling anyone, because the first question will be 'how wrong'. Recompute both markets on sellable nights, which excludes units_blocked, and report the corrected gap and its sign, not just that the original was wrong.
  2. Separate the numerical error from the decision error. The comparison was invalid, but the spend move may still have been right; establishing whether the decision would have flipped is a different analysis and it is the one the business needs.
  3. Tell the decision-maker directly and first, before it appears in a dashboard or a peer surfaces it. Order matters because being told by a third party converts a mistake into a credibility problem.
  4. Write the note with the correction, the size, the decision implication, and the reversal cost in that order. Three weeks of moved spend has a real cost to undo, and a correction that does not price the reversal forces the reader to do the work you skipped.
  5. Name the specific control that would have caught it and put it in place in the same note: an assertion in the query that both arms use the same denominator expression, and the denominator named in the chart title. A retraction without a mechanism reads as an apology rather than a fix.

Follow-up

  • The corrected numbers still support the original decision. Do you still send the note, and does it read differently?
  • Your manager suggests quietly fixing the dashboard and not raising it. How do you respond?
  • What would you have had to do differently three weeks ago, in the query itself, rather than in your review habits?