Zeta · Data Scientist
Updated · 2026-09-22

Zeta Data Scientist
Interview Questions & Guide 2026

THE 60-SECOND BRIEF

This guide prepares a Data Scientist candidate at Zeta using original exercises built around B2B software and infrastructure data problems. PracHub assigns that framing to shape the practice; it is not a claim about Zeta's business or its interviews.

Coding rounds for this role are usually data-manipulation shaped rather than data-structure shaped: group-bys, joins, time windows, ranking within a partition. Confirm the format before spending a week on graph traversal.

These checkpoints cover the technical spine only. Communication and behavioural preparation are handled separately in this guide, and neither ordering is a list of rounds.

Power experiments for heavy-tailed account revenueRead NRR on a fixed account cohortStrip CI, retry and synthetic traffic first

49 min read

Practice 12 Data Scientist prompts
12Practice promptsAcross five skill areas
3With worked solutionsIncluded in the practice prompts

What makes the analysis hard here is the shape of the data rather than its volume. The account population is small by consumer standards, often a few thousand paying accounts, and revenue across it is extremely skewed, so the top fraction of a percent holds a large share of the total and any mean over accounts is effectively a statement about a handful of customers. Outcomes within an account are strongly correlated because colleagues share workspaces and influence each other, which both inflates apparent sample size and lets treatment leak between arms when randomisation is done at the user level. Contracts are annual, so churn is a rare, censored, calendar-driven event that most monthly metrics are structurally incapable of measuring. And the telemetry itself is largely machine-generated: a customer changing one line in a CI configuration can multiply request volume overnight without a single human deciding anything about the product.

Data science work in business software, infrastructure and developer tooling sits between a telemetry stream and a contract system, and most of the job is making those two agree. The unit of analysis is the account, not the person: an organisation signs, an organisation renews, and a hundred engineers inside it are correlated observations rather than a hundred independent ones. The recurring questions are narrow and concrete, covering which accounts will expand at renewal and which will contract, what a unit of usage costs to serve for a given account at its negotiated discount, whether a packaging change moved consumption or merely moved it between SKUs, and which accounts a capacity-limited customer-success team should contact this week. The deliverable is usually an account-level list with a threshold attached, handed to a team with finite capacity, so a ranking without an operating point is an unfinished piece of work. The audience is rarely another data scientist, so a definition also has to survive being repeated by someone who did not write it.

The skills that carry the most weight are, in order, SQL across mismatched grains with an explicitly defended denominator, cohort and survival thinking applied to contracts rather than to individuals, and experiment design that survives clustering and heavy tails. Concretely that means as-of joins against a versioned subscription table, randomisation at the account level with the design effect accounted for in the power calculation, pre-registered winsorisation or a capped metric when the outcome is revenue, and variance reduction using pre-period usage as a covariate. Unit economics matters more than in most domains because infrastructure cost is a real and allocable per-account quantity, so being able to say which accounts are unprofitable and why is a routine expectation. The last skill is unglamorous and decisive: when the product number and the finance number disagree, being able to say which one is right, by what definition, and what the reconciling difference is.

01

Account-grain SQL against versioned contracts

editorial

Query usage, membership and contract tables together at the account grain without fanning out rows, and resolve every contract question as of a date rather than as of now. This is the substrate for nearly every other analysis in the domain, because the subscription table is versioned and the usage tables are not.

What to demonstrate

  • Writing an as-of join from a daily usage fact to fct_subscription_period, selecting the version whose term_start_date and term_end_date bracket the usage_date, rather than joining to is_current and silently backdating today's plan over last year's usage
  • Joining a one-row-per-account dimension to a many-row-per-account fact without multiplying revenue, including the case where dim_account is type 2 and contributes several versions in the window
  • Stating the denominator and its exclusions out loud before writing the SELECT, specifically is_internal accounts, non-production environments, service accounts and synthetic traffic classes
  • Recognising when a question needs a rollup from fct_api_request and when it must come from fct_usage_daily, and explaining why the two will not reconcile exactly

How to prepare

  • Write one query returning monthly net revenue, allocated COGS and gross margin per account, joined as-of to the contract version live during each month, and confirm the total ties to the sum of net_amount_cents
  • Practise the type 2 dimension pattern until it is automatic: filter to the version live at the event timestamp, never to is_current, and be able to say what breaks when you get it backwards
  • Build a query that separates interactive, CI, batch and synthetic traffic into columns for the same account and week, and look at how different the engagement story is under each filter
  • Rehearse explaining, in two sentences, why request counts from fct_api_request and billable quantities from fct_usage_daily diverge, including retries, failed requests and restatement
PracHub interview preparation framework
02

Retention, expansion and contract cohort arithmetic

editorial

Build net revenue retention, gross logo retention and expansion rate from a versioned contract table, and defend each denominator. This is the numerical language the domain uses to talk about itself, and small definitional errors here change the headline number by tens of points.

What to demonstrate

  • Constructing net revenue retention as a ratio of sums over a cohort frozen twelve months earlier, and explaining why the mean of per-account ratios gives a different and much noisier answer given that contraction is floored at zero while expansion is not
  • Restricting logo churn to the renewal-eligible base and articulating why a monthly rate on the whole base is off by roughly the reciprocal of the annual renewal fraction
  • Handling censoring properly when contracts have not yet reached their renewal date, including when a survival estimator is required instead of a simple rate
  • Separating booked_at from term_start_date and knowing which one belongs in a sales-effectiveness question and which in a revenue question

How to prepare

  • Compute net revenue retention two ways on the same data, ratio of sums and mean of ratios, and be ready to explain the gap from the shape of the expansion distribution
  • Derive from first principles how much a naive monthly churn rate understates the truth when all contracts are annual, and be able to do that arithmetic aloud
  • Work through the ways net revenue retention can rise while the business shrinks, and name the guardrail that catches each one
  • Build a renewal-eligible cohort for a given month from fct_subscription_period, including the 45-day grace for late paperwork, and sanity-check that roughly a twelfth of the base appears
PracHub interview preparation framework
03

Experimentation with clustered, skewed outcomes

editorial

Design and read out experiments where the randomisation unit is an account, the sample is a few thousand clusters at most, and the outcome of interest is revenue or consumption with an extreme right tail. Most standard experimentation instincts are calibrated on large-sample consumer data and mislead here.

What to demonstrate

  • Choosing the account as the randomisation unit and computing power with the design effect 1 + (m - 1) * rho, rather than quoting a user-level sample size
  • Recognising when the experiment is simply not powerable on revenue and proposing a proximate outcome, a longer horizon, or a switchback design for a platform-level change that cannot be randomised across accounts at all
  • Applying variance reduction with a pre-period covariate, for example CUPED using the account's prior consumption, and stating the approximate variance reduction as one minus the squared correlation
  • Pre-registering the winsorisation or capping rule and being explicit that it changes the estimand rather than pretending it is a neutral cleaning step

How to prepare

  • Run a power calculation for an account-randomised test on consumption, with and without the design effect, and note how much longer the clustered version has to run
  • Practise the diagnostic for a heavy tail: plot the running sample variance against sample size and say what it means when it does not stabilise
  • Prepare one clear explanation of interference inside a shared workspace and how it biases a user-level estimate toward zero
  • Have a switchback or stepped-wedge design ready for an infrastructure change that is global by nature, including how you would handle carryover between periods
PracHub interview preparation framework
04

Usage telemetry hygiene and unit economics

editorial

Turn raw machine-generated traffic into a defensible measure of human value, and attach cost to it. The gap between what the platform serves and what a customer values is wider in this domain than almost anywhere else, and allocating infrastructure cost to an account is a routine expectation rather than a finance task.

What to demonstrate

  • Systematically excluding internal accounts, non-production environments, synthetic monitors, load tests, service-account traffic and retried requests, and being able to quantify how much each exclusion removes
  • Computing gross margin at the account level from net_amount_cents and cogs_cents, and identifying structurally margin-negative accounts rather than reporting a blended rate that conceals them
  • Establishing the metering settling time empirically from first_written_at against restated_at, and excluding an appropriate trailing window from every reported figure
  • Explaining how a rising error rate can inflate metered volume through client retries, and designing a metric that does not reward that

How to prepare

  • Take one week of request-level data and produce the same engagement number under four progressively stricter traffic filters, then be ready to argue for one of them
  • Build the per-account margin distribution and answer, with a number, what share of revenue sits in margin-negative accounts
  • Measure how much a given usage_date's total shifts between first write and settlement, and set a lag window from that measurement rather than from convention
  • Write down the specific mechanism by which a platform outage can increase billable units, and the guardrail metric that would expose it
PracHub interview preparation framework
05

Renewal-risk and expansion scoring with an operating point

editorial

Build an account-level model that ranks renewal risk or expansion opportunity, with leakage-free features and a threshold tied to the capacity of the team that will act on it. The output is a worklist, so ranking quality at the top matters far more than calibration across the whole distribution.

What to demonstrate

  • Constructing features strictly from data available before the prediction point, and spotting the leakage that comes from features updated retroactively, such as a churn reason code, a downgrade amendment or a support ticket opened after the renewal conversation began
  • Choosing an evaluation metric that matches how the list is used, meaning precision or recall at the k the team can actually work, not a global AUC over thousands of accounts most of which will never be contacted
  • Handling class imbalance and small positive counts honestly, since annual contracts produce few churn events per period, and knowing when the honest answer is that there is not enough signal to model
  • Turning the score into a decision by attaching a threshold to the expected value of an intervention and the capacity constraint, and stating what happens to accounts below the line

How to prepare

  • Build a leakage audit habit: for every feature, name the timestamp that guarantees it existed before the prediction date, and drop anything you cannot date
  • Practise reporting precision at k for several plausible team capacities instead of a single headline number
  • Prepare an argument for why a simple, inspectable model that a customer-success team will actually trust can outperform a stronger model they ignore
  • Work out how you would evaluate whether the intervention itself worked, given that assignment of coverage is not random, and name the design you would ask for
PracHub interview preparation framework

PracHub editorial advice for the preparation topics above.

01

Reporting a mean over accounts when account revenue is heavy-tailed

When a small number of accounts hold most of the revenue, the sample mean is dominated by whichever of them happens to be in the sample, and the sample variance keeps growing as more data arrives instead of stabilising. In that regime the usual central-limit-based confidence interval understates uncertainty, and a single renewal or a single large account's batch job can flip the sign of a measured effect. The fixes are to pre-register a winsorisation or capping rule before looking at the outcome, to report account counts crossing a threshold alongside the revenue figure, or to define the estimand on a bounded transform. Choosing the cap after seeing the result is a separate and worse problem, because the cap then encodes the answer.

02

Treating raw request or usage volume as engagement

Most traffic in this domain is emitted by machines. Continuous-integration pipelines, scheduled batch jobs, synthetic monitors, backfills and client retries can all grow by an order of magnitude from one configuration change made by one engineer, and none of it represents a new decision to use the product. The inversion is what makes it dangerous: when the platform degrades, clients retry, so error-driven retry volume rises at the exact moment the customer is most likely to leave, and an engagement dashboard built on raw counts shows growth immediately before a churn. Filter on traffic_class and on successful status before anything else, and keep failed-request volume as its own separate series.

03

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.

04

Generalising beyond the population the sample actually supports

State the frame the sample was drawn from and where it diverges from the population you want to talk about: time window, platform, geography, opt-in. If a group is excluded from the frame, either weight to known margins or narrow the claim rather than quietly extending it.

Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.

9 technical prompts3 include a worked solution

Implement seven-day activation rate from its written definition

easy
metricspandasactivation

Given dim_account (account_id, created_at, is_internal, is_current) and fct_api_request (account_id, request_at, http_status, api_key_id, traffic_class), implement activation_rate(accounts, requests, week_start). Definition: the numerator is accounts whose first request with http_status < 400, api_key_id not null and traffic_class != 'synthetic_monitor' occurs no later than 168 hours after created_at; the denominator is non-internal accounts created during the ISO week starting week_start. All timestamps are tz-aware UTC. Return the rate and both counts, and refuse to report a week until every account in it has had its full 168 hours.

Approach
  1. Reduce dim_account to one row per account_id before joining anything. It is a type 2 dimension, so several versions of the same account exist; joining the versioned table to requests multiplies the denominator by the number of plan changes an account happened to make.
  2. Build the denominator first and freeze it: is_internal == False and week_start <= created_at < week_start + 7 days. Everything after this is a filter on the numerator only, because an account that never sent a request must still sit in the bottom of the fraction.
  3. Filter requests to qualifying rows and only then take groupby('account_id').request_at.min(). The first qualifying request is not the same object as the global first request filtered afterwards, and the two answers differ for every account whose first call was a 4xx.
  4. Left-join the first qualifying timestamp onto the cohort and test (first_ok - created_at) <= Timedelta(hours=168). NaT propagates to False in that comparison, which is the behaviour you want, but assert it rather than assume it.
  5. Guard reportability explicitly: if week_start + 7 days + 168 hours exceeds the maximum request_at in the data, the week is censored and will read as a drop, so return None or raise rather than emit a number.
Follow-up
  • Median time-to-first-call is more informative. What breaks if you take the median over activated accounts only, and what estimator fixes it?
  • How would you decide whether 168 hours is the right window rather than 72 or 336?
  • An account signs up, does nothing for 20 days, then integrates heavily. Where does it land in this metric, and is that what you want?

Sessionise an API event stream with a 30-minute inactivity gap

medium
sessionisationevent-streamspandas

fct_api_request arrives as a DataFrame with account_id, user_id, request_at (tz-aware UTC), traffic_class and http_status, roughly 5 million rows. Assign a session_id to every human-attributable request: drop rows where user_id is null or traffic_class is in ('ci','synthetic_monitor','load_test'), then open a new session whenever the gap since that user's previous remaining request exceeds 30 minutes. Return the filtered frame plus session_id, and a per-session summary with user_id, account_id, session start, session end and request count. Do not loop over rows.

Approach
  1. Settle the filter-then-gap ordering before writing code. Removing CI and synthetic rows changes the gaps, so sessionising the raw stream and filtering afterwards is a different answer; the definition given filters first, and the two diverge most for accounts whose CI runs every ten minutes.
  2. Sort once by (user_id, request_at) with a stable kind, then gap = df.groupby('user_id', sort=False).request_at.diff(). The first row of each user yields NaT, which is exactly the boundary condition you want rather than a special case to patch.
  3. new_session = gap.isna() | (gap > Timedelta(minutes=30)); session_id = new_session.cumsum(). The cumsum runs over the whole sorted frame and therefore produces globally unique ids in one pass; a per-user cumcount collides across users and forces a composite key on every downstream join.
  4. Build the summary with a single groupby('session_id').agg(...). user_id and account_id can be carried with 'first' only because the sort key groups them — state that dependency, since it silently breaks if someone later re-sorts the frame.
  5. Decide explicitly what a session means when one user_id holds memberships in several accounts: either add account_id to the sort and group keys, or document that sessions may cross accounts. Leaving it undecided produces sessions whose account_id is whichever row sorted first.
Follow-up
  • Where does 30 minutes come from, and how would you pick it from this data instead of from convention?
  • An engineer reused their personal key for a nightly batch job, so machine traffic carries a human user_id. How would you detect that, and should those requests form sessions?
  • How much does the session count change if you sessionise before dropping CI traffic rather than after?

Permutation-test a consumption experiment randomised at account level

hardWorked solution
permutation-testheavy-tailscupedexperiment-readout

An experiment randomised 900 accounts into two arms. You have one row per account: account_id, arm, consumption_28d (billable units after launch) and consumption_pre (the 28 days before). Consumption is heavy-tailed and the largest account is several percent of the total. Write a permutation test from scratch: winsorise at the pooled 99th percentile as a pre-registered rule, use the difference in arm means of the winsorised outcome as the statistic, and obtain a two-sided p-value from 20,000 relabellings of the account-level arm vector. Report the observed effect, the p-value, and the same test on a CUPED-adjusted outcome.

Approach
  1. Be precise about what the permutation test needs. Under the sharp null of no effect for any account, the outcomes are exchangeable across arm labels, and the test is valid for ANY statistic T(outcomes, labels) provided the identical function is applied to the observed labels and to all 20,000 relabellings. The pooled 99th percentile is a function of the outcome vector alone, so recomputing it inside the loop returns the same number 20,000 times: that is wasted CPU, not a bias, and hoisting it out is an optimisation rather than a correctness fix. Say plainly that capping at all changes the estimand from mean consumption to mean capped consumption; it is not a neutral cleaning step.
  2. The mistake that does invalidate the test is an asymmetry between the observed statistic and the permuted ones, and the easiest way to create it is to derive the cleaning rule from the observed arm labels and then freeze it — winsorise each arm at its own observed 99th percentile, hold those two caps fixed, and permute. The observed value is then computed with caps matched to its own partition while every relabelling is scored with caps belonging to a different one, so the null distribution no longer answers the question the p-value claims to answer. A per-arm cap recomputed consistently inside every permutation is a valid test, but it estimates a contrast whose two sides are capped at different thresholds, so prefer the pooled cap on estimand grounds and pre-register it.
  3. Permute the account-level arm vector, because the account is the randomisation unit. Relabelling anything finer — users, workspaces, requests — generates a null distribution narrower than the design actually supports and returns p-values that are anti-conservative.
  4. Vectorise the null: tile the treatment indicator into a (B, n) matrix and permute along axis 1 with rng.permuted(..., out=...). The statistic is a difference of means, so the treated sum alone determines it and the whole null is one matrix-vector product. Use the two-sided p-value (1 + count(|stat_perm| >= |stat_obs|)) / (B + 1); the plus-one on each side is not cosmetic, it keeps the p-value away from exactly zero and keeps the test valid at finite B.
  5. For CUPED, fit theta = cov(y, x) / var(x) on the pooled data and use that same theta for the observed statistic and every relabelling. Pooled theta, like the pooled cap, carries no label information, so where in the loop you compute it is again only a performance question; fitting theta within arms is what goes wrong, because the adjusted outcome then depends on the labels and an observed-label fit frozen across all 20,000 relabellings breaks the match between observed and permuted statistics. x must be measured entirely before launch, which consumption_pre is. Expected variance reduction is about 1 - corr(y, x)^2; measure the achieved reduction from the two null distributions rather than asserting it.
Worked solution 45 min
  1. cap_y = np.quantile(df.consumption_28d, 0.99); y = np.minimum(df.consumption_28d.to_numpy(float), cap_y); cap_x = np.quantile(df.consumption_pre, 0.99); x = np.minimum(df.consumption_pre.to_numpy(float), cap_x)
  2. t = (df.arm == 'treatment').to_numpy(); n1 = int(t.sum()); n0 = len(t) - n1; obs = y[t].mean() - y[~t].mean()
  3. rng = np.random.default_rng(11); L = np.tile(t.astype(np.int8), (20_000, 1)); rng.permuted(L, axis=1, out=L); s1 = L @ y; stats = s1/n1 - (y.sum() - s1)/n0
  4. p = (1 + int(np.sum(np.abs(stats) >= abs(obs)))) / (20_000 + 1)
  5. theta = np.cov(y, x, ddof=1)[0,1] / np.var(x, ddof=1); y_adj = y - theta*(x - x.mean()); repeat steps 2 to 4 on y_adj and compare stats.std(ddof=1) between the two runs.
EXPECTED RESULTA two-sided p-value strictly between 1/20001 and 1, an observed effect expressed in capped billable units per account, and a CUPED null whose standard deviation is smaller than the unadjusted one by roughly sqrt(1 - corr(y, x)^2). The CUPED point estimate stays close to the unadjusted one, since the adjustment removes variance rather than shifting the effect.
Follow-up
  • The p-value is 0.04 with the cap and 0.31 without it. What do you report, and what did you pre-register?
  • Colleagues in a shared workspace can see the treated behaviour. How does that change the design and the estimate?
  • How many accounts would you need to detect a 5% lift given this outcome's distribution?

For a candidate whose interviews will centre on A/B testing, metric movement and causal claims. Design comes before arithmetic, arithmetic before analysis, and the week ends by rehearsing the readout rather than the derivation.

Small steps. Visible outcomes.0 / 7 completed
ONE WEEK · YOUR PACE

Prepare, practise & reflect

One practical outcome each day. Spend longer where you need it.

0 / 7 done
01Design one test end to end on paper
  • Take a single feature change and write the full design: randomization unit, the exact point of exposure, the primary metric with its grain, guardrails, allocation, planned duration, and the decision rule committed before any data exists.
  • Write why the randomization unit must sit at or above the level where treatment can spill over, and give one case where user-level randomization is still contaminated (shared accounts or devices, or two participants in the same marketplace).
  • State in advance what you will do if the primary metric is flat while a secondary metric is significant.

Deliverable: A one-page test design with a decision rule written before launch.

Practice prompt ↗Practice prompt ↗Worked solution ↗
02Power arithmetic until it is automatic
  • Compute required sample size per arm for a binary metric with the normal approximation, n is approximately 2 times (z for alpha/2 plus z for power) squared times p(1 minus p) divided by delta squared, for baselines of 2, 10 and 40 percent at a 5 percent relative lift, and note that for a fixed relative lift the requirement falls as the baseline rises because delta grows proportionally with p.
  • Redo the calculation for a continuous metric using variance in place of p(1 minus p), and show why a heavy-tailed quantity such as revenue per user needs either far more traffic or a capped version with a stated cap.
  • Convert one of the results into weeks given a weekly eligible traffic figure, then list the two honest ways to shorten it (accept a larger detectable effect, or reduce variance) and write why quietly lowering the power target is a decision to miss more real wins, not a speedup.

Deliverable: A small script or sheet that maps baseline, minimum detectable effect, alpha and power to sample size and weeks, cross-checked against a published calculator.

Practice prompt ↗Practice prompt ↗
03Variance and the unit-of-analysis problem
  • Take a ratio metric whose denominator is not the randomization unit (clicks per session, randomized by user) and compute the standard error twice, once naively at session level and once by the delta method or a user-level bootstrap, then record how much the naive version understates it.
  • Implement CUPED on simulated data: choose a pre-period covariate X measured before assignment, estimate theta as Cov(Y, X) divided by Var(X), and analyse Y minus theta times (X minus its mean) in place of Y. Confirm the variance of the adjusted outcome equals the raw variance multiplied by one minus the squared correlation between Y and X, so a correlation of 0.45 removes about 20 percent of the variance and not 80.
  • Now run that simulation a few hundred times and confirm the adjusted effect estimate is unbiased for the same effect rather than numerically identical to the raw one. Within any single run the two differ, sometimes by a large fraction of the true effect, because the two arms' pre-period covariate means never coincide exactly in a finite sample; they agree in expectation, which is the property that matters and the one to state out loud.

Deliverable: A notebook showing the adjusted estimator with a measurably smaller variance than the raw one, plus a repeated-simulation table showing the two estimators agreeing on average while differing run by run.

Practice prompt ↗Practice prompt ↗Worked solution ↗
04Validity threats you can actually test for
  • Run a sample ratio mismatch check as a chi-square goodness-of-fit test against the intended allocation, and write the three causes you would chase first (assignment logged before exposure, an arm-specific redirect or load failure, bot filtering applied asymmetrically).
  • Simulate peeking: generate A/A data, test daily at alpha 0.05 across 14 looks, record the inflated false positive rate, then apply an alpha-spending boundary or commit to a fixed horizon and confirm the rate returns to nominal.
  • Write how you would separate a novelty effect from a durable lift using the treatment effect plotted against days since first exposure, and what shape would change your recommendation.

Deliverable: One table showing the peeking false positive rate before and after correction, plus a written SRM triage list.

Practice prompt ↗Practice prompt ↗
05When randomization is not available
  • Write the identifying assumption for difference-in-differences (parallel trends in the absence of treatment), then plot pre-period trends for two candidate control groups and justify rejecting one of them.
  • Design a switchback test for a change where user-level randomization would leak across participants, choosing a time-block length against the carryover you expect and saying how you would detect carryover in the data.
  • List what an interrupted time series or a synthetic control buys you and the one thing neither can rule out: an unobserved shock that coincides with the launch.

Deliverable: A one-page memo recommending a single quasi-experimental design and naming its weakest assumption explicitly.

Practice prompt ↗Practice prompt ↗Worked solution ↗
06The readout query
  • Write the assignment-to-exposure join that returns exactly one row per unit per experiment, and handle units appearing in both arms by excluding and counting them rather than silently keeping one.
  • Compute the per-arm metric, its variance and the relative lift with a confidence interval in SQL, then reproduce the identical numbers in a notebook as a cross-check.
  • Add a segment breakdown and write the sentence that keeps it from being p-hacking: segments declared in advance, everything else reported as exploratory and corrected for multiplicity.

Deliverable: A single query that outputs the full readout table, matched to a notebook recomputation.

Practice prompt ↗
07Present it to someone who will not read the appendix
  • Give a 10-minute readout of a real or simulated experiment in the order decision, number, uncertainty, caveat.
  • Have your listener ask "can we ship it" in the case where the primary is flat and a guardrail moved, and answer with a recommendation rather than a request for more data.
  • Rewrite your opening line so the recommendation lands before any methodology.

Deliverable: A one-page readout whose first line is the recommendation.

Practice prompt ↗Worked solution ↗

Expand any day for tasks and deliverables. Your progress is saved on this device.

An answer without a quantity is hard to interrogate, so interviewers keep probing until they find one. Come with the baseline, the change, the window it was measured over, and how confident you were. If the effect never got measured, say so and say what you would have measured. Fabricated precision is worse than an honest gap.

Announce a metric fix that cuts the headline number

medium
metric definitionsstakeholderscommunication

Weekly active organisations, the count on the company dashboard, has never excluded rows where dim_account.is_internal is true, and it counts traffic with traffic_class in synthetic_monitor and load_test. Correcting both reduces that count by 11 percent and removes most of the growth reported over two quarters. The figure appears in a board deck and in two teams' quarterly goals, one written on the count and one on the weekly active organisation ratio, whose denominator is accounts whose account_status was in ('trial','free','active_paid') through the week. Decide the order in which you tell people, what the dashboard shows during the transition, and what you propose happens to goals already set against the old definition.

Approach
  1. The interviewer is probing whether you can land a correction as an operational change with a plan attached, rather than as an announcement other people then have to clean up after.
  2. Quantify each exclusion separately before telling anyone: internal accounts, synthetic monitors, load tests. Three known quantities are a discussion; one alarming total is an argument.
  3. Be precise about which side of the metric each exclusion touches, because one team's goal is on a count and the other's is on a ratio. The traffic-class filters remove requests, so they shrink the numerator only. Dropping internal accounts removes them from the ratio's denominator as well, since internal accounts carry ordinary account_status values and therefore sit in that denominator. Internal accounts are active in almost every week while the real base is not, so the numerator loses a larger share than the denominator and the ratio falls by less than the count does. Compute both and say which one the 11 percent is before anybody assumes.
  4. Check whether the trend changes, not only the level. A constant 11 percent shift is a rebasing and nothing more. A shift that widens over time means the reported growth was partly internal or synthetic, which makes the existing goals unachievable as written and changes what you are asking teams to do.
  5. Sequence the disclosure: the metric owner and the two teams whose goals move first and privately, then the board channel with a written bridge, then the dashboard. The dashboard is last because a number that changes without explanation is read as instability rather than as a fix.
  6. Run both series for one reporting period with the bridge visible, restate history rather than letting the series break at a date, and set the date the old series is removed.
  7. Propose the goal treatment yourself: rebase each target by the shift measured on the metric that target is written against, rather than leaving each team to negotiate individually, which is where corrections of this kind usually die.
Follow-up
  • One team's quarterly goal is now unreachable. Rebase the target or let it miss, and what does each choice teach the organisation?
  • How would this have been caught when the metric was first defined?
  • What else on that dashboard shares this failure mode, and how would you find out this week?

Defend a churn number twelve times the one in the board deck

hard
stakeholder managementretention metricsdefinitions

You recompute logo churn on the renewal-eligible base from fct_subscription_period, counting only accounts whose term_end_date fell in the month and allowing a 45-day grace for late paperwork. Annualised, about 14 percent of accounts that reach a renewal date do not renew. A revenue leader has been quoting 1.2 percent to the board for two quarters, computed by dividing non-renewed accounts by the entire customer base in each month and printing that monthly figure with no period attached. You have 20 minutes with that leader and the finance lead. Decide which number is reported from now on, and what happens to the two quarters already published.

Approach
  1. The interviewer is probing whether you can hold a correct definition under social pressure without turning it into a competence dispute. Open by reproducing their 1.2 percent exactly, with their denominator and their months, so the disagreement is arithmetic both sides can see rather than a claim about who was careless.
  2. Separate the two defects, because they are different in kind. The denominator is wrong: on annual contracts only about one twelfth of the base reaches a renewal date in any month, so an account eleven months from renewal sits in the denominator while being structurally incapable of entering the numerator, which suppresses the rate by a factor near twelve. The period is merely unstated: a monthly figure printed beside annual revenue targets gets read as an annual rate.
  3. Say out loud that those two defects nearly cancel in the level, before the leader finds it. Twelve times 1.2 percent is about 14 percent, which is your number. That is the strongest thing you can say in the room, because it proves both figures rest on the same non-renewal count and moves the meeting onto which denominator and which period get published rather than onto whose query is right.
  4. The level is recoverable; the series is not. Non-renewals in a month are the eligible base for that month times the churn rate, so dividing by a fixed whole base makes the published line proportional to how many contracts happen to come up that month. Where signings cluster at quarter ends, the eligible base in a quarter-end month can be several times a quiet month's, and the month-over-month moves the board has been reading as satisfaction are the signing calendar.
  5. Separate the measurement change from a business change. Nothing got worse this week; the loss rate was always this. Bring net revenue retention over the same period as a ratio of sums on a cohort frozen twelve months earlier, because logo churn concentrated in small accounts can sit beside healthy revenue retention, and that combination is the actual story.
  6. Offer a migration path rather than a correction. Report both rates for one quarter with a written bridge, restate the prior two quarters in an appendix instead of silently, and pin the definition, including the period it is stated over, somewhere finance and product both read it. Concede the limits of your own number: the 45-day grace means the most recent 45 days are not reportable, and churn must be dated on term_end_date rather than on updated_at. A strong answer volunteers this; a generic one only defends.
Follow-up
  • The leader multiplies their monthly figure by twelve, lands on your annual number, and concludes nothing was ever wrong. What do you say?
  • The leader says publishing the corrected rate costs the team its credibility with the board this quarter. What do you do?
  • Gross logo retention worsened while net revenue retention improved. Which do you lead with, and what does the combination tell you about who is leaving?

Allocate one analyst week across three competing requests

easy
prioritisationstakeholdersscoping

Three requests arrive the same morning and you have one week. Finance wants per-account gross margin from fct_usage_daily for a pricing review in three weeks. Sales wants a renewal-risk list for accounts with term_end_date inside 60 days. A product manager wants an experiment readout for a decision being taken on Thursday. Produce your allocation with hours attached, what you say to whoever receives less, and one thing you refuse to do this week, with the reason each decision is defensible to the person it costs.

Approach
  1. The interviewer is probing whether you prioritise on decision timing and reversibility or on who asked most forcefully. Sort by the date each decision is actually taken and by what the default outcome is if nothing arrives.
  2. Apply that sort concretely. The Thursday readout has a hard irreversible deadline and no value afterwards. The pricing review has three weeks of slack. The renewal list has a rolling deadline set by term_end_date, so part of it is urgent this week and the rest is not, which means it can be split rather than deferred whole.
  3. Find the cheapest sufficient version of each request rather than the full version. The readout goes in full. The renewal list ships as a filtered query over renewal-eligible accounts ranked by two inspectable signals rather than as a model. The margin work is scoped to the accounts that dominate the pricing decision, since revenue is heavily skewed and the tail will not change the conclusion.
  4. Make the trade visible in one written note to all three at once, with dates. Telling each person separately that they are the priority is how an allocation becomes a credibility problem.
  5. Refuse something explicitly and say why. The model version of the renewal list is the usual candidate, because it cannot be evaluated without a holdout nobody has agreed to yet, and building it this week forecloses that.
  6. Leave slack. A plan with none is a plan to miss the one deadline that cannot move.
Follow-up
  • The sales leader escalates to your manager. What did you already do that makes that a short conversation?
  • Which of the three deadlines would you push back on, and what exactly would you ask for?
  • What would you change about how these requests reach you so next week is not the same?
  • 01

    Weekly active organisations, the count on the company dashboard, has never excluded rows where dim_account.is_internal is true, and it counts traffic with traffic_class in synthetic_monitor and load_test. Correcting both reduces that count by 11 percent and removes most of the growth reported over two quarters. The figure appears in a board deck and in two teams' quarterly goals, one written on the count and one on the weekly active organisation ratio, whose denominator is accounts whose account_status was in ('trial','free','active_paid') through the week. Decide the order in which you tell people, what the dashboard shows during the transition, and what you propose happens to goals already set against the old definition.

  • 02

    You recompute logo churn on the renewal-eligible base from fct_subscription_period, counting only accounts whose term_end_date fell in the month and allowing a 45-day grace for late paperwork. Annualised, about 14 percent of accounts that reach a renewal date do not renew. A revenue leader has been quoting 1.2 percent to the board for two quarters, computed by dividing non-renewed accounts by the entire customer base in each month and printing that monthly figure with no period attached. You have 20 minutes with that leader and the finance lead. Decide which number is reported from now on, and what happens to the two quarters already published.

  • 03

    Three requests arrive the same morning and you have one week. Finance wants per-account gross margin from fct_usage_daily for a pricing review in three weeks. Sales wants a renewal-risk list for accounts with term_end_date inside 60 days. A product manager wants an experiment readout for a decision being taken on Thursday. Produce your allocation with hours attached, what you say to whoever receives less, and one thing you refuse to do this week, with the reason each decision is defensible to the person it costs.

PracHub interview preparation framework
Are these confirmed Zeta interview questions?

No. Every prompt here is an original PracHub practice exercise written for the Data Scientist role and for B2B software and infrastructure data problems. This guide does not claim to reproduce Zeta's interview questions, rounds or hiring timeline. Confirm the actual format, team and scope with your recruiter.

PracHub Data Scientist practice
What if I think the interviewer's premise is wrong?

Test it before announcing it. Ask a clarifying question that separates the two readings, because the premise is often a deliberate simplification. If it really is wrong, say what you observed, why it changes the answer, and offer to proceed under either reading. Some cases plant a flawed premise on purpose and noticing it is the question being asked. Being right about it and graceless about it still costs you.

PracHub Data Scientist practice
How do I prepare for a case study with no data in it?

Practise estimation out loud. Take a quantity you cannot look up, decompose it into factors you can bound, state each assumption as you use it, and sanity-check the total against something you do know. The grading is on structure and on whether your assumptions are stated and defensible, not on the number. Get comfortable saying "call it ten million, and the conclusion holds even if I am off by a factor of two."

PracHub Data Scientist practice
How much SQL is enough for a data scientist interview?

Enough to write a correct multi-table query under time pressure with no reference open. That means joins including anti-joins, aggregation with HAVING, window functions such as ROW_NUMBER, LAG and running sums, date bucketing, and conditional aggregation with CASE inside SUM. Recursive CTEs and query tuning rarely decide a loop. The usual failure is not exotic syntax but silently changing the row count with a fan-out join.

PracHub Data Scientist practice
Sources & methodology 2 sources ↗

Official role evidence, timestamped platform data and clearly labeled preparation advice.