cswork / libraryOpen practice workspace

Interview guides · Environmental Protection Agency

Environmental Protection Agency Data Scientist Interview Questions & Guide 2026

Data-quality drills for Environmental Protection Agency data scientist prep: survivorship and selection bias, leakage through time-ordered features, and extreme values where provenance decides the call.

The 60-second brief

As a Data Scientist at the Environmental Protection Agency (EPA), you play a crucial role in leveraging data to support environmental decision-making and policy formulation. Your work directly impacts initiatives that aim to protect human health and the environment by providing insights derived from complex datasets. You will contribute to projects that analyze air and water quality, assess climate change impacts, and evaluate the effectiveness of environmental regulations. This position is essential in ensuring that the EPA fulfills its mission to safeguard the environment through informed, data-driven strategies.

Coding rounds for this role are usually data-manipulation shaped rather than data-structure shaped: group-bys, joins, time windows, ranking within a partition. Confirm the format before spending a week on graph traversal.

Focus areas

  • Reason about bid shading under first-price auctions
  • Defend attribution windows and model choice
  • Separate invalid traffic from genuine performance shifts

Overview

In this role, you will engage with diverse teams across the agency, including environmental scientists, policy analysts, and IT professionals, to tackle complex environmental challenges. The work you do will not only enhance the agency's operational efficiency but also help in communicating critical information to stakeholders and the public. Expect to work on high-impact projects that utilize advanced analytics and machine learning techniques, making your contributions vital to national and global environmental efforts.

The Environmental Protection Agency interview process

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

Initial Screening Interview

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

Technical Interview

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

Case Studies

A case round is decided by whether you leave the interviewer with a recommendation, not by how much analysis you narrate on the way there. The prompt is open on purpose, so the first job is to convert it into a decision someone could act on: ask what would be done differently depending on the answer. From there name the quantity that would settle it, state the assumptions you need, and commit. Candidates who cover more ground than anyone expected and still end on "it depends" score below candidates who scoped narrowly and said what they would do.

What to demonstrate

  • Whether the version of the question you choose to answer is genuinely narrower than the prompt and still worth answering
  • Whether the recommendation arrives as an action with a number attached, rather than as a summary of what you looked at
  • Whether assumptions are stated at the moment you rely on them, instead of collected into a disclaimer at the end
  • Whether you notice when a branch you are exploring would not change the decision either way

How to prepare

  • Take six open prompts and write only the scoping move for each: the one-sentence question you would actually answer and the decision it feeds. Give yourself three minutes per prompt and stop there.
  • Put a five-minute warning into every practice case and force a closing statement that names the action, the result that would justify it, and the result that would reverse it.
  • Record one case and count how long you talked before naming a measurable quantity. Past roughly five minutes, what you are calling scoping is narration.

Panel Interview

A loop is not scored one interview at a time. The people you meet compare notes afterwards, usually in a meeting you are not in, and the outcome turns on what each of them can say about you when asked. That rewards something other than survival: every room needs one specific thing worth repeating, and none of them can contradict another. The common way to lose is to tell the same project four times with different numbers in it, or to be uniformly fine in a way that leaves nobody with anything to argue for.

What to demonstrate

  • Whether your account of a project survives being told twice, with the same scale, the same metric definition and the same numbers each time
  • Whether each interviewer leaves with one concrete claim they could make on your behalf later, rather than an absence of complaints
  • Whether a question you already answered in an earlier room gets the same answer at the same depth, without visible impatience

How to prepare

  • Write a one-page fact sheet for your two or three main projects that fixes the numbers you will quote: rows of data, the metric as a single sentence, the effect you measured and how long the work took. Say them aloud from the sheet until they come out identical every time
  • For each kind of room you expect, decide the one sentence you want that interviewer repeating in a debrief, then check during the mock that you said it outright instead of implying it
  • Rehearse answering the same project question twice in one sitting, the second time as though you had not just answered it, because the thing that needs fixing is the flatness that creeps into a repeated story

Where candidates lose points

Treating a bid as if it does not affect the price paid, under a first-price auction.

In a second-price auction the winner pays the runner-up's bid, so bidding true value is close to optimal and bid and price are usefully separable. Under first-price clearing, which is now the common case in open exchanges, the winner pays exactly what it bid, so raising the bid raises both the win probability and the cost of every win that would have been won anyway. A bid-increase experiment can therefore show more impressions, more conversions and worse profit simultaneously, and reading only the volume metrics will call it a success. The related second-order problem is that your own bid increase raises the clearing prices your control cells face, which contaminates the comparison unless the cells are separated by market.

Reading a recent week's cost per action or return on ad spend before the conversion window has closed.

Spend for a period is final within hours, but conversions attributed to that period keep arriving for as long as the click window allows plus ingestion lag, and offline uploads can land weeks later. The denominator of cost per action is therefore systematically incomplete for recent dates, so recent CPA is biased high and recent ROAS biased low, and a dashboard comparing a fresh week to a matured week will show a regression that does not exist. The correct handling is to freeze a period only after click_window_days plus the observed ingestion lag, and to publish a maturation curve so readers can see how much of a given day's conversions have landed so far.

Answering a product-sense question with a list of features

Answer with a decision and the measurement that would settle it: the hypothesis, the primary metric, the guardrails, and the result that would make you not ship. A feature brainstorm cannot be wrong, which is exactly why it earns no points.

Building features from data that postdates the prediction time

Check every feature against the timestamp at which the model would actually score, and drop anything computed from a window that includes or follows the label event. For a forecasting use case, split train and test by time rather than at random, and split by entity when the same entity recurs.

Questions & practice

Write a function that calculates the correlation coefficient between two variables.

Medium · statistics and probability

Approach

  1. Sanity-check the answer against a simple bound or a simulated case.
  2. Write down the assumption the method needs before you use the method.
  3. Translate the result into the decision it informs, in one plain sentence.

Follow-up

  • What sample size would you need to detect an effect half this size?
  • Which assumption here is most likely to be violated in practice?

How would you implement a random forest algorithm in Python?

Medium · machine learning and modelling

Approach

  1. Frame the prediction: the label, the moment of prediction, and the action it triggers.
  2. Pick an evaluation metric that matches the cost of each error type, not a default.
  3. Check what information would not exist at prediction time, and exclude it.

Follow-up

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

Point-in-time join of impressions to versioned line items

Medium · scd2 · point-in-time-join · numpy · pandas

impressions has 20 million rows: impression_id, served_ts, line_item_id, billed_price_micros_usd, is_billable. line_item is SCD type 2 with 400,000 versions: line_item_id, bid_strategy, bid_value_usd, daily_budget_usd, pacing_mode, status, valid_from_ts, valid_to_ts (null on the current row), is_current. A version covers the half-open interval from valid_from_ts to coalesce(valid_to_ts, infinity). Attach to every impression the attributes of the version in force at its served_ts. You may not use merge_asof and you may not materialise the impression-by-version cross product. Return the joined frame and a count of impressions no version covers.

Approach

  1. Validate the dimension before joining: per line_item_id, sort by valid_from_ts and assert the intervals are non-overlapping, and assert at most one is_current row per key. Overlapping versions make the as-of answer ambiguous and are the usual root cause behind duplicated-revenue reports, so finding them is part of the job.
  2. Encode the composite as-of key into one sortable integer so a single np.searchsorted does the work: factorise line_item_id to codes shared across both frames, dense-rank all timestamps from both frames into a small integer, then form code * (n_ranks + 1) + rank. That keeps the composite inside int64 and makes lexicographic order on (key, time) equal to numeric order.
  3. Sort the dimension by that composite, run np.searchsorted(dim_keys, imp_keys, side='right') - 1, and you have a candidate version index for every impression in one vectorised call rather than 400,000 per-group searches.
  4. Reject candidates that do not actually cover the impression: the candidate must belong to the same line_item_id, and served_ts must be less than coalesce(valid_to_ts, far future). Report the uncovered count instead of dropping those rows; impressions before a key's first version normally indicate a backfill gap in the dimension, not bad events.
  5. Materialise attributes with a positional .take on the matched index rather than a merge. One row in, one row out by construction, so fan-out is impossible regardless of what the dimension contains.

Worked solution (30 min)

  1. Run the dimension validation and collect two lists: keys with overlapping intervals and keys with more than one is_current row.
  2. codes, uniques = pd.factorize(pd.concat([...])) applied consistently to both frames; dense-rank the union of served_ts and valid_from_ts; build the int64 composites.
  3. Sort the dimension by composite, np.searchsorted(side='right') - 1 for the impressions, and mask positions of -1 as uncovered.
  4. Validate coverage vectorised: same code, and served_ts < coalesce(valid_to_ts, Timestamp.max); then .take the attribute columns at the matched positions and assemble the output.

Expected result: A frame with exactly len(impressions) rows carrying one attribute set each, an integer count of uncovered impressions, and the two dimension-integrity lists (overlapping keys, duplicate-current keys).

Verify it yourself

  • Output row count equals input row count exactly; any increase is fan-out, any decrease is a silently dropped uncovered row.
  • Asserted vectorised over the whole output: valid_from_ts <= served_ts < coalesce(valid_to_ts, Timestamp.max) on every covered row.
  • On a hand-built case with three versions and an impression exactly at a boundary timestamp, the impression at served_ts == valid_from_ts attaches to the newer version, matching the half-open interval.
  • On a 10,000-row sample, results agree row for row with a deliberately slow reference implementation that loops each impression over its key's versions. That brute-force check is what proves the composite-key encoding is sound.

Follow-up

  • Two versions overlap by one second because the writer used an inclusive end timestamp. What does your join return, and how would you make that fail loudly instead of quietly picking one?
  • The pacing report needs the budget in force at every hour boundary, not just at impression time. What changes in the construction?
  • Estimate the memory and time cost of the naive merge-on-key-then-filter at these row counts, and say where it breaks first.

Describe a database schema you would use for storing environmental data.

Medium · sql and data manipulation

Approach

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

Follow-up

  • What breaks if events arrive late or out of order?
  • How would you verify this result without re-running the same query?

Provide a code snippet that cleans a dataset by removing outliers.

Medium · sql and data manipulation

Approach

  1. Compute rates by summing numerator and denominator separately, never by averaging rates.
  2. Check whether any join is one-to-many before aggregating, or the sums inflate.
  3. Say which table is the grain you start from, and join outward from it.

Follow-up

  • How would you verify this result without re-running the same query?
  • How does the query change if the join becomes one-to-many?

Sessionise device bid requests with a thirty-minute inactivity gap

Medium · window functions · gaps-and-islands · null handling

bid_request_log holds one row per (auction_id, line_item_id) with auction_id, request_ts TIMESTAMPTZ, device_id TEXT (nullable), device_type and exchange_id. For a single UTC day, assign each distinct auction opportunity a session number per device, starting a new session whenever more than 30 minutes have elapsed since that device's previous request. Return device_id, session_number, session_start_ts, session_end_ts, request count and distinct exchange count. A large share of rows carry no device_id; state how you treat them and why that choice is not arbitrary.

Approach

  1. Collapse to auction grain first with SELECT DISTINCT auction_id, device_id, request_ts. The table repeats each opportunity once per line item considered, so sessionising it raw counts a single request as many as there are eligible line items, and the multiplier varies by targeting breadth.
  2. Exclude device_id IS NULL rather than partitioning on it. The null share is a property of the supply source, not noise, so one null bucket becomes a synthetic device with millions of requests and no gaps. Report the excluded share as its own number in the same output.
  3. Get the previous request per device with LAG(request_ts) OVER (PARTITION BY device_id ORDER BY request_ts), then flag a boundary when the lag is null or the gap exceeds interval '30 minutes'.
  4. Turn the boundary flags into session numbers with a running SUM over the identical window. This is the gaps-and-islands pattern: the flag must be an integer 0 or 1, and the SUM must repeat the same PARTITION BY and ORDER BY, or the islands will not line up with the boundaries.
  5. Specify the frame explicitly as ROWS UNBOUNDED PRECEDING. The default frame for an ordered aggregate is RANGE UNBOUNDED PRECEDING, which gives every row sharing a timestamp the same running total, so simultaneous requests from one device collapse into one session number.
  6. Group by (device_id, session_number) and take MIN and MAX of request_ts for the bounds, COUNT(*) for volume and COUNT(DISTINCT exchange_id) for breadth.

Worked solution (25 min)

  1. Build ev AS (SELECT DISTINCT auction_id, device_id, request_ts FROM bid_request_log WHERE request_ts in the UTC day) and compare its row count with the raw count to see the per-auction multiplier.
  2. Split off device_id IS NULL, count it, and record its share of ev; run everything below on the non-null remainder.
  3. Add prev_ts := LAG(request_ts) OVER (PARTITION BY device_id ORDER BY request_ts) and new_session := CASE WHEN prev_ts IS NULL OR request_ts - prev_ts > interval '30 minutes' THEN 1 ELSE 0 END.
  4. Add session_number := SUM(new_session) OVER (PARTITION BY device_id ORDER BY request_ts ROWS UNBOUNDED PRECEDING).
  5. Group by (device_id, session_number) with MIN(request_ts), MAX(request_ts), COUNT(*) and COUNT(DISTINCT exchange_id).
  6. Pick the device with the highest session count, list its raw timestamps, and read the gaps by hand against the output.

Expected result: One row per (device_id, session_number). A device with requests at 09:00, 09:05, 09:50 and 09:52 yields session 1 spanning 09:00 to 09:05 with 2 requests and session 2 spanning 09:50 to 09:52 with 2 requests, because the 45-minute gap exceeds the threshold.

Verify it yourself

  • The sum of per-session request counts equals the count of distinct auctions with a non-null device_id for that day.
  • Within each device, session_number starts at 1 and increases by exactly 1, and session_start_ts of session n+1 is strictly greater than session_end_ts of session n.
  • Re-running LAG inside each session shows no internal gap above 30 minutes.
  • Raising the threshold to 60 minutes reduces the session count and never increases it.

Follow-up

  • A device shows 400 requests in 12 minutes across 30 exchanges. Is that a session or an invalid-traffic signature, and what would you look at next?
  • The null device_id rate differs sharply between mobile_app and mobile_web supply. What does that asymmetry do to any per-device metric computed on this table?
  • Where does 30 minutes come from? What in the inter-request gap distribution would justify a different threshold?

Describe a complex analytical problem you faced and how you solved it.

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. State what result would change your recommendation, so the answer is falsifiable.
  3. Fix the population and the time window before naming any metric.

Follow-up

  • How would you detect that the metric is being gamed rather than genuinely improving?
  • What would you do if the primary metric and the guardrail moved in opposite directions?

How do you prioritize tasks when managing multiple projects?

Medium · product sense and metrics

Approach

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

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 approach designing a study to assess the impact of a new environmental regulation?

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. Fix the population and the time window before naming any metric.
  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?

Demonstrate how you would visualize data trends using a programming language of your choice.

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. State what result would change your recommendation, so the answer is falsifiable.
  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?

Discuss the architecture of a system that integrates data from multiple environmental sensors.

Medium

Approach

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

Follow-up

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

Cut the variance of a market-level holdout with pre-period data

Medium · cuped · variance-reduction · stratification

A 28-day holdout randomises 80 geo_dma markets 40/40; treated markets run the campaign and held-out markets get no delivery. The outcome is deduplicated conversion_event rows per market over the flight plus a 7-day window. Market sizes are heavily skewed and the unadjusted design detects only a 12% relative lift at 80% power. Twelve months of pre-period market conversions are available. Specify a variance-reduction plan, state the precision you expect, and state what would disqualify a covariate.

Approach

  1. Apply CUPED: define Y_adj = Y - theta*(X - mean(X)) where X is pre-period conversions for the same market and theta = Cov(Y,X)/Var(X), estimated on the pooled arms. Variance falls by a factor of (1 - rho^2), where rho is the correlation between pre-period and in-period outcomes.
  2. Estimate rho from history before the test runs, by regressing a past 28-day market window on its own preceding pre-period covariate. At rho = 0.9 the variance multiplier is 0.19, the standard error multiplier is 0.436, and the 12% MDE falls to roughly 5.2%.
  3. Disqualify any covariate measured at or after assignment. In-flight impressions, in-flight spend and in-flight clicks are all downstream of treatment; adjusting on them shifts the point estimate rather than only shrinking its variance.
  4. Stratify as well as adjust: form matched pairs on pre-period conversion volume, randomise within pair, and then analyse with pair fixed effects or a paired difference. Pairing that is not carried into the estimator buys nothing at all.
  5. Handle the skew at design time by analysing the ratio metric, conversions per thousand eligible bid requests, or by weighting on pre-period volume, and commit to the choice before seeing the arms.
  6. Pre-register theta's estimation rule and the covariate window, so neither can be tuned after the outcome becomes visible.

Worked solution (30 min)

  1. On 12 months of history, build market-level 28-day outcome windows with matching pre-period covariates and compute rho; assume it returns 0.90.
  2. Compute the variance multiplier 1 - rho^2 = 0.19 and the standard error multiplier sqrt(0.19) = 0.436.
  3. Scale the unadjusted MDE: 12% * 0.436 = about 5.2% relative lift at 80% power, equivalent to 1/0.19 = 5.3 times the market count.
  4. Form 40 matched pairs on pre-period volume, randomise within pair, and write the estimator as a paired difference on CUPED-adjusted outcomes.
  5. Replay the whole pipeline as an A/A on two historical windows and confirm the adjusted estimate is centred on zero with the predicted standard error.

Expected result: rho of about 0.9 gives an 81% variance reduction, a 56% standard error reduction, and an MDE improvement from 12% to roughly 5.2% relative. Matched-pair assignment adds further reduction only if the analysis carries the pairing.

Verify it yourself

  • Adjusted and unadjusted point estimates must agree well within one standard error; a larger shift means the covariate is post-treatment or the assignment is imbalanced.
  • theta-hat should equal the OLS slope of Y on X in the pooled data.
  • The realised variance ratio between adjusted and unadjusted outcomes should land near 1 - rho-hat^2; if it does not, rho was estimated on a window that does not match the flight length.
  • An A/A replay across historical windows should produce 95% intervals covering zero about 95% of the time.

Follow-up

  • CUPED moves the point estimate by 30% of its own standard error. What do you conclude?
  • Would you use pre-period conversions or pre-period spend as the covariate, and why?
  • How would the plan change if only 20 markets were available?

Click-through rate fell while click volume stayed flat

Easy · ctr · supply mix · denominator

Platform click-through rate fell from 0.42% to 0.34% day over day, while the raw count of ad_click rows was unchanged. CTR here is valid billable clicks over valid billable impressions, both bucketed on the parent impression's served_ts. You have ad_impression (impression_id, auction_id, served_ts, is_billable, ivt_status, publisher_id, placement_id, creative_id, os_family), ad_click (click_id, impression_id, click_ts, ivt_status, is_billable) and bid_request_log (auction_id, exchange_id, ad_format, device_type). Deliver a one-paragraph verdict: real engagement decline or denominator artefact, with the evidence that decides it.

Approach

  1. Plot the numerator and the denominator as absolute counts for 14 days before computing any ratio. A flat numerator against a step in the denominator localises the change in one plot and rules out creative and audience explanations without a single join.
  2. Split the denominator into its four filter populations by served_ts date — all rows, is_billable only, ivt_status='valid' only, and both. If the billable-and-valid count jumped while total impressions did not, a filter moved, not the traffic.
  3. If total impressions genuinely grew, join ad_impression to bid_request_log on auction_id and cut the increment by exchange_id, publisher_id and ad_format, looking specifically for publisher_id values whose first served_ts falls inside the window.
  4. Recompute CTR within each publisher_id present in both periods. Flat within-publisher CTR alongside a falling pooled rate is a mix shift; the pooled number is describing a different population, not different behaviour.
  5. Check the click side for the mirror error: clicks bucketed on click_ts rather than the parent impression's served_ts shift by roughly one impression-to-click latency, which depresses the most recent day and recovers by itself tomorrow.

Follow-up

  • The new publisher is 30% of billable impressions and 4% of clicks. What do you need before recommending it be blocked, and what does blocking it do to eligible RPM?
  • How does your answer change if the invalid-traffic job back-filled sivt labels onto impressions served three days ago?
  • Would you publish CTR pooled across supply sources at all, and if so with what accompanying number?

Suggested practice checklist

Roughly 90 minutes a night on weekdays with one longer weekend block. The plan deliberately cuts scope rather than compressing everything, on the assumption that finishing one thing a night beats half-starting four.

Day 1 — Fix the scope and set a baseline

  • Read the role description and write the three things the loop will almost certainly test, then write an explicit not-doing list for everything else and keep it visible all week.
  • Take one 20-minute SQL prompt and one 10-minute metric question cold, and write the single sentence that says what blocked each attempt, since that sentence is what decides which two topics get the most evenings.
  • Set the week's one rule: one problem finished to completion every night, including the night you only have 40 minutes.

Deliverable: A one-page scope with an explicit not-doing list and two cold attempts, each carrying one sentence on what blocked it.

Day 2 — One query pattern, written three times

  • Choose the single pattern most likely to appear (a cohort retention grid, or a funnel counted by user) and write it three times from a blank file rather than editing the previous attempt.
  • On the third attempt, write the grain of every CTE as a comment before writing its body.
  • Stop at 90 minutes even if the third version is imperfect, and write the one thing you would fix with another hour.

Deliverable: Three independent versions of the same query plus a note on what changed between them.

Day 3 — Only the statistics you will be asked to defend

  • Write, in under 200 words, how you would decide whether a difference between two groups is real: the test, its assumptions, and what you would switch to when an assumption fails.
  • Compute a 95 percent confidence interval for a difference in proportions by hand on realistic numbers, then write in one sentence what changes if the two samples are paired rather than independent.
  • Write your answer to "what does a p-value mean", check it against a definition, and delete the version that describes it as the probability the hypothesis is true.

Deliverable: A 200-word written answer and one hand-computed interval you can reproduce under pressure.

Day 4 — One case, and the assumptions holding it up

  • Answer one product case aloud in 20 minutes with a recording running, then listen back with a pen and mark every claim you asserted without saying what it rested on: an assumed user behaviour, an assumed data source, an assumed baseline rate, an assumed grain.
  • Pick the three assumptions the recommendation actually depends on, write how you would check each one against data, and say which one being wrong would flip the recommendation rather than merely weaken it.
  • Write the four-step structure you used onto a card small enough to hold in working memory when you are nervous.

Deliverable: One recording, three load-bearing assumptions each with a written check, and a four-step structure card.

Day 5 — Your own work, timed

  • Write a 90-second version and a four-minute version of your main project, and time both out loud rather than reading them.
  • Prepare answers to the two follow-ups that always come: what you would do differently, and how you knew it worked.
  • Put one number in the first sentence and be able to say exactly where that number came from and what it excludes.

Deliverable: Two timed narratives with one defensible number in the opening line.

Day 6 — The one full rehearsal, in a longer weekend block

  • Run a 60-minute mock covering query work, a case and a behavioural question in a single sitting with no breaks, because sustained attention is the thing evenings have not trained.
  • Immediately afterwards, and before hearing any feedback, write the three moments you lost the thread.
  • Spend the rest of the block only on those three moments, and on nothing you merely feel shaky about.

Deliverable: Mock notes naming three failure moments with a specific fix written under each.

Day 7 — Taper

  • Write the 20-minute warm-up you will actually do on the morning of the interview: one query you can already write from a blank file, one metric you can define out loud, and nothing you have never seen before.
  • Re-read only your own notes from this week, and open no new material.
  • Write down the logistics: the tool you will be asked to work in, whether lookups are allowed, and the sentence you will use when you do not know something.

Deliverable: A one-page card holding the case structure, the project numbers, and the logistics.

Being wrong in public

A number you shipped turned out to be wrong, and someone had already acted on it. That is one of the most useful stories a data person can carry. What is being scored is how fast you noticed, who you told first, and what you changed in the process so the same class of error could not repeat quietly.

Describe a time when your data analysis led to actionable recommendations.

Medium · behavioural and stakeholder questions

Approach

  1. Name the disagreement or constraint, and how you resolved it with evidence.
  2. Quantify the outcome, including what you would not claim credit for.
  3. State the situation in two sentences and spend the rest on your reasoning.

Follow-up

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

Claiming impact for three projects with honest counterfactuals

Easy · self-assessment · impact · measurement

You are writing a performance self-review. Over two quarters you shipped a bid-shading recalibration, a rewrite of the click-through-rate query that fixed an invalid-traffic filter applied to only one side of the ratio, and an incrementality test that led to a line item being paused. Deliverable: for each, the number you would claim, the counterfactual you claim it against, and the portion of credit that belongs to someone else. One of the three has no defensible dollar figure. Say which, and what you claim instead.

Approach

  1. The interviewer is probing whether you apply the same causal standard to your own work that you apply to an advertiser's, so write the counterfactual for each claim before the number: what would the metric have been over the same period without this work?
  2. For the shading recalibration, compare clearing price and profit on won impressions at matched supply mix across exchange_id, ad_format, device_type and geo_country, and report the profit change rather than the win-rate change; under first-price clearing a higher win rate can accompany worse profit.
  3. For the query fix, claim the magnitude of the bias removed rather than a revenue number: the gap between the old rate, which filtered invalid traffic on the numerator only, and the corrected symmetric rate, plus the decision that was being made on the wrong figure.
  4. For the incrementality test, claim the decision, not the savings. The paused budget's fate was never measured, so any dollar figure would assume that money vanished rather than moved. This is the item with no defensible number.
  5. Name specific collaborators and what they contributed, and pre-empt the obvious challenge by stating the confound you know about, such as seasonality or supply mix drift, and the bound you put on it.

Follow-up

  • Your manager points out the paused line item's spend simply moved to another line item. Does your claimed impact survive?
  • How do you claim credit for work whose value is a bad decision that did not happen?

Explaining an incrementality interval to a non-technical budget owner

Medium · communication · uncertainty · incrementality

A 20-market geo holdout has closed. Incremental return on ad spend is estimated at 1.4 with a 90% interval of 0.6 to 2.2. The same account's last-click attributed ROAS is 4.1. A vice president with no statistics background has ten minutes and must decide whether to renew a $3M annual budget. Deliverable: the explanation you give verbally plus one slide. You may not use the words confidence interval, p-value, or significance, and you must give a recommendation rather than only a range.

Approach

  1. The interviewer is probing whether you can carry uncertainty into a decision instead of either hiding it or hiding behind it, so convert the interval into money at the real budget first: $3M at 0.6 to 2.2 is $1.8M to $6.6M of incremental revenue, with $4.2M as the central estimate.
  2. Get the break-even ROAS from the advertiser's gross margin and mark where it falls inside the range. That single number turns a statistical interval into two decision regions, and it is the only quantity the executive needs to reason about.
  3. Explain the 4.1 without calling it wrong: it counts every conversion that followed an ad, including the ones that would have happened anyway, while the test counts only the ones that would not have. Both are computed correctly and they answer different questions.
  4. State what would narrow the range and what it costs, in the same units: roughly four times the markets or four times the flight length to halve the width, against the cost of running longer at current spend.
  5. Pre-commit to the action at each end of the range so the recommendation is falsifiable: what you do if the true value is near 0.6, and what you do if it is near 2.2.

Follow-up

  • The vice president asks whether it is working, yes or no. What is your one-sentence answer?
  • Break-even is 1.0 and the range is 0.6 to 2.2. Do you renew at full budget, and what would you change about the flight?