As a Data Scientist at the Federal Reserve System, you operate at the intersection of economic policy, financial oversight, and advanced computational research. You are not merely building models; you are providing the analytical foundation that informs critical decisions affecting the national economy. Whether working within the AI Program Office or specialized divisions like DCCA, your work directly influences how the Federal Reserve System processes data at scale to ensure stability and transparency.
The role involves navigating complex, large-scale datasets that are unique to the central banking environment. You will collaborate with economists, IT architects, and policy experts to translate ambiguous, high-stakes business problems into actionable data products. This position is ideal for candidates who thrive on intellectual rigor and wish to apply sophisticated machine learning and statistical techniques to problems of national significance.
The Federal Reserve System values candidates who can bridge the gap between complex technical methodology and clear, policy-relevant communication.
Preparation focus
editorialNo round sequence has been reported for this company, so work the categories below and confirm the format with your recruiter.
What to demonstrate
- Breadth across SQL, experimentation and product reasoning
- Ability to state assumptions before choosing a method
How to prepare
- Drill the practice exercises below and time yourself
- Prepare three quantified stories about decisions you drove
PracHub editorial advice for the preparation topics above.
Assuming record-linkage error is random noise that averages out
False non-matches concentrate among people with name changes, transliterated or hyphenated names, unstable addresses and no durable identifier, which are the same people a disparity analysis is usually about. The linked cohort is therefore systematically more stable than the population, and any disparity estimate computed on it is biased toward finding no disparity. Carry link_method and link_confidence into the analysis, test whether results hold as the confidence threshold moves, and report the match rate by subgroup as a diagnostic rather than a footnote.
Reporting mean or median days to decision over cases that have already been decided
Cases still open at the extract are exactly the slow ones, so restricting to decided cases is length-biased. The pathology is that the metric improves as the backlog of hard cases grows, because those cases are excluded from the sample precisely while they are getting worse. Treat undecided cases as right-censored at (extract_date - submitted_at) and use a Kaplan-Meier estimate, or report the cohort completion curve at fixed horizons such as share decided by day 30 and day 90.
Comparing periods without accounting for seasonality or day-of-week
Compare whole weeks against whole weeks and check whether the same swing appeared in prior cycles or prior years before attributing it to anything you changed. Weekday and weekend populations often differ enough that a Tuesday-to-Saturday comparison is meaningless.
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.
Explain the trade-offs between different machine learning models for a…
Explain the trade-offs between different machine learning models for a classification problem.
Approach
- Say how the offline result would be validated online before it is trusted.
- 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
- What would you monitor after launch to know the model is still valid?
- How would you choose the decision threshold, and who owns that choice?
What statistical methods do you use to validate your model’s performan…
What statistical methods do you use to validate your model’s performance?
Approach
- Say how the offline result would be validated online before it is trusted.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Set a baseline first, so any model has something honest to beat.
Follow-up
- What would you monitor after launch to know the model is still valid?
- Where could label leakage enter this setup?
Point-in-time join across a type 2 dimension and boundary vintages
Three frames: applications (application_id, constituent_id, submitted_at); constituent_scd2 (constituent_id, effective_from, effective_to nullable, residence_geo_id, preferred_language_code, link_confidence); geography (geo_id, geo_code, geo_level, vintage_year, effective_from, effective_to, deprivation_quartile). geo_id is unique to a geo_code and vintage pair. Attach to each application the constituent row in force at submitted_at and the geography row whose vintage window contains submitted_at. Do not use pandas.merge_asof. Return the enriched frame, unmatched counts broken out by reason, and the count of applications whose quartile differs from a naive join on the current vintage.
Approach
- Do the interval join as a key merge plus a filter: merge on constituent_id, then keep rows where effective_from <= submitted_at and (effective_to is NaT or submitted_at < effective_to). Check the intermediate row count first, because a constituent with many versions fans out before the filter runs.
- Assert one surviving match per application. Two means overlapping effective ranges in the dimension, and taking the first hides a source bug that resurfaces later as duplicated facts in every downstream aggregate.
- Re-vintage the geography rather than trusting the foreign key. The matched residence_geo_id names a geo_code plus a vintage, so read off its geo_code and run a second interval join on geo_code against geography's own validity window. A single join on geo_id skips this step entirely.
- Keep both joins as left joins, then classify the unmatched: constituent absent from the dimension, submitted before the earliest effective_from, or geo_code not present in the vintage covering that date. Each has a different owner and a different fix.
- Fix one interval convention, half open [from, to), and use it in both joins so a record dated exactly on a boundary matches exactly one row rather than zero or two.
- Quantify the alternative: join the current-vintage geography instead and count applications whose deprivation_quartile changes. That count is the number of historical records a naive join would reassign.
Worked solution 35 min
- Merge applications to constituent_scd2 on constituent_id, filter with the half-open interval test, and assert the result groups to at most one row per application_id.
- Join the matched residence_geo_id to geography to recover geo_code, then merge on geo_code and apply the same interval test against the vintage window.
- Left merge the surviving attributes back onto the full applications frame so no row is lost, and label every NaN with a reason code.
- Build the naive comparison by merging geography filtered to the latest vintage_year on geo_code, and count quartile disagreements.
- Return the enriched frame, a reason-code tally, and the disagreement count.
Follow-up
- merge_asof with direction='backward' runs on this data and returns a row for nearly everything. What does it get wrong, and on which records?
- The same job has to run over 40 million applications and the key merge exhausts memory. What is your plan, and which correctness property must survive it?
- link_confidence is below 0.9 for 8 percent of matched constituents. How does that change what you are willing to publish from this frame, particularly a subgroup comparison?
Point-in-time join across slowly changing dimension and boundary vintage
fact_application has application_id, constituent_id and submitted_at. dim_constituent is type 2 with constituent_id, residence_geo_id, effective_from and effective_to (NULL marks the current row). dim_geography has geo_id as the primary key, unique to one geography and one boundary vintage, plus geo_code, vintage_year, effective_from, effective_to and deprivation_quartile; the same geo_code refers to different physical areas in different vintages. Count applications submitted between 2021 and 2025 by submission year and by the deprivation quartile of the residence as it stood on submitted_at. Exactly one output row per application must feed the count.
Approach
- Join dim_constituent with a range predicate, submitted_at >= effective_from AND (effective_to IS NULL OR submitted_at < effective_to), using a half-open interval so an application submitted exactly at a version boundary matches one version and not two.
- Join dim_geography on residence_geo_id, not on geo_code: geo_id is already unique to the geography-and-vintage pair, so it carries the vintage for free, while geo_code multiplies each application by the number of vintages that code has existed in and reassigns records across a redraw.
- Assert the grain rather than assuming it, by counting rows before and after each join; a type 2 dimension joined on the natural key alone produces one row per version of the person and the inflation is largest for constituents who moved, which is exactly the population a quartile breakdown is about.
- If the dimension has overlapping ranges from a bad load, deduplicate with ROW_NUMBER() OVER (PARTITION BY application_id ORDER BY effective_from DESC) and keep rn = 1, but record how many applications needed it instead of hiding the repair.
- Report the count of applications that failed to match any version as its own line, because a point-in-time join that silently drops pre-history applications understates the earliest years and produces a fake upward trend.
Follow-up
- A boundary redraw happened in 2023. What exactly goes wrong in a year-over-year quartile comparison if you attach current-vintage geography to every year, and which direction does it bias?
- How would you present a 2021-to-2025 trend when the quartile definitions themselves were re-derived within each vintage?
- Some applications match no constituent version because the identity was resolved after submission. Include them, exclude them, or report separately?
Award value from signed actions, ceilings and outlays
fact_obligation holds one row per funding action: obligation_action_id, award_id, action_type, obligated_delta_cents (signed, negative for deobligation and termination), ceiling_amount_cents, outlay_to_date_cents, action_at, fiscal_year, snapshot_date, is_current_snapshot. Within a single snapshot, ceiling_amount_cents and outlay_to_date_cents are award-level values repeated on every action row of that award; obligated_delta_cents is per action. Return one row per award_id active in fiscal_year 2026 with net obligated, current ceiling, outlay to date, and outlay divided by net obligated. Drop awards whose net obligated is not positive.
Approach
- Filter is_current_snapshot = TRUE before any aggregation; the table is a stack of as-of views, so an unfiltered SUM adds every historical snapshot of the same award together and inflates the total by roughly the number of snapshots retained.
- Select the award population with a semi-join on fiscal_year: inside the current snapshot take the DISTINCT award_id values having at least one action row with fiscal_year = 2026, then aggregate against that set. Skipping this step is not a narrower answer, it is a different one — the query returns every award in the snapshot, including awards whose last action was years earlier.
- Do not push fiscal_year = 2026 into the aggregation itself. That sums only fiscal-2026 deltas while ceiling_amount_cents and outlay_to_date_cents stay award-to-date values, so the ratio divides lifetime cash by one year of obligations and runs above 1.0 on every multi-year award. Scope is per award; net obligated, ceiling and outlay are all award-to-date.
- Sum obligated_delta_cents, which is the only column where summation is correct, because a modification or deobligation is an increment against the award and not a restatement of it.
- Take ceiling_amount_cents and outlay_to_date_cents with MAX (or from the latest action_at) rather than SUM: they are repeated award-level facts within the snapshot, so summing multiplies them by the action count.
- Guard the ratio with NULLIF on the denominator, then read a ratio above 1.0 as a finding rather than a number to report, since cash out cannot exceed money committed under normal accounting.
- State in the output or the header that ceiling is not money committed, so nobody downstream sums the ceiling column as a spending figure.
Worked solution 20 min
- Count rows per award_id without the snapshot filter, then with it, and confirm the ratio between them is the snapshot retention depth.
- Build the in-scope set: SELECT DISTINCT award_id FROM fact_obligation WHERE is_current_snapshot AND fiscal_year = 2026. Record its size next to COUNT(DISTINCT award_id) over the whole current snapshot so the scope reduction is a number you can defend, not an assumption.
- Aggregate the current-snapshot rows of those awards only — join the in-scope set on award_id and do not repeat the fiscal_year predicate here — with SUM(obligated_delta_cents) AS net_obligated_cents, MAX(ceiling_amount_cents) AS ceiling_cents, MAX(outlay_to_date_cents) AS outlay_cents grouped by award_id.
- Apply HAVING SUM(obligated_delta_cents) > 0 so fully deobligated and terminated awards drop out.
- Compute outlay_cents::numeric / NULLIF(net_obligated_cents, 0) and sort descending to surface ratios above 1.0 for inspection.
- Spot-check one award by listing its individual actions and adding the deltas by hand.
Follow-up
- An award has actions in both fiscal 2025 and 2026. What does 'net obligated for fiscal 2026' mean, and which of the two plausible definitions would you ship?
- Outlay-to-obligation ratios cluster near zero for awards signed in the last quarter. Is that a data problem?
- How would you detect an award that was terminated, given only action_type and the signed delta?
Separate obligations, outlays and ceilings in a funding execution metric
fact_obligation holds one row per funding action with obligation_action_id, award_id, action_type, obligated_delta_cents (signed), ceiling_amount_cents, outlay_to_date_cents, action_at, fiscal_year, fiscal_quarter, program_code, competition_type, period_of_performance_start, snapshot_date and is_current_snapshot. A proposed execution rate divides obligations by ceiling. Define the execution metric you would report instead, write the aggregation rule that avoids double counting across snapshots and across actions within an award, and give the guardrails that catch obligating money late in the fiscal year to avoid a lapse rather than to deliver anything.
Approach
- Reject the proposed ratio on a definitional ground: ceiling_amount_cents is the maximum an award may reach, not money committed, so obligations over ceiling measures how much of an optional headroom has been used and rises whenever ceilings are set tightly. It is not an execution rate.
- Define execution as outlays against obligations on a fixed obligation vintage. Take awards whose obligating actions fall in fiscal year t, then measure cumulative outlay at t plus 12 and t plus 24 months. Cash lags obligation by months to years, so an execution rate measured inside the same fiscal year is mechanically near zero and says nothing about delivery.
- Write the aggregation rule in two parts because the table has two distinct double-counting traps. First, the table is a series of as-of views, so an unfiltered query sums the same action once for every snapshot that contains it: filter to one snapshot_date or is_current_snapshot = TRUE. Note that this inflation factor is not uniform, since an action is absent from every snapshot taken before it was recorded, so it cannot be divided out after the fact. Second, obligated_delta_cents is per action and must be summed across actions within award_id, with deobligations and terminations entering as negatives, while outlay_to_date_cents is a cumulative as-of value carried on the award and repeats across that award's action rows, so summing it across actions multiplies it by the action count.
- Add the fiscal-timing guardrail: share of the fiscal year's obligated_delta_cents with action_at in the final 30 days, compared against the 1 in 12 share that an even distribution would give. A concentration there is the signature of obligating to avoid a lapse rather than to deliver.
- Add two guardrails that catch what year-end haste costs. Deobligation and termination rate within 12 months of the original award, which catches money committed to work that was never viable, and the competition_type mix, because the fastest way to obligate quickly is to move volume from full_and_open toward sole_source.
Follow-up
- An award is modified upward in fiscal year t plus 1. Which vintage does the new money belong to for the execution metric, and what does your choice do to the denominator of the original vintage?
- Outlay data itself arrives with reporting lag. How would you tell a genuine execution problem apart from a reporting lag in the outlay feed?
Design an intake completion metric that survives a channel mix shift
Intake runs through six channels in fact_application.channel: online, phone, mail, in_person, kiosk and partner_org. Only online and kiosk write a row at created_at before submitted_at is populated. The rest appear only once submitted, so submitted_at is never NULL for them. Leadership wants one application completion rate on the weekly dashboard. Define the metric, state which channels it is valid for, propose what you report for the others, and show how you stop a shift in channel mix from moving the pooled number on its own.
Approach
- Define completion on the cohort that can express the event: applications with created_at in the week and submitted_at NOT NULL within 30 days of created_at, over all applications with created_at in that week including those still in status 'draft' or 'abandoned'. Evaluate the cohort 30 days after it closes so every row has had the full window.
- Rule the metric out for mail, phone, in_person and partner_org. Those channels have no observable draft state, so the numerator equals the denominator and they report a structural 100 percent that will drag the pooled figure toward a constant.
- Give the offline channels a different metric that their data can support, such as the share of submitted applications that reach a decision without an evidence request, or the share returned as incomplete at intake. Say clearly that it is a different quantity and cannot be added to the online number.
- Show that a pooled rate over mixed channels moves with mix alone. Publish per-channel rates as the primary display and, if a single headline is required, a fixed-weight standardised total using a frozen base-period channel mix so that the headline moves only when a channel's own rate moves.
- Name the base period and the rule for refreshing the weights, because a standardised series with silently updated weights is the same problem wearing a different label.
Worked solution 20 min
- Write the cohort definition with the exact 30-day evaluation lag and the inclusion of draft and abandoned rows in the denominator.
- List the six channels and mark each valid or invalid for this metric, with the reason stated as a data fact rather than a preference.
- Construct a two-channel numerical example where each channel's rate falls but the pooled rate rises, to demonstrate the mix effect rather than assert it.
- Recompute that example with fixed base-period weights and confirm the standardised figure falls.
- Write the dashboard specification: per-channel rates as the primary panel, standardised total as a single secondary line, with the base period printed next to it.
Follow-up
- A new kiosk deployment doubles kiosk volume in one district. What does the standardised headline do, and what should the district-level report show instead?
- How would you detect that partner_org submissions are being drafted offline and keyed in as complete, which would hide abandonment entirely?
Quarterly obligations double in an as-of snapshot table
Obligated dollars for one program appear to double in a fiscal quarter. fact_obligation holds one row per funding action per snapshot: obligation_action_id, award_id, action_type, obligated_delta_cents, ceiling_amount_cents, outlay_to_date_cents, fiscal_year, fiscal_quarter, snapshot_date, is_current_snapshot. Establish whether obligations actually rose, produce the corrected quarterly series, and name the comparison period you would publish it against.
Approach
- Check snapshot hygiene before any aggregation. Count distinct snapshot_date per obligation_action_id: if actions repeat across snapshots, an unfiltered SUM counts the same action once per snapshot. Restrict to is_current_snapshot = TRUE, or to a single chosen snapshot_date, and re-run.
- Sum the signed obligated_delta_cents across all action types rather than filtering to action_type = 'new_award'. Deobligations and terminations are negative rows, so dropping them inflates every period; award value is only correct when summed across all actions grouped by award_id.
- Confirm no one has substituted a different money column. ceiling_amount_cents is a maximum the award may reach and is not money committed; outlay_to_date_cents is cumulative cash as of the snapshot and lags obligation by months to years, so differencing it across snapshots gives a flow while summing it gives nonsense.
- Choose the comparison period on the basis of how the appropriation behaves. Where funds lapse at fiscal year end, obligations concentrate in the closing weeks, so quarter over quarter compares a peak against a trough; compare the same fiscal quarter across years instead, and say so in the note.
- If the rise survives all of that, decompose by award_id, action_type and competition_type. A single option exercise or one multi-year award booked in full is a different finding from broad growth, and the two should never be reported with the same sentence.
Follow-up
- Obligations rose and outlays did not. What are the benign explanations, and which one would you test first?
- How would you publish a series that is stable when a prior quarter is restated in a later snapshot?
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 ↗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 ↗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 ↗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 ↗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 ↗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 ↗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 ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Have two ready. In one, the data was on your side and you had to move someone who outranked you. In the other, the pushback was correct and you changed position. The second is the harder story and it lands better, because it shows you separate being right from being attached to an answer. Name the person's actual objection.
How do you handle missing or noisy data in a large, sensitive dataset?
How do you handle missing or noisy data in a large, sensitive dataset?
Approach
- Close with what you would do differently, concretely.
- Name the disagreement or constraint, and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on your reasoning.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Tell me about a time you had to work with a team to solve a difficult …
Tell me about a time you had to work with a team to solve a difficult technical hurdle.
Approach
- Name the disagreement or constraint, and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on your reasoning.
- Quantify the outcome, including what you would not claim credit for.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Describe a time you had to explain a complex model to a non-technical …
Describe a time you had to explain a complex model to a non-technical stakeholder.
Approach
- Quantify the outcome, including what you would not claim credit for.
- State the situation in two sentences and spend the rest on your reasoning.
- Close with what you would do differently, concretely.
Follow-up
- How did you know the outcome was caused by your change?
- What would you do differently if you ran that project again?
- 01
How do you handle missing or noisy data in a large, sensitive dataset?
- 02
Tell me about a time you had to work with a team to solve a difficult technical hurdle.
- 03
Describe a time you had to explain a complex model to a non-technical stakeholder.
Is this an official Federal Reserve System interview guide?
No. It is PracHub's own research and practice material for the Data Scientist role at Federal Reserve System. Rounds and questions reflect what candidates have reported, not a process Federal Reserve System has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult are the technical interviews?
The difficulty is generally considered average to challenging, focusing more on the application of concepts than on abstract puzzles. Preparation should focus on your past projects and your ability to explain the reasoning behind your technical choices.
PracHub interview research ↗What is the typical timeline from the first interview to an offer?
The process can take several weeks, as the Federal Reserve System conducts thorough evaluations. Expect a smooth and communicative experience, but plan for a timeline that allows for multiple rounds of discussion.
PracHub interview research ↗Is there a focus on specific programming languages?
Python and R are standard. Being able to demonstrate high proficiency in one of these, along with SQL, is essential for the technical rounds.
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