As a Data Scientist at Fanatics, you operate at the intersection of massive sports merchandise scale, consumer behavior, and advanced analytics. You drive data-informed strategies across a global sports platform that reimagines the fan experience spanning gear, trading cards, sports betting, content, and events. Your work directly influences product roadmaps, optimizes commerce supply chains, and shapes how millions of sports fans interact with their favorite teams.
Your primary impact lies in translating ambiguous business problems into rigorous, data-driven solutions. Whether you are building predictive machine learning models, designing large-scale experiments, or optimizing supply chain logistics, you work closely with cross-functional partners in operations, finance, marketing, and engineering. The environment is fast-paced, mobile-first, and data-obsessed, requiring you to balance out-of-the-box thinking with disciplined technical execution.
Success in this role demands both technical depth and business acumen. You will handle complex structured and unstructured datasets, shepherd models through their full lifecycle from discovery to deployment, and establish playbooks that drive consistent, measurable outcomes. Expect to be challenged by high-scale data challenges and empowered to influence strategic decisions at a late-stage industry leader.
Phone Screen
reportedMost candidates lose this call inside the first two minutes, during the walkthrough of their own background. The account runs chronologically, sits at the level of tools and titles, and never arrives at a decision anyone could have disagreed with. Anchor on a problem instead of a timeline: what the team could not answer, what you did about it, what happened next. Ninety seconds is enough, and stopping on time leaves room for the half of the call that belongs to you. What you ask about how work gets prioritised signals your level more reliably than the walkthrough does.
What to demonstrate
- Whether your background summary has a shape (problem, decision, consequence) or is a chronological list of tools and employers
- Whether you can account for gaps, short stints and the reason you are looking, unprompted and without hedging
- The substance of the questions you ask back, which an experienced screener reads as a level signal
How to prepare
- Time your opening walkthrough against a clock. If it runs past two minutes, compress the earliest role into a single clause and spend the recovered time on the most recent one
- Write one honest sentence for every gap or short stint visible on your resume and offer it before being asked about it
- Prepare questions about how work arrives and gets prioritised: who writes the request, how often priorities change, and what happens to an analysis after it is delivered
Technical Assessments
reportedA handful of shapes account for most of what gets asked in this format: a ranking or deduplication inside groups, a running or rolling total, a period-over-period comparison, and a cohort tracked forward over time. Recognising the shape quickly is most of the speed here; deriving it from scratch while a clock runs is where the time goes. Know that a window function keeps every row while a GROUP BY collapses them, and know which one the question needs. If the exercise is in Python instead of SQL, the same shapes arrive as groupby with transform, shift and merge, and the same grain mistakes are available.
What to demonstrate
- Whether you reach the right construct without a detour, such as ROW_NUMBER over a partition to deduplicate instead of a self-join against a MAX subquery
- Whether you know what your window frame actually is, since adding ORDER BY inside OVER changes the default frame and silently changes a running total
- Whether the thing runs. A near-miss that throws an error scores below a plainer query that returns the right rows.
How to prepare
- Write each of the four shapes once from memory against a small schema and keep the working version somewhere you will reread it: dedupe with ROW_NUMBER, a running total, a month-over-month change with LAG, and a retention table
- Compute one running total twice on data with tied timestamps, once on the default frame and once with ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and look at where the two disagree
- If Python is on the table, rebuild the dedupe and the running total with groupby and cumsum, then assert the two implementations return identical rows
Onsite Interviews
reportedWhere 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
PracHub editorial advice for the preparation topics above.
Judging merchandising and recommendation changes on the surface they touch
Click-through or attributed revenue on a recommendation slot rises whenever the slot shows items the customer was going to buy anyway, so the surface metric measures capture rather than creation, and the units almost always come from a different slot, a search result or a later visit. The correct read is site-wide net revenue per session over a holdout, adjusted for returns, because surfacing more apparel or more discounted stock reliably moves both the return rate and the discount depth in the wrong direction while the click metric improves.
Fitting demand models on sales when sales are censored by availability
Units sold equal the minimum of demand and what was sellable, so every day a SKU was out of stock contributes a zero that looks identical to genuine indifference. A model trained on that history forecasts the stockout, the buy shrinks, availability falls further, and the error compounds each cycle, which is why a declining SKU forecast should always be checked against minutes_unavailable before it is believed. The fixes are to restrict the fit to in-stock periods, to model availability explicitly as an exposure term, or to use a censored likelihood; all three require the availability history to be retained at a finer grain than a daily end-of-day snapshot, which is exactly what teams tend to discard.
Reporting a mean for a heavy-tailed metric without saying what it hides
For spend, session length or items per order, a small fraction of units carries most of the total, so the mean has a wide standard error and one account can move it. Fix the handling before you see the result: cap or winsorise at a pre-declared percentile, and report the median or the share above a threshold next to the mean. Capping changes the estimand, so say which question the capped number answers, and check how much of any difference comes from the top 0.1 percent of units.
Optimising accuracy on a heavily imbalanced target
State the base rate first, then choose the metric from the relative cost of a false positive against a false negative: precision and recall at the operating threshold, PR-AUC, or expected cost. At a 1 percent positive rate, predicting the majority class for everyone scores 99 percent accuracy and is worthless.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
What is the difference between parametric and non-parametric testing m…
What is the difference between parametric and non-parametric testing methods, and when would you apply each in an e-commerce setting?
Approach
- Write down the assumption the method needs before you use the method.
- Say what the estimate is of, and over what population it generalises.
- 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?
When would you choose a tree-based ensemble model over a deep learning…
When would you choose a tree-based ensemble model over a deep learning architecture for a supply chain optimization problem?
Approach
- Set a baseline first, so any model has something honest to beat.
- Pick an evaluation metric that matches the cost of each error type, not a default.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
Follow-up
- Where could label leakage enter this setup?
- What would you monitor after launch to know the model is still valid?
Sessionise a raw event stream with a thirty-minute inactivity rule
events has anonymous_id, customer_id (nullable, populated only after sign-in), event_ts_utc, event_type in {page_view, product_view, search, add_to_cart, checkout_start, order_placed}, order_id (non-null only on order_placed). Rows arrive unsorted and timestamps can tie. Build the session table: session_id, anonymous_id, customer_id (last non-null in the session), started_at_utc, ended_at_utc, counts per event type, and order_id. A gap of strictly more than 30 minutes from the previous event of the same anonymous_id opens a new session. Do not use groupby.apply or a Python loop over rows.
Approach
- Sort by (anonymous_id, event_ts_utc) with a stable tie-break on a secondary key such as event_id, so that tied timestamps produce a deterministic ordering and the result is reproducible across runs.
- Compute gap = groupby('anonymous_id')['event_ts_utc'].diff(). A new session starts where gap is null (first event for that visitor) or gap > 30 minutes; take the cumulative sum of that boolean to get a session ordinal, which is the vectorised equivalent of a gaps-and-islands query.
- Build session_id as the pair (anonymous_id, ordinal) rather than a global running integer, so re-running on a different date partition does not renumber existing sessions.
- Aggregate once with a single groupby on the session key: min and max of the timestamp, value_counts of event_type pivoted to columns, and order_id taken as the max of a column that is null everywhere except order_placed.
- Key on anonymous_id, not customer_id, because sign-in happens mid-session and keying on the identity would split one visit into a signed-out session and a signed-in one. Carry customer_id forward as the last non-null value instead.
Worked solution 30 min
- Shuffle the fixture deliberately before you start, so any dependence on input order shows up immediately.
- Sort, diff within anonymous_id, and materialise is_new_session = gap.isna() | (gap > Timedelta('30min')).
- session_ordinal = is_new_session.groupby(anonymous_id).cumsum(); confirm it starts at 1 for every visitor.
- Aggregate with a single groupby on (anonymous_id, session_ordinal), pivoting event_type counts with pd.crosstab or unstack.
- Test the boundary explicitly: two events exactly 1800 seconds apart must stay in one session under 'strictly more than'.
Follow-up
- Two visits 29 minutes apart are one session under this rule, and a visit resumed the next morning is two. What breaks if you instead cap sessions at a fixed wall-clock length?
- The same person browses on mobile web and then buys on the app. How would you stitch those, and what does the stitching do to a conversion-rate denominator?
- How do you handle events that arrive late, after the session containing them has already been written?
Given a user activity table, how would you write a query to calculate …
Given a user activity table, how would you write a query to calculate rolling 7-day active users using advanced SQL?
Approach
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- 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.
Follow-up
- How would you verify this result without re-running the same query?
- How does the query change if the join becomes one-to-many?
Write a query to identify churned customers based on purchase interval…
Write a query to identify churned customers based on purchase intervals and inactivity thresholds.
Approach
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- State the window function and its partition and ordering out loud before writing it.
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
Follow-up
- What breaks if events arrive late or out of order?
- How does the query change if the join becomes one-to-many?
Find consecutive stockout runs per SKU and node with gaps-and-islands
From fct_inventory_snapshot (snapshot_date, sku_id, node_id, available_to_promise_units, was_listed, minutes_unavailable, forecast_units), find maximal runs of consecutive calendar days over the last 90 days where was_listed = TRUE and available_to_promise_units = 0, for each SKU and node. Return sku_id, node_id, run_start, run_end, run_length_days and forecast_units_lost, the sum of forecast_units over the run, keeping only runs of three days or more. The table is not guaranteed to contain a row for every SKU-node-day, nor at most one; state how you treat a missing day and how you treat a duplicated one.
Approach
- Measure density and uniqueness before writing any window function. Per (sku_id, node_id), compare COUNT(), COUNT(DISTINCT snapshot_date) and the 90 days in the window. Rows fewer than distinct days means missing snapshots; COUNT() above COUNT(DISTINCT snapshot_date) means a SKU-node-day was written twice. They are different defects and they break different things.
- Filter to the stockout condition, then flag a new island with LAG: new_run = (snapshot_date - LAG(snapshot_date) OVER (PARTITION BY sku_id, node_id ORDER BY snapshot_date)) > 1 OR LAG IS NULL.
- Turn the flag into a group key with SUM(new_run::int) OVER (PARTITION BY sku_id, node_id ORDER BY snapshot_date ROWS UNBOUNDED PRECEDING).
- Know what the arithmetic key does before choosing between them. Over the same filtered rows, snapshot_date - ROW_NUMBER() * INTERVAL '1 day' yields identical islands to the LAG running sum whenever snapshot_date is distinct within the partition: any break in the date sequence, whether the day is missing or merely in stock, shifts the arithmetic key and trips the LAG flag alike. The two diverge only on duplicated dates, where ROW_NUMBER advances while the date stands still: the key steps back a day, splitting one run into two that overlap on the duplicated date, whereas the LAG form sees a zero-day gap, keeps one island, and inflates COUNT(*) and forecast_units_lost instead. DENSE_RANK() OVER (PARTITION BY sku_id, node_id ORDER BY snapshot_date) makes the arithmetic key duplicate-safe; collapsing to one row per SKU-node-day first is cleaner still.
- Decide and state the missing-day rule, because no island key decides it for you. Both forms end a run at a day with no row, which is the same as asserting the SKU was available that day. If a missing row instead means the snapshot job did not run, that day is unknown, and ending the run there shortens every incident that spans the outage.
- Make censoring visible rather than assuming it away. Left join a generated date spine and record, per run, whether the day before run_start and the day after run_end carry a row at all. A run bounded by an observed available day genuinely ended; a run bounded by an absent day may be a fragment of a longer one, and the two must not be reported as the same thing.
Worked solution 30 min
- Run the density and uniqueness checks; record how many (sku_id, node_id) pairs have fewer rows than days in the window, and how many carry any duplicated snapshot_date.
- Build the filtered CTE, add the LAG-based new_run flag and the running-sum island key.
- Aggregate to one row per island: MIN and MAX of snapshot_date, COUNT(DISTINCT snapshot_date), COUNT(*) and SUM(forecast_units).
- Add run_length_days = run_end - run_start + 1, confirm it equals the distinct-day count on every row, and treat any row where COUNT(*) exceeds it as a duplicated SKU-node-day rather than a longer run.
- Left join the date spine at run_start - 1 and run_end + 1 and carry a censored flag onto the output.
- Filter to run_length_days >= 3 and order by forecast_units_lost descending.
Follow-up
- A SKU was delisted mid-stockout, so was_listed flips to FALSE. Is the run over, and what does each choice do to the lost-demand number?
- How would you extend this to partial-day unavailability using minutes_unavailable rather than the binary zero-stock test?
- The same SKU is out at one node and in stock at another. Does that count as a stockout for the customer?
Suppose we want to introduce a personalized fan feed on our platform. …
Suppose we want to introduce a personalized fan feed on our platform. What core product metrics would define its success?
Approach
- Restate the decision this analysis has to support, and who acts on the answer.
- Name one primary metric, then the guardrail that stops it being gamed.
- 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?
What metrics would you track to evaluate the success of a newly launch…
What metrics would you track to evaluate the success of a newly launched mobile-first checkout feature?
Approach
- 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.
- Decompose the metric into the rates that drive it, and say which one you would check first.
Follow-up
- What would you do if the primary metric and the guardrail moved in opposite directions?
- How would you detect that the metric is being gamed rather than genuinely improving?
How do you design a holistic health scorecard for an e-commerce supply…
How do you design a holistic health scorecard for an e-commerce supply chain optimization project?
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- 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.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- What would you do if the primary metric and the guardrail moved in opposite directions?
How would you determine whether a drop in user engagement on our mobil…
How would you determine whether a drop in user engagement on our mobile app is driven by seasonality or a product bug?
Approach
- Name one primary metric, then the guardrail that stops it being gamed.
- Fix the population and the time window before naming any metric.
- 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?
When is it appropriate to use multi-armed bandit designs instead of tr…
When is it appropriate to use multi-armed bandit designs instead of traditional A/B testing?
Approach
- Say whether units interfere with each other, and switch design if they do.
- Name the guardrails that would stop a launch even on a positive primary result.
- Name the randomisation unit first; it decides the variance and what the test can detect.
Follow-up
- What would you conclude if the result is positive but the test is underpowered?
- How would you handle interference between treated and control units?
How do you establish statistical significance when your key metrics ex…
How do you establish statistical significance when your key metrics exhibit high day-of-week seasonality during football season?
Approach
- Name the guardrails that would stop a launch even on a positive primary result.
- Say whether units interfere with each other, and switch design if they do.
- Name the randomisation unit first; it decides the variance and what the test can detect.
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 would you design a recommendation engine for sports memorabilia an…
How would you design a recommendation engine for sports memorabilia and licensed gear to maximize click-through rate?
Approach
- State your assumptions explicitly before working the problem.
- Work from the decision backwards to the evidence you would need.
- Clarify what is being asked and what a complete answer would contain.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Make on-time-in-full honest when the team sets the promise
Fulfilment is measured on on-time-in-full: orders where every line has delivered_at_utc at or before promised_delivery_at_utc and no line is cancelled_out_of_stock or undeliverable. The same team sets promised_delivery_at_utc. The rate rose from 91 to 97 percent over a quarter with no operational change reported. Using fct_order_line and fct_session, design the metric set that makes this number honest. Deliverable: the primary, the guardrail that closes the gaming route, and the customer-side metric neither of those captures.
Approach
- Name the loophole. promised_delivery_at_utc is an input controlled by the team being measured, so the rate can be driven to any level by padding the promise. A metric whose own target is set by the measured party is an agreement, not a measurement.
- Keep on-time-in-full as the primary, computed at order grain rather than line grain, because a customer receiving three of four items has not had an order delivered in full. But pin the promise: publish median and 90th-percentile promised horizon (promised_delivery_at_utc minus placed_at_utc) by node and destination zone as a guardrail with its own target that cannot move without sign-off.
- Add the absolute measure the pair still misses: actual click-to-door time, the distribution of delivered_at_utc minus placed_at_utc at p50 and p90. A padded promise and a slower network produce the same on-time rate with opposite customer experiences, and only this metric separates them.
- Quantify how much of the quarter's gain was padding. Recompute the end-of-quarter rate against the start-of-quarter promise distribution, matched on node and destination zone. The gap between actual and fixed-promise rate is the padding contribution, stated as points rather than described.
- Audit the denominator. Orders with lines still in non-terminal states are excluded, so a growing backlog of stuck orders improves the published rate silently. Report the count and age distribution of excluded orders next to it.
- Bring in the demand side that fulfilment metrics cannot see: conversion rate and revenue per session by promised-horizon bucket, from fct_session joined to orders, controlled for device_type and entry_channel. Label it observational, because promise horizon correlates with node coverage and SKU mix, so the gradient is an association until a promise test is run.
Worked solution 35 min
- Compute on-time-in-full at order grain by week for the quarter, requiring every line terminal and none cancelled_out_of_stock or undeliverable, and record the count of orders excluded for non-terminal lines.
- Compute the promised horizon distribution at p50 and p90 by week, fulfilment node and destination zone.
- Compute click-to-door at p50 and p90 on the same cut.
- Recompute the end-of-quarter rate against the start-of-quarter promise horizon, matched on node and destination zone, and report the difference as the padding contribution in points.
- Join fct_session to orders and estimate conversion and revenue per session by promised-horizon bucket, controlling for device_type and entry_channel, labelled as observational.
Follow-up
- On-time-in-full is 97 percent, p90 click-to-door is unchanged, promised horizon is up two days. What happened, and what do you tell the team?
- Conversion falls 1.5 percent when the promise moves from two days to four. How do you turn that into a target for promise horizon?
- Would you split the metric into on-time and in-full? What do you gain and what do you lose?
App conversion fell overnight while orders held flat
Visit-to-order conversion on ios_app fell from 3.1% to 2.4% between Tuesday and Wednesday; desktop, mobile_web and android_app are flat. You have fct_session (session_id, customer_id, order_id, device_type, entry_channel, campaign_id, landing_path, tracking_consent, is_bot_flagged, product_views, add_to_cart_events, checkout_starts) and fct_order_line (order_id, channel, placed_at_utc, quantity). No app build shipped that day, but a consent banner change went out across all surfaces on Tuesday evening. Deliverable: one page splitting the 0.7 point drop into real demand and denominator composition, with the evidence for each.
Approach
- Check the numerator outside the session table first: count distinct order_id in fct_order_line where channel = 'ios' per day. If orders are flat, no demand was lost and the whole question is about the denominator or the stitching between the two tables.
- Profile the denominator by composition, not size: sessions per day on ios_app split by tracking_consent and by is_bot_flagged. A session with tracking_consent = 'denied' can still produce an order, but it can never be joined to one, so it is a guaranteed zero in the numerator and must be excluded from both sides.
- Recompute the rate with denied-consent and bot-flagged sessions removed from numerator and denominator, and report the denied share as its own series so the exclusion is visible rather than hidden.
- Walk the in-session funnel on the cleaned population: product_views per session, add_to_cart_events per session, checkout_starts per session, orders per checkout_start. A break confined to the last ratio points at checkout or payment; a uniform sag across all four points at traffic quality or mix.
- Cut the residual by entry_channel, campaign_id and landing_path to separate a single campaign or deep-link path from a surface-wide change, then state the remaining real effect with its size.
Follow-up
- The denied-consent sessions still buy. How would you estimate the orders they placed, and would you put that estimate in the published conversion rate or beside it?
- If orders had also fallen but only on one entry_channel, what would you check before blaming the app?
- How do you keep this class of break from reading as a business result next time: what monitor would have fired on Tuesday evening?
For someone who can already write the query and train the model but stalls when asked what to measure or whether a change is worth making. Metric definition and case structure come first; the technical work is kept as maintenance rather than the centre of the week.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Metric anatomy
- For three products you use daily, write one primary metric, two input metrics that plausibly move it, and one guardrail that would catch a cheap way of moving the primary at the cost of the product.
- For one of them, specify the metric precisely enough that two analysts would return the same number: numerator, denominator, unit of observation, time window, and how returning and deleted accounts are treated.
- Pick a ratio metric and write what happens to it when the denominator shrinks for reasons unrelated to the numerator, with a concrete example of that happening.
Deliverable: A one-page metric tree for one product, with the primary metric written as an unambiguous spec.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Diagnosing a drop without guessing
- Take the prompt "weekly active users fell 8 percent week over week" and write the segmentation plan before proposing any cause: platform, region, tenure cohort, acquisition channel, and whether the movement sits in the numerator or in a changed denominator.
- List the instrumentation failures that manufacture fake drops (a client release that stopped firing an event, a bot filter change, a shifted date boundary or timezone) and write the query that rules out each one.
- Rehearse stating the boring explanations first, seasonality and day-of-week composition, before reaching for a product cause.
Deliverable: A drop-diagnosis checklist short enough to recite from memory in under a minute.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Should we build it
- Take a feature idea and write it as a bet: what you believe is true, what would have to be true for it to pay off, the metric that would confirm it, and the effect size that would justify the engineering cost.
- Size the opportunity top-down and bottom-up, then reconcile the two numbers in writing instead of quoting whichever is friendlier.
- Write the counter-metric that would make you kill the feature even if it wins on the primary metric.
Deliverable: A one-page product memo ending in a decision rather than a list of considerations.
Practice prompt ↗Practice prompt ↗Practice prompt ↗04The places aggregate numbers lie
- Construct a Simpson's paradox numerically: two segments where the treatment wins within each segment yet loses overall, and identify the shift in segment weights that causes it.
- Take a heavy right-tailed quantity such as revenue per user and write why the mean is the wrong summary, which percentile you would report instead, and what a moving mean with a stable median tells you.
- Write your definition of a session for the product from day one, then name two real behaviours it misclassifies.
Deliverable: One page holding a worked Simpson's paradox table and a session definition with its two known failure cases.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗05Technical maintenance, aimed at metrics
- Solve four timed SQL prompts that all end in a ratio metric, so the question of grain stays live in every answer.
- Compute a 95 percent confidence interval for a proportion on a small sample, and state why the normal approximation is unreliable when either np or n(1 minus p) falls below roughly 10, along with which interval you would use instead.
- Take one metric from your day-one tree, write the query that computes it correctly, then write the query that computes it wrong in the most plausible way and explain how you would notice.
Deliverable: Four solved prompts plus a matched correct and plausible-wrong query for one metric.
Practice prompt ↗Practice prompt ↗06Turning engineering work into data science stories
- Write three project stories as situation, decision, trade-off, outcome, each carrying one number and one thing you got wrong.
- For the story you will lead with, prepare an answer to "what would you do differently" that names a decision you made, not a constraint you were handed.
- Practise the sentence that reframes a systems project as a question project: the question the work answered, ahead of the pipeline it shipped.
Deliverable: Three written stories with the lead story delivered aloud and timed under four minutes.
Practice prompt ↗Practice prompt ↗07Mock case and gap list
- Run a 40-minute mock case with someone playing a product manager who pushes back on your metric choice, and record it.
- Listen back and mark every moment you proposed a solution before the success metric existed.
- Rewrite those moments as the question you should have asked, and rehearse the first 90 seconds of the case until scoping comes before solving.
Deliverable: A recorded case plus a rewritten opening 90 seconds.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Half of this section is about translation. Be ready to describe how you explained a result to someone who did not want the method, only the implication, and what you did when the simplified version started being repeated in a way that overstated it. Correcting your own simplification is a strong beat.
How do you handle multiple testing corrections when evaluating hundred…
How do you handle multiple testing corrections when evaluating hundreds of metrics simultaneously in a large product experiment?
Approach
- State the situation in two sentences and spend the rest on your reasoning.
- Quantify the outcome, including what you would not claim credit for.
- Pick a story where you drove the decision, not one where you observed it.
Follow-up
- How did you know the outcome was caused by your change?
- What did you decide not to do, and why?
Scope a one-line request for our best customers
A merchandising director messages you: can you pull our best customers. There is no other context. You have dim_customer, fct_order_line and fct_return_line. Best could mean highest net spend, highest contribution margin, most frequent, most recent, lowest return rate or highest expected future value, and the resulting lists differ enormously. You get one clarifying exchange before the director is in meetings for the rest of the day. Write what you send back, and describe how you proceed if no reply comes.
Approach
- Ask about the decision, not the definition. What are you going to do with the list narrows six candidate metrics faster than asking which of six metrics they meant.
- Put a stated default in the same message so a non-reply is still progress: trailing 365 days, customer grain, net of returns, and say so in one clause.
- Make the ambiguity concrete with a measured number rather than a menu. One line reporting the overlap between the top decile by net revenue and the top decile by contribution margin forces the choice without a meeting.
- Name the filters that depend on the use rather than on the metric. A mailing list needs email_consent = TRUE and excludes account_status in ('closed','fraud_blocked'); a buy-planning read wants neither filter.
- If nothing comes back, ship the default with the definition, window and return treatment written at the top of the output, and attach the alternative cut so the conversation continues on an artefact.
Follow-up
- The director replies that it is for a loyalty upgrade offer. What changes in the query?
- How do you stop this becoming six different best-customer lists across the company?
Choose between three teams asking for the same week
Three requests land on the same Monday. Merchandising wants a size-curve read before a buy deadline on Thursday. Growth wants a channel attribution rebuild that has been requested twice and dropped twice. Supply chain wants a stockout root-cause on a category that cancelled four thousand units last month. You have one week and no help, and each requester believes theirs is first. State what you do, in what order, and what you tell the two people who do not get the week.
Approach
- Sort by the decision behind each request rather than by who sent it. A buy deadline is an irreversible commitment with a fixed date; an attribution rebuild changes no decision this week.
- Ask each requester two questions: which decision changes, and what happens if the answer lands a week later. Those two separate a real deadline from felt urgency without arguing about either.
- Look for the cheap partial before assuming any request consumes the week. If the size curve already exists at style grain, the merchandising read may be two hours rather than four days.
- Decline explicitly with a start date attached instead of leaving a request in a silent queue. Growth has been dropped twice, so a third silent drop is a relationship cost you are choosing to pay; name it rather than incur it by default.
- Escalate the collision upward once, with the three decisions and their dates side by side, so the tradeoff is resolved where it is owned rather than by whoever follows up hardest.
Follow-up
- Growth escalates to your manager saying analytics never supports them. What did you do before that happened, and what do you do now?
- The buy deadline moves to Tuesday. What do you cut from the size-curve read, and what do you refuse to cut?
- 01
How do you handle multiple testing corrections when evaluating hundreds of metrics simultaneously in a large product experiment?
- 02
A merchandising director messages you: can you pull our best customers. There is no other context. You have dim_customer, fct_order_line and fct_return_line. Best could mean highest net spend, highest contribution margin, most frequent, most recent, lowest return rate or highest expected future value, and the resulting lists differ enormously. You get one clarifying exchange before the director is in meetings for the rest of the day. Write what you send back, and describe how you proceed if no reply comes.
- 03
Three requests land on the same Monday. Merchandising wants a size-curve read before a buy deadline on Thursday. Growth wants a channel attribution rebuild that has been requested twice and dropped twice. Supply chain wants a stockout root-cause on a category that cancelled four thousand units last month. You have one week and no help, and each requester believes theirs is first. State what you do, in what order, and what you tell the two people who do not get the week.
Is this an official Fanatics interview guide?
No. It is PracHub's own research and practice material for the Data Scientist role at Fanatics. Rounds and questions reflect what candidates have reported, not a process Fanatics has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult is the interview loop at Fanatics, and how much preparation time is recommended?
The interview process is rigorous and evaluates both technical depth and product intuition. Candidates typically benefit from 4 to 6 weeks of dedicated preparation, focusing heavily on advanced SQL, A/B testing principles, and system design case studies.
PracHub interview research ↗What differentiates an average candidate from a top-tier candidate during the onsite loop?
Top-tier candidates stand out by constantly connecting technical solutions back to business impact. Instead of just writing a working SQL query or building a model, they proactively discuss edge cases, infrastructure trade-offs, and how their solution moves key product metrics.
PracHub interview research ↗How does Fanatics evaluate culture fit during the interview process?
Culture evaluation centers around your ability to collaborate in a fast-moving, cross-functional team environment. Interviewers look for a passion for sports culture, humility, intellectual curiosity, and a demonstrated bias for action when faced with ambiguity.
PracHub interview research ↗What is the typical timeline from the initial recruiter screen to a final offer?
The entire process generally spans 3 to 5 weeks from the initial recruiter chat through the technical screens and final onsite loop, depending on scheduling availability and team urgency.
PracHub interview research ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-22 - 02PracHub Data Scientist practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-22 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-22