Interview guides · Johns Hopkins University
Johns Hopkins University Data Scientist Interview Questions & Guide 2026
Data scientist interview prep for Johns Hopkins University: original practice on experiment design, SQL and metric reasoning, with worked solutions.
The 60-second brief
A Data Scientist at Johns Hopkins University occupies a unique and highly impactful position at the intersection of advanced computation, statistical modeling, and world-class scientific research. Unlike traditional corporate roles focused purely on commercial optimization, data scientists here drive discoveries that directly affect global health, education, and public policy. Whether embedded within the Bloomberg School of Public Health, the School of Medicine, or dedicated research centers, you will work on complex, real-world datasets to solve some of the humanity's most pressing challenges.
Seniority shifts the scope more than the words in the title do. Earlier-career loops mostly check that you execute a well-posed analysis correctly; senior loops check that you can decide which question is worth answering and defend what you chose not to do.
Focus areas
- Model seat activation as the renewal leading indicator
- Reconstruct active time from raw heartbeat streams
- Cluster experiments at class or school level
Overview
The impact of this role is felt across diverse research initiatives, clinical trials, and epidemiological studies. You will collaborate closely with world-renowned faculty, principal investigators, and clinical staff to design study methodologies, analyze massive genomic or observational datasets, and build predictive models. Your work does not just live in a codebase; it directly informs peer-reviewed literature, grant proposals, and clinical decision-making systems that shape healthcare delivery worldwide.
To succeed in this environment, you must possess a rare blend of rigorous statistical expertise, software engineering discipline, and a deep curiosity for scientific domain areas. The role is intellectually demanding, requiring you to navigate highly complex, unstructured data environments while translating sophisticated mathematical concepts into actionable insights for non-technical stakeholders. It is a career path that offers immense intellectual freedom and the opportunity to make a tangible, positive contribution to global society.
The Johns Hopkins University interview process
Johns Hopkins University candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
Screening Call
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 Panel Interview
A day of back-to-back interviews samples your floor, not your ceiling. Four hours in, the habits that carry a good answer are the first to go: restating the question before solving it, asking what the data would have to look like, checking a number before quoting it. What the day decides is whether the tired version of you is still someone to leave alone with an ambiguous problem. The round that sinks a candidate is usually not the hardest one. It is the one immediately after the round that went badly.
What to demonstrate
- Whether the late rounds get the same clarifying questions as the first one, or whether you start answering immediately to save effort
- Whether a weak answer stays in the room it happened in, instead of following you into the next conversation as apology or distraction
- Whether the quality of your questions holds up, since fatigue removes curiosity about the problem before it removes knowledge of the method
How to prepare
- Rehearse the length, not just the content: book four mock interviews of different types in one afternoon with short gaps, because the one you need to observe is the fourth
- Put the two or three questions you ask at the start of any problem on a card in front of you, so that under fatigue it is a habit you run rather than a decision you make
- Decide in advance what the gap between rooms is for: water, one line of notes on anything you promised to follow up, and an explicit close on the round that just ended so it does not travel
- Prepare a different closing question for each interviewer, so the end of a long day does not produce the same one four times
On-Site Interview
Where a loop ends with a senior leader, that conversation is rarely another skills test. The technical signal already exists by then, so the questions tend to open up: what you would look at first, where a metric you have heard about could mislead, what you would push back on. The decision being made is scope, which in practice means level and how much you would be trusted to own unsupervised. Treating it as a formality is the usual mistake. An open question late in the day is still being scored, and a vague answer reads as someone who has not run anything themselves.
What to demonstrate
- Whether your view of the business has anything specific behind it, given that you are working only from what is public and are expected to say so
- Whether the scope of work you describe owning matches the scope of the role, instead of sitting a level below it
- Whether you can disagree with something concrete and stay useful about it, rather than agreeing with everything said in the room
- Whether your questions are ones only this person could answer, as opposed to ones the recruiter already covered
How to prepare
- Build one view you could defend for two minutes using only public information: what the funnel probably looks like, which metric likely drives decisions, and where that metric could mislead. Being wrong for a stated reason survives this round; having no view does not
- Write down the largest piece of work you have owned from question to decision, who else touched it, and what you decided alone, then check that it reads at the level you are interviewing for
- Prepare one thing you would want changed if you joined and phrase it as a question rather than a verdict, so it opens a conversation instead of closing one
Where candidates lose points
Pre/post gain studies that select on low pretest scores manufacture improvement.
Any measure with reliability below 1 produces regression to the mean, so a group chosen for scoring in the bottom quartile will score higher on retest with no intervention at all. The apparent gain scales with measurement error, which for a short quiz is large. The fix is a control group selected by the identical rule, or a design that models the pretest as a covariate rather than as a selection filter.
Learner-level standard errors on class-level interventions.
Anything an instructor controls, and anything deployed by school, is assigned at the section or school level, and outcomes within a section are correlated through the shared instructor, schedule, and device fleet. Treating the learner as the unit of independence understates variance by the design effect 1 + (m - 1) * rho for roughly equal cluster sizes. At a typical section size of 25 and rho of 0.15 that is a factor near 4.6 on variance, which routinely converts a null into a 'significant' result.
Naming a model class before naming the deployment constraints
Set out the latency budget, the label delay, the retraining cadence, the interpretability requirement and the number of labelled examples, then pick the model that fits them. A boosted-tree answer to a problem where each decision must be explained to the affected user is a well-executed answer to the wrong question.
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.
Questions & practice
What is the difference between association and causation, and how do you address this when analyzing observational data?
Medium · statistics and probability
Approach
- Quantify uncertainty explicitly rather than reporting a point estimate alone.
- Sanity-check the answer against a simple bound or a simulated case.
- Translate the result into the decision it informs, in one plain sentence.
Follow-up
- How would you explain this result to someone who does not know statistics?
- Which assumption here is most likely to be violated in practice?
How do you validate your predictive models to ensure they generalize well to external patient populations?
Medium · statistics and probability
Approach
- Say what the estimate is of, and over what population it generalises.
- Translate the result into the decision it informs, in one plain sentence.
- Sanity-check the answer against a simple bound or a simulated case.
Follow-up
- Which assumption here is most likely to be violated in practice?
- What sample size would you need to detect an effect half this size?
Explain how you would perform power analysis and sample size calculations for a proposed research study.
Medium · statistics and probability
Approach
- Say what the estimate is of, and over what population it generalises.
- Sanity-check the answer against a simple bound or a simulated case.
- Quantify uncertainty explicitly rather than reporting a point estimate alone.
Follow-up
- Which assumption here is most likely to be violated in practice?
- What sample size would you need to detect an effect half this size?
How do you determine the appropriate statistical model (e.g., survival analysis, logistic regression, mixed-effects models) for a given clinical dataset?
Medium · machine learning and modelling
Approach
- Check what information would not exist at prediction time, and exclude it.
- Set a baseline first, so any model has something honest to beat.
- Say how the offline result would be validated online before it is trusted.
Follow-up
- How would you choose the decision threshold, and who owns that choice?
- Where could label leakage enter this setup?
How do you explain highly technical machine learning or statistical concepts to a clinical researcher or principal investigator who has no quantitative background?
Medium · machine learning and modelling
Approach
- Pick an evaluation metric that matches the cost of each error type, not a default.
- Say how the offline result would be validated online before it is trusted.
- Set a baseline first, so any model has something honest to beat.
Follow-up
- What would you monitor after launch to know the model is still valid?
- Where could label leakage enter this setup?
Attach the calibration in force without merge_asof
Medium · as-of join · searchsorted · vectorisation · slowly changing dimensions
You have responses (response_id, content_item_id, submitted_at, is_correct) and calibration_history (content_item_id, valid_from, valid_to, irt_a, irt_b), where intervals within an item are non-overlapping and the open interval carries valid_to as NaT. Attach to each response the irt_a and irt_b in force at submitted_at. You may not use pandas.merge_asof and you may not apply row-wise. responses has about 5 million rows, calibration_history about 40 thousand. Return the input frame plus two columns, NaN where no interval covers the timestamp.
Approach
- Sort calibration_history by (content_item_id, valid_from) and factorise content_item_id across both frames into a shared integer code, so an unknown item on the response side is detectable immediately rather than joining to nothing.
- Convert both timestamps to int64 seconds and build one composite key per side, code * 2**32 + seconds. With codes well under two billion and seconds near 1.8e9, that stays inside int64 and makes a single global search possible. State the second-level resolution assumption; interval boundaries are set at second granularity, so nothing is lost.
- Call np.searchsorted(calibration_keys, response_keys, side='right') - 1 once over the whole array. That gives, for each response, the position of the latest interval starting at or before it within the same item, because the composite key orders by item first.
- Invalidate bad candidates in two places: index -1, and a candidate whose code does not match the response's code, which happens when a response precedes its item's first interval and lands on the previous item's last row.
- Invalidate a third case that is easy to miss: the candidate has a non-null valid_to and submitted_at is at or after it, meaning the response falls in a coverage gap. Without this the previous interval's parameters leak onto uncovered responses.
- Take the parameters positionally with np.take and write NaN where any invalidation fired.
Worked solution (30 min)
- codes = pd.factorize on the concatenated item ids, applied to both frames so the mapping is shared.
- cal_key = cal_code.astype('int64') * (1 << 32) + valid_from_seconds; resp_key built the same way; sort cal by cal_key.
- pos = np.searchsorted(cal_key, resp_key, side='right') - 1.
- valid = (pos >= 0) & (cal_code[pos] == resp_code) & (cal_valid_to_seconds[pos].isna() | (resp_seconds < cal_valid_to_seconds[pos])).
- Assign irt_a and irt_b via np.take(pos) where valid, NaN elsewhere, and report the NaN count by reason.
Expected result: Every response inside a covered interval carries that interval's irt_a and irt_b. Responses before the item's first valid_from, inside a coverage gap, after a closed final valid_to, or on an item absent from calibration_history carry NaN. The whole join runs in seconds on 5 million rows with no Python-level row loop.
Verify it yourself
- Brute-force 20 randomly chosen responses with a boolean mask over calibration_history and confirm an exact match.
- The NaN count equals the count produced by an independent naive filter for uncovered timestamps.
- Shifting every submitted_at forward by one second reassigns exactly the responses sitting on an interval boundary and nothing else.
Follow-up
- A backfill wrote overlapping intervals for 200 items. How do you detect that before the join rather than after, and what do you do with those responses?
- The join is correct but peak memory is unacceptable. What changes, and which part of the approach survives?
- How would you test this without a golden output to compare against?
Rebuild sessions and active seconds from heartbeat events
Medium · window functions · gaps and islands · sessionisation
Given raw_heartbeat(learner_id, content_item_id, content_version_no, heartbeat_at TIMESTAMP UTC, session_hint VARCHAR NULL), reconstruct the grain of fct_lesson_activity: one row per learner per content item per session. Close a session after 30 minutes with no heartbeat. Compute active_seconds as the sum of inter-heartbeat gaps inside a session with each gap capped at 120 seconds, and wall_seconds as last minus first heartbeat. Ignore session_hint, which the client sets unreliably. Then reconcile your active_seconds against the stored column in fct_lesson_activity and explain any systematic difference.
Approach
- Compute prev_at = LAG(heartbeat_at) OVER (PARTITION BY learner_id, content_item_id, content_version_no ORDER BY heartbeat_at) and gap_seconds from the difference. Partition on the version too, since the same item at a new version is different content.
- Flag is_new_session = (prev_at IS NULL OR gap_seconds > 1800), then session_seq = SUM(is_new_session::int) OVER (same partition ORDER BY heartbeat_at ROWS UNBOUNDED PRECEDING). The running sum over an ordered flag is the island assignment and it stays correct at any data volume, unlike a self-join on nearby timestamps.
- Aggregate per (partition, session_seq): active_seconds = SUM(CASE WHEN is_new_session THEN 0 ELSE LEAST(gap_seconds, 120) END), wall_seconds = MAX(heartbeat_at) - MIN(heartbeat_at), plus heartbeat_count.
- State the two consequences of the definition before anyone asks: a single-heartbeat session has active_seconds = 0 and wall_seconds = 0, and the per-gap cap means active_seconds can never exceed 120 * (heartbeat_count - 1), so a client with a slow heartbeat interval understates real time.
- Reconcile by joining on (learner_id, content_item_id, started_at) and examining the distribution of the difference, not a row count. A uniform shift points at the cap or the close rule; a heavy one-sided tail points at heartbeats the stored column excludes.
Worked solution (30 min)
- Write the LAG and gap CTE and eyeball ten rows for one busy learner before adding anything else.
- Add the is_new_session flag and the running-sum session_seq, then verify session_seq increments exactly where the gap exceeds 1800 seconds.
- Aggregate to the session grain with the CASE-guarded capped sum.
- Join to fct_lesson_activity on learner, item and started_at, and plot or bucket the signed difference in active_seconds.
Expected result: One row per (learner_id, content_item_id, content_version_no, session_seq) with started_at, ended_at, wall_seconds, active_seconds and heartbeat_count, where active_seconds <= LEAST(wall_seconds, 120 * (heartbeat_count - 1)) holds on every single row.
Verify it yourself
- active_seconds <= wall_seconds on every row. A violation means the boundary gap leaked into the sum.
- Sessions with heartbeat_count = 1 carry active_seconds = 0 and wall_seconds = 0.
- Your rebuilt active_seconds is at or above the stored column, because raw_heartbeat carries no visibility flag while the stored column excludes backgrounded tabs.
- Tightening the close rule from 30 to 20 minutes raises session count and lowers mean wall_seconds while leaving total active_seconds nearly flat, since each new split only removes one already-capped 120-second gap. A large move in the total means the cap is being applied per session instead of per gap.
Follow-up
- The stored column excludes backgrounded tabs but raw_heartbeat has no visibility flag. How would you detect backgrounded stretches from timing alone, and how confident should you be?
- Two heartbeats can share a timestamp. What does that do to LAG, and does it change the answer?
- A learner studies for two hours with a 40-minute dinner break in the middle. Your rule gives two sessions. Which downstream metrics care about that choice and which do not?
Consecutive instructional-week streaks and term-over-term return
Hard · gaps and islands · window functions · retention
Using fct_lesson_activity(learner_id, enrollment_id, activity_date, active_seconds) and a calendar dim_term_week(term_id, term_week_no, week_start_date, week_end_date, is_instructional), where holiday weeks carry is_instructional = FALSE, compute for each learner and term the longest run of consecutive instructional weeks containing at least one activity day. A non-instructional week must not break a run, and activity inside one neither extends nor starts a run. Then compute term-over-term return rate into term T+1 with a floor of five activity days in term T. Return learner_id, term_id, activity_days counted over the whole term including non-instructional weeks, longest_streak_weeks and the return flag.
Approach
- Collapse activity to (learner_id, term_id, term_week_no) with COUNT(DISTINCT activity_date). fct_lesson_activity is one row per item per session, so a learner with twelve rows on one afternoon is one activity day and counting rows inflates everything downstream.
- Re-index before differencing. Apply DENSE_RANK() OVER (PARTITION BY term_id ORDER BY term_week_no) across instructional weeks only, producing a gap-free sequence where a holiday week has simply been removed. Differencing raw term_week_no instead splits a run at every holiday, which is exactly the artefact the task forbids.
- Accept what dropping those weeks costs. Activity in a non-instructional week is removed from the streak calculation entirely, so a learner whose only activity lands in a holiday week has activity_days >= 1 and longest_streak_weeks = 0. That is what a streak over instructional weeks means, but it is why the two columns can disagree and why the checks must expect a zero streak alongside non-zero activity rather than assert a floor of 1.
- Apply the island trick on the re-indexed sequence: instructional_index - ROW_NUMBER() OVER (PARTITION BY learner_id, term_id ORDER BY instructional_index) is constant within a run. Group on that constant, count rows per group, take the max per learner and term.
- Build activity_days from the unfiltered term aggregate, then LEFT JOIN the streak result onto it and COALESCE longest_streak_weeks to 0. An inner join here deletes the holiday-only learners from the output and from the return-rate denominator, which is the same bug as the streak-floor assertion wearing different clothes.
- Set the activity floor on COUNT(DISTINCT activity_date) >= 5 across term T, applied to the return-rate denominator only. The floor exists to remove provisioned-but-unused accounts, so leaking it into the numerator silently redefines the metric as active in both terms. Note that a learner can clear the floor on holiday-week activity alone and enter the denominator with a zero streak.
- Define the return flag as EXISTS any activity row in term T+1 for that learner, pairing terms by an explicit term ordering rather than date arithmetic, since term lengths differ. Learners whose org has no term T+1 loaded must be excluded and counted, not treated as non-returners.
Follow-up
- This streak rewards a learner doing one minute a week over one doing four hours in a single week. Which of those is the product claim, and what would you pair the streak with to catch the difference?
- Roster sync deactivates accounts in bulk at term end. How does that interact with the five-day floor and with the return denominator?
- Show how return rate moves as the floor goes from one day to five, and argue which floor is the honest one to publish.
How do you prioritize your deliverables when supporting multiple research grants or principal investigators simultaneously?
Medium · product sense and metrics
Approach
- Restate the decision this analysis has to support, and who acts on the answer.
- State what result would change your recommendation, so the answer is falsifiable.
- Fix the population and the time window before naming any metric.
Follow-up
- What would you do if the primary metric and the guardrail moved in opposite directions?
- Which segment would you cut first, and what would that rule out?
How have you handled missing data, selection bias, or confounding variables in your past observational studies?
Medium
Approach
- State your assumptions explicitly before working the problem.
- Clarify what is being asked and what a complete answer would contain.
- 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?
Metrics that survive an adaptive item selector
Medium · adaptive selection · irt · measurement design
Content selection targets a 70% success probability per learner. First-attempt accuracy reads 69.8% before a model change and 70.3% after. Using dim_content_item (irt_a, irt_b, calibration_n, version_no, status) and fct_assessment_response (attempt_no, is_correct, objective_id), propose a measurement design that can detect a real learning change under this selector. Specify the fixed-form probe set, its size and calibration requirement, the statistic you report, and say why the adaptive accuracy series carries no information about learning.
Approach
- Show why the series is pinned. Under a 2PL item, P(correct) = 1 / (1 + exp(-a(theta - b))). A selector targeting 0.70 solves for b given theta: a(theta - b) = ln(0.7/0.3) = 0.847, so b = theta - 0.847/a. As theta rises the selector raises b with it and measured accuracy stays at the target.
- Demote accuracy accordingly: it measures how well the selector hit its own target, so keep it as a selector-calibration guardrail and never as an outcome.
- Build a fixed-form probe: 10 to 15 items with frozen (content_item_id, version_no), status='published', calibration_n at least 200, spanning irt_b roughly -1.5 to +1.5, served on a fixed schedule identically to every arm and never chosen adaptively.
- Report a theta estimate or mean probe score. For 2PL, test information is I(theta) = sum of a_i^2 P_i Q_i, so 12 items with a = 1.0 measured near their peak give I = 3 and SE = 1/sqrt(3) = 0.58 logits per learner. Individual estimates are noisy, but a 400-learner group mean has SE near 0.029 logits, which is where the comparison lives.
- Protect the probe: exclude its items from the adaptive pool so practice cannot leak into the measurement, and freeze versions, because editing an item changes the meaning of every historical response to it.
- Add the difficulty audit as a second guardrail: mean irt_b of the items backing mastery decisions, which exposes a selector that simply got easier.
Worked solution (30 min)
- Derive the selector identity b = theta - ln(0.7/0.3)/a = theta - 0.847/a, and evaluate it at a = 1.2, where the served item sits 0.71 logits below the learner's theta.
- Specify the probe: 12 frozen item versions, calibration_n >= 200 each, irt_b spread across -1.5 to +1.5, administered every four weeks to all arms.
- Compute probe precision: I = sum a_i^2 P_i Q_i = 12 x 1.0 x 0.25 = 3, SE = 0.58 logits per learner, SE of a 400-learner mean = 0.58/20 = 0.029 logits.
- Write the reporting rule: probe mean as the outcome, adaptive accuracy and mean irt_b of mastery-backing items as guardrails.
Expected result: A 10 to 15 item fixed-form probe on frozen versions with calibration_n >= 200, excluded from the adaptive pool, reporting a group mean theta or probe score with SE derived from test information; adaptive first-attempt accuracy demoted to a selector-calibration guardrail alongside mean irt_b, with 69.8% versus 70.3% declared uninformative about learning.
Verify it yourself
- b = theta - ln(0.7/0.3)/a = theta - 0.847/a, and at a = 1.2 the offset is 0.71 logits.
- Probe items are frozen at one version_no and removed from adaptive selection.
- Per-learner SE from 12 items at a = 1.0 is about 0.58 logits, and you did not present individual theta as precise.
Follow-up
- How many probe items would you need before reporting an individual learner's theta rather than a group mean?
- What happens to the probe if the selector begins serving items that share a template with probe items?
- The probe costs learner time every cycle. How do you justify that cost to a curriculum owner?
Scored submissions sag in the dashboard's final five days
Easy · reporting-lag · seasonality · right-censoring
A daily chart counts fct_assessment_response rows by DATE(scored_at). The last five days each sit below the one before, and every Saturday and Sunday is roughly half of a weekday. Columns available: response_id, learner_id, submitted_at, scored_at, scoring_mode, max_points, attempt_no. Say whether scored submissions are actually declining, produce the corrected series, and state the reporting lag the correction forces on this dashboard.
Approach
- Rebuild the same count on DATE(submitted_at) over sixty days and overlay the two. scored_at lags submitted_at for rubric-scored work, so a scored_at series is right-censored at the recent end: the last days are unfinished, not falling.
- Quantify the lag rather than asserting it. Compute percentiles of scored_at minus submitted_at split by scoring_mode; auto sits near zero and rubric_human carries the tail that produces the sag.
- Take the rubric_human p95 lag as the close threshold and truncate or grey out the scored_at series at max(date) minus that threshold, so the chart cannot be read as a decline again.
- Handle the weekly pattern separately from the lag: compare each day to the same weekday a week earlier, or report a trailing seven-day sum. In an academic product weekend volume is structurally a fraction of a weekday's and is not signal.
- Restate the dashboard definition with the lag written on it, and name which of the two series answers which question.
Follow-up
- What reporting lag would you publish, and what do you do when a rubric grading backlog makes the lag itself drift?
- Which series belongs on an operational grading-capacity dashboard, and why is the other one the right basis for the learning metric?
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.
Getting an analysis acted on
Work that nobody used is a common and unflattering pattern in data careers, and interviewers probe for it. Have a story about an analysis that changed a decision, and be specific about how you got it in front of the person who could act. Also have one about work that went nowhere, with your reading of why.
Tell me about your experience working with large-scale datasets. What tools did you use to manage computational limitations?
Medium · behavioural and stakeholder questions
Approach
- Quantify the outcome, including what you would not claim credit for.
- Name the disagreement or constraint, and how you resolved it with evidence.
- 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?
- How did you know the outcome was caused by your change?
Tell me about a time you identified an error in a dataset or an analysis mid-project. How did you communicate this to your team?
Medium · behavioural and stakeholder questions
Approach
- State the situation in two sentences and spend the rest on your reasoning.
- Close with what you would do differently, concretely.
- Name the disagreement or constraint, and how you resolved it with evidence.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Defend a null result against an already-announced launch
Medium · null results · experiment design · stakeholder communication
A section-randomised test of a new practice-sequencing feature ran across 62 class sections in 11 orgs. Primary metric: verified mastery events per active learner. With section-clustered standard errors the effect is +1.8% (95% CI -4.1% to +7.9%). Leadership has already told two district administrators that the feature improves outcomes. You have ten minutes in a review. Deliverable: what you say, what you put on one slide, and what you propose next. Probed: whether you can hold a null under commercial pressure without either caving or lecturing the room on statistics.
Approach
- Lead with the decision, not the p-value: state the interval in the unit the audience already uses (mastery events per active learner per week) and say plainly what the study could and could not have detected.
- Separate the interval's half-width from the minimum detectable effect before you put either on a slide. The stated 95% CI of -4.1 to +7.9 has a half-width of 6.0 points, so the clustered standard error is 6.0 / 1.96 = 3.06 points; the MDE at 80% power and two-sided alpha = 0.05 is (1.96 + 0.8416) * 3.06 = about 8.6 points, roughly 1.43 times the half-width. Quote 8.6, and show that it follows from 62 sections at the section size and intraclass correlation you assumed via the design effect 1 + (m - 1) * rho, so that 'no effect found' is visibly separated from 'too small to see'.
- Check the pre-registered guardrails before conceding anything: median active_seconds per mastery event and the calibrated irt_b distribution of the items backing mastery decisions. If both are flat, the null is about learning, not about a broken pipeline.
- Handle the promise made to the two districts as its own item with a named owner, rather than leaving the correction implicit in a statistics discussion.
- Close with the cheapest decision-grade next step: extend to enough additional already-instrumented sections to reach the effect size leadership actually cares about, with the stopping rule fixed before the extension starts.
Follow-up
- If the interval had been -0.5% to +8.0%, would you support launch, and what exactly changes in your reasoning?
- Leadership asks you to re-run with learner-level standard errors since it is the same data. What do you say?
- What would you have set up at design time so that this meeting was easier?