cswork / libraryOpen practice workspace

Interview guides · Bertrandt AG Spain

Bertrandt AG Spain Data Scientist Interview Questions & Guide 2026

Case practice for Bertrandt AG Spain data scientist candidates: scope a vague prompt into a measurable question before you analyze it.

The 60-second brief

As a Data Scientist at Bertrandt AG Spain, you will sit at the intersection of complex engineering challenges and data-driven innovation. Bertrandt AG is a leading development partner to the automotive and technology industries, and your role is critical in transforming raw data into actionable insights that drive vehicle performance, safety, and efficiency. You will be tasked with solving intricate problems that directly impact the future of mobility, often working on projects that require a deep understanding of both statistical modeling and the physical constraints of industrial systems.

Learn the economics of the product category before the loop. Marketplaces, subscription products and ad-supported products turn on different core quantities (match rate and liquidity, retention and churn, fill rate and yield) and fail in different characteristic ways.

Focus areas

  • Decompose expected loss into PD, LGD, EAD
  • Separate authorization, settlement and dispute outcomes cleanly
  • Set fraud thresholds by expected cost

Overview

This role is not merely about building models; it is about providing the intelligence that supports high-stakes engineering decisions. You will collaborate with cross-functional teams, including software developers, mechanical engineers, and research directors, to bring data-centric solutions from concept to deployment. Expect a work environment that values technical rigor, professional curiosity, and the ability to translate complex analytical findings into clear, strategic recommendations for stakeholders.

Where to focus your preparation

Treat the sections below as preparation areas and confirm the format with your recruiter.

Preparation focus

No round sequence has been reported for this company, so work the categories below and confirm the format with your recruiter.

What to demonstrate

  • Breadth across SQL, experimentation and product reasoning
  • Ability to state assumptions before choosing a method

How to prepare

  • Drill the practice exercises below and time yourself
  • Prepare three quantified stories about decisions you drove

Where candidates lose points

Using written premium as the denominator of a loss ratio

Premium is written at inception and earned pro rata across the exposure period, so in a growing book written premium runs ahead of earned premium and the loss ratio comes out too low, with the error reversing when the book shrinks. The numerator has the mirror-image problem if it omits incurred-but-not-reported reserves, since recent accident periods then look profitable twice over. Both sides must refer to the same exposure period, which is what an accident-period view at a fixed development age enforces.

Assuming a model is fair because protected attributes are not among its inputs

Postcode, device, tenure, income proxies and even transaction patterns correlate with protected characteristics, so a model can produce a disparate outcome without ever reading the attribute. Credit decisions additionally carry an explainability obligation in many jurisdictions, since a denial has to be accompanied by its principal reasons, which constrains model form and feature engineering rather than being a reporting afterthought. Treating fairness testing and reason-code generation as design constraints from the first model version is far cheaper than retrofitting them to a deployed one.

Sizing estimates built on unnamed, unrevisable assumptions

Write each assumption as a named number you can change, then show the arithmetic so the interviewer can challenge one input instead of the whole answer. Finish by saying which assumption the result is most sensitive to, which matters more than the point estimate.

Extrapolating a first-week lift inflated by novelty effects

Plot the treatment effect by days since first exposure instead of quoting one pooled average. A lift that decays toward zero across the test window is behaviour that will not persist, and annualising it produces a forecast that misses by an order of magnitude.

Questions & practice

Walk us through a past project where you had to iterate on a model based on performance feedback.

Medium · machine learning and modelling

Approach

  1. Check what information would not exist at prediction time, and exclude it.
  2. Say how the offline result would be validated online before it is trusted.
  3. Pick an evaluation metric that matches the cost of each error type, not a default.

Follow-up

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

Describe your process for validating a model before it is moved into a production environment.

Medium · machine learning and modelling

Approach

  1. Set a baseline first, so any model has something honest to beat.
  2. Say how the offline result would be validated online before it is trusted.
  3. 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?
  • Where could label leakage enter this setup?

How do you stay updated with the latest advancements in machine learning?

Medium · machine learning and modelling

Approach

  1. Say how the offline result would be validated online before it is trusted.
  2. Check what information would not exist at prediction time, and exclude it.
  3. Set a baseline first, so any model has something honest to beat.

Follow-up

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

Write integrity checks for the authorization and settlement lifecycle

Easy · data quality · minor units · fx reconciliation

You are given fct_payment_authorization as a pandas DataFrame with auth_id, requested_at, amount_minor, transaction_currency, auth_result, decline_reason_code, is_reversal, parent_auth_id, captured_at, captured_amount_minor, settled_at, settlement_amount_minor, settlement_currency and settlement_fx_rate. Write a function returning one row per integrity check with the check name, failing row count, failing share and up to five example auth_id values. Cover at least six checks, one of which reconciles captured_amount_minor against settlement_amount_minor through settlement_fx_rate. Partial capture, zero-amount verification and a decline with no capture are all legitimate and must not be flagged.

Approach

  1. Separate contract violations from observations before writing any code: an approved row carrying a decline_reason_code is structurally impossible, while a capture two days after requested_at is merely slow and belongs in a different severity tier.
  2. Express each check as a boolean mask over the whole frame and collect the masks in a dict, so the summary table is one comprehension over mask.sum() rather than a row loop.
  3. For the reconciliation, leave minor units before comparing: expected = captured_amount_minor / 10exponent[transaction_currency] * settlement_fx_rate * 10exponent[settlement_currency]. Build the exponent table covering zero-decimal and three-decimal currencies instead of assuming two everywhere.
  4. Guard the legitimate cases explicitly so each mask fires only on the genuine contradiction: captured_amount_minor below amount_minor is partial capture, amount_minor of zero on an approved row is account verification, a null captured_at on a declined row is correct.
  5. Sort the output by failing share times a stated severity weight, because a check firing on 0.01 percent of rows can still be the one that breaks a ledger reconciliation.

Worked solution (25 min)

  1. Assert auth_id is unique, then build a currency exponent lookup that includes the zero-decimal and three-decimal currencies present in the data.
  2. Define masks for: approved with non-null decline_reason_code; declined with non-null captured_at; captured_amount_minor above amount_minor with parent_auth_id null; captured_at before requested_at; settled_at before captured_at; is_reversal true with parent_auth_id null; settlement_currency differing from transaction_currency while settlement_fx_rate is null.
  3. Add the exponent-aware reconciliation mask with a tolerance of one minor unit plus a small relative term.
  4. Assemble a frame of check_name, n_failing, pct_failing and up to five sample auth_id values, ordered by severity then share.
  5. Read five flagged rows per check by hand and confirm each is genuinely contradictory before reporting any counts.

Expected result: A DataFrame of at least eight rows, one per check, each with n_failing, pct_failing and example auth_id values. The reconciliation check should fire on a whole currency at once rather than on scattered rows, because an exponent error is systematic while an FX error is not.

Verify it yourself

  • Every mask returns false on three hand-constructed legitimate rows: a partial capture, a zero-amount verification and a decline with no capture.
  • Each pct_failing uses the denominator that check actually applies to, with reversals and verifications excluded where they are out of scope.
  • The reconciliation residual is centred at zero with spread under one minor unit; residuals clustered at exactly 100x or 0.01x mean an exponent is wrong, not a rate.

Follow-up

  • Which of these would you run as a blocking pipeline assertion and which as a monitored metric, and why?
  • The FX check fails on 3 percent of rows, all in one settlement currency. How do you decide between a data bug and a rounding convention?
  • How would you detect that a currency's minor-unit exponent is wrong in your reference table, using only the transaction data?

Reconcile captured authorizations against the daily settlement total

Easy · reconciliation · currency · rounding

fct_payment_authorization holds captured_amount_minor in transaction_currency, and settlement_amount_minor in settlement_currency with settlement_fx_rate applied at settlement rather than at authorization. The rate is quoted in major units of settlement_currency per major unit of transaction_currency, and dim_currency.minor_unit_exponent carries the ISO 4217 exponent for each code (0, 2 or 3 depending on the currency). Produce a daily reconciliation: for each settled_at date and settlement_currency, return settled_count, total settlement_amount_minor, and the sum of captured_amount_minor converted into settlement minor units. Flag any date and currency pair whose two totals differ by more than one minor unit per settled authorization. Do not sum amounts across currencies anywhere in the output.

Approach

  1. Restrict to rows that actually settled: settled_at is not null and settlement_amount_minor is not null, which is a smaller population than captured rows because a capture can still be in flight.
  2. Truncate settled_at to a date with an explicit time zone so the cut matches the ledger's cut, since settled_at is timestamptz and date_trunc on timestamptz silently uses the session time zone.
  3. Join dim_currency twice, once on transaction_currency and once on settlement_currency, so both exponents are on the row. Minor units are not a common scale: a bare captured_amount_minor * settlement_fx_rate is correct only when the two exponents are equal, and a zero-decimal currency settling into a two-decimal one is wrong by a factor of 100.
  4. Convert per row as ROUND(captured_amount_minor::numeric / POWER(10::numeric, exp_txn) * settlement_fx_rate * POWER(10::numeric, exp_settle)) — minor units to major in the transaction currency, apply the major-per-major rate, then back to minor units in the settlement currency. The collapsed form ROUND(captured_amount_minor::numeric * settlement_fx_rate * POWER(10::numeric, exp_settle - exp_txn)) is the same expression. Round per row and then sum, not SUM(...) * an average rate, because the rate varies row by row and rounding per row is what the settlement file did.
  5. Group by the settlement date and settlement_currency together, never by date alone, and carry the currency into every output column name or row.
  6. Compare the two totals with a tolerance scaled by settled_count, since per-row rounding accumulates linearly in the number of rows rather than being a fixed constant.

Follow-up

  • A partial capture means captured_amount_minor is less than amount_minor. Where does that show up in this reconciliation, and where does it not?
  • On one currency pair the converted total is consistently about one hundredth of the settlement total, on every date, while the other pairs reconcile. Which two columns do you inspect first, and what single change fixes it?
  • The rate is documented as major-per-major. If a feed started publishing it minor-per-minor instead, which pairs would still reconcile and which would break?
  • How would you present a total across currencies to a finance partner who has asked for one number?

Monthly delinquency roll rates with honest exiting-account denominators

Hard · roll rates · window functions · self joins

From fct_loan_performance_monthly, compute month-over-month roll rates: for each as_of_month_end and delinquency_bucket, the share of loans in that bucket that are in a worse state at the following month end. Use this disposition convention and apply it without exception: charge-off is the terminal state one rank past dpd_90_plus, so a loan that charges off has rolled forward, not exited; loans that leave the book any other way — prepaid in full, sold, matured, or simply absent at the next month end — are exited. Bucket order is current, dpd_1_29, dpd_30_59, dpd_60_89, dpd_90_plus, then charged_off. Return from_month, from_bucket, accounts, rolled_forward, cured, stayed, exited and roll_rate, and show that the four dispositions sum to accounts on every row.

Approach

  1. Map the five delinquency_bucket values to ranks 0 through 4 in a small inline VALUES list, and reserve rank 5 for charged_off, because 'worse' is an ordering statement and enum text does not sort that way reliably. Read charge-off from charge_off_flag on the next month's row rather than from delinquency_bucket, which keeps carrying whatever delinquency value the loan held when it was written off.
  2. Use LEAD(as_of_month_end), LEAD(bucket_rank) and LEAD(charge_off_flag) OVER (PARTITION BY loan_id ORDER BY as_of_month_end), then verify the leading month end is exactly one month after the current one, since a loan missing a month would otherwise appear to cure or roll across a gap it never traversed.
  3. Classify each loan-month into exactly one of four labels off the adjacent next row, whose rank is 5 when its charge_off_flag is true and its mapped bucket rank otherwise. rolled_forward: the next rank is strictly greater. cured: strictly lower. stayed: equal. exited: there is no adjacent next row at all, which is where prepayment, sale, maturity and a gap in the monthly history land.
  4. Define rolled_forward as 'any strictly worse rank', not 'rank plus one'. Under normal monthly aging the only reachable worse state is the next bucket, so the two definitions coincide — except at charge-off, where a loan can go from dpd_60_89 straight to rank 5. A strict plus-one test leaves those loan-months matching no branch and breaks the exhaustiveness invariant.
  5. Be explicit about why charge-off is a roll rather than an exit: routed to exited, dpd_90_plus has no worse state left, so its roll_rate is identically zero by construction — arithmetic, not a credit finding. At rank 5 the dpd_90_plus row reports roll-to-loss, which is the number a loss forecast actually consumes.
  6. Aggregate by from_month and from_bucket and require that rolled_forward + cured + stayed + exited equals accounts on every row, which is the invariant that proves the classification is exhaustive and mutually exclusive.

Worked solution (45 min)

  1. CTE ranks: an inline VALUES mapping of the five delinquency_bucket values to ranks 0 through 4, joined onto the monthly rows. Rank 5 is not in the map; it is assigned during classification.
  2. CTE leads: add LEAD(as_of_month_end), LEAD(bucket_rank) and LEAD(charge_off_flag) partitioned by loan_id ordered by as_of_month_end.
  3. CTE classified: derive next_rank as CASE WHEN next_charge_off_flag THEN 5 ELSE next_bucket_rank END, then a CASE expression producing exactly one label per loan-month — exited when the next month end is null or is not the current month end plus one month, otherwise rolled_forward / cured / stayed by comparing next_rank to bucket_rank.
  4. Aggregate to from_month and from_bucket with COUNT(*) and four FILTER counts, then compute roll_rate as rolled_forward over accounts with a numeric cast.
  5. Assert the exhaustiveness invariant in a final HAVING or a separate verification query before trusting any number.

Expected result: One row per month and originating bucket, with the four disposition counts summing to accounts on every row and roll_rate between 0 and 1. Roll rates climb across current, dpd_1_29, dpd_30_59 and dpd_60_89, because each successive bucket holds loans that have already failed to cure. Do not expect that climb to continue into dpd_90_plus: that bucket is a catch-all holding several monthly cohorts at once until the charge-off policy retires them, so its roll-to-loss rate is set by the policy horizon — under a 180-day policy roughly a third of the bucket rolls in a given month — and usually sits below the dpd_60_89 rate. Cure out of dpd_90_plus is well under ten percent.

Verify it yourself

  • rolled_forward + cured + stayed + exited equals accounts for every row, with no exceptions.
  • SUM(accounts) for a given from_month equals the count of loan rows at that month end, so no loan-month was dropped or counted twice.
  • Removing the adjacency test changes the cure and roll counts, and the size of that change is exactly the number of loans with a gap in their monthly history.
  • The rolled_forward count for from_bucket dpd_90_plus in month M is at least the number of loans whose first charge_off_flag = true row falls at month end M+1. If it is materially lower, charge-off is being routed into exited.
  • roll_rate for dpd_90_plus is strictly greater than zero in any month with charge-offs. A flat zero on that bucket across every month is the signature of charge-off classified as an exit, not of an unusually stable book.
  • No loan-month is counted as both rolled_forward and exited: a loan with a charged-off row at M+1 has an adjacent next row, so it can never reach the exited branch.

Follow-up

  • Project the next three months of dpd_90_plus inflow from these roll rates. What assumption does that projection make, and when does it break?
  • A restructure resets days_past_due to zero. What does that do to the cure rate out of dpd_60_89, and how would you separate a real cure from a reset?
  • Argue the other convention: prepayment in exited but charge-off also in exited, with roll rates reported only for the four non-terminal buckets. What does that series answer better, and what does it lose?
  • The charge-off policy changed from 180 to 120 days. Show where that appears in this table and what it does to the dpd_90_plus roll rate specifically.

How would you approach a predictive maintenance project for vehicle 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

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

How do you ensure your models are scalable and production-ready?

Medium

Approach

  1. Clarify what is being asked and what a complete answer would contain.
  2. Say what you would check first and why it is the highest-information step.
  3. State your assumptions explicitly before working the problem.

Follow-up

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

Size a two-week test on the ecommerce authorization path

Easy · power · mde · ratio metrics · clustering

Your team wants to test a new decline-retry policy on the ecommerce channel. The metric is the dollar-weighted authorization approval rate from fct_payment_authorization: approved amount_minor over attempted amount_minor, after collapsing retry chains within a 15-minute window on (card_token_id, merchant_id, amount_minor) and excluding is_reversal rows and zero-amount verifications, all converted to one reporting currency. Baseline is 87 percent on roughly 900,000 collapsed attempts in two weeks. Randomisation is by customer_id, mean six attempts per customer. Give the minimum detectable effect at 80 percent power, two-sided alpha 0.05, and say what you would change if it is too large.

Approach

  1. Compute the count-weighted binomial anchor first, because it is the number everyone expects and you need it to show why it is wrong. With n = 450,000 attempts per arm and p = 0.87, MDE = (1.96 + 0.8416) * sqrt(2p(1-p)/n).
  2. State that the real metric is a ratio of two sums with a random denominator, not a Bernoulli mean, so the binomial standard error is not the right one. Linearise: Var(R) is approximately Var(Y_i - R * D_i) / (n * Dbar^2), where Y_i and D_i are the approved and attempted amount totals for customer i, n is the number of customers, and Dbar is the mean attempted amount per customer.
  3. Apply the clustering correction, since assignment is by customer and attempts within a customer are correlated. Ask for or estimate the intra-customer correlation; the design effect is 1 + (m - 1) * rho with m = 6, so rho = 0.05 gives 1.25 and inflates the MDE by sqrt(1.25) = 1.118.
  4. Do not assume the amount-weighted variance; estimate it by bootstrapping customers from the last eight weeks of history and resampling whole customers, which captures both the skew in amount_minor and the within-customer correlation in one step.
  5. If the MDE exceeds the plausible effect, list the levers in order of cost: extend duration (MDE falls as 1/sqrt(n)), restrict to the segment where the rule can bind at all rather than diluting across all traffic, apply CUPED on the customer's pre-period approved amount, or switch the primary to the count-weighted rate and demote the dollar-weighted rate to a secondary read.

Worked solution (20 min)

  1. Split 900,000 collapsed attempts into 450,000 per arm and compute the binomial anchor: sqrt(2 * 0.87 * 0.13 / 450000) = sqrt(5.027e-7) = 7.090e-4; multiply by 2.8016 to get 1.99e-3.
  2. Derive the customer count: 900,000 / 6 = 150,000 customers, 75,000 per arm, and note that this is the true sample size for inference.
  3. Apply the design effect at rho = 0.05: 1 + 5 * 0.05 = 1.25, so the count-weighted MDE becomes 1.99e-3 * 1.118 = 2.22e-3.
  4. State that the dollar-weighted MDE requires the linearised variance and cannot be derived from p alone; specify the customer-level bootstrap that would produce it and note it will be larger because amount_minor is right-skewed.

Expected result: Count-weighted MDE is about 0.20 percentage points unclustered and about 0.22 percentage points at a design effect of 1.25, against an 87 percent baseline (roughly 0.26 percent relative). The dollar-weighted MDE is not computable from the stated inputs and must be estimated by a customer-level bootstrap; the candidate should say so rather than quote the binomial figure for it.

Verify it yourself

  • z(0.975) + z(0.80) = 1.9600 + 0.8416 = 2.8016; any MDE quoted with a different multiplier is using different power or alpha than stated.
  • Doubling the test duration should shrink the MDE by exactly sqrt(2) = 1.414 if the traffic mix is stable; if a candidate's arithmetic does not, the formula is wrong.
  • Run the bootstrap on two historical periods split A/A: the bootstrap standard error of the difference times 2.8016 should reproduce the analytic MDE.
  • A/A splits of historical data should reject at roughly 5 percent; materially more means the variance model is understated.

Follow-up

  • The rule only changes behaviour on declined attempts, which are 13 percent of traffic. How does restricting the analysis population to attempts that could have triggered the rule change both the MDE and the estimand?
  • How would your sizing change if the split were 90/10 instead of 50/50, and why is the loss more than proportional?
  • Currency: the metric sums amounts converted to one reporting currency. Would you use settlement_fx_rate or a rate table pinned at test start, and what does the choice do to variance?

Approval rate fell but approved value did not

Easy · approval rate · retry chains · denominators

Over ten days the count-weighted 7-day approval rate on fct_payment_authorization fell from 91 to 86 percent, while captured value in the reporting currency is flat. Available columns: auth_id, card_token_id, merchant_id, mcc, channel, requested_at, amount_minor, transaction_currency, auth_result, decline_reason_code, risk_score, is_reversal, parent_auth_id, captured_at, captured_amount_minor, issuer_country. In twenty minutes, decide whether approved value is actually at risk, and hand back a corrected rate together with its denominator and every exclusion written down.

Approach

  1. Restate the denominator before querying anything. The current one counts every row, so reversals (is_reversal = true), incremental authorizations (parent_auth_id not null) and zero-amount verification attempts (amount_minor = 0) are all sitting in it.
  2. Chart numerator and denominator separately by day. If approved counts are flat and total attempts rose, the rate moved because the denominator grew, which is a different investigation from a rule change and points at a different owner.
  3. Group declines by decline_reason_code and merchant_id. Retry-driven inflation concentrates in a few soft decline codes at a few merchants; a genuine policy change spreads across merchants within one code family.
  4. Collapse retry chains: partition by (card_token_id, merchant_id, amount_minor), keep one attempt per 15-minute window taking the best outcome, and recompute both count-weighted and dollar-weighted rates on the collapsed set.
  5. Convert amount_minor to one reporting currency using each currency's ISO 4217 exponent before any dollar weighting, then cut by channel and issuer_country to confirm nothing is hiding underneath a flat total.
  6. Report both rates side by side with the exclusion list attached, and state which definition the alert should have been built on.

Follow-up

  • How would you pick the retry-collapsing window when merchants retry on different schedules?
  • Flat captured value could itself be hiding a mix shift. How do you rule that out?
  • What monitor would have caught denominator inflation on the day it started, rather than ten days later?

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.

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.

How do you handle missing or noisy data in a large dataset?

Medium · behavioural and stakeholder questions

Approach

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

Follow-up

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

What is your experience with the SCRUM framework in a data science context?

Medium · behavioural and stakeholder questions

Approach

  1. Close with what you would do differently, concretely.
  2. Pick a story where you drove the decision, not one where you observed it.
  3. Quantify the outcome, including what you would not claim credit for.

Follow-up

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

Describe your experience with SQL and your approach to optimizing complex queries.

Medium · behavioural and stakeholder questions

Approach

  1. Close with what you would do differently, concretely.
  2. Name the disagreement or constraint, and how you resolved it with evidence.
  3. Pick a story where you drove the decision, not one where you observed it.

Follow-up

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