The role of a Data Scientist at Universitätsklinikum Tübingen is at the intersection of cutting-edge clinical research and high-stakes data infrastructure. You are tasked with transforming complex, often heterogeneous medical and biological datasets into actionable insights that directly influence patient care, diagnostic accuracy, and research outcomes. Your work is not merely academic; it is foundational to the development of clinical decision support systems and the optimization of healthcare processes within one of Germany’s leading medical research institutions.
You will operate in an environment where precision and scalability are paramount. Whether you are working on research infrastructure, clinical data modeling, or predictive analytics, you will be expected to bridge the gap between technical implementation and clinical utility. The role requires a unique blend of domain-specific empathy—understanding the realities of clinical workflows—and rigorous mathematical or computational expertise. You will collaborate with interdisciplinary teams of clinicians, biologists, and software engineers to solve problems that have a direct, real-world impact on human health.
Your ability to communicate complex statistical results to non-technical stakeholders is just as important as your technical proficiency in coding and modeling.
Technical Assessment
reportedBefore anything else, this round is a reading test. You are given a small schema and a question phrased in business language, and most of the difficulty sits in the gap between them. Who counts as an active user, does a refunded order still count as an order, is that date column an event time or a load time. Weak answers start typing immediately and compute something precise about the wrong population. Strong ones pin the definition in one sentence, name the column that encodes it, then write the query. On a timed assessment with nobody to tell, write the definition in a comment anyway.
What to demonstrate
- Whether an ambiguous term becomes a specific column and filter before any computation happens
- Whether you read the schema for keys and cardinality rather than only for column names
- Whether the result answers the question at the grain it was asked at, per user or per session or per day
How to prepare
- Take three metrics you already use and write down the exact filter and exact grain behind each, then practise stating one of them in a single sentence out loud
- On a schema you have never seen, spend the first minute writing what one row of each table means and which key it is unique on, then predict which joins can duplicate rows
- Rehearse a version where the definition changes halfway through, and edit the query you have instead of starting over
Deep-Dive Sessions
reportedBecause the format is not fixed, prepare the reasoning rather than the ritual. Nearly every version of this round draws on the same underlying material: a design you can defend, a metric you can define exactly, an analysis whose assumptions you can state out loud. Only the wrapper changes, whether that is a take-home, a live case, a deep dive on past work, or a rough estimate on a whiteboard. Answers rehearsed to fit one shape stall the moment the shape differs. Practise naming the assumption behind a number, then saying how much the conclusion moves if that assumption is wrong.
What to demonstrate
- Whether your justification for a method survives the question 'why not the simpler thing', including when the simpler thing would have worked
- Precision under pressure: what exactly counts as an active user, a conversion or a success, over what window, with what exclusions
- Whether you carry an argument through to a recommendation instead of stopping at a list of tradeoffs
How to prepare
- For each project you plan to mention, write the metric definition in one sentence: numerator, denominator, time window, exclusions. Say it out loud once, because vagueness shows up in speech before it shows up on paper.
- Rehearse the same project at three lengths: two minutes, ten minutes, and a deep dive on one technical decision. Cutting live is harder than it sounds.
- For your headline result, write down what would have had to be true for it to be wrong, and how you ruled that out.
Final Decision-Making
reportedA loop is not scored one interview at a time. The people you meet compare notes afterwards, usually in a meeting you are not in, and the outcome turns on what each of them can say about you when asked. That rewards something other than survival: every room needs one specific thing worth repeating, and none of them can contradict another. The common way to lose is to tell the same project four times with different numbers in it, or to be uniformly fine in a way that leaves nobody with anything to argue for.
What to demonstrate
- Whether your account of a project survives being told twice, with the same scale, the same metric definition and the same numbers each time
- Whether each interviewer leaves with one concrete claim they could make on your behalf later, rather than an absence of complaints
- Whether a question you already answered in an earlier room gets the same answer at the same depth, without visible impatience
How to prepare
- Write a one-page fact sheet for your two or three main projects that fixes the numbers you will quote: rows of data, the metric as a single sentence, the effect you measured and how long the work took. Say them aloud from the sheet until they come out identical every time
- For each kind of room you expect, decide the one sentence you want that interviewer repeating in a debrief, then check during the mock that you said it outright instead of implying it
- Rehearse answering the same project question twice in one sitting, the second time as though you had not just answered it, because the thing that needs fixing is the flatness that creeps into a repeated story
PracHub editorial advice for the preparation topics above.
Consent and age rules silently truncate the data, not just the joins.
Where age_gated is TRUE, behavioural logging and cross-system joins are restricted, so those learners are missing from the very tables used to compute engagement. An analysis that simply inner-joins will produce a population skewed to older learners and self-serve accounts while appearing complete. Check the age_gated share of every population you report on, and state it.
Roster sync creates and deactivates accounts in bulk, and it is not behaviour.
An automated roster import at term start can create thousands of accounts in one hour and deactivate thousands more at term end. Signup, activation, and churn series computed without filtering enrollment_source IN ('roster_sync','admin_bulk') will show spikes and cliffs driven entirely by an integration job. It also breaks cohort retention: a roster-created account that never activates is a provisioning artefact, not a churned learner.
Explaining an aggregate move without decomposing the mix shift
Split the change in the aggregate into within-segment movement and movement in segment weights before you explain it. Every segment's rate can fall while the overall rate rises, purely because volume shifted toward segments that already had higher rates.
Never asking what decision the analysis will inform
Open with who makes the decision, what the options are, and by when. The answer determines the precision you need, the segments worth cutting, and whether an observational read suffices or an experiment is required.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
How do you balance the trade-off between model sensitivity and specifi…
How do you balance the trade-off between model sensitivity and specificity in a clinical setting?
Approach
- Check what information would not exist at prediction time, and exclude it.
- Pick an evaluation metric that matches the cost of each error type, not a default.
- 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?
Permutation test with sections as the randomisation unit
You have outcomes with learner_id, section_id, arm ('treatment' or 'control'), and mastery_events, an integer count over the study window. Assignment was made at section level, so every learner in a section shares an arm. There are 48 sections of roughly 25 learners. Write a permutation test from scratch in numpy that respects the clustering, using the difference in learner-weighted arm means as the statistic. Report the observed statistic, a two-sided p-value, and the number of permutations. No t-test, no statistics package.
Approach
- Collapse to a section table first: section_id, arm, n_learners, sum_mastery. The learner-weighted difference in means is a function of those two columns only, and computing it from the section table makes the permutation unit structurally obvious rather than a comment in the code.
- Fix the number of treated sections at the observed count and permute arm labels over the 48 sections. Permuting learner labels destroys the within-section correlation and reproduces exactly the false precision that section-level assignment exists to avoid.
- Vectorise: build a (B, 48) boolean assignment matrix, then get treated numerators and denominators with two matrix products. That runs 20,000 permutations in well under a second, which matters because you want enough replicates for the p-value to be stable in its third digit.
- Compute the statistic as sum of treated sums over sum of treated n, minus the control equivalent. This is a ratio of sums, not a mean of section means; the two differ whenever sections vary in size, and you should say which one the study was powered on.
- Two-sided p-value is (1 + count of |T_perm| >= |T_obs|) / (1 + B). Both the numerator and denominator offsets are required for validity at finite B; without them a p-value of exactly zero is reportable, and it is wrong.
- Diagnose the clustering rather than assume it: estimate the intraclass correlation from the section table and check that the permutation null SD exceeds the naive learner-level standard error by roughly sqrt(1 + (m - 1) * rho). At m = 25 and rho = 0.15 that factor is about 2.1 on the standard error, or 4.6 on variance.
Worked solution 35 min
- sec = outcomes.groupby('section_id').agg(arm=('arm','first'), n=('learner_id','size'), s=('mastery_events','sum')).
- k = (sec.arm == 'treatment').sum(); T_obs = s[treat].sum()/n[treat].sum() - s[ctrl].sum()/n[ctrl].sum().
- Build assign = np.zeros((B, 48), bool); for each row set k randomly chosen columns True via rng.permuted on a tiled index array.
- num_t = assign @ s; den_t = assign @ n; T_perm = num_t/den_t - (s.sum() - num_t)/(n.sum() - den_t).
- p = (1 + (np.abs(T_perm) >= abs(T_obs)).sum()) / (1 + B); report T_obs, p, B, T_perm.std(), and the estimated rho.
Follow-up
- 48 sections, 24 per arm, baseline and variance as observed. What effect can this design actually detect at 80 percent power, and would you have run it?
- Sections range from 8 to 60 learners. Does that change which statistic you should permute?
- The institution insists on assigning by school rather than by section. What changes in the test, and what changes in the power?
Audit response rows against the versioned content dimension
Write a check function over fct_assessment_response (response_id, learner_id, content_item_id, content_version_no, attempt_no, submitted_at, scoring_mode, scored_at, score_points, max_points) and dim_content_item (content_item_id, version_no, status, published_at, retired_at). Return one row per check with check_name, failing_count, denominator, and up to five example response_ids. Cover at least four checks: version pairs missing from the dimension, responses submitted before published_at or after retired_at, attempt_no that is duplicated or not gapless from 1 within a learner and item, and rubric_human rows still unscored more than 5 days after submitted_at.
Approach
- Do the existence check as a left merge on the pair (content_item_id, content_version_no) to (content_item_id, version_no) with indicator=True. Joining on content_item_id alone both hides the defect you are looking for and fans rows out by the number of versions per item.
- Evaluate the publication window only on rows that matched, and treat NULL retired_at as open-ended. Report the two directions separately: an early submission points at a publishing race, a late one points at a retirement that never removed the item from a live form.
- For attempt_no, group by (learner_id, content_item_id) and compare the sorted attempts to range(1, n+1). A duplicate and a gap are different defects with different causes, so emit them as two rows.
- For scoring lag, restrict to scoring_mode = 'rubric_human' with scored_at null and submitted_at older than 5 days. Anything more recent is expected lag, not a defect, and counting it makes the check permanently red.
- Give every check its own denominator. A shared total row count makes the version check look negligible and the attempt check look enormous when neither is true, because their natural units are rows and (learner, item) groups respectively.
Follow-up
- Which of these four would you page someone about at 2am, and which is a ticket for the week?
- How do you keep this check from timing out once the fact table is in the billions of rows, without weakening what it detects?
How do you optimize a query that is performing slowly on a large clini…
How do you optimize a query that is performing slowly on a large clinical database?
Approach
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
- State the window function and its partition and ordering out loud before writing it.
- Check whether any join is one-to-many before aggregating, or the sums inflate.
Follow-up
- What breaks if events arrive late or out of order?
- How does the query change if the join becomes one-to-many?
Explain how you would handle missing values in a longitudinal patient …
Explain how you would handle missing values in a longitudinal patient dataset.
Approach
- Compute rates by summing numerator and denominator separately, never by averaging rates.
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- Say which table is the grain you start from, and join outward from it.
Follow-up
- What breaks if events arrive late or out of order?
- How does the query change if the join becomes one-to-many?
Seat activation rate by institution within thirty days
You have dim_learner(learner_id, org_id, created_at, first_activity_at, current_status) and fct_subscription_period(subscription_period_id, account_id, org_id, seats_purchased, seats_provisioned, period_start, period_end, status), plus dim_term(term_id, org_id, term_start_date). For each org and term, return the share of provisioned seats whose learner's first activity falls inside the 30 days beginning at term_start_date, meaning first_activity_at >= term_start_date AND first_activity_at < term_start_date + 30 days. The denominator is seats_provisioned on the subscription period covering term_start_date, not a count of learner rows. first_activity_at is NULL for a seat that was created but never used, and dim_learner stores one first-ever activity per learner, so this measures new-seat activation inside a rollout window and not per-term usage. Report orgs where seats_provisioned exceeds seats_purchased rather than clipping them.
Approach
- Pick the covering subscription period with term_start_date BETWEEN period_start AND period_end. If overlapping renewal rows both match, choose a deterministic tie-break such as the latest period_start and say so, because the denominator is otherwise ambiguous.
- Write the window closed at both ends: COUNT(DISTINCT learner_id) where dim_learner.org_id matches and first_activity_at >= term_start_date AND first_activity_at < term_start_date + INTERVAL '30 days'. The lower bound is the load-bearing half. With only the upper bound, every learner who activated in any earlier term still satisfies the predicate, so each term inherits all of its predecessors' activations, the rate climbs past 1 on orgs that renew, and the movement has nothing to do with the rollout being measured.
- The NULL is the signal for a dead seat. A NULL first_activity_at fails both bounds on its own, so no explicit IS NOT NULL is needed, but the predicate must live in the numerator aggregate and never in a WHERE clause that also feeds the denominator: an unused seat still occupies a provisioned seat.
- Take the denominator from seats_provisioned on the period. Counting dim_learner rows instead removes never-activated seats from both sides of the fraction and pushes the rate toward 1.
- Mind the types. term_start_date is a DATE and first_activity_at is a timestamp, so the window opens at midnight of term_start_date in whatever zone the timestamps carry and closes at midnight 30 days later. State the zone you are assuming rather than letting the cast pick one.
- LEFT JOIN the learner aggregate onto the period so an org with zero activations returns 0 rather than disappearing from the result set, and emit an over_provisioned flag where seats_provisioned > seats_purchased instead of capping the denominator. Over-provisioning is a contract fact and hiding it makes activation look worse than the contract says.
Worked solution 20 min
- Write the period selection CTE first and count how many (org_id, term_id) pairs match more than one period. Fix the tie-break before going further.
- Write the activation aggregate as its own CTE keyed on (org_id, term_id) with both bounds on first_activity_at, so the window is visibly separate from the denominator.
- Self-join dim_term on org_id and count pairs of terms whose term_start_date values sit within 30 days of each other. That count is exactly how far SUM(activated_seats) can exceed the distinct learner count, and on most calendars it is zero.
- LEFT JOIN aggregate onto period, compute activated_seats::numeric / NULLIF(seats_provisioned, 0).
- Add the over_provisioned flag and a count of orgs where activation_rate exceeds 1.
Follow-up
- dim_learner stores one first-ever activity, so an org with a stable roster shows activation falling every term as returning learners are already spent. What would you add to the model to measure per-term activation, and which of the two does an administrator actually want?
- seats_provisioned can also trail the number of accounts on the license. What does your rate do then, and what do you report instead of a number above 1?
- An administrator wants this weekly during the first month of term. What does the metric look like on day 3, and how do you present a partially matured cohort?
If you noticed a sudden drop in the usage of a specific diagnostic das…
If you noticed a sudden drop in the usage of a specific diagnostic dashboard, how would you go about diagnosing the root cause?
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- State what result would change your recommendation, so the answer is falsifiable.
- 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?
- How would you detect that the metric is being gamed rather than genuinely improving?
How do you prioritize tasks when you are juggling multiple high-priori…
How do you prioritize tasks when you are juggling multiple high-priority research requests?
Approach
- Fix the population and the time window before naming any metric.
- Name one primary metric, then the guardrail that stops it being gamed.
- Restate the decision this analysis has to support, and who acts on the answer.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- Which segment would you cut first, and what would that rule out?
What metrics would you use to determine if a research infrastructure p…
What metrics would you use to determine if a research infrastructure project is providing value to the end-user?
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Fix the population and the time window before naming any metric.
- State what result would change your recommendation, so the answer is falsifiable.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- Which segment would you cut first, and what would that rule out?
How would you design a metric to measure the effectiveness of a new cl…
How would you design a metric to measure the effectiveness of a new clinical decision support tool?
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Name one primary metric, then the guardrail that stops it being gamed.
- State what result would change your recommendation, so the answer is falsifiable.
Follow-up
- Which segment would you cut first, and what would that rule out?
- What would you do if the primary metric and the guardrail moved in opposite directions?
When is it appropriate to use a non-parametric test instead of a t-tes…
When is it appropriate to use a non-parametric test instead of a t-test?
Approach
- State the primary metric and the minimum effect worth shipping, then size the test.
- Say whether units interfere with each other, and switch design if they do.
- Decide the analysis before seeing data, including how long it runs and when you look.
Follow-up
- What would you do if you could not randomise at all?
- What would you conclude if the result is positive but the test is underpowered?
How do you determine the required sample size for a clinical pilot stu…
How do you determine the required sample size for a clinical pilot study?
Approach
- State the primary metric and the minimum effect worth shipping, then size the test.
- Name the randomisation unit first; it decides the variance and what the test can detect.
- Decide the analysis before seeing data, including how long it runs and when you look.
Follow-up
- What would you do if you could not randomise at all?
- How would you handle interference between treated and control units?
Weekly engagement metric robust to term calendars
Weekly active learners fell 42% week over week. The prior week was term-end and the current week contained a holiday. Using fct_lesson_activity (activity_date, learner_id, active_seconds), fct_enrollment (enrollment_source, status) and a term dimension mapping each date to a term, a term-week index and an instructional-day flag, write a weekly engagement metric definition a stakeholder can read week to week. Specify the numerator, the denominator, the alignment rule, and which enrollment sources you exclude and why.
Approach
- Refuse the comparison before defining anything: a term-end week and a holiday week are not comparable units, so the -42% is a calendar fact, not a product signal.
- Define the numerator with a floor: distinct learner_id in fct_lesson_activity with at least 60 active_seconds inside the term-week, so a stray page open does not count as engagement.
- Define the denominator deliberately: learners with an active enrollment in that term-week, excluding accounts created by roster_sync or admin_bulk that have never had first_activity_at set. Those are provisioning artefacts from an integration job, not dormant learners, and leaving them in makes every rate move when a district runs a sync.
- Handle short weeks explicitly: report the rate per instructional day, or restrict to full instructional weeks and label the short ones on the chart, rather than averaging a three-day week into the series.
- Compare term-week W of this term to term-week W of the prior term through the term dimension, never to the adjacent calendar week.
- Publish the definition string beside the series and state the age_gated share of the population, since behavioural logging is restricted for those learners and the series is missing them by construction.
Worked solution 20 min
- Join activity_date to the term dimension to get (term_id, term_week, is_instructional_day) for every row.
- Write the numerator with the 60-second floor and the denominator with the active-enrollment rule and the roster_sync and admin_bulk exclusion.
- Add the per-instructional-day normalisation and the same-term-week prior-term comparison.
- Recompute the reported drop under the aligned definition and state what remains after the calendar is removed.
Follow-up
- Two institutions run terms of different lengths. How do you align term-week 6 between them?
- A district runs its roster sync two weeks late. What does your series do, and what would you want it to do?
- What would you show a stakeholder who insists on a week-over-week number anyway?
Pace adherence at term-week six rose eleven points
Pace adherence at term-week 6 rose from 48 percent to 59 percent between two consecutive terms, and the dashboard query changed owners last month. Tables: fct_enrollment (enrollment_id, learner_id, course_id, units_completed, units_total, status, scheduled_start_date, due_date, enrolled_at) and dim_term (term_id, term_start, term_length_weeks). Nothing shipped to pacing in that period. Decide whether learners are pacing better, and reconcile the old and new numbers to the point.
Approach
- Diff the definitions before touching the data. Recover the prior query and compare it line by line against the current one on four axes: the denominator population, the status filter, how term-week 6 is derived, and what happens to enrollments with NULL scheduled_start_date.
- Recompute both definitions on the same term and the same enrollment set. If the difference does not account for the full eleven points there is a second cause, and knowing that before you start hunting saves the hunt.
- Test the survivorship mechanism directly: count enrollments with status in ('dropped','expired') at the end of term-week 6 and compute their units_completed over units_total. If that ratio sits well below the active median, removing them from the denominator lifts the rate mechanically and the lift is arithmetic, not behaviour.
- Verify the numerator independently, because units_completed is maintained incrementally rather than derived on read. Recount it from lesson activity for a sample of a couple of hundred enrollments and compare against the stored column to rule out a backfill as a second mover.
- Confirm term-week 6 maps to the intended dates in both terms through dim_term rather than calendar week, since an edited term_start silently shifts the evaluation window and can move the number on its own.
Follow-up
- The old definition penalises a course for enrollments that dropped in week two. Is that the behaviour you actually want from a pacing metric, and who is the metric for?
- How do you treat enrollments with NULL scheduled_start_date, and what does each choice do to the reported number?
For someone who has spent the last year in notebooks, dashboards or modelling work and has not written raw SQL under time pressure. The first four days rebuild query fluency against a fixture you control and can verify by hand; the last three attach that fluency to the rest of the loop.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Build a fixture you can check answers against
- Create a local Postgres or SQLite database with four tables (users, sessions, events, orders) holding roughly 200 rows you generated yourself, so you know the contents well enough to predict every result.
- Deliberately seed the cases that break queries: a user with no sessions, a session with no events, two orders sharing a timestamp, a NULL in one join key, and one duplicated user row.
- Before writing any SQL, hand-compute five answers on paper (how many users placed at least one order, median orders per ordering user, and three others) and save them as the ground truth for the week.
Deliverable: A one-command seed script plus a text file of five hand-computed answers to grade every later query against.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Joins, filters and NULL semantics
- Answer "which users have no orders" three ways (LEFT JOIN with IS NULL, NOT EXISTS, NOT IN) and confirm that the NOT IN version returns zero rows once the subquery contains a NULL, because the comparison is never TRUE.
- Reproduce the LEFT JOIN that silently collapses to an inner join by putting a right-table predicate in WHERE, then fix it by moving the predicate into the ON clause, and record both row counts.
- Create a fan-out bug on purpose by joining orders to order_items and summing the order total, then correct it with a pre-aggregated subquery and explain in one line which table changed the grain.
Deliverable: One annotated .sql file holding the three join traps, each with the wrong result and the corrected result side by side.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Window functions and frames
- Write three window queries against the fixture: a running order total per user, the rank of each order within its user by value, and the day gap to that user's previous order, then check each against the day-one ground truth.
- Run ROW_NUMBER, RANK and DENSE_RANK over a column containing ties, print all three side by side, and write one sentence on when each is the correct choice.
- Switch one query from the default frame (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which is what you get when ORDER BY is present and no frame is written) to ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and explain why the output differs only when the ORDER BY column has duplicates.
Deliverable: Three verified window queries plus a short note explaining the RANGE versus ROWS difference in your own words.
Practice prompt ↗Practice prompt ↗Practice prompt ↗04The four analytical query patterns
- Write a monthly retention grid: first order month per user, then months-since-first as the column, and verify that month zero equals the cohort size exactly.
- Sessionize the events table under a 30-minute inactivity rule using LAG plus a cumulative sum over a new-session flag.
- Build a four-step funnel that counts distinct users rather than events at each step, and state the rule you applied to a user who reaches step three without ever logging step two.
Deliverable: One file with the retention, sessionization and funnel patterns, each carrying a one-line note on the assumption it bakes in.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Write SQL the way you will have to write it live
- Set a 12-minute timer and solve three medium prompts in a plain editor with no execution and no autocomplete, then run them and tally syntax errors separately from logic errors.
- Narrate one solution aloud while writing it, stating the grain of each intermediate result (one row per user, one row per user-day) before you type its body.
- Rewrite your slowest solution as a CTE chain where every CTE name states its grain, and time yourself re-solving it from blank.
Deliverable: A recording of one narrated solution plus an error tally that separates syntax from logic.
Practice prompt ↗Practice prompt ↗06One day for everything that is not SQL
- Write the preconditions of the two-sample t-test from memory, then check them: independent observations, and a difference in means whose sampling distribution is approximately normal, which at large sample sizes follows from the central limit theorem rather than from normality of the raw values.
- Write the difference between an odds ratio from logistic regression and a relative risk, and state the condition under which the two are close (low outcome prevalence).
- Prepare a 90-second answer to "how would you know this model is any good" that names the metric, the baseline you would beat, and the cost of the errors you care about.
Deliverable: One page of notes covering test preconditions, the odds-ratio caveat and the model-quality answer.
Practice prompt ↗Practice prompt ↗07Full loop rehearsal
- Run a 45-minute mock with someone willing to interrupt: 20 minutes of SQL, 15 minutes defining a metric, 10 minutes on a past project.
- Re-solve from blank the two queries you were slowest on this week and compare the times against day five.
- Write a five-line answer to "walk me through a project" that puts a number in the first sentence and names the decision the work changed.
Deliverable: Mock feedback notes plus a timed project narrative you can deliver without reading it.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
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.
Explain the concept of statistical significance to a clinical stakehol…
Explain the concept of statistical significance to a clinical stakeholder who does not have a background in statistics.
Approach
- Close with what you would do differently, concretely.
- Pick a story where you drove the decision, not one where you observed it.
- State the situation in two sentences and spend the rest on your reasoning.
Follow-up
- What did you decide not to do, and why?
- How did you know the outcome was caused by your change?
Allocate one week across three competing team requests
In one week you receive three requests. Sales wants a renewal-risk list for 40 institutional accounts whose period_end falls in 30 days. Curriculum suspects an item-quality problem on a published unit that is currently collecting responses. Growth wants a signup-flow test sized. You have capacity for roughly one and a half of them and cannot escalate for arbitration. Deliverable: your allocation, the reasoning you give each requester, and the one question you ask each before deciding. Probed: whether you prioritise on decisions and reversibility rather than on who asked loudest.
Approach
- Score each request on deadline and reversibility, not on seniority. The renewal list is worthless after period_end; a bad published item compounds with every response collected against it; a test sizing costs almost nothing to delay a week.
- Ask each requester the one question that could collapse their request: whether sales already has a workable heuristic list, whether the suspect unit can simply be set to retired today, whether the growth test has a launch date at all.
- Look for the cheap partial that still buys the deadline. A rules-based risk cut from first_activity_at, units_completed against units_total, and days to period_end ships in a day and captures most of the value of a model.
- Decide, then tell the person who is not getting the work directly and with a date. Unmanaged silence costs more trust than an explicit decline.
- Write the decision and its reasoning somewhere durable so next week's triage does not relitigate the same three requests.
Follow-up
- The growth PM escalates to your skip-level. What do you do, and what do you send ahead of that conversation?
- Two weeks later the risk list you shipped went unused. What changes in how you triage next time?
Explain a risk score to a non-technical sales executive
Your renewal-risk score joins fct_subscription_period to seat activation from dim_learner.first_activity_at and to pace adherence from fct_enrollment. For one institution with 900 seats_provisioned and 31% activation at term-week 6, it outputs a 0.62 probability of non-renewal. A sales VP asks whether you are losing the account or not. You have two minutes and no slides. Deliverable: the spoken answer, plus the one line you would put in the weekly account review so the number is not later quoted as a certainty. Probed: whether you can carry uncertainty and still give a decision.
Approach
- Answer the question that was asked before qualifying it. 'Treat this as at-risk and act this week' is the answer; the probability is the support for it.
- Translate the score into a frequency on a reference class the VP already trusts, such as how many of the last fifty accounts scored in the 0.55-0.70 band did not renew, instead of explaining calibration in the abstract.
- Name the driver that is still movable before period_end: 31% seat activation at term-week 6 can be changed this term, whereas last term's usage cannot, so that is where the conversation goes.
- State what would move the score and by when, so the number arrives attached to an action rather than as a verdict.
- For the written line, use a band and a review date rather than a bare decimal, because a single number in a document will be re-quoted without its interval.
Follow-up
- The VP asks for every account scored above 0.50. What do you tell them about where that threshold came from?
- How would you check whether 0.62 is calibrated at all, and on what sample?
- 01
Explain the concept of statistical significance to a clinical stakeholder who does not have a background in statistics.
- 02
In one week you receive three requests. Sales wants a renewal-risk list for 40 institutional accounts whose period_end falls in 30 days. Curriculum suspects an item-quality problem on a published unit that is currently collecting responses. Growth wants a signup-flow test sized. You have capacity for roughly one and a half of them and cannot escalate for arbitration. Deliverable: your allocation, the reasoning you give each requester, and the one question you ask each before deciding. Probed: whether you prioritise on decisions and reversibility rather than on who asked loudest.
- 03
Your renewal-risk score joins fct_subscription_period to seat activation from dim_learner.first_activity_at and to pace adherence from fct_enrollment. For one institution with 900 seats_provisioned and 31% activation at term-week 6, it outputs a 0.62 probability of non-renewal. A sales VP asks whether you are losing the account or not. You have two minutes and no slides. Deliverable: the spoken answer, plus the one line you would put in the weekly account review so the number is not later quoted as a certainty. Probed: whether you can carry uncertainty and still give a decision.
Is this an official Universitätsklinikum Tübingen interview guide?
No. It is PracHub's own research and practice material for the Data Scientist role at Universitätsklinikum Tübingen. Rounds and questions reflect what candidates have reported, not a process Universitätsklinikum Tübingen has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How much time should I spend preparing for the SQL portion?
Dedicate significant time to mastering window functions and complex joins. The clinic's data is complex, and you will need to demonstrate efficiency in extracting the right information.
PracHub interview research ↗Is there a focus on Machine Learning?
While the team uses ML, the focus is often on the interpretability and reliability of models in a clinical context. Understand the trade-offs between model complexity and transparency.
PracHub interview research ↗What is the company culture like?
The culture is research-focused, collaborative, and highly rigorous. Candidates report that the team values intellectual curiosity and a commitment to improving patient outcomes through data.
PracHub interview research ↗How long does the process take?
The process typically spans a few weeks, allowing time for thorough evaluation and team alignment. Candidates report that the team prioritizes finding the right fit for its long-term research goals.
PracHub interview research ↗Sources & methodology 3 sources ↗
No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
PracHub page · Accessed 2026-09-22 - 02PracHub Data Scientist practice ↗
Cross-company practice questions for this role.
PracHub page · Accessed 2026-09-22 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
PracHub page · Accessed 2026-09-22