cswork / libraryOpen practice workspace

Interview guides · 7-Eleven

7-Eleven Data Scientist Interview Questions & Guide 2026

SQL practice for 7-Eleven data scientist candidates: window functions, funnel and retention queries, joins that quietly drop rows.

The 60-second brief

The role of a Data Scientist at 7-Eleven is pivotal in driving data-driven decision-making that influences the company's strategic direction. As a Data Scientist, you will harness large datasets to extract meaningful insights that enhance operational efficiency, improve customer experiences, and optimize supply chain processes. Your work will directly impact key areas such as inventory management, sales forecasting, and customer behavior analysis, making it a critical position within the organization.

In modelling rounds the live question is usually why this model class for this problem, and how you would know six weeks after launch that it is still working. Deriving gradients by hand is rarely what is being probed.

Focus areas

  • Cohort revenue by first delivery, not signup
  • Size promotions on incremental margin, not revenue
  • Separate true demand from stockout-censored sales

Overview

At 7-Eleven, you will collaborate with cross-functional teams to develop models and analytics that inform product offerings and marketing strategies. The scale and complexity of the datasets you’ll work with are substantial, given the vast number of transactions and interactions occurring daily across numerous locations. This offers an exciting opportunity to apply your analytical skills in a dynamic and fast-paced retail environment, where your contributions can lead to significant improvements in service and profitability.

The 7-Eleven interview process

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

Initial Screening

Most candidates lose this call inside the first two minutes, during the walkthrough of their own background. The account runs chronologically, sits at the level of tools and titles, and never arrives at a decision anyone could have disagreed with. Anchor on a problem instead of a timeline: what the team could not answer, what you did about it, what happened next. Ninety seconds is enough, and stopping on time leaves room for the half of the call that belongs to you. What you ask about how work gets prioritised signals your level more reliably than the walkthrough does.

What to demonstrate

  • Whether your background summary has a shape (problem, decision, consequence) or is a chronological list of tools and employers
  • Whether you can account for gaps, short stints and the reason you are looking, unprompted and without hedging
  • The substance of the questions you ask back, which an experienced screener reads as a level signal

How to prepare

  • Time your opening walkthrough against a clock. If it runs past two minutes, compress the earliest role into a single clause and spend the recovered time on the most recent one
  • Write one honest sentence for every gap or short stint visible on your resume and offer it before being asked about it
  • Prepare questions about how work arrives and gets prioritised: who writes the request, how often priorities change, and what happens to an analysis after it is delivered

Technical Interviews

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

Behavioral Assessments

Rounds of this kind usually include one question about work that did not go well, and it is the part that carries the most information. Anyone can narrate a shipped win. What the interviewer learns from a project that stalled is how you behave without a result to hide behind: whether you noticed the problem yourself, how long it took, and who you told. Answers that route the failure onto a data pipeline or a reorganisation close the topic without answering it, and the follow-up comes back to your own part.

What to demonstrate

  • Whether you found the error yourself or someone else found it, and how long it sat before anyone knew
  • What you changed afterwards, stated as a check you now run rather than a lesson you now believe
  • Whether the mistake you choose has real cost attached, such as a quarter of misdirected roadmap or a metric that was reported upward, instead of one that flatters you

How to prepare

  • Choose a failure you caught yourself and be ready to say what tipped you off. A story where someone else caught it is still usable, but you will be asked why you missed it.
  • Write down the check you added afterwards and where it lives now, so the correction is a concrete artefact rather than a resolution.
  • Rehearse saying the cost out loud. Candidates shrink the number by instinct once the interviewer is in the room.

Final Interviews

A day of back-to-back interviews samples your floor, not your ceiling. Four hours in, the habits that carry a good answer are the first to go: restating the question before solving it, asking what the data would have to look like, checking a number before quoting it. What the day decides is whether the tired version of you is still someone to leave alone with an ambiguous problem. The round that sinks a candidate is usually not the hardest one. It is the one immediately after the round that went badly.

What to demonstrate

  • Whether the late rounds get the same clarifying questions as the first one, or whether you start answering immediately to save effort
  • Whether a weak answer stays in the room it happened in, instead of following you into the next conversation as apology or distraction
  • Whether the quality of your questions holds up, since fatigue removes curiosity about the problem before it removes knowledge of the method

How to prepare

  • Rehearse the length, not just the content: book four mock interviews of different types in one afternoon with short gaps, because the one you need to observe is the fourth
  • Put the two or three questions you ask at the start of any problem on a card in front of you, so that under fatigue it is a habit you run rather than a decision you make
  • Decide in advance what the gap between rooms is for: water, one line of notes on anything you promised to follow up, and an explicit close on the round that just ended so it does not travel
  • Prepare a different closing question for each interviewer, so the end of a long day does not produce the same one four times

Where candidates lose points

Comparing average order value across a change that alters basket composition

Free-shipping thresholds, multi-buy offers and minimum-spend coupons make customers bunch their basket just above a cut-off, so average order value moves because the mix of orders changed, not because anyone is spending more per item. The same trap appears when a change suppresses small orders: average order value rises while total revenue falls. Decompose into units per order and price per unit, look at the whole basket-value distribution rather than the mean (the bunching is visible as a spike at the threshold), and evaluate the change on revenue per session rather than on the conditional mean of orders that happened.

Fitting demand models on sales when sales are censored by availability

Units sold equal the minimum of demand and what was sellable, so every day a SKU was out of stock contributes a zero that looks identical to genuine indifference. A model trained on that history forecasts the stockout, the buy shrinks, availability falls further, and the error compounds each cycle, which is why a declining SKU forecast should always be checked against minutes_unavailable before it is believed. The fixes are to restrict the fit to in-stock periods, to model availability explicitly as an exposure term, or to use a censored likelihood; all three require the availability history to be retained at a finer grain than a daily end-of-day snapshot, which is exactly what teams tend to discard.

Treating a non-significant result as proof of no effect

Say whether the confidence interval excludes the effect sizes you would have cared about. If it does not, the honest reading is that the test was underpowered, so report the minimum detectable effect the design could have found and what sample size would resolve it.

Writing SQL without stating NULL and tie-breaking behaviour

Before calling a query finished, say what it does with NULLs, ties and empty groups. NOT IN against a subquery containing a single NULL returns no rows at all, and RANK, DENSE_RANK and ROW_NUMBER differ precisely on ties, so name which one the question requires.

Questions & practice

How do you implement a decision tree algorithm from scratch?

Medium · machine learning and modelling

Approach

  1. Say how the offline result would be validated online before it is trusted.
  2. Set a baseline first, so any model has something honest to beat.
  3. Pick an evaluation metric that matches the cost of each error type, not a default.

Follow-up

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

Explain a machine learning project you worked on and your role in it.

Medium · machine learning and modelling

Approach

  1. Set a baseline first, so any model has something honest to beat.
  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?

Net revenue per order without fanning out return rows

Easy · join grain · pandas · fan-out · net revenue

You get two DataFrames. order_lines has order_line_id, order_id, customer_id, quantity, unit_paid_price_cents, line_status, delivered_at_utc. return_lines has return_line_id, order_line_id, quantity_returned, refund_amount_cents, initiated_at_utc. One order line can have several return rows. Produce one row per order_id with gross_cents over delivered lines only, refund_cents from returns initiated within 90 days of that line's delivery, net_cents, and units_kept. The output must have exactly one row per order_id, and gross_cents must equal a total you compute without ever touching return_lines.

Approach

  1. Filter order_lines to line_status == 'delivered' first, and compute gross_cents = quantity * unit_paid_price_cents per line. Keep that per-line frame as the control total before any merge.
  2. Aggregate return_lines to one row per order_line_id with groupby('order_line_id').agg(sum of quantity_returned, sum of refund_amount_cents, min of initiated_at_utc) so the right side of the join is unique on the join key.
  3. Because the 90-day window is per return row, not per order line, apply the window before aggregating: join initiated_at_utc against the parent line's delivered_at_utc, keep rows inside the window, then aggregate. Doing it after aggregation loses the individual initiation dates.
  4. Left merge the aggregated returns onto the delivered lines with validate='m:1' so pandas raises instead of silently duplicating, then fillna(0) on the refund and quantity columns.
  5. Group to order_id, sum gross_cents, refund_cents, and units_kept = quantity - quantity_returned, and assert the gross total equals the control total computed in step one.

Worked solution (25 min)

  1. Build a fixture where order 1 has one line of quantity 3 at 2000 cents with two return rows (1 unit refunded 2000, then 1 unit refunded 2000), and order 2 has one clean delivered line of quantity 1 at 5000 cents.
  2. Compute control_gross = 32000 + 15000 = 11000 from order_lines alone.
  3. Run the naive merge first and print the gross sum: it reports 17000, because the three-unit line appears twice.
  4. Run the pre-aggregated version and confirm total gross 11000, total refund 4000, total net 7000, and for order 1 specifically gross 6000, refund 4000, units_kept 1.
  5. Add an assertion that result.index.is_unique and result['gross_cents'].sum() == control_gross.

Expected result: A DataFrame indexed by order_id with gross_cents, refund_cents, net_cents and units_kept, where gross_cents sums to the delivered-line total computed independently, and every order_line_id appears exactly once regardless of how many RMA rows it has.

Verify it yourself

  • len(result) == order_lines.loc[delivered, 'order_id'].nunique()
  • result['gross_cents'].sum() equals the control total computed before any join
  • Setting validate='m:1' on the merge raises if the return aggregation was skipped
  • net_cents <= gross_cents for every row, and units_kept is never negative

Follow-up

  • The order line was paid in a non-USD currency with fx_rate_to_usd on the line. Where does the conversion belong, and what breaks if you convert after aggregating to order grain?
  • Refunds arrive weeks after the order. Which date do you attribute refund_cents to when you report a monthly net revenue series, and why does the answer change how long you must hold the report back?
  • How would the query change if a partial return could be refunded in two instalments under the same rma_id?

Given a dataset, perform exploratory data analysis and summarize the findings.

Medium · sql and data manipulation

Approach

  1. Compute rates by summing numerator and denominator separately, never by averaging rates.
  2. State the window function and its partition and ordering out loud before writing it.
  3. Check whether any join is one-to-many before aggregating, or the sums inflate.

Follow-up

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

Ninety-day repeat rate by first-delivery cohort using window functions

Medium · window functions · cohort retention · self-join

Using fct_order_line (order_line_id, order_id, customer_id, quantity, delivered_at_utc, line_status) and fct_return_line (order_line_id, quantity_returned), define a kept line as a delivered line whose total returned quantity is less than its ordered quantity, and an order's delivery time as MAX(delivered_at_utc) across its delivered lines. Cohort each customer on the calendar month of their first order containing a kept line. Return, per cohort month, the cohort size and the share of customers who placed a later order with a kept line within 90 days of that first delivery. Report only cohorts matured 120 days.

Approach

  1. Pre-aggregate returns to order_line_id first, then mark kept lines; doing this before any customer-level logic keeps the fan-out out of the cohort counts.
  2. Roll kept lines up to an order grain CTE: order_id, customer_id, delivered_at = MAX(delivered_at_utc). State the choice, because MIN would date the order at its first parcel and shift borderline customers a cohort earlier.
  3. Pick the first order per customer with ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY delivered_at, order_id) = 1, breaking the tie on order_id so the result is deterministic across runs.
  4. Find repeaters with EXISTS against the same order CTE (later order_id, delivered or placed within the 90-day interval), not a join, so a customer with three qualifying second orders is still one numerator unit.
  5. Group by date_trunc('month', first_delivered_at) and filter to cohorts whose month ended at least 120 days ago, so every cohort has had the full window plus return maturity.
  6. Report cohort size next to the rate, since the ratio moves whenever acquisition volume moves.

Worked solution (35 min)

  1. Build returns_by_line, then kept_lines, then orders_kept (order_id, customer_id, delivered_at).
  2. Apply ROW_NUMBER to extract first_order per customer and materialise the cohort month.
  3. Add the EXISTS subquery for a later kept order inside 90 days of first delivery.
  4. Aggregate to cohort month: COUNT() as cohort_size, COUNT() FILTER (WHERE repeated) as repeaters, and the ratio.
  5. Apply the 120-day maturity filter and inspect the trailing three cohorts for a downward artefact.

Expected result: One row per mature cohort month with cohort_size, repeaters and a repeat rate strictly between 0 and 1. The sum of cohort_size across all months equals the number of distinct customers with at least one kept delivered order in the period.

Verify it yourself

  • repeaters never exceeds cohort_size for any month.
  • Every customer appears in exactly one cohort; a GROUP BY customer_id HAVING COUNT(*) > 1 over the first-order CTE returns nothing.
  • Removing the maturity filter makes the newest cohorts drop sharply, confirming the filter is doing real work rather than being decorative.
  • A customer whose only order was fully returned is absent from both numerator and denominator.

Follow-up

  • Why cohort on first delivery rather than account_created_at_utc or first_order_placed_at_utc, and what does the choice do to accounts that order and cancel?
  • The 90 days runs from first delivery. What changes if you measure the second order by placed_at_utc rather than by its own delivery?
  • Repeat rate rose four points last quarter while revenue fell. What do you check first?

How would you approach optimizing supply chain processes using data?

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. 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?
  • How would you detect that the metric is being gamed rather than genuinely improving?

How do you prioritize tasks when working on multiple projects?

Medium · product sense and metrics

Approach

  1. Name one primary metric, then the guardrail that stops it being gamed.
  2. State what result would change your recommendation, so the answer is falsifiable.
  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?
  • How would you detect that the metric is being gamed rather than genuinely improving?

Given a dataset, how would you identify trends in customer purchasing behavior?

Medium · product sense and metrics

Approach

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

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?

Describe how you would design an experiment to test a new marketing strategy.

Medium · a/b testing and experimentation

Approach

  1. Say whether units interfere with each other, and switch design if they do.
  2. Name the randomisation unit first; it decides the variance and what the test can detect.
  3. State the primary metric and the minimum effect worth shipping, then size the test.

Follow-up

  • How would you handle interference between treated and control units?
  • What would you conclude if the result is positive but the test is underpowered?

What tools and libraries do you prefer for data analysis, and why?

Medium

Approach

  1. Work from the decision backwards to the evidence you would need.
  2. Say what you would check first and why it is the highest-information step.
  3. Clarify what is being asked and what a complete answer would contain.

Follow-up

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

Build a five-factor revenue tree for a weekly trading review

Easy · metric tree · decomposition · reporting lag

A weekly trading review needs one page explaining why net merchandise revenue moved. You have fct_session, fct_order_line, fct_return_line and dim_sku. Build a multiplicative decomposition of weekly net revenue into factors that are separately owned and separately actionable, naming the table and columns behind each factor and the window it is computed on. Constraint: the page publishes on the Monday after the trading week closes, so returns against that week's orders have not arrived yet. Deliverable: the tree, plus your handling of the factor that is not yet mature.

Approach

  1. Write the identity before writing any SQL: net revenue = sessions x orders per session x units per order x paid price per unit x keep rate. Every factor has to chain, so the denominator of one is the numerator of the previous, or the product will not reconcile.
  2. Bind each factor to columns. Sessions: fct_session with is_bot_flagged = FALSE. Orders per session: sessions with non-NULL order_id. Units per order: SUM(quantity) over delivered lines divided by distinct order_id. Price per unit: SUM(quantity * unit_paid_price_cents) / SUM(quantity). Keep rate: 1 minus refunded revenue share from fct_return_line.
  3. Close the channel leak. Orders with channel IN ('store_pos','call_centre','subscription_autoship') have no session, and lines from sessions with tracking_consent = 'denied' carry session_id NULL, so either restrict the whole identity to session-attributable orders or carry the other channels as a separate additive term. Mixing them breaks the identity without any error being raised.
  4. Hold currency fixed. Convert at line level with fx_rate_to_usd, then hold the rate at a fixed reference so an FX move does not land in the price-per-unit factor and get read as a pricing decision.
  5. Handle the immature keep rate by substituting the matured keep rate from the most recent closed delivery cohort in the same category, labelling the week provisional and restating when it matures. Do not compute the week's own keep rate from returns received so far: that number is close to 1 for any fresh week, so every week would open as a keep-rate improvement.
  6. Run the same identity split by device_type and entry_channel so a movement lands in an identifiable cell instead of being averaged across the site.

Worked solution (25 min)

  1. Pick a trading week that closed more than 120 days ago so its returns are mature, and compute net merchandise revenue directly from fct_order_line and fct_return_line as the target number.
  2. Compute the five factors for that same week from the columns above, restricting the identity to session-attributable orders.
  3. Multiply the five factors and compare against the direct number; reconcile any gap before going further, since a gap means a denominator does not chain.
  4. Repeat for the adjacent week and express the week-over-week change as the contribution of each factor, holding the others at their prior-week value.
  5. Redo the exercise as of that week's Monday publication date, substituting the matured category keep rate, and record how far the provisional net revenue sat from the final figure.

Expected result: A five-factor multiplicative tree whose product reproduces the week's net merchandise revenue to rounding, a week-over-week attribution that sums to the observed change, and a provisional-versus-final gap for the substituted keep rate that is stated as a number rather than as a caveat.

Verify it yourself

  • The product of the five factors equals directly computed net revenue for the closed week within rounding error.
  • The session denominator used in the factor and in the direct calculation both exclude is_bot_flagged sessions; the counts should match exactly.
  • Orders excluded because they have no session (store, call centre, autoship) plus session-attributable orders equals total distinct order_id for the week.
  • Holding fx_rate_to_usd fixed changes the price-per-unit factor and no other factor.

Follow-up

  • Your tree shows conversion down and price per unit up in the same week. Give two mechanisms that produce both at once.
  • How do you roll these weekly factors up to a quarter? Which of them can be averaged and which have to be re-derived from summed numerators and denominators?

Every device improved but the aggregate rate fell

Medium · mix shift · simpsons paradox · segmentation

Week over week, visit-to-order conversion rose in all four device_type values, yet the all-up rate fell 18 basis points. fct_session gives session_id, order_id, device_type, entry_channel, campaign_id, customer_id (NULL when signed out), tracking_consent and is_bot_flagged. Deliverable: decompose the aggregate change into a within-segment component and a mix component that reconcile to the observed delta, name the traffic shift that produced the mix term, and state which single number belongs in the weekly review.

Approach

  1. Write the aggregate as p = sum over segments of w_i * p_i, where w_i is the segment share of eligible sessions and p_i is the segment rate. Every claim that follows has to come out of that identity rather than out of intuition.
  2. Compute the three terms explicitly: within = sum w_i_old * (p_i_new - p_i_old), mix = sum (w_i_new - w_i_old) * p_i_old, interaction = the remainder. Confirm they sum to the total delta before interpreting anything; if they do not, the eligibility filter differs between the two weeks.
  3. Locate the mix term. Rank segments by (w_i_new - w_i_old) * (p_i_old - p_old) to see which share move did the damage, then push one level deeper: entry_channel by device, campaign_id, signed-in versus anonymous, country.
  4. Decide whether the mix shift is the business result or an artefact. A paid social campaign buying low-intent mobile traffic is a real and intentional mix shift; a bot-rule change or an app tracking loss that removes high-converting sessions is not.
  5. Publish the standardised rate (new segment rates at last week's mix) next to the raw rate, and say plainly that the raw rate fell because the traffic changed, not because the site got worse.

Follow-up

  • Which mix do you standardise on: last week, a rolling 13-week base, or plan mix? What breaks when the base is stale?
  • If one device segment is small enough that its rate is noisy, how does that noise propagate into the standardised number?
  • Marketing wants credit for the within-segment gain. What do you need before you agree the campaign caused it?

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.

Ownership when the result is inconvenient

Sometimes the honest read is that the initiative did not work, and the person who commissioned the analysis was hoping otherwise. Interviewers want to know whether you softened it. Prepare the case where you delivered an unwelcome result, how you presented the uncertainty without hiding behind it, and what the team did next.

Can you describe a time when you optimized a model for better performance?

Medium · behavioural and stakeholder questions

Approach

  1. Quantify the outcome, including what you would not claim credit for.
  2. Pick a story where you drove the decision, not one where you observed it.
  3. Name the disagreement or constraint, and how you resolved it with evidence.

Follow-up

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

Defend a promotion read that contradicts the revenue number

Medium · incrementality · pull-forward · stakeholder pushback

You analysed a site-wide twenty-percent weekend event. Revenue over the event week rose eleven percent year over year, contribution margin per delivered order fell, and the two weeks after the event ran below the pre-period baseline. The merchandising lead who owns the event calendar has already circulated the revenue figure. You have ten minutes at a planning meeting attended by that lead and their director. Deliver the finding and the recommendation you would make for the next event, and describe how you handle the pushback in the room.

Approach

  1. Before the meeting, write the finding as one sentence about the event, not about the person who owns it: the event moved price and timing, not demand. Keeping the event owner's judgement out of the sentence is what keeps the holdout you want on the table.
  2. Open with the number they already have, then decompose revenue into traffic, conversion, units per order, net price per unit and keep rate, and show which term moved. If price per unit fell and units per order held, the revenue gain was volume bought at a lower price.
  3. Quantify the post-period dip as the pull-forward estimate, and state the measurement window explicitly. The window has to extend past the event because the demand was borrowed from the weeks after it.
  4. Convert to incremental contribution margin. At gross margin m on list price and discount depth d, holding contribution flat requires a unit lift of d / (m - d), valid when m > d, when the discount applies to every unit sold, when unit cost is constant, and when nothing was cannibalised from full-price SKUs. Compare the observed lift to that threshold.
  5. Say what would change your mind and propose the design that would settle it: a geo or customer holdout on the next event, with the number of units and weeks it needs.
  6. End on the next event, not a verdict on the last one, so the meeting produces a decision rather than a defence.

Follow-up

  • The lead says the event acquired new customers whose lifetime value justifies the margin loss. How would you test that claim with dim_customer and fct_order_line?
  • Your director wants one number to take to finance. Which one, and what caveat travels with it?

State the impact of your model without overclaiming

Hard · attribution · confounding · self-assessment

Writing your own review, you want to claim the reorder model you shipped in February. Since launch, demand-weighted in-stock rate on covered categories rose from ninety-one to ninety-five percent and net merchandise revenue on them is up eight percent year over year. In the same window a supplier consolidation shortened lead times, two categories were re-merchandised, and the fiscal calendar shifted the promotional week. Nobody will check your number. Write the impact claim you would actually put in the document, and explain what you did to earn the right to it.

Approach

  1. Decide what is attributable before deciding what is impressive. Three known changes move the same metrics in the same direction, so the year-over-year revenue figure is an upper bound rather than an estimate of your effect.
  2. Find the cleanest contrast still available after the fact: categories the model covers against comparable uncovered ones over the same weeks. That is a difference-in-differences read, its identifying assumption is parallel pre-trends, so show the pre-period rather than assert it.
  3. Claim the mechanism you can trace end to end. The model changed reorder points, reorder points changed minutes_unavailable, availability changed units sold. Each link is measurable, and a confounder has to break a specific one of them.
  4. Lead with the in-stock movement and put revenue second, because availability is closest to the thing you changed and hardest for the supplier consolidation to explain away.
  5. Write the confounders into the claim instead of omitting them. A claim that survives a reader naming the consolidation is worth more than a larger one you retract when they do.
  6. State the counterfactual you cannot rule out, and the design you would run next time to rule it out.

Follow-up

  • Your manager wants one number for the promotion packet. Which one, and what sentence goes with it?
  • A peer claims the same revenue for the re-merchandising work. How do you resolve that without both of you shrinking your claims to nothing?