Interview guides · Bosch Automotive Aftermarket
Bosch Automotive Aftermarket Data Scientist Interview Questions & Guide 2026
Causal inference drills for Bosch Automotive Aftermarket data scientist prep: diff-in-diff, matching, instruments, and the limits of observed data.
The 60-second brief
As a Data Scientist within Bosch Automotive Aftermarket, you sit at the intersection of traditional engineering excellence and the digital transformation of the automotive industry. Your work is critical to maintaining the global supply chain, optimizing vehicle maintenance diagnostics, and leveraging data to enhance the performance and longevity of automotive components. You will be tasked with turning vast amounts of telemetry and logistics data into actionable insights that directly influence how vehicles are serviced and how parts are distributed worldwide.
When randomisation is off the table, the skill being checked is naming an identification strategy together with the assumption it rests on: parallel trends for difference-in-differences, relevance and exclusion for an instrument, overlap and conditional ignorability for matching. Say the assumption out loud and say how you would try to break it.
Focus areas
- Price a service-level change in working capital
- Size safety stock under variable lead times
- Evaluate forecasts at the lag ordering actually uses
Overview
This role is both challenging and intellectually stimulating, requiring you to navigate complex data ecosystems that bridge the gap between physical hardware and digital services. You will work alongside cross-functional teams to build predictive models that anticipate customer needs and improve operational efficiency. Whether you are working on supply chain forecasting or advanced diagnostic algorithms, your contributions will have a tangible impact on the Bosch commitment to "Invented for Life," ensuring that the automotive aftermarket remains agile, efficient, and customer-centric.
The Bosch Automotive Aftermarket interview process
Bosch Automotive Aftermarket candidates report 2 rounds · ≈ 2-4 weeks. The stages below are what candidates describe, not a published process.
Technical Screening
This round decides whether someone can hand you a schema and a question and trust the number that comes back. Correctness under a clock is the bar, not clever syntax. The habit that separates strong from weak answers is checking the grain: after every join, know how many rows you expect and whether the count moved. Most wrong answers in this format are not wrong logic, they are a fan-out from a key that turned out not to be unique, or a filter applied before an aggregate when it belonged after. Say what you expect before you run it.
What to demonstrate
- Whether your row counts survive each join, and whether you notice on your own when they do not
- Deliberate handling of rows that fail to match, including whether the question needs an inner join or a left join with the non-matches kept and counted
- Whether NULLs are treated on purpose, given that a NULL compares equal to nothing and that COUNT of a column skips it
- Reaching a defensible answer inside the window instead of a refined one after it
How to prepare
- Take a two-table schema, write a join that fans out on purpose, then fix it by collapsing the many-side to one row per key before joining. Repeat until the fix is reflex rather than recall.
- Write a funnel as one query and print the distinct user count at each stage, then confirm each stage is a subset of the one above it rather than assuming it
- Do a few timed runs in a plain text box with no autocomplete and no formatter, since assessment editors often have neither
Deep-Dive Rounds
An added round often puts you in front of someone outside the core hiring team: a partner engineer, a product owner, a domain expert, sometimes a more senior manager. The question they are really asking is not whether you can do the work but whether they would trust a number that came from you. That changes what a good answer looks like. Lead with what the decision cost and what it changed, keep the method available but not central, and be plain about the limits of your evidence. Overstating a result is the fastest way to lose this round.
What to demonstrate
- Whether you can explain a technical choice to someone who will never read your code, without either flattening it into nothing or hiding inside jargon
- Honesty about evidence strength: what the analysis establishes, what it only suggests, and what it cannot say at all
- How you take disagreement, specifically whether you update on a good objection, hold your position with reasons, or fold on contact
How to prepare
- Write the two-sentence version of your most technical project for a non-specialist, then check that neither sentence needs a method name to make sense.
- For one result you are proud of, write the strongest objection someone could raise and a response that concedes the part of it that is correct.
- Prepare one decision that turned out to be wrong: how you found out, what it cost, and what you changed afterwards. A senior cross-functional interviewer asks for this more often than a technical one does.
Where candidates lose points
Treating shipped units as demand
Shipments are censored at available inventory: on a stockout day the recorded quantity is a supply ceiling, not customer intent, and orders that were never placed because the item showed as unavailable leave no row at all. A forecast fitted on that history learns the constraint, under-forecasts the fast movers that stock out most often, and produces the replenishment that causes the next stockout, so the error compounds in one direction rather than averaging out. The fix is to model demand_qty rather than shipped_qty, flag stockout days with stockout_flag and treat them as censored (fit with a censored likelihood, or estimate unconstrained demand from uncensored periods and comparable locations), and to report how much of the history was censored alongside any accuracy number.
Computing average inventory from period-end snapshots
Shipments cluster before period close, so the month-end on-hand position is systematically the lowest point of the month; turns computed against it are biased high and days of supply biased low, frequently by ten to twenty percent, and the bias grows precisely when close-period push is strongest. The same shape of error appears when a daily snapshot is joined to shipment events on date equality: a SKU with several legs on one day fans the snapshot out and multiplies the valued inventory. Average across every daily snapshot in the window for the denominator, and join snapshots to events with an explicit as-of condition and a row-count check at each grain before aggregating.
Defining the cohort on a post-treatment condition
Ask how rows entered the table. Filtering on something that treatment itself influences, such as users who finished onboarding or accounts still active at ninety days, breaks comparability between arms; define the population at an entry point that precedes exposure and keep everyone in it.
Crediting a treatment for regression to the mean
Selecting a group because it is extreme (lowest-engagement users, accounts having their worst month, the bottom decile of a score) moves that group's expected next-period value back toward the average even under no treatment, by exactly as much as the selecting measure is imperfectly correlated with its own later value. Compare against units that met the same selection rule and went untreated, or use two pre-periods so the bounce-back is visible before the intervention starts. A pre-post number on a group chosen for being extreme measures the selection rule, not the treatment.
Questions & practice
How do you structure an end-to-end MLOps pipeline to ensure model reproducibility?
Medium · machine learning and modelling
Approach
- Check what information would not exist at prediction time, and exclude it.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Pick an evaluation metric that matches the cost of each error type, not a default.
Follow-up
- What would you monitor after launch to know the model is still valid?
- How would you choose the decision threshold, and who owns that choice?
Simulate service levels under a variable inbound lead time
Medium · safety stock · monte carlo · service level
Daily demand for one SKU at one node is Poisson with mean 40. Replenishment lead time is 7 days with probability 0.7, 10 days with 0.2, and 14 days with 0.1, drawn independently per order. The policy is continuous review: order Q = 400 whenever the inventory position drops to the reorder point. Size safety stock two ways for a nominal 95 percent target, once as z times sigma_D times sqrt(mean lead time) and once with the variable-lead-time term. Simulate both and report achieved cycle service level and achieved unit fill rate.
Approach
- Compute both safety stock numbers analytically before simulating, so the simulation is a check on arithmetic you already understand rather than the source of the answer.
- Track inventory position (on hand plus on order) for the reorder trigger and on hand separately for the stockout test. Triggering off on hand alone re-orders while stock is already in transit and silently changes the policy you are measuring.
- Define the two outcomes precisely and separately: cycle service level is the fraction of replenishment cycles containing at least one unit of unmet demand, while fill rate is total units shipped from stock over total units demanded. They answer different questions and z targets only the first.
- Before running anything, bound the achievable service level by hand from the lead-time mixture. Each lead-time branch is a separate Poisson demand, so cycle service level is the probability-weighted sum of P(Poisson(40 x L) <= reorder point) across the three branches, and a reorder point that cannot cover the 14-day branch caps the policy at 0.9 no matter how the simulation is tuned.
- Run enough cycles that Monte Carlo error is small against the gap you are trying to see. The standard error of a service level is sqrt(p*(1-p)/n_cycles), about 0.005 at p = 0.7 with 10,000 cycles, so the 20-point gap between the two policies is unambiguous and a 1-point one is not.
- Let unmet demand be lost rather than backordered, state that choice, and note that switching to full backordering raises measured fill rate for the same policy.
Worked solution (35 min)
- Compute mean lead time 8.3 days, variance of lead time 5.01, sigma_D = sqrt(40) = 6.32, and mean demand 40 per day.
- Naive safety stock: 1.645 * 6.32 * sqrt(8.3) = 30 units, giving a reorder point of 332 + 30 = 362. Correct safety stock: 1.645 * sqrt(8.3 * 40 + 40^2 * 5.01) = 1.645 * 91.4 = 150 units, giving a reorder point of 482.
- Write one simulation loop parameterised by safety stock: draw daily Poisson demand, decrement on hand, record shortfall, trigger an order of 400 when position hits the reorder point, and schedule the receipt after a sampled lead time.
- Run 10,000 cycles per policy with a burn-in of at least two lead times discarded so the starting position does not flatter the first cycles.
- Report achieved cycle service level, achieved fill rate and the average on-hand position for each of the two safety stock settings.
Expected result: Naive safety stock is 30 units and the variable-lead-time figure is 150 units, a factor of five. At Q = 400 with lost sales, the naive policy achieves a cycle service level of about 0.70 and the correct policy about 0.90, so the naive policy misses the nominal 0.95 by roughly 25 points and the correct one by 5. The naive ceiling is arithmetic, not simulation noise: with a reorder point of 362 the 7-day branch is essentially always covered, the 10-day branch survives with probability P(Poisson(400) <= 362) = 0.029, and the 14-day branch never does, so cycle service level cannot exceed 0.7 + 0.20.029 + 0.10 = 0.706, and reorder-point undershoot of about 20 units under daily demand arrivals pulls it to roughly 0.70. The correct policy tops out near 0.90 for the same structural reason: a reorder point of 482 covers the 10-day branch almost completely (P = 0.99997) but not the 14-day one (P = 0.0004), so that 0.1 of probability mass is near-pure loss. Fill rate is higher than cycle service level under both, about 0.92 against 0.70 and about 0.98 against 0.90, because expected units short per cycle (roughly 33 and 10) are small against Q = 400.
Verify it yourself
- Set the lead time to a constant 8.3 days and confirm the two formulas return an identical safety stock, since the lead-time variance term vanishes.
- Compare the naive policy's simulated cycle service level against its analytic ceiling, 0.7 + 0.2P(Poisson(400) <= 362) + 0.1P(Poisson(560) <= 362) = 0.706 before undershoot. A simulated figure above that is a bug, usually triggering off on hand instead of inventory position, and three seeds should land a little below it and agree within about one point, consistent with a standard error of about 0.005 at 10,000 cycles.
- Confirm fill rate exceeds cycle service level at Q = 400 under both policies, and treat that ordering as a consequence of Q rather than a law: expected units short per cycle depends on the reorder point and the lead-time demand distribution, not on Q, so holding the reorder point fixed and halving Q leaves cycle service level roughly unchanged while fill rate falls by about the same shortfall over a smaller denominator.
- Confirm simulated demand over the lead time has mean near 332 units and standard deviation near 91.4, which is exactly the quantity the correct formula multiplies by z.
Follow-up
- With Q = 100 against lead-time demand of 332 units, three or four orders are open at once. What happens to the meaning of a replenishment cycle, and to each of the two service measures?
- Lead time and demand are correlated because the supplier is capacity constrained in peak weeks. What does that do to the formula you used?
- How would you convert the extra 120 units of safety stock into an annual cost the business can weigh against the service gain?
As-of join snapshots to order lines without merge_asof
Medium · as-of join · fan-out · searchsorted
You have fct_order_line (order_line_id, sku_id, ship_from_location_id, actual_ship_at_utc, shipped_qty, shipment_id) and fct_inventory_daily (inventory_date, sku_id, location_id, available_qty). For every shipped line, attach available_qty from the latest snapshot strictly before the ship date for that exact sku and node, plus the shipment's delivery timestamp from fct_shipment_leg (leg_id, shipment_id, delivered_at_utc, leg_status). The output must have exactly one row per input order line. pandas.merge_asof is not available; implement the as-of match yourself.
Approach
- Collapse fct_shipment_leg to one row per shipment_id before joining anything. A shipment is one or more legs, so a direct line-to-leg join multiplies every line by its leg count and quietly inflates every sum computed afterwards.
- Define the shipment-level delivery as the maximum delivered_at_utc across its legs, and set it null if any leg is not delivered, since the customer has the order only when the last leg lands.
- Sort the snapshot frame by (sku_id, location_id, inventory_date) and the line frame by the same key plus ship date, then use np.searchsorted with side='left' on the sorted dates within each key group to find the insertion point; the matching snapshot is the row at position minus one.
- Guard the boundary explicitly: an insertion point of 0 means no snapshot exists before that ship date, and the result must be null rather than the first available future snapshot. Count those lines and report them.
- Assert the output row count equals the input line count immediately after each merge, before any aggregation. Row-count drift is the only symptom a silent fan-out produces, and it is invisible once the frame has been grouped.
Follow-up
- The snapshot is taken at the node's local end of day and actual_ship_at_utc is UTC. Where does that break the strictly-before rule and what do you need from dim_location to fix it?
- How would you extend this to allocate each leg's linehaul cost back to the order lines it carried?
- What changes if a line ships in two partial shipments?
Perfect order rate against the original promise in local time
Hard · timezone boundaries · order-grain rollup · semi-join
Compute weekly perfect order rate at order grain. An order qualifies only if every fct_order_line has line_status = 'shipped_full', every line's actual_delivery_at_utc falls on or before original_promised_delivery_date at the ship-from node's local end of day (dim_location.timezone holds IANA names), and no fct_shipment_leg reached through shipment_id carries exception_code in ('damage','address_issue') or leg_status in ('refused','lost'). The denominator is orders whose original_promised_delivery_date lands in the week. Undelivered lines fail. Publish with a fourteen-day lag and declare invoice accuracy as out of scope.
Approach
- Convert the event into local time rather than comparing against a UTC-anchored deadline: (actual_delivery_at_utc AT TIME ZONE 'UTC' AT TIME ZONE loc.timezone)::date <= original_promised_delivery_date. Casting the promise date to a timestamp puts the cut-off at UTC midnight starting the promise day, which is up to a full day early and wrong in both directions depending on the node's offset.
- Evaluate the three components as booleans at line grain, then roll to the order with BOOL_AND, coercing a NULL actual_delivery_at_utc to FALSE explicitly so an undelivered line fails rather than propagating NULL and dropping the order out of the comparison.
- Test the shipment exceptions with EXISTS against fct_shipment_leg, never with a join. One shipment has many legs and one order has many lines, so a join multiplies the order into the denominator and changes the metric.
- Aggregate to order grain first, then count qualifying orders over orders promised in the week. The rate is orders over orders; a line-level average is a different and easier metric.
- Apply the publication lag by restricting to weeks whose promise dates are at least fourteen days old, and emit the three component pass rates beside the headline so any movement is attributable to completeness, timeliness or exceptions.
Worked solution (40 min)
- CTE line_flags: join fct_order_line to dim_location on ship_from_location_id, producing is_full, is_on_time using the AT TIME ZONE conversion with COALESCE to FALSE, and has_exception from an EXISTS over fct_shipment_leg.
- CTE order_flags: GROUP BY order_id with BOOL_AND(is_full), BOOL_AND(is_on_time), BOOL_AND(NOT has_exception) and MIN(original_promised_delivery_date) for bucketing.
- Aggregate per DATE_TRUNC('week', promise date): COUNT() as denominator, COUNT() FILTER (WHERE all three) as perfect_orders, and the three component counts.
- Restrict output to weeks ending at least fourteen days ago.
- Re-run the on-time expression once with a naive UTC comparison and record the delta as evidence the conversion is live.
Expected result: One row per week with orders_in_denominator, perfect_orders, rate, and three component pass rates, where the rate is less than or equal to every component rate and weeks inside the fourteen-day lag are absent from the output.
Verify it yourself
- perfect_orders is less than or equal to the count passing any single component, for every week.
- Switching the deadline to a naive UTC comparison shifts the on-time component by roughly the share of orders at nodes offset from UTC. No change at all means the conversion is not being applied.
- The denominator equals COUNT(DISTINCT order_id) over orders promised in the week and does not change when the legs EXISTS clause is removed.
- No order_id appears in more than one week.
Follow-up
- All three component rates are flat but perfect order fell two points. How is that arithmetically possible, and what does it tell you about where the failures now land?
- A node sits at UTC+13 and many orders are promised on the last day of the month. What does that do to your weekly and monthly buckets?
- How do you handle an order whose lines ship from two nodes in different timezones, and which node's clock owns the promise?
Dead stock cells excluding SKUs used as substitutes
Medium · anti-join · null semantics · dead stock
Find dead stock: sku_id x location_id cells whose latest fct_inventory_daily row has on_hand_qty > 0 and which have no fct_order_line with that sku_id and ship_from_location_id in the last 90 days. Also exclude any SKU that filled another line as a substitute in that window; fct_order_line.substituted_sku_id is NULL on every line that was not substituted. Value each cell at on_hand_qty * standard_cost_cents taken from the snapshot, not from dim_sku. Return the cell, days since its last demand line, and value, ordered by value. Treat line_status = 'cancelled_no_supply' as demand.
Approach
- Take the latest snapshot per sku-location with ROW_NUMBER() OVER (PARTITION BY sku_id, location_id ORDER BY inventory_date DESC) = 1, or an equivalent MAX(inventory_date) join, and keep rows with on_hand_qty > 0.
- Anti-join demand with NOT EXISTS against fct_order_line on both sku_id and ship_from_location_id over the 90-day window, counting every line status including cancelled_no_supply, because unmet demand is still demand and a cell with it is not dead.
- Write the substitute exclusion as NOT EXISTS, or force substituted_sku_id IS NOT NULL inside the subquery. NOT IN over a column that contains NULLs evaluates to NULL for every candidate that is not an explicit match, so the predicate is never true and the result set is empty.
- Value from the snapshot's standard_cost_cents rather than dim_sku, since the dimension carries only the cost in force today and is revised at period close without back-dating.
- Get days since last demand from a LEFT JOIN LATERAL MAX(ordered_at_utc), and report cells that never had a line in a separate bucket instead of as a very large number.
Follow-up
- The query returns zero rows. Name the two likeliest causes and say which you would test first, with the one-line test.
- How do you separate genuinely dead stock from a seasonal item out of season and from a lifecycle_status = 'pre_launch' SKU?
- The same SKU is dead at one node and short at another. Write the query that finds that pattern and say what it implies about the policy.
Can you take this existing Python script and optimize it for better performance?
Medium · product sense and metrics
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- State what result would change your recommendation, so the answer is falsifiable.
- Restate the decision this analysis has to support, and who acts on the answer.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- Which segment would you cut first, and what would that rule out?
What metrics do you prioritize when monitoring a deployed model for data drift?
Medium · product sense and metrics
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Name one primary metric, then the guardrail that stops it being gamed.
- State what result would change your recommendation, so the answer is falsifiable.
Follow-up
- Which segment would you cut first, and what would that rule out?
- What would you do if the primary metric and the guardrail moved in opposite directions?
Explain the difference between concurrency and parallelism in Python, and provide a use case for each.
Medium
Approach
- Say what you would check first and why it is the highest-information step.
- Clarify what is being asked and what a complete answer would contain.
- State your assumptions explicitly before working the problem.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
What are the limitations of the Global Interpreter Lock (GIL) in multi-threaded applications?
Medium
Approach
- Say what you would check first and why it is the highest-information step.
- Clarify what is being asked and what a complete answer would contain.
- State your assumptions explicitly before working the problem.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Cut variance on a safety-stock test with CUPED and strata
Medium · cuped · stratification · variance reduction · ratio metrics
You are testing a new safety-stock formula on 300 SKU-location cells inside one region, randomised 150/150. The outcome is eight-week unit fill rate per cell from fct_order_line; you also have 26 weeks of pre-period fill rate per cell, and each cell carries abc_class and xyz_class in dim_sku. The cell-level correlation between pre-period and post-period fill rate is 0.7. Show the CUPED adjustment, state exactly what it does to the standard error, and say which cells it will fail on and why.
Approach
- Define the adjusted outcome: Y* = Y - theta * (X - Xbar), where X is the pre-period cell fill rate and theta = Cov(Y, X) / Var(X). Because randomisation makes X independent of assignment, the treatment effect estimate is unbiased for any theta; theta only controls how much variance is removed.
- Quantify the win rather than asserting it: Var(Y*) = Var(Y) * (1 - rho^2) = 0.51 * Var(Y), so the standard error falls by a factor of sqrt(0.51) = 0.71, the MDE falls 29%, and the same precision would otherwise have needed 51% of the cells.
- Estimate theta pooled across both arms on the observed data. Fitting it per arm re-introduces a treatment-dependent term into the adjustment; the O(1/n) bias from estimating theta at all is negligible at 300 cells but should be named rather than ignored.
- Stratify the randomisation itself on abc_class by xyz_class and fit with stratum fixed effects. Strata remove between-class variance that a single pre-period covariate does not, and XYZ class is precisely the axis along which demand variability, and therefore fill-rate variance, differs.
- Name where it fails. Fill rate is a ratio whose denominator is random and small for C and Z cells, so the pre-period value is a noisy estimate of the cell's true rate, rho collapses and the adjustment buys little; a cell with zero pre-period demand has no covariate at all. Handle the ratio by adjusting numerator and denominator and applying the delta method for the variance, never by averaging cell-level ratios.
Worked solution (30 min)
- Build a cell-level table: 26-week pre-period fill rate X, 8-week post-period fill rate Y, arm, abc_class, xyz_class, and pre-period ordered_qty as a weight.
- Regress Y on X pooled across arms to obtain theta, then compute Y* = Y - theta * (X - Xbar).
- Estimate the effect by regressing Y* on treatment with abc by xyz stratum fixed effects and heteroskedasticity-robust standard errors.
- Compare the adjusted and unadjusted standard errors; the ratio should sit near sqrt(1 - 0.49) = 0.71.
- Split by xyz_class and report rho within each; expect a materially lower correlation and a much smaller reduction on Z cells.
Expected result: theta = Cov(Y, X) / Var(X). At rho = 0.7 the adjusted variance is 51% of the unadjusted, the standard error falls 29%, and the MDE falls by the same 29%. Unbiasedness does not depend on theta being right; only the size of the reduction does. The adjustment buys almost nothing on low-volume C and Z cells whose pre-period rate is itself noisy.
Verify it yourself
- An A/A replay on the pre-period must give a CUPED effect estimate centred on zero; if it does not, the covariate is contaminated by post-assignment data.
- The empirical ratio Var(Y*) / Var(Y) should land near 1 - rho^2, which is 0.51 here.
- The adjusted and unadjusted point estimates should agree within roughly one unadjusted standard error; a larger move means theta is absorbing a real imbalance rather than noise, and the randomisation deserves a second look.
Follow-up
- The policy change alters ordered_qty through substitution behaviour. What does a treatment-affected denominator do to a ratio metric, and does CUPED still help?
- Three cells carry 40% of regional volume. What does that do to your variance, and which estimand should you actually report?
Perfect order rate falls only in the newest two weeks
Medium · right censoring · reporting lag · metric maturation
A weekly perfect order chart holds at 94 percent for eleven weeks, then reads 91 percent and 84 percent in the two most recent weeks. The metric requires every line shipped_full, actual_delivery_at_utc on or before original_promised_delivery_date, and no joined shipment leg carrying exception_code IN ('damage','address_issue') or leg_status IN ('refused','lost'). It is published on a 14-day lag. Using fct_order_line and fct_shipment_leg, decide whether service regressed and specify what the chart should show for incomplete weeks.
Approach
- Start from the structure of the metric: the denominator is fixed at promise date while the numerator requires an event that may not have happened yet, so any week younger than the full delivery-plus-claims window is mechanically depressed. Establish that before hunting for a cause.
- Quantify the incompleteness instead of assuming it. Per promise week, report the share of lines with actual_delivery_at_utc IS NULL and the share of shipments whose legs are still leg_status IN ('tendered','in_transit').
- Build the maturation curve from history. For each of the eleven settled weeks, recompute the metric as it would have looked at 3, 7 and 14 days after the promise date, so a recent week can be compared against a healthy week at the same age rather than against a settled one.
- Only after age-matching, test whether any residual is real by cutting it by ship_from_location_id, mode and exception_code, and asking whether the loss concentrates where a genuine failure would concentrate.
- Fix the chart rather than only the analysis: suppress or grey weeks younger than the publication lag, or publish an age-matched estimate with an interval, and write the rule down so the next reader is not caught by the same shape.
Follow-up
- Damage claims can arrive up to thirty days later. Does that argue for a longer lag or for a different numerator?
- How would you detect a genuine regression inside the lag window without waiting two weeks for it to settle?
- P90 order cycle time shows the same maturation shape but biased the other way. Why, and which metric is more dangerous to read early?
Suggested practice checklist
Four days spend equal time on query work, statistics, modelling and product judgement at deliberately shallow depth, which produces a scored map of where you actually stand. The last three days spend everything on the two areas the role weights most, and close by re-running day one to measure movement.
Day 1 — Breadth pass: query fluency
- Solve six prompts spanning aggregation, joins, window functions and date arithmetic in 60 minutes total, stopping at 10 minutes each whether or not it works, and mark every prompt as solved, solved slowly, or stuck.
- For each unsolved prompt write the single blocking sentence (I lost the grain, I did not know the frame clause, I could not express the date boundary) instead of reading the solution.
- Translate one pandas transformation you know well into SQL and one SQL query into pandas, checking that both return the same row count and the same totals.
Deliverable: A scored six-row table, one line per prompt, saved for the day-seven re-run.
Day 2 — Breadth pass: statistics and inference
- Answer ten short questions in writing with nothing open: what a p-value is conditional on, what a 95 percent interval covers across repeated samples, when a paired test is the right one, what the bootstrap estimates, why multiple comparisons inflate false positives, how controlling the family-wise error rate differs from controlling the false discovery rate, what power depends on, what a missed real effect costs a product, the three situations where the central limit theorem does not rescue you (small n, very heavy tails, dependent observations), and what a standard error is the standard deviation of.
- Grade yourself against a reference and count only the answers that were exactly right, not the ones that were nearly right.
- Rewrite the two weakest answers the following morning from memory in full sentences.
Deliverable: Ten graded answers with an honest count of exact hits.
Day 3 — Breadth pass: modelling
- Take one tabular dataset end to end in 90 minutes: a leakage-safe split, a baseline that is not a model (majority class or historical mean), one regularized linear model, one gradient-boosted tree, and a single evaluation metric chosen before you look at any result.
- Write why that metric fits the cost structure: precision at a fixed recall for alerting, calibration for anything feeding a price or a threshold, ranking metrics for retrieval, and note that area under the ROC curve is insensitive to class balance in a way that can flatter a rare-positive problem.
- Name the leak you were most likely to introduce (an encoding fit on all rows before splitting, or a feature computed after the label's timestamp) and write the check that would have caught it.
Deliverable: A notebook whose first cell states the metric and the baseline, plus two lines on what beat what and by how much.
Day 4 — Breadth pass: product judgement
- Answer three case prompts aloud at 15 minutes each, timing how long passes before you state a success metric.
- For one case write the first segmentation you would run and the row counts you expect per segment, so that a tiny segment cannot quietly drive the conclusion.
- Take a metric definition you did not write, from a public dashboard, a textbook, or documentation you already have open, and list every place two analysts implementing it would diverge: which rows the denominator admits, whether the unit is an account or a person, what the time window is anchored to, and what happens to data that arrives late. Then write the one question that would close the largest of those gaps.
Deliverable: Three recorded case answers plus an ambiguity list for a metric someone else defined, ending in the single question you would ask about it.
Day 5 — Depth, first area
- Rank the four areas by how many bullet points in the role description each one covers, pick the top one, and spend the entire day inside it.
- Work the six hardest problems you can find in that area and for each write the generalizable move you should have reached for first, rather than the answer.
- Re-solve the two you failed the same evening with notes closed.
Deliverable: Six generalizable moves written as instructions to yourself, not as solutions.
Day 6 — Depth, second area, and the seam between them
- Repeat the depth protocol on the second-ranked area with the same six-problem structure.
- Construct one problem that requires both areas at once, for example a metric redefinition whose effect you must validate with a test whose readout you then have to query.
- Solve your own combined problem end to end and note where the handoff between the two areas cost you time.
Deliverable: One combined problem, solved end to end, with the handoff failure written down.
Day 7 — Integration and re-measurement
- Re-run the six prompts from day one under the same clock and compare both correctness and time.
- Run a 60-minute mixed mock that moves between areas without warning, since switching cost is what breadth passes do not train.
- Write the two areas you would still fail on, and the sentence you will use in the interview when you hit one of them.
Deliverable: A before-and-after score table plus a written plan for the two remaining gaps.
Bring numbers, including the ones you are unsure of
An answer without a quantity is hard to interrogate, so interviewers keep probing until they find one. Come with the baseline, the change, the window it was measured over, and how confident you were. If the effect never got measured, say so and say what you would have measured. Fabricated precision is worse than an honest gap.
How do you handle feature engineering for time-series data in the automotive domain?
Medium · behavioural and stakeholder questions
Approach
- Name the disagreement or constraint, and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on your reasoning.
- Close with what you would do differently, concretely.
Follow-up
- What would you do differently if you ran that project again?
- How did you know the outcome was caused by your change?
Describe a time you had to troubleshoot a model that was performing poorly in production.
Medium · behavioural and stakeholder questions
Approach
- Close with what you would do differently, concretely.
- Pick a story where you drove the decision, not one where you observed it.
- Name the disagreement or constraint, and how you resolved it with evidence.
Follow-up
- How did you know the outcome was caused by your change?
- What did you decide not to do, and why?
Tell a sponsor the effect cannot be measured in six weeks
Hard · power · selection bias · quasi experiments · saying no
A VP wants a read in six weeks on a forward-stocking policy now live at eight of forty nodes. Replenishment lead time on the affected lanes is five weeks, the eight nodes were chosen by the ops team because they were the worst performers, and the cross-node standard deviation of weekly fill rate is about 3 points. The VP has already told his leadership team that a number is coming. Tell him what you can and cannot deliver in six weeks, and propose the design and timeline you would commit to instead.
Approach
- Do the power arithmetic in front of him rather than asserting the test is underpowered: comparing eight treated against thirty-two untreated node means with a between-node standard deviation of 3 points gives a standard error near 3 times the square root of one eighth plus one thirty-second, about 1.2 points, so the minimum detectable effect at 80 percent power and a two-sided 5 percent test is roughly 3.3 points. Any real effect smaller than that comes back as a null you cannot interpret.
- Name the burn-in problem separately: with a five-week lead time, six weeks covers barely one replenishment cycle, so whatever you measure is the transition rather than the new steady state, and the transition usually looks worse than the policy is.
- Name the selection problem third: nodes picked because they were worst will improve toward the network mean without any policy, so a simple before-and-after at those nodes is biased upward and will over-claim.
- Offer what is genuinely deliverable in six weeks: an implementation read, meaning whether the policy is actually in force at the eight nodes, whether inventory positions moved as designed, and whether any leading indicator such as short_reason_code mix is moving, framed explicitly as operational verification and not an effect estimate.
- Propose the real design with dates: extend to sixteen weeks covering roughly three cycles, use a difference in differences against matched comparison nodes chosen on pre-period fill rate and volume, cluster variance at the node, and pre-register the burn-in window that will be excluded.
- Help him with the commitment he already made: give him the exact wording for what he reports at week six, so the honest answer arrives as something he can say rather than as a refusal.
Follow-up
- He asks you to add the remaining thirty-two nodes to the rollout next month. What does that do to your design?
- If the effect really is 1 point, is the policy worth keeping, and how would you ever know?