cswork / libraryOpen practice workspace

Interview guides · Dassault Systèmes

Dassault Systèmes Data Scientist Interview Questions & Guide 2026

Dassault Systèmes data scientist prep: probability, sampling distributions, and stating the assumption behind every test you run.

The 60-second brief

In the role of Data Scientist at Dassault Systèmes, you will be pivotal in transforming complex data into actionable insights that drive innovation and improve user experiences across a variety of industries. As a leader in 3D design, digital mock-up, and product lifecycle management (PLM) solutions, Dassault Systèmes relies on data scientists to harness the power of data analytics, machine learning, and artificial intelligence to enhance their cutting-edge products and services.

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

  • Correct discovery slates for position bias
  • Diagnose rebuffering by device, network and POP
  • Define a qualified stream and defend it

Overview

Your contributions will not only optimize existing processes but also influence product development and strategic decisions within cross-functional teams. You will engage with diverse datasets, applying advanced analytical techniques to solve real-world problems, thus directly impacting the company's ability to deliver value to its users. Expect to work on exciting projects, such as predictive analytics in health care or optimization models for manufacturing, where your insights will shape the future of these sectors.

The Dassault Systèmes interview process

Dassault Systèmes candidates report 4 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

HR Screening

Whoever runs this call is usually not a practitioner. They take notes, and a hiring manager skims those notes later, so the real question is whether your work survives being written down by someone outside the field. Test every project sentence against that: could a non-specialist repeat it correctly without knowing what a propensity score is? Carry a plain-language version of each project and one reason you want this particular role that you could not copy onto another application. Vagueness at this stage reads as inexperience, even when the underlying work was genuinely deep.

What to demonstrate

  • Whether a non-specialist can restate your projects accurately, since their paraphrase is what reaches the hiring manager
  • Whether your reason for wanting the role points at the work itself rather than the company's reputation
  • Whether your language signals the level being screened for: what you decided yourself versus what you were handed

How to prepare

  • Write a two-sentence, jargon-free version of each major project: the question nobody could answer, and the decision your work changed. Read it to someone outside data and have them repeat it back
  • Point your 'why this role' answer at something concrete in the job description or the product surface you would be working on, and keep it to two sentences
  • Have two questions ready about measurement: which metric the team is held to, and who acts on an analysis once it lands

Technical Interviews

Much of what gets scored here happens out loud while you type. Nobody can see your reasoning inside a half-written query, so five silent minutes read as being stuck even when they are not. State the plan in plain language first: which tables, what grain you are aggregating to, and the one filter that defines the population. Then write it. The narration doubles as insurance, because a wrong plan gets caught early and cheaply while a wrong query gets caught at the end with no time left to redo it. A timed statistics section, where one exists, is a separate test with its own clock.

What to demonstrate

  • Whether the query you write matches the plan you just described
  • What you do with a hint, meaning whether the correction gets absorbed or the first approach gets defended
  • Whether you can debug your own wrong output by reading the result set and naming which part of the query produced the anomaly

How to prepare

  • Solve three problems while screen-sharing into a recording, then watch it back and mark every stretch longer than thirty seconds where you said nothing
  • Practise compressing the plan into one sentence before typing, then check afterwards whether the finished query actually matched it
  • Time yourself on statistics questions that carry a business reading, such as what a confidence interval does and does not claim, rather than re-reading notes without a clock

Behavioral Interviews

Behavioural answers from data candidates get audited in a way that answers from other roles do not. When you say a model lifted retention, the next question is the denominator, the window, and how you knew the lift was not seasonal. So attach the measurement to each claim while you tell it: what the metric was before, over what period, and against what comparison. Numbers with no baseline read as rounded-up memory, and one unsupported figure tends to make the rest of the story sound rehearsed.

What to demonstrate

  • Whether each impact number arrives with a baseline, a window and a comparison, or as a bare percentage
  • Whether you can name the method that attributed the effect to your work (an experiment, a staged rollout, a seasonal control) or concede the link was correlational
  • Whether the magnitudes stay internally consistent when the interviewer multiplies them against the scale you described earlier

How to prepare

  • For each story, write the impact line as metric, value before, value after, window, and how attribution was established. Any line missing two of those five is a follow-up you will answer badly.
  • Re-derive one headline number from the source table rather than the deck that reported it. Resume numbers drift upward across retellings.
  • Decide in advance which figures you cannot share, and prepare the ratio or relative change you can give instead, so a confidentiality limit does not read as evasion.

Experience Discussion

An extra round usually exists because something is still open after the standard loop: a skill the earlier interviews did not sample, a level decision, or two interviewers who disagreed. It is rarely a rerun of what you already did well. Ask the recruiter who you are meeting, what function they sit in, and how long the session runs. That is an ordinary scheduling question, and the answer changes what you should prepare. What separates a strong candidate here is treating the round as a fresh evaluation with its own bar, rather than assuming earlier performance carries you through or sinks you.

What to demonstrate

  • Whether you can answer well on ground the earlier rounds did not cover, without leaning on what you already said to someone else
  • Consistency of the facts in your stories: the same sample size, timeframe, team size and scope of your own role as in earlier conversations
  • How you handle an unfamiliar format live, including whether you ask what kind of answer is wanted before producing one

How to prepare

  • Ask the recruiter for the interviewer's function, the length, and whether to expect a coding surface, a discussion, or a presentation. Preparing for a 30 minute conversation with a partner team is not the same work as preparing for a 60 minute technical block.
  • Write out what each earlier round actually covered, then list the two or three areas nobody probed. That gap is the most likely subject of the extra round.
  • Re-read the numbers in the project stories you have already told, so a second telling does not quietly contradict the first.

Where candidates lose points

Counting plays without a qualification threshold, or changing the threshold without restating history.

Playback arrives as heartbeats, so a play only exists once you decide what counts, and the common 30-second convention is not a neutral analytics choice: in music it is also the boundary at which a play becomes payable, which makes the warehouse definition a payout definition. The threshold interacts violently with content length, so a catalogue of three-minute tracks and one of forty-minute episodes move in opposite directions when you change it, and a skip-heavy surface can add plays while adding no hours. Any metric mixing pre-threshold and post-threshold counts, or pooling short-form and long-form on a per-stream basis, moves by double digits for reasons that have nothing to do with the product.

Treating the account as the person, or the profile as the person.

A household account carries several people, profiles frequently are not switched, and shared-screen, car and speaker playback often lands on a default profile with no user behind it. Personalisation trained on a profile therefore learns a mixture, retention regressions attribute one member's behaviour to another, and a per-account taste statistic describes a household composite. The practical consequence is that apparent personalisation wins can be device or context effects, so any identity-level claim needs a stated unit and an acknowledgement of what that unit actually aggregates.

Reporting a mean for a heavy-tailed metric without saying what it hides

For spend, session length or items per order, a small fraction of units carries most of the total, so the mean has a wide standard error and one account can move it. Fix the handling before you see the result: cap or winsorise at a pre-declared percentile, and report the median or the share above a threshold next to the mean. Capping changes the estimand, so say which question the capped number answers, and check how much of any difference comes from the top 0.1 percent of units.

Analysing at a different unit than the one randomised

Say out loud what was randomised (user, device, account, cluster) and make the analysis unit match, or account for the clustering with cluster-robust standard errors, the delta method, or aggregation up to the randomised unit. Randomising users and then running a test over sessions understates variance and inflates the false-positive rate.

Questions & practice

Provide a solution for a common algorithmic problem like finding duplicates in an array.

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

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

What are embeddings, and how are they used in machine learning?

Medium · machine learning and modelling

Approach

  1. Check what information would not exist at prediction time, and exclude it.
  2. Frame the prediction: the label, the moment of prediction, and the action it triggers.
  3. Set a baseline first, so any model has something honest to beat.

Follow-up

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

Given a dataset, how would you approach building a predictive model?

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. Check what information would not exist at prediction time, and exclude it.

Follow-up

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

Attribute streams to prior impressions without merge_asof

Medium · pandas · joins · attribution

You have impressions (impression_id, profile_id, content_version_id, rendered_at, surface, slate_position, viewport_visible_ms) and streams (stream_id, profile_id, content_version_id, started_at). Attribute each stream to the most recent impression of the same profile and content version with rendered_at at or before started_at, a gap of at most 30 minutes, and viewport_visible_ms above zero; break ties on rendered_at by the smaller slate_position. pd.merge_asof and any groupby-apply over individual rows are off limits. Return streams with impression_id added, plus the attributed share by surface.

Approach

  1. Filter impressions to viewport_visible_ms above zero first. A row that never scrolled into view is not an exposure, and leaving it in lets an unseen impression win the as-of match over a seen one.
  2. Stack both frames into one long frame with a ts column and an is_stream flag, then sort by (profile_id, content_version_id, ts, is_stream, negative slate_position) so that at an identical timestamp impressions sort before the stream, and among tied impressions the smallest slate_position sorts last.
  3. Forward-fill the candidate impression_id and rendered_at with groupby(['profile_id','content_version_id']).ffill(). The grouping is what stops the fill leaking across profiles; a global ffill on a sorted frame is the usual wrong answer.
  4. Apply the 30-minute window as a post-filter on the filled candidate, and let a stream that fails it go unattributed rather than falling back to an older row. Every candidate precedes the stream, so the gap grows monotonically as you walk backwards: if the nearest in-view impression is more than 1800 seconds old, no earlier one is inside the window either, and null is the correct answer. The window cannot be pushed onto the impressions frame beforehand in any case, because "within 30 minutes" is defined against a particular stream's started_at and one impression is a candidate for many streams. Dropping impressions rendered more than 30 minutes before the earliest stream is a safe prune, but it is an optimisation, not the rule.
  5. Slice the stream rows back out and compute the attributed share by surface, stating that unattributed streams (resume, direct link, radio seed) are not a surface and belong in their own bucket.

Worked solution (30 min)

  1. imp = impressions[impressions.viewport_visible_ms > 0]; build an impression side with ts = rendered_at, is_stream = 0 and a sort helper neg_pos = -slate_position, and a stream side with ts = started_at, is_stream = 1, neg_pos = 0.
  2. Concatenate both, then sort_values(['profile_id','content_version_id','ts','is_stream','neg_pos']).
  3. Add cand_id and cand_ts columns carrying impression_id and rendered_at on impression rows and NaN on stream rows, then groupby(['profile_id','content_version_id'])[['cand_id','cand_ts']].ffill().
  4. Slice to is_stream == 1, compute gap = (ts - cand_ts) in seconds, and set cand_id to NA where the gap is null or above 1800.
  5. Merge cand_id back onto streams by stream_id, then group the attributed rows by the winning impression's surface for the share.

Expected result: streams with an impression_id column, row count unchanged, non-null only where an in-view impression of the same profile and content version exists within 1800 seconds before started_at, plus a per-surface share over a stated denominator of attributed streams rather than all streams.

Verify it yourself

  • Every attributed pair satisfies profile_id equality, content_version_id equality, and a gap between 0 and 1800 seconds. Assert all three rather than eyeballing the head.
  • On a 10,000-row sample the result matches pd.merge_asof(by=['profile_id','content_version_id'], tolerance=30 minutes, direction='backward', allow_exact_matches=True) once slate_position ties are made unique.
  • The output row count equals len(streams) exactly; a frame that grew matched several impressions to one stream.
  • Setting viewport_visible_ms to 0 on the winning impression of a known stream moves that stream to an earlier impression or to null, never leaves it unchanged.

Follow-up

  • The same item was rendered in two slates inside the window. Your tie-break picks one. What does that do to per-surface credit, and what would a fractional rule change?
  • How would you validate this against the impression_id already carried on fct_stream, and what would a systematic disagreement tell you?
  • At 2 million impressions and 500 thousand streams this fits in memory. What changes at 200 million?

Write a function to drop a column from a Pandas DataFrame.

Medium · sql and data manipulation

Approach

  1. Check whether any join is one-to-many before aggregating, or the sums inflate.
  2. Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
  3. Compute rates by summing numerator and denominator separately, never by averaging rates.

Follow-up

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

Home-row impressions that never converted, and an empty result

Medium · anti-join · null semantics · attribution

fct_impression has impression_id, profile_id, content_version_id, surface, slate_position, rendered_at and viewport_visible_ms. fct_stream has stream_id, profile_id, content_version_id, started_at, is_qualified and a nullable impression_id, populated only when the play started from a rendered slate. For surface = 'home_row' impressions rendered yesterday with viewport_visible_ms > 0, return content_version_id and the number of impressions that produced no qualified stream within 30 minutes. A colleague's draft filters WHERE impression_id NOT IN (SELECT impression_id FROM fct_stream) and returns zero rows on data that plainly contains misses. Explain why, then write the correct query.

Approach

  1. Name the mechanism rather than the symptom. fct_stream.impression_id is nullable, so the subquery returns a set containing NULL. In SQL's three-valued logic x NOT IN (..., NULL) evaluates to UNKNOWN whenever x matches nothing else, never to TRUE, so the WHERE clause admits no rows. This is correct engine behaviour on correct data.
  2. Fix with NOT EXISTS, which is a correlated existence test and is NULL-safe by construction, or with LEFT JOIN ... WHERE s.impression_id IS NULL. Adding IS NOT NULL to the subquery also works but leaves the same landmine armed for whoever edits the query next.
  3. Put is_qualified = true and the 30-minute window inside the join or the correlated predicate, never in an outer WHERE over a LEFT JOIN. An outer filter on a right-side column turns a LEFT JOIN back into an inner join and returns the exact complement of the set you were asked for.
  4. Match on impression_id, not on (profile_id, content_version_id). The second is a many-to-many between two event tables and will count one stream against every impression of that title for that profile.
  5. Keep viewport_visible_ms > 0. A row that was never scrolled into view is not a recommendation that failed; it is a recommendation that was never made, and including it depresses every title's conversion by whatever share of the row went unseen.

Worked solution (20 min)

  1. Prove the mechanism in one line: SELECT COUNT(*) FROM fct_stream WHERE impression_id IS NULL. A non-zero count is the entire explanation and should be shown before the rewrite.
  2. Rewrite as NOT EXISTS, with is_qualified and started_at BETWEEN rendered_at AND rendered_at + INTERVAL '30 minutes' inside the correlated predicate.
  3. Compute eligible and converted impression counts per content_version_id separately; the requested answer must be their difference.
  4. Spot-check one high-count content_version_id by listing its impressions alongside any streams sharing those impression_ids.

Expected result: Rows of (content_version_id, unconverted_impressions) where, for every title, unconverted + converted equals the count of eligible home_row impressions with viewport_visible_ms > 0 rendered yesterday.

Verify it yourself

  • The NOT IN version returns zero rows while NOT EXISTS returns a non-empty result on the identical filtered data.
  • converted + unconverted reconciles to the eligible impression count for every content_version_id, with no title missing from either side.
  • Every impression counted as converted has a matching fct_stream row with is_qualified = true and started_at inside the 30-minute window.

Follow-up

  • Two home rows on the same screen render the same title, and the stream carries one impression_id. Should the other impression count as a miss? Defend whichever rule you pick.
  • Would NOT EXISTS and LEFT JOIN ... IS NULL produce the same plan here, and is there a case where you would prefer one over the other?
  • What attribution window between rendered_at and started_at do you allow, and why does an unbounded window overstate conversion rather than merely add noise?

How do you prioritize tasks when you have multiple deadlines?

Medium · product sense and metrics

Approach

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

Follow-up

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

Can you walk us through your process for feature selection?

Medium · product sense and metrics

Approach

  1. Restate the decision this analysis has to support, and who acts on the answer.
  2. State what result would change your recommendation, so the answer is falsifiable.
  3. Fix the population and the time window before naming any metric.

Follow-up

  • Which segment would you cut first, and what would that rule out?
  • What would you do if the primary metric and the guardrail moved in opposite directions?

Explain the difference between supervised and unsupervised learning.

Medium

Approach

  1. State your assumptions explicitly before working the problem.
  2. Work from the decision backwards to the evidence you would need.
  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?

Raise the qualification threshold when it is also a payout rule

Hard · metric design · definition change · payouts · history restatement

A proposal raises the qualification threshold behind fct_stream.is_qualified from 30 to 60 played_seconds for content_type = 'music_track'. The same flag feeds the engagement metrics and the pro-rata payout pool split across rights_holder_id. Using fct_stream (content_version_id, played_seconds, is_qualified, started_at) and dim_content_version (content_version_id, content_id, content_type, duration_seconds, rights_holder_id), design the decision. Give the primary metric, guardrails, what you would compute before anyone votes, and how published history is handled. Name who is made worse off.

Approach

  1. Open by separating the two jobs the flag is doing: an engagement definition is a measurement choice that can be changed as long as history is restated, while a payout definition is a contractual rule whose change is a transfer of money between counterparties. Proposing one change to a shared flag is proposing both, and the first design decision is whether to split the flag into two named definitions.
  2. Compute the redistribution before the argument starts: on one closed month, recompute every rights_holder_id's pro-rata share of the pool under both thresholds and report the distribution of the change. The loss concentrates on short-duration catalogue and on skip-heavy start sources, so the transfer is systematic by counterparty rather than noise.
  3. Choose a primary metric that the threshold does not control: qualified hours per active account-week sums played_seconds, so a threshold move changes only which rows enter the sum and not the unit of the answer. Publish alongside it the share of total played_seconds that the threshold excludes, which is the single number that says how consequential the choice is.
  4. Set the guardrail on the distortion the threshold creates: median and tenth-percentile duration_seconds of qualified music_track streams, and the count of distinct content_id receiving any qualified stream. A 60-second gate demotes genuinely short forms as well as engineered ones, and catalogue breadth is where that shows first.
  5. Fix the history rule before any number is published: recompute at least thirteen months under the new definition and publish only the restated series, because a series that changes definition mid-line will be read as a product event by everyone who was not in this meeting, and the next quarter's review will attribute it to whatever shipped that week.
  6. State the trade-off and the loser by name: the higher gate reduces the payout incentive for engineered short items, and it also cuts short tracks, interludes and kids content whose real completion is under 60 seconds. The per-content_type threshold is the alternative; its cost is that per-stream comparisons across content types stop being meaningful, which the metric tree must then forbid.

Worked solution (40 min)

  1. Recompute is_qualified at both thresholds over one closed month and report two totals: qualified stream count and the share of total played_seconds that each threshold excludes.
  2. Aggregate qualified streams to rights_holder_id under both thresholds, convert each to a pro-rata share of a fixed pool, and report the distribution of share change, calling out the largest losers and their median duration_seconds.
  3. Show the mechanism numerically: for a catalogue of 180-second tracks a 60-second gate requires completion_ratio above 0.33 rather than 0.167, so the exclusion rate is a direct function of the duration distribution and is not comparable across content types.
  4. Write the primary metric and the excluded-seconds share, then the breadth guardrail as distinct content_id with any qualified stream, before and after.
  5. Write the history rule, the restatement horizon and the publication ban on mixed-definition series, and the one-paragraph statement of who loses and by roughly how much.

Expected result: The answer separates the engagement definition from the payout rule and says a shared flag conflates them. Primary: qualified hours per active account-week, threshold-invariant in unit, published with the share of played_seconds excluded. Guardrails: duration distribution of qualified music_track streams and count of distinct content_id with any qualified stream. History restated at least thirteen months with no mixed-definition series published. The losers are short-form catalogue owners, named before the vote with the redistribution quantified.

Verify it yourself

  • The redistribution figure exists before any recommendation is made, so the decision is priced rather than argued.
  • The primary metric's unit does not change when the threshold changes, which is what makes it publishable across the transition.
  • The exclusion share is reported per content_type, since a single pooled exclusion rate hides that the gate bites unevenly by duration.
  • The restated history is the only series published, and the restatement horizon is stated as a number of months rather than left to judgement.

Follow-up

  • Would a per-account allocation of the pool rather than pro-rata change your recommendation, and which rights holders swap places between the two schemes?
  • Who has to be told before this ships, and what is the smallest honest description of the change you would give them?
  • A stakeholder proposes running the two thresholds in parallel for a quarter. What does that solve and what does it not?

Involuntary churn share jumped after a dashboard rebuild

Medium · definition-change · churn · late-resolving-data

Involuntary share of gross churn — fct_subscription_period rows with renewal_outcome = 'failed_involuntary' over rows with renewal_outcome in ('cancelled_voluntary','failed_involuntary'), keyed on period_end_ts month — reads 31% for last month against 19% for the three months before. Billing reports no change in decline rates and dunning_attempts per failed period is unchanged. Using fct_subscription_period (period_end_ts, renewal_outcome, payment_status, dunning_attempts, cancel_requested_ts, billing_provider), determine whether involuntary churn actually rose. Deliverable: the corrected month-on-month series and the specific change that produced the step.

Approach

  1. Check the month's maturity before anything else. Periods ending recently still have retries in flight, and a row reading 'failed_involuntary' today becomes payment_status = 'retried_paid' next week. Recompute every one of the last twelve months as of a fixed age — period_end_ts plus the maximum retry window — instead of as of today.
  2. If the step survives age alignment, hunt for a definition change: diff the model or query against the version that produced 19%, and check specifically whether the month is now keyed on cancel_requested_ts rather than period_end_ts, and whether 'refunded' or 'chargeback' rows have entered one side of the ratio.
  3. Settle it with one query rather than a narrative. Run the old definition and the new definition on the same month. Two numbers from one month under two definitions is decisive; two numbers from two months under one definition is not.
  4. If a definition did change, restate history rather than leaving a step in the chart: run the new definition back across all twelve months and publish both series with the cutover marked, so nobody re-discovers this in a quarter.
  5. Cut the horizon-closed series by billing_provider before concluding nothing moved. Card-direct, app-store and carrier billing have different retry mechanics and different success rates, so a mix shift between them moves the ratio with every provider's own rate flat.

Follow-up

  • How would you set the dunning horizon from the data instead of taking billing's stated maximum?
  • Under cancel-at-period-end, the cancellation request and the entitlement end fall in different months. Which timestamp keys this metric, and what does the other choice do to the curve?

Suggested practice checklist

Instead of guessing where the week should go, day one measures it under a fixed rubric and allocates the remaining hours in proportion to the gaps. The method is deliberately rigid: the allocation is written down before any studying starts and is not renegotiated when a topic turns out to be unpleasant.

Day 1 — Diagnostic, scored before you study anything

  • Sit a 100-minute timed diagnostic in four blocks: 30 minutes of SQL across three prompts, 25 minutes of short-answer statistics, 25 minutes on one modelling or case prompt, and 20 minutes delivering one behavioural story aloud.
  • Score each block from 0 to 3 on a fixed rubric where 3 is correct and fluent, 2 is correct but slow or prompted, 1 is partially correct, and 0 is stuck, grading the output rather than how the attempt felt.
  • Allocate the hours for days two to five roughly in proportion to 3 minus the score in each block, write the allocation down, and commit to not revising it midweek.

Deliverable: A scored rubric and a fixed hour allocation for the rest of the week.

Day 2 — Largest gap: find the boundary rather than the subject

  • Break the weakest area into five named sub-skills (for query work: grain control, window frames, date arithmetic, set logic with NULLs, and reading a query plan) and rate each one, so the rest of the week targets a sub-skill instead of a subject.
  • Solve three problems chosen to sit just above where the rating drops off, and for each write the first move you failed to make.
  • Re-solve one of them from memory four hours later, on paper, with nothing open.

Deliverable: A five-item sub-skill map with the two blocking sub-skills circled.

Day 3 — Largest gap: drill the blocking sub-skill

  • Do eight short repetitions of the same shape rather than eight different problems, so what you practise is the pattern and not the puzzle.
  • Write the rule you now hold in one sentence, then test it against a case built to break it: a ranking function over a column with ties, or a two-sample test on observations that are obviously dependent.
  • Have someone else read your one-sentence rule and find the precondition you left out.

Deliverable: One rule statement with its preconditions attached and one counterexample that would have caught the incomplete version.

Day 4 — Second gap, plus maintenance on your strongest area

  • Run the same sub-skill map and boundary protocol on the second-largest gap, compressed into half the day.
  • Spend 25 timed minutes on your strongest area to stop it decaying, choosing the hardest problem you can still finish rather than an easy warm-up.
  • Compare how the two areas fail: whether you lose time on recall, on setup, or on arithmetic, because the fix differs for each.

Deliverable: A second sub-skill map plus a one-line diagnosis of how each area fails you.

Day 5 — The gap that is not a skill

  • Record yourself answering one technical and one behavioural prompt, then count two things in the playback: how many seconds before your first clarifying question, and how many sentences you started without knowing where they ended.
  • Rewrite your three most-used stock phrases into shorter versions, and practise saying "I do not know, here is how I would find out" without softening it into a guess.
  • Deliver one answer again with a hard 90-second limit to force structure before detail.

Deliverable: Two recordings with a counted improvement in time-to-first-question.

Day 6 — Retest under day-one conditions

  • Sit the same 100-minute diagnostic structure with new prompts of comparable difficulty and score it on the identical rubric.
  • Compare block by block, and for any block that did not move, change the method rather than adding hours: a block stuck at 1 usually means the practice was too varied, not too short.
  • Write which single block you would still lose the offer on.

Deliverable: A second scored rubric placed next to the first, with one named remaining risk.

Day 7 — Full loop under interview conditions

  • Run a 60-minute mock covering the two blocks that moved least, with an interviewer instructed to interrupt and change direction.
  • Write your recovery script for the moment you go blank: restate the question, state your assumption, name the first thing you would check.
  • Reduce the week to the rule statements you wrote, each with its preconditions attached, then say every one of them out loud without reading it and cut any you cannot state in a single sentence, since a rule you have to reconstruct mid-answer will not survive being interrupted.

Deliverable: A one-page card holding the recovery script and only the rules you could state from memory.

Where your data came from

Data people depend on systems owned by other teams, and much of the job is negotiating for instrumentation, access, or a fix to a broken pipeline. Prepare an example of getting something changed upstream that you did not control. Describe what you asked for, what you traded, and how you worked while you waited.

How do you handle missing data in a dataset?

Medium · behavioural and stakeholder questions

Approach

  1. Name the disagreement or constraint, and how you resolved it with evidence.
  2. State the situation in two sentences and spend the rest on your reasoning.
  3. Pick a story where you drove the decision, not one where you observed it.

Follow-up

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

What motivates you to work in the field of data science?

Medium · behavioural and stakeholder questions

Approach

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

Follow-up

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

Disagree with a product manager about a completion metric

Medium · disagreement · metric design · influence

A product manager proposes making duration-normalised completion rate the team's primary metric for the quarter, and wants a plain global unweighted version "so it's simple enough to put on a wall." You expect the unweighted version to move several points on catalogue mix alone, with no change in how satisfying anything was. You have one meeting, they own the roadmap, and you will work with them for years. Deliverable: the argument, the evidence you bring, and the fallback you accept if they still want the simple version.

Approach

  1. The probe is whether you can lose the decision without damaging either the metric or the relationship. Bring the failure already reproduced: recompute the unweighted global rate over the last two quarters and point at the weeks it moved several points where the only input that changed was which content people played.
  2. Decompose the variance instead of objecting. Attribute the movement in the unweighted rate to between-cell mix — (content_type, duration decile) — versus within-cell movement. A decomposition is an argument; an assertion that mix matters is a preference.
  3. Concede the real cost honestly: the mix-weighted version is harder to explain and harder to recompute. Bring the mitigation rather than dismissing the objection — weights fixed from a stated reference month, published once, so anyone can reproduce the number without redoing the weighting.
  4. Offer the fallback deliberately and in advance: ship both, with the unweighted rate labelled a diagnostic and the weighted one as the decision metric, and pre-agree the one observation that would mean the simple version has misled the team.
  5. Leave the decision with them, with the consequence written down before it happens. That is what makes a later correction a shared prediction coming true rather than a retrospective argument about who was right.

Follow-up

  • They ship the simple version and it moves four points during a heavy release week. How do you raise it without saying you told them so?
  • Is there a case where the unweighted rate is genuinely the right primary metric?
  • Suppose you are wrong and the mix effect turns out to be small. What would you change about how you argued this?