Coding questions · Amazon
Manipulate time-series with Pandas groupby
This question evaluates proficiency in time-series data manipulation, including timezone-aware datetime parsing and DST handling, groupby and rolling-window aggregations, resampling to fill calendar gaps, and user-level retention and DAU calculations using idiomatic, vectorized Pandas or SQL operations.
Given a DataFrame events(user_id, event_type, ts_utc, revenue):
- Parse ts_utc as timezone-aware, convert to America/Los_Angeles, and handle DST transitions.
- Compute daily active users (DAU) and a 7-day moving average.
- For each user and event_type, compute a 7-day rolling count.
- Produce weekly retention: the number and rate of users active in week w who return in week w+1.
- Resample to fill missing calendar dates with zeros. Provide idiomatic, vectorized Pandas code (no explicit Python loops).
Convert UTC event timestamps to America/Los_Angeles with DST handling
You are given an events table where ts_utc stores event timestamps in UTC as TIMESTAMP WITH TIME ZONE values.
Write a PostgreSQL query that returns every event ordered by event_id, including the original event fields plus:
ts_utc: the UTC timestamp rendered asYYYY-MM-DD HH24:MI:SS+00ts_pacific: the same instant rendered as localAmerica/Los_Angelestime inYYYY-MM-DD HH24:MI:SSformatevent_date_pacific: the local Pacific calendar date inYYYY-MM-DDformat
Use PostgreSQL time zone conversion so daylight saving time transitions are handled by the database.
Table events
| column | type | constraints |
|---|---|---|
| event_id | INT | PRIMARY KEY |
| user_id | INT | NOT NULL |
| event_type | VARCHAR(20) | NOT NULL |
| ts_utc | TIMESTAMP WITH TIME ZONE | NOT NULL |
| revenue | DECIMAL(10,2) | NOT NULL |
Sample data:
| event_id | event_type | revenue | ts_utc | user_id |
|---|---|---|---|---|
| 1 | view | 0 | 2025-03-07 08:00:00+00 | 1 |
| 2 | purchase | 20 | 2025-03-07 09:00:00+00 | 1 |
| 3 | view | 0 | 2025-03-08 18:00:00+00 | 2 |
| 4 | signup | 0 | 2025-03-09 00:30:00+00 | 3 |
| 5 | view | 0 | 2025-03-09 10:00:00+00 | 1 |
| 6 | purchase | 35 | 2025-03-10 02:00:00+00 | 2 |
| 7 | view | 0 | 2025-03-11 16:00:00+00 | 3 |
| 8 | signup | 0 | 2025-03-11 20:00:00+00 | 4 |
| 9 | view | 0 | 2025-03-13 23:00:00+00 | 4 |
| 10 | view | 0 | 2025-03-14 04:00:00+00 | 2 |
| 11 | purchase | 15 | 2025-03-15 12:00:00+00 | 1 |
| 12 | purchase | 50 | 2025-03-16 03:00:00+00 | 3 |
Expected output
All events with their UTC timestamp, converted America/Los_Angeles local timestamp (handling DST), and the local calendar date. Ordered by event_id.
| event_date_pacific | event_id | event_type | revenue | ts_pacific | ts_utc | user_id |
|---|---|---|---|---|---|---|
| 2025-03-07 | 1 | view | 0.00 | 2025-03-07 00:00:00 | 2025-03-07 08:00:00+00 | 1 |
| 2025-03-07 | 2 | purchase | 20.00 | 2025-03-07 01:00:00 | 2025-03-07 09:00:00+00 | 1 |
| 2025-03-08 | 3 | view | 0.00 | 2025-03-08 10:00:00 | 2025-03-08 18:00:00+00 | 2 |
| 2025-03-08 | 4 | signup | 0.00 | 2025-03-08 16:30:00 | 2025-03-09 00:30:00+00 | 3 |
| 2025-03-09 | 5 | view | 0.00 | 2025-03-09 03:00:00 | 2025-03-09 10:00:00+00 | 1 |
| 2025-03-09 | 6 | purchase | 35.00 | 2025-03-09 19:00:00 | 2025-03-10 02:00:00+00 | 2 |
| 2025-03-11 | 7 | view | 0.00 | 2025-03-11 09:00:00 | 2025-03-11 16:00:00+00 | 3 |
| 2025-03-11 | 8 | signup | 0.00 | 2025-03-11 13:00:00 | 2025-03-11 20:00:00+00 | 4 |
| 2025-03-13 | 9 | view | 0.00 | 2025-03-13 16:00:00 | 2025-03-13 23:00:00+00 | 4 |
| 2025-03-13 | 10 | view | 0.00 | 2025-03-13 21:00:00 | 2025-03-14 04:00:00+00 | 2 |
| 2025-03-15 | 11 | purchase | 15.00 | 2025-03-15 05:00:00 | 2025-03-15 12:00:00+00 | 1 |
| 2025-03-15 | 12 | purchase | 50.00 | 2025-03-15 20:00:00 | 2025-03-16 03:00:00+00 | 3 |
Hints
- Use PostgreSQL
AT TIME ZONEto convert aTIMESTAMP WITH TIME ZONEvalue to local wall-clock time. - Format timestamps with
TO_CHAR; DuckDB-stylestrftimeis not valid PostgreSQL. - Compute the Pacific date after converting the timestamp to
America/Los_Angeles.
Daily active users and 7-day moving average
Using the events table, treat ts_utc as a UTC timestamp and convert it to America/Los_Angeles local time. Based on the local calendar date (event_date_pacific), compute:
- Daily active users (DAU) as the count of distinct user_id per event_date_pacific.
- A 7-day moving average of DAU, where the window covers the current day and the previous 6 calendar days (using the local date). Return event_date_pacific, dau, and dau_7d_moving_avg, ordered by event_date_pacific.
Table events
| column | type | constraints |
|---|---|---|
| event_id | INT | PRIMARY KEY |
| user_id | INT | NOT NULL |
| event_type | VARCHAR(20) | NOT NULL |
| ts_utc | TIMESTAMP WITH TIME ZONE | NOT NULL |
| revenue | DECIMAL(10,2) | NOT NULL |
Sample data:
| event_id | event_type | revenue | ts_utc | user_id |
|---|---|---|---|---|
| 1 | view | 0 | 2025-03-07 08:00:00+00 | 1 |
| 2 | purchase | 20 | 2025-03-07 09:00:00+00 | 1 |
| 3 | view | 0 | 2025-03-08 18:00:00+00 | 2 |
| 4 | signup | 0 | 2025-03-09 00:30:00+00 | 3 |
| 5 | view | 0 | 2025-03-09 10:00:00+00 | 1 |
| 6 | purchase | 35 | 2025-03-10 02:00:00+00 | 2 |
| 7 | view | 0 | 2025-03-11 16:00:00+00 | 3 |
| 8 | signup | 0 | 2025-03-11 20:00:00+00 | 4 |
| 9 | view | 0 | 2025-03-13 23:00:00+00 | 4 |
| 10 | view | 0 | 2025-03-14 04:00:00+00 | 2 |
| 11 | purchase | 15 | 2025-03-15 12:00:00+00 | 1 |
| 12 | purchase | 50 | 2025-03-16 03:00:00+00 | 3 |
Expected output
One row per America/Los_Angeles local date that has at least one event, with daily active users (distinct users) and a 7-day moving average over the previous 6 days plus the current day.
| dau | dau_7d_moving_avg | event_date_pacific |
|---|---|---|
| 1 | 1 | 2025-03-07 |
| 2 | 1.5 | 2025-03-08 |
| 2 | 1.6667 | 2025-03-09 |
| 2 | 1.75 | 2025-03-11 |
| 2 | 1.8 | 2025-03-13 |
| 2 | 2 | 2025-03-15 |
Hints
- First convert ts_utc to a Pacific local DATE and aggregate distinct users per day.
- Use a window function with RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW to compute the 7-day moving average.
7-day rolling event count per user and event_type
Write a PostgreSQL query. Using the events table, compute for each row a 7-day rolling count of events for that user_id and event_type. The 7-day window is defined on ts_utc as the current event and all events for the same (user_id, event_type) that occurred in the previous 6 days. Return event_id, user_id, event_type, ts_utc formatted in UTC as 'YYYY-MM-DD HH24:MI:SS', revenue, and rolling_7d_count, ordered by user_id, event_type, and ts_utc.
Table events
| column | type | constraints |
|---|---|---|
| event_id | INT | PRIMARY KEY |
| user_id | INT | NOT NULL |
| event_type | VARCHAR(20) | NOT NULL |
| ts_utc | TIMESTAMP WITH TIME ZONE | NOT NULL |
| revenue | DECIMAL(10,2) | NOT NULL |
Sample data:
| event_id | event_type | revenue | ts_utc | user_id |
|---|---|---|---|---|
| 1 | view | 0 | 2025-03-07 08:00:00+00 | 1 |
| 2 | purchase | 20 | 2025-03-07 09:00:00+00 | 1 |
| 3 | view | 0 | 2025-03-08 18:00:00+00 | 2 |
| 4 | signup | 0 | 2025-03-09 00:30:00+00 | 3 |
| 5 | view | 0 | 2025-03-09 10:00:00+00 | 1 |
| 6 | purchase | 35 | 2025-03-10 02:00:00+00 | 2 |
| 7 | view | 0 | 2025-03-11 16:00:00+00 | 3 |
| 8 | signup | 0 | 2025-03-11 20:00:00+00 | 4 |
| 9 | view | 0 | 2025-03-13 23:00:00+00 | 4 |
| 10 | view | 0 | 2025-03-14 04:00:00+00 | 2 |
| 11 | purchase | 15 | 2025-03-15 12:00:00+00 | 1 |
| 12 | purchase | 50 | 2025-03-16 03:00:00+00 | 3 |
Expected output
All events with a 7-day rolling count per (user_id, event_type), ordered by user_id, event_type, ts_utc.
| event_id | event_type | revenue | rolling_7d_count | ts_utc | user_id |
|---|---|---|---|---|---|
| 2 | purchase | 20.00 | 1 | 2025-03-07 09:00:00 | 1 |
| 11 | purchase | 15.00 | 1 | 2025-03-15 12:00:00 | 1 |
| 1 | view | 0.00 | 1 | 2025-03-07 08:00:00 | 1 |
| 5 | view | 0.00 | 2 | 2025-03-09 10:00:00 | 1 |
| 6 | purchase | 35.00 | 1 | 2025-03-10 02:00:00 | 2 |
| 3 | view | 0.00 | 1 | 2025-03-08 18:00:00 | 2 |
| 10 | view | 0.00 | 2 | 2025-03-14 04:00:00 | 2 |
| 12 | purchase | 50.00 | 1 | 2025-03-16 03:00:00 | 3 |
| 4 | signup | 0.00 | 1 | 2025-03-09 00:30:00 | 3 |
| 7 | view | 0.00 | 1 | 2025-03-11 16:00:00 | 3 |
| 8 | signup | 0.00 | 1 | 2025-03-11 20:00:00 | 4 |
| 9 | view | 0.00 | 1 | 2025-03-13 23:00:00 | 4 |
Hints
- Partition by user_id and event_type and order by ts_utc to define the rolling window.
- Use a window COUNT with RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW to cover a 7-day period.
Weekly retention: users active in week w who return in week w+1
Define weeks using the America/Los_Angeles local calendar, with weeks starting on Monday (i.e., use date_trunc('week', local_date)). A user is considered active in a week if they have at least one event in that week. Using the events table:
- Convert ts_utc to America/Los_Angeles local date.
- For each week w, compute cohort_users: the number of distinct users active in week w.
- For each week w that has a following week w+1 with any activity, compute returning_users: the number of users who are active in both week w and week w+1.
- Compute retention_rate = returning_users / cohort_users. Return week_start (DATE for week w, truncated to Monday), cohort_users, returning_users, and retention_rate, ordered by week_start.
Table events
| column | type | constraints |
|---|---|---|
| event_id | INT | PRIMARY KEY |
| user_id | INT | NOT NULL |
| event_type | VARCHAR(20) | NOT NULL |
| ts_utc | TIMESTAMP WITH TIME ZONE | NOT NULL |
| revenue | DECIMAL(10,2) | NOT NULL |
Sample data:
| event_id | event_type | revenue | ts_utc | user_id |
|---|---|---|---|---|
| 1 | view | 0 | 2025-03-07 08:00:00+00 | 1 |
| 2 | purchase | 20 | 2025-03-07 09:00:00+00 | 1 |
| 3 | view | 0 | 2025-03-08 18:00:00+00 | 2 |
| 4 | signup | 0 | 2025-03-09 00:30:00+00 | 3 |
| 5 | view | 0 | 2025-03-09 10:00:00+00 | 1 |
| 6 | purchase | 35 | 2025-03-10 02:00:00+00 | 2 |
| 7 | view | 0 | 2025-03-11 16:00:00+00 | 3 |
| 8 | signup | 0 | 2025-03-11 20:00:00+00 | 4 |
| 9 | view | 0 | 2025-03-13 23:00:00+00 | 4 |
| 10 | view | 0 | 2025-03-14 04:00:00+00 | 2 |
| 11 | purchase | 15 | 2025-03-15 12:00:00+00 | 1 |
| 12 | purchase | 50 | 2025-03-16 03:00:00+00 | 3 |
Expected output
Weekly retention from week w to week w+1 based on America/Los_Angeles local weeks starting on Monday. Only weeks that have a subsequent active week are included.
| cohort_users | retention_rate | returning_users | week_start |
|---|---|---|---|
| 3 | 1 | 3 | 2025-03-03 |
Hints
- First derive user-week activity using date_trunc('week', local_date).
- Compute cohorts per week and then self-join user_weeks to itself shifted by 7 days to find users active in consecutive weeks.
Resample events to daily calendar and fill missing dates with zeros
Using the events table, consider America/Los_Angeles local calendar dates derived from ts_utc. Generate a continuous sequence of local dates from the minimum to the maximum event_date_pacific, and for each date compute the number of distinct active users (DAU). For dates with no events, DAU should be 0. Return event_date_pacific and dau for every date in this range, ordered by event_date_pacific.
Table events
| column | type | constraints |
|---|---|---|
| event_id | INT | PRIMARY KEY |
| user_id | INT | NOT NULL |
| event_type | VARCHAR(20) | NOT NULL |
| ts_utc | TIMESTAMP WITH TIME ZONE | NOT NULL |
| revenue | DECIMAL(10,2) | NOT NULL |
Sample data:
| event_id | event_type | revenue | ts_utc | user_id |
|---|---|---|---|---|
| 1 | view | 0 | 2025-03-07 08:00:00+00 | 1 |
| 2 | purchase | 20 | 2025-03-07 09:00:00+00 | 1 |
| 3 | view | 0 | 2025-03-08 18:00:00+00 | 2 |
| 4 | signup | 0 | 2025-03-09 00:30:00+00 | 3 |
| 5 | view | 0 | 2025-03-09 10:00:00+00 | 1 |
| 6 | purchase | 35 | 2025-03-10 02:00:00+00 | 2 |
| 7 | view | 0 | 2025-03-11 16:00:00+00 | 3 |
| 8 | signup | 0 | 2025-03-11 20:00:00+00 | 4 |
| 9 | view | 0 | 2025-03-13 23:00:00+00 | 4 |
| 10 | view | 0 | 2025-03-14 04:00:00+00 | 2 |
| 11 | purchase | 15 | 2025-03-15 12:00:00+00 | 1 |
| 12 | purchase | 50 | 2025-03-16 03:00:00+00 | 3 |
Expected output
Continuous daily series from min to max local event date in America/Los_Angeles, with DAU per day; missing dates have DAU = 0.
| dau | event_date_pacific |
|---|---|
| 1 | 2025-03-07 |
| 2 | 2025-03-08 |
| 2 | 2025-03-09 |
| 0 | 2025-03-10 |
| 2 | 2025-03-11 |
| 0 | 2025-03-12 |
| 2 | 2025-03-13 |
| 0 | 2025-03-14 |
| 2 | 2025-03-15 |
Hints
- Convert ts_utc to a Pacific local DATE and aggregate distinct users per day.
- Use generate_series over the min and max dates, then LEFT JOIN to the daily aggregates and COALESCE nulls to zero.