The role of a Data Scientist at Rystad Energy is pivotal in driving data-driven decision-making and enhancing the company’s analytical capabilities. As a Data Scientist, you will be tasked with analyzing complex datasets to extract meaningful insights that inform strategic initiatives across various sectors, including energy, finance, and technology. Your work will directly impact product development, operational efficiency, and client solutions, making it essential for Rystad Energy’s mission to provide high-quality data and analysis to its clients.
In this role, you will collaborate with cross-functional teams, including engineers and domain experts, to tackle real-world challenges using advanced statistical methods and machine learning techniques. The complexity and scale of the data you will work with are significant, providing you with an exciting opportunity to influence business decisions and contribute to innovative projects. Expect to engage in a dynamic work environment where your analytical skills will not only be tested but also celebrated as you help shape the future of energy analytics.
Initial Screening
reportedA screening call is a matching exercise run by someone who will not evaluate your statistics. They are checking that the work described on your resume is work you personally did, and that its scope matches the level the role is written for. Logistics get settled in the same half hour so nobody spends an interviewer's afternoon on a mismatch. The answer that fails is the one narrated in the plural. If every sentence is 'we built' and 'the team decided', there is nothing specific to write down about you. Name the piece that was yours, the decision you made inside it, and what changed after.
What to demonstrate
- Whether the ownership implied by your resume survives one round of follow-up about who actually did which part
- Whether your described scope (data size, stakeholders, what shipped) matches the seniority the role is written at
- Whether timeline, location and compensation expectations make the rest of the loop worth scheduling
How to prepare
- Rewrite your top three resume bullets in the first person singular, each with the decision you made and what moved afterwards, then say them out loud once so the 'we' does not return under pressure
- Attach one number to each project: the baseline, the change, and the window it was measured over. Where impact was never measured, say that plainly rather than inventing a figure
- Settle your compensation range before the call and give it as a range with a reason behind it, such as current total comp or a competing timeline, instead of deflecting the question twice
Technical Assessment
reportedThis round decides whether someone can hand you a schema and a question and trust the number that comes back. Correctness under a clock is the bar, not clever syntax. The habit that separates strong from weak answers is checking the grain: after every join, know how many rows you expect and whether the count moved. Most wrong answers in this format are not wrong logic, they are a fan-out from a key that turned out not to be unique, or a filter applied before an aggregate when it belonged after. Say what you expect before you run it.
What to demonstrate
- Whether your row counts survive each join, and whether you notice on your own when they do not
- Deliberate handling of rows that fail to match, including whether the question needs an inner join or a left join with the non-matches kept and counted
- Whether NULLs are treated on purpose, given that a NULL compares equal to nothing and that COUNT of a column skips it
- Reaching a defensible answer inside the window instead of a refined one after it
How to prepare
- Take a two-table schema, write a join that fans out on purpose, then fix it by collapsing the many-side to one row per key before joining. Repeat until the fix is reflex rather than recall.
- Write a funnel as one query and print the distinct user count at each stage, then confirm each stage is a subset of the one above it rather than assuming it
- Do a few timed runs in a plain text box with no autocomplete and no formatter, since assessment editors often have neither
Interviews with Stakeholders
reportedAn extra round usually exists because something is still open after the standard loop: a skill the earlier interviews did not sample, a level decision, or two interviewers who disagreed. It is rarely a rerun of what you already did well. Ask the recruiter who you are meeting, what function they sit in, and how long the session runs. That is an ordinary scheduling question, and the answer changes what you should prepare. What separates a strong candidate here is treating the round as a fresh evaluation with its own bar, rather than assuming earlier performance carries you through or sinks you.
What to demonstrate
- Whether you can answer well on ground the earlier rounds did not cover, without leaning on what you already said to someone else
- Consistency of the facts in your stories: the same sample size, timeframe, team size and scope of your own role as in earlier conversations
- How you handle an unfamiliar format live, including whether you ask what kind of answer is wanted before producing one
How to prepare
- Ask the recruiter for the interviewer's function, the length, and whether to expect a coding surface, a discussion, or a presentation. Preparing for a 30 minute conversation with a partner team is not the same work as preparing for a 60 minute technical block.
- Write out what each earlier round actually covered, then list the two or three areas nobody probed. That gap is the most likely subject of the extra round.
- Re-read the numbers in the project stories you have already told, so a second telling does not quietly contradict the first.
PracHub editorial advice for the preparation topics above.
Computing average inventory from period-end snapshots
Shipments cluster before period close, so the month-end on-hand position is systematically the lowest point of the month; turns computed against it are biased high and days of supply biased low, frequently by ten to twenty percent, and the bias grows precisely when close-period push is strongest. The same shape of error appears when a daily snapshot is joined to shipment events on date equality: a SKU with several legs on one day fans the snapshot out and multiplies the valued inventory. Average across every daily snapshot in the window for the denominator, and join snapshots to events with an explicit as-of condition and a row-count check at each grain before aggregating.
Sizing safety stock as z times sigma_D times the square root of lead time
That form assumes lead time is deterministic. When lead time itself varies, the standard deviation of demand over lead time is sqrt(L_bar * sigma_D^2 + D_bar^2 * sigma_L^2), and the second term dominates whenever supply is unreliable, so the familiar formula can understate the requirement severalfold for a long, variable inbound lane. Two further preconditions are routinely forgotten: demand is assumed independent across periods, which promotions and order batching break, and z maps to cycle service level (the probability of no stockout in a replenishment cycle), not to fill rate, which additionally depends on order quantity through the unit normal loss function. Quoting a z-derived number as a fill rate overstates achieved service, and the gap widens as order quantity shrinks.
Explaining an aggregate move without decomposing the mix shift
Split the change in the aggregate into within-segment movement and movement in segment weights before you explain it. Every segment's rate can fall while the overall rate rises, purely because volume shifted toward segments that already had higher rates.
Reading an observational correlation as a causal effect
Name the confounder you are most worried about and the design that would remove it: an experiment, a difference-in-differences with a checked pre-period trend, an instrument, or a regression discontinuity. When none is available, state which direction the bias likely runs and bound the claim accordingly.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
How do you evaluate the performance of a predictive model?
How do you evaluate the performance of a predictive model?
Approach
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Pick an evaluation metric that matches the cost of each error type, not a default.
- Check what information would not exist at prediction time, and exclude it.
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 machine learning algorithms are you most familiar with, and when …
What machine learning algorithms are you most familiar with, and when would you use each?
Approach
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Say how the offline result would be validated online before it is trusted.
- Check what information would not exist at prediction time, and exclude it.
Follow-up
- What would you monitor after launch to know the model is still valid?
- Where could label leakage enter this setup?
As-of join snapshots to order lines without merge_asof
You have fct_order_line (order_line_id, sku_id, ship_from_location_id, actual_ship_at_utc, shipped_qty, shipment_id) and fct_inventory_daily (inventory_date, sku_id, location_id, available_qty). For every shipped line, attach available_qty from the latest snapshot strictly before the ship date for that exact sku and node, plus the shipment's delivery timestamp from fct_shipment_leg (leg_id, shipment_id, delivered_at_utc, leg_status). The output must have exactly one row per input order line. pandas.merge_asof is not available; implement the as-of match yourself.
Approach
- Collapse fct_shipment_leg to one row per shipment_id before joining anything. A shipment is one or more legs, so a direct line-to-leg join multiplies every line by its leg count and quietly inflates every sum computed afterwards.
- Define the shipment-level delivery as the maximum delivered_at_utc across its legs, and set it null if any leg is not delivered, since the customer has the order only when the last leg lands.
- Sort the snapshot frame by (sku_id, location_id, inventory_date) and the line frame by the same key plus ship date, then use np.searchsorted with side='left' on the sorted dates within each key group to find the insertion point; the matching snapshot is the row at position minus one.
- Guard the boundary explicitly: an insertion point of 0 means no snapshot exists before that ship date, and the result must be null rather than the first available future snapshot. Count those lines and report them.
- Assert the output row count equals the input line count immediately after each merge, before any aggregation. Row-count drift is the only symptom a silent fan-out produces, and it is invisible once the frame has been grouped.
Worked solution 30 min
- Aggregate legs by shipment_id to produce delivered_at_utc as the max, leg_count, and a delivered_all flag from leg_status.
- Left join that one-row-per-shipment frame onto fct_order_line by shipment_id and assert the row count is unchanged.
- Sort snapshots and lines by the composite key, then compute per-group insertion points with np.searchsorted and subtract one.
- Mask positions below zero to null, take available_qty at the remaining positions, and attach it as available_qty_prior.
- Validate on a single sku-location pair by printing its snapshot dates and one line's ship date and confirming the chosen date by eye.
Follow-up
- The snapshot is taken at the node's local end of day and actual_ship_at_utc is UTC. Where does that break the strictly-before rule and what do you need from dim_location to fix it?
- How would you extend this to allocate each leg's linehaul cost back to the order lines it carried?
- What changes if a line ships in two partial shipments?
As-of inventory position behind each short-shipped line
For every fct_order_line with line_status in ('shipped_short','backordered','cancelled_no_supply') and requested_ship_date in the last quarter, attach the inventory state that governed it: the fct_inventory_daily row for the same sku_id and ship_from_location_id with the greatest inventory_date strictly before requested_ship_date. Snapshots are taken at the location-local end of day. Return the line, ordered_qty, shipped_qty, and that snapshot's available_qty, blocked_qty, on_order_qty and in_transit_qty, plus the snapshot's age in days. Lines with no prior snapshot must appear with NULLs rather than disappearing from the result.
Approach
- Recognise the join is an inequality, not an equality. The snapshot is an end-of-day state, so the position that governed allocation is the last close strictly before the requested ship date, and the same-date row reflects the outcome rather than the cause.
- Implement it with LEFT JOIN LATERAL (SELECT ... WHERE sku_id and location_id match AND inventory_date < requested_ship_date ORDER BY inventory_date DESC LIMIT 1) ON TRUE, or equivalently ROW_NUMBER() OVER (PARTITION BY order_line_id ORDER BY inventory_date DESC) = 1 over a bounded candidate set.
- Bound the lateral with a floor such as inventory_date >= requested_ship_date - 14 so the scan does not walk the whole history for each line, and let that floor produce NULLs rather than silently dropping the line.
- Keep the join LEFT. Lines at a node with no prior snapshot are a data-quality finding worth counting, and an INNER join converts them into an invisible bias toward nodes with clean snapshot coverage.
- Check the row count before and after the join: the output must have exactly one row per order_line_id, and any excess means the as-of match is returning more than one candidate.
Worked solution 30 min
- Filter fct_order_line to the three statuses and the quarter, and record the row count as the target.
- Attach LEFT JOIN LATERAL over fct_inventory_daily with the strict inequality, the fourteen-day floor, ORDER BY inventory_date DESC and LIMIT 1.
- Project the four quantity columns plus snapshot_age_days = requested_ship_date - inventory_date.
- Compare the result row count with the target; investigate any difference before aggregating anything.
- Count and group the NULL-snapshot rows by ship_from_location_id to see whether the gap is a node or a SKU launch artefact.
Follow-up
- Split the output by short_reason_code. Which codes should show available_qty at or near zero, and what does it mean when 'no_stock' lines show healthy availability?
- blocked_qty is large on many of these lines. Is that a planning problem, and who owns it?
- How would you extend this to the position at the moment of allocation rather than the previous close, and what would you need that the daily snapshot does not give you?
Perfect order rate against the original promise in local time
Compute weekly perfect order rate at order grain. An order qualifies only if every fct_order_line has line_status = 'shipped_full', every line's actual_delivery_at_utc falls on or before original_promised_delivery_date at the ship-from node's local end of day (dim_location.timezone holds IANA names), and no fct_shipment_leg reached through shipment_id carries exception_code in ('damage','address_issue') or leg_status in ('refused','lost'). The denominator is orders whose original_promised_delivery_date lands in the week. Undelivered lines fail. Publish with a fourteen-day lag and declare invoice accuracy as out of scope.
Approach
- Convert the event into local time rather than comparing against a UTC-anchored deadline: (actual_delivery_at_utc AT TIME ZONE 'UTC' AT TIME ZONE loc.timezone)::date <= original_promised_delivery_date. Casting the promise date to a timestamp puts the cut-off at UTC midnight starting the promise day, which is up to a full day early and wrong in both directions depending on the node's offset.
- Evaluate the three components as booleans at line grain, then roll to the order with BOOL_AND, coercing a NULL actual_delivery_at_utc to FALSE explicitly so an undelivered line fails rather than propagating NULL and dropping the order out of the comparison.
- Test the shipment exceptions with EXISTS against fct_shipment_leg, never with a join. One shipment has many legs and one order has many lines, so a join multiplies the order into the denominator and changes the metric.
- Aggregate to order grain first, then count qualifying orders over orders promised in the week. The rate is orders over orders; a line-level average is a different and easier metric.
- Apply the publication lag by restricting to weeks whose promise dates are at least fourteen days old, and emit the three component pass rates beside the headline so any movement is attributable to completeness, timeliness or exceptions.
Follow-up
- All three component rates are flat but perfect order fell two points. How is that arithmetically possible, and what does it tell you about where the failures now land?
- A node sits at UTC+13 and many orders are promised on the last day of the month. What does that do to your weekly and monthly buckets?
- How do you handle an order whose lines ship from two nodes in different timezones, and which node's clock owns the promise?
How would you approach a case where sales data showed an unexpected de…
How would you approach a case where sales data showed an unexpected decline?
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.
- 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 stay current with industry trends and advancements in data …
How do you stay current with industry trends and advancements in data science?
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Name one primary metric, then the guardrail that stops it being gamed.
- Restate the decision this analysis has to support, and who acts on the answer.
Follow-up
- Which segment would you cut first, and what would that rule out?
- What would you do if the primary metric and the guardrail moved in opposite directions?
Given a dataset with missing values, what strategies would you use to …
Given a dataset with missing values, what strategies would you use to handle them?
Approach
- Name one primary metric, then the guardrail that stops it being gamed.
- State what result would change your recommendation, so the answer is falsifiable.
- Fix the population and the time window before naming any metric.
Follow-up
- 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?
If you were given a large dataset, how would you prioritize which anal…
If you were given a large dataset, how would you prioritize which analyses to conduct?
Approach
- Restate the decision this analysis has to support, and who acts on the answer.
- Fix the population and the time window before naming any metric.
- 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?
Describe how you would design an experiment to test a new product feat…
Describe how you would design an experiment to test a new product feature.
Approach
- Name the randomisation unit first; it decides the variance and what the test can detect.
- 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.
Follow-up
- How would you handle interference between treated and control units?
- What would you do if you could not randomise at all?
Describe a complex dataset you worked with and how you approached the …
Describe a complex dataset you worked with and how you approached the analysis.
Approach
- Work from the decision backwards to the evidence you would need.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Success metric for opening one forward-stocking node
The network will open one forward-stocking location at echelon 3, replenished by an existing regional DC. There is one treated node and about thirty candidate comparison nodes. Using fct_order_line (ordered_at_utc, actual_delivery_at_utc, ship_from_location_id, line_status), fct_inventory_daily (on_hand_qty, standard_cost_cents) and fct_shipment_leg (direction, shipped_units, linehaul_cost_cents, accessorial_cost_cents, delivered_at_utc), define the primary metric, the guardrail that catches the working-capital cost of de-pooling, and the readout design. Say what this sample cannot detect.
Approach
- Anchor the primary on what the node is for: P90 order cycle time for the lines it serves, taken over the pooled line-level distribution rather than as a mean of daily percentiles, with never-delivered lines reported as a censored count instead of dropped.
- Make the inventory guardrail network-wide, not node-local. Splitting one pooled stock into two locations raises total safety stock at the same service level; under independent demand, equal service and equal lead times the square-root law puts the factor at sqrt(2) on the safety component, so measure parent and new node combined at cost.
- Add a freight guardrail across both the new inbound interfacility leg and the outbound legs, because the opening adds a movement that any node-local outbound cost metric will miss entirely.
- Choose a design suited to one treated unit: a synthetic control or matched-pair comparison built from donor nodes on pre-period cycle time, volume, SKU mix and lane structure, with the donor pool and matching variables frozen before the readout.
- Set the burn-in at two full replenishment cycles, and book the fill-and-build inventory into the cost side rather than letting it contaminate the service read.
- State the limit rather than implying precision: with one treated unit there is no cross-unit variance, so a placebo distribution over the donors is the only inferential statement available, and effects smaller than the donor spread cannot be distinguished from node noise.
Worked solution 35 min
- Build a weekly node panel covering at least 26 pre-period weeks: P90 cycle time, line volume, fill rate, on-hand at cost, delivered cost per unit.
- Screen the donor pool, dropping nodes that opened, closed or changed parent_location_id inside the window, then fit synthetic weights on pre-period P90 and volume only.
- Check pre-period fit, then run placebo assignments across every donor to build the distribution of pseudo-effects.
- Estimate the post-period effect from the burn-in end, and compute the working-capital series as parent plus new node combined.
- Report the effect against the placebo distribution together with the minimum effect this design could have detected.
Follow-up
- A donor node runs a promotion during the post period. What do you do, and at what point did you have to decide it?
- The service gain is real and the cost guardrail is also up. Who decides, and on which number?
Unit fill rate fell four points in one region
Weekly unit fill rate for one region fell from 96.1 percent to 92.0 percent over four weeks and has not recovered. Network fill rate is flat. You have fct_order_line (ordered_qty, shipped_qty, requested_ship_date, ship_from_location_id, sku_id, line_status, short_reason_code), dim_location (location_type, region_code, echelon) and dim_sku (abc_class, xyz_class, lifecycle_status). Produce the first four query cuts, say what each one rules in or out, and name the single most likely cause together with the evidence that would confirm it. Thirty minutes, SQL plus a short written argument.
Approach
- Rebuild the metric by summing shipped_qty and ordered_qty to region level and dividing once, rather than averaging per-SKU-location rates. An unweighted mean of cell rates moves with portfolio mix on its own, so if the drop does not survive re-summing there is nothing physical to diagnose.
- Cut by ship_from_location_id first. If one node carries the whole drop the mix of demand across nodes is not the story and the problem is local; if every node fell a little, the cause sits upstream in supply or forecast rather than in any warehouse.
- Decompose the region change over abc_class by xyz_class cells into within-cell, mix and interaction terms: delta R = SUM(w0 * (r1 - r0)) + SUM(r0 * (w1 - w0)) + SUM((w1 - w0) * (r1 - r0)), where w is a cell's share of the denominator and r is its rate. A dominant mix term means demand moved toward hard-to-serve intermittent items and no cell actually got worse.
- Group the lost units (ordered_qty minus shipped_qty) by short_reason_code. 'no_stock' points at planning, 'pick_shortage' and 'damage_on_pick' mean the stock was physically present and execution lost it, 'allocation_rule' means the units existed and were promised to someone else, and 'credit_hold' is not a supply chain failure at all.
- Only then cross the weekly series against the promotion calendar, against SKUs with lifecycle_status = 'pre_launch' cutting in, and against accounts placing their first order in the window. A step change starting in the same week as a promotion or a new-item onboarding is a demand-side event, not a degradation.
Follow-up
- Fill rate is flat nationally but down in this region. What does that tell you about where the stock went, and how would you test it?
- Most of the failed units carry short_reason_code = 'no_stock', yet on_hand_qty at the node stayed above reorder_point_qty all month. How is that possible?
- Would you publish this rate weighted by units or by revenue, and which team gets a different answer from each?
For someone who has spent the last year in notebooks, dashboards or modelling work and has not written raw SQL under time pressure. The first four days rebuild query fluency against a fixture you control and can verify by hand; the last three attach that fluency to the rest of the loop.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Build a fixture you can check answers against
- Create a local Postgres or SQLite database with four tables (users, sessions, events, orders) holding roughly 200 rows you generated yourself, so you know the contents well enough to predict every result.
- Deliberately seed the cases that break queries: a user with no sessions, a session with no events, two orders sharing a timestamp, a NULL in one join key, and one duplicated user row.
- Before writing any SQL, hand-compute five answers on paper (how many users placed at least one order, median orders per ordering user, and three others) and save them as the ground truth for the week.
Deliverable: A one-command seed script plus a text file of five hand-computed answers to grade every later query against.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Joins, filters and NULL semantics
- Answer "which users have no orders" three ways (LEFT JOIN with IS NULL, NOT EXISTS, NOT IN) and confirm that the NOT IN version returns zero rows once the subquery contains a NULL, because the comparison is never TRUE.
- Reproduce the LEFT JOIN that silently collapses to an inner join by putting a right-table predicate in WHERE, then fix it by moving the predicate into the ON clause, and record both row counts.
- Create a fan-out bug on purpose by joining orders to order_items and summing the order total, then correct it with a pre-aggregated subquery and explain in one line which table changed the grain.
Deliverable: One annotated .sql file holding the three join traps, each with the wrong result and the corrected result side by side.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Window functions and frames
- Write three window queries against the fixture: a running order total per user, the rank of each order within its user by value, and the day gap to that user's previous order, then check each against the day-one ground truth.
- Run ROW_NUMBER, RANK and DENSE_RANK over a column containing ties, print all three side by side, and write one sentence on when each is the correct choice.
- Switch one query from the default frame (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which is what you get when ORDER BY is present and no frame is written) to ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and explain why the output differs only when the ORDER BY column has duplicates.
Deliverable: Three verified window queries plus a short note explaining the RANGE versus ROWS difference in your own words.
Practice prompt ↗Practice prompt ↗04The four analytical query patterns
- Write a monthly retention grid: first order month per user, then months-since-first as the column, and verify that month zero equals the cohort size exactly.
- Sessionize the events table under a 30-minute inactivity rule using LAG plus a cumulative sum over a new-session flag.
- Build a four-step funnel that counts distinct users rather than events at each step, and state the rule you applied to a user who reaches step three without ever logging step two.
Deliverable: One file with the retention, sessionization and funnel patterns, each carrying a one-line note on the assumption it bakes in.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Write SQL the way you will have to write it live
- Set a 12-minute timer and solve three medium prompts in a plain editor with no execution and no autocomplete, then run them and tally syntax errors separately from logic errors.
- Narrate one solution aloud while writing it, stating the grain of each intermediate result (one row per user, one row per user-day) before you type its body.
- Rewrite your slowest solution as a CTE chain where every CTE name states its grain, and time yourself re-solving it from blank.
Deliverable: A recording of one narrated solution plus an error tally that separates syntax from logic.
Practice prompt ↗Practice prompt ↗06One day for everything that is not SQL
- Write the preconditions of the two-sample t-test from memory, then check them: independent observations, and a difference in means whose sampling distribution is approximately normal, which at large sample sizes follows from the central limit theorem rather than from normality of the raw values.
- Write the difference between an odds ratio from logistic regression and a relative risk, and state the condition under which the two are close (low outcome prevalence).
- Prepare a 90-second answer to "how would you know this model is any good" that names the metric, the baseline you would beat, and the cost of the errors you care about.
Deliverable: One page of notes covering test preconditions, the odds-ratio caveat and the model-quality answer.
Practice prompt ↗Practice prompt ↗07Full loop rehearsal
- Run a 45-minute mock with someone willing to interrupt: 20 minutes of SQL, 15 minutes defining a metric, 10 minutes on a past project.
- Re-solve from blank the two queries you were slowest on this week and compare the times against day five.
- Write a five-line answer to "walk me through a project" that puts a number in the first sentence and names the decision the work changed.
Deliverable: Mock feedback notes plus a timed project narrative you can deliver without reading it.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Most data work is done by groups, so an interviewer has to work out which piece was yours. An answer that runs on 'we' for several minutes gets interrupted with a question about what you personally did, and by then the answer sounds defensive even when it is true. Mark your own contribution as you go, and name the parts that belonged to someone else instead of leaving them ambiguous. Keep a few specifics back as well, like the name of the metric or who actually objected, so a probe can be answered with something you had not already said.
Can you discuss a time when your analysis led to a significant busines…
Can you discuss a time when your analysis led to a significant business decision?
Approach
- Pick a story where you drove the decision, not one where you observed it.
- Name the disagreement or constraint, and how you resolved it with evidence.
- 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?
Walk through an inventory analysis that turned out wrong
Six months ago you published inventory turns by node using the month-end on_hand_qty snapshot from fct_inventory_daily as the denominator, and two DCs cut cover on the strength of it. A finance review later showed month-end is systematically the lowest on-hand point of the month, because shipments cluster before close, so your turns were overstated and days of supply understated. Tell the story: what you built, how the error surfaced, what it cost, what you corrected, and the control that now prevents this class of mistake rather than this specific instance.
Approach
- Give the facts in order and own the decision, not just the code: you chose the month-end snapshot because it was one row per sku-location and fast, and you did not check whether the sampling point was representative.
- Quantify the error rather than describing it: recompute the same window against the average of every daily snapshot and state the gap in turns and in days of supply, which in a network with close-period push typically runs ten to twenty percent.
- Separate the consequence from the mistake honestly: say what the two DCs did, whether service actually degraded, and if it did not, say so instead of inflating the damage to sound accountable.
- Describe the correction and the notification: who was told, how quickly, and whether the restated number changed the recommendation.
- Close on the generalised control: a denominator convention written into the metric definition, plus a row-count assertion at each join grain, because the same shape of error appears when a daily snapshot is joined to shipment events on date equality and one shipment with several legs fans the snapshot out.
Follow-up
- How did you decide whom to tell first, and how did you phrase it?
- What made you trust the month-end snapshot in the first place, and what would have caught it in review?
Explain forecast uncertainty to a non-technical general manager
A general manager who does not use statistics must approve a 2.1 million dollar finished-goods build. Your recommendation rests on a demand forecast with a wide error band on the Z-class items and on a 97 percent cycle service target. You get two minutes, no formulas, and the question you will get back is whether the forecast is right. Deliver the explanation as you would say it aloud, including how you describe the error band in physical terms and what the 97 percent number does and does not promise.
Approach
- Convert the error band into the units the approver already thinks in: weeks of cover, pallets, or dollars at risk, not a percentage or a confidence interval.
- Answer the right-or-wrong question directly rather than deflecting: the forecast will be wrong, the size of the wrongness is what you measured, and the build is sized against that size.
- State precisely what the 97 percent buys: it is a cycle service level, the probability of not running out during one replenishment cycle, so roughly three cycles in a hundred see a stockout. It is not the share of units shipped from stock. Unit fill rate also depends on the replenishment quantity, and when that quantity is large relative to the standard deviation of lead-time demand, which is the normal case outside lot-for-lot ordering, the fill rate sits above the cycle service number, often above 99 percent at a 97 percent cycle target.
- Give the two-sided consequence in money: what the build costs to carry at the applicable cost of capital plus obsolescence risk on shelf-life items, against the margin at risk from the stockouts it prevents.
- Close with the decision you want and the trigger that would reverse it, for example a lag-7 bias check after four weeks that reopens the number.
Follow-up
- The GM says just give me one number. What do you give, and what do you refuse to give?
- How would your answer change if the items were frozen with a 90-day shelf life?
- 01
Can you discuss a time when your analysis led to a significant business decision?
- 02
Six months ago you published inventory turns by node using the month-end on_hand_qty snapshot from fct_inventory_daily as the denominator, and two DCs cut cover on the strength of it. A finance review later showed month-end is systematically the lowest on-hand point of the month, because shipments cluster before close, so your turns were overstated and days of supply understated. Tell the story: what you built, how the error surfaced, what it cost, what you corrected, and the control that now prevents this class of mistake rather than this specific instance.
- 03
A general manager who does not use statistics must approve a 2.1 million dollar finished-goods build. Your recommendation rests on a demand forecast with a wide error band on the Z-class items and on a 97 percent cycle service target. You get two minutes, no formulas, and the question you will get back is whether the forecast is right. Deliver the explanation as you would say it aloud, including how you describe the error band in physical terms and what the 97 percent number does and does not promise.
Is this an official Rystad Energy interview guide?
No. It is PracHub's own research and practice material for the Data Scientist role at Rystad Energy. Rounds and questions reflect what candidates have reported, not a process Rystad Energy has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What is the typical interview difficulty for this role?
The interview process is generally rigorous, reflecting the technical nature of the role. Candidates should expect a combination of technical assessments and behavioral interviews.
PracHub interview research ↗How much preparation time is typical for candidates?
Most candidates spend several weeks preparing, focusing on both technical skills and behavioral interview techniques.
PracHub interview research ↗What differentiates successful candidates?
Successful candidates typically demonstrate a strong mix of technical expertise, problem-solving skills, and the ability to communicate effectively.
PracHub interview research ↗What is the culture like at Rystad Energy?
The culture emphasizes collaboration, innovation, and a commitment to excellence, making it essential for candidates to align with these values.
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