Times Series Analysis of Orders and Sales Performance
This section will explore the time series analysis of orders and sales performance for Olist, including month-over-month growth rates, seasonality effects, and the distribution of order volume by day and hour to identify key trends and patterns in customer behavior.
10Project Roadmap: Step-by-Step Navigation
MoM and Seasonality Analysis
29. What is the Month-over-Month (MoM) growth rate for total revenue across the entire dataset? Can we identify specific months where the business experienced ‘Hyper-Growth’ (>20% increase) versus months of stagnation or contraction?
Solution:
-
To calculate the month-over-month growth rate, the core metrics must first be aggregated by month. This should be done in the first CTE by joining the orders, payments, and calendar tables. The total number of orders and the total revenue must be calculated for each specific year and month. Canceled or unavailable orders must also be filtered out to ensure the revenue figures are perfectly accurate.
-
Next, the historical data must be retrieved to allow for comparison. The lag window function should be used here to fetch the revenue and order volume from both the previous month and the previous year. The data must be ordered chronologically by year and month number to ensure the lag function pulls the correct prior periods for every single row.
-
Once the current and previous metrics are aligned, the actual percentage growth must be calculated. The mathematical formula for percentage change will be applied to both month-over-month and year-over-year revenue. A nullif function must be included during the division step to prevent any mathematical errors just in case the previous month had zero recorded revenue.
-
Finally, the calculated growth rates must be categorized to clearly highlight business performance. A case statement will be used in the main query to assign a growth performance tag, easily identifying periods of hyper-growth above twenty percent, standard positive growth, or stagnation. An additional case statement should also be applied to flag key seasonal periods like Black Friday and the holiday season to provide necessary business context for any major revenue fluctuations.
Show SQL Code
/* growth and seasonality analysis
objective: calculate mom and yoy growth rates for revenue and order volume
logic:
1. aggregate revenue and order counts by month.
2. use lag() window function to compare current month vs prior month and prior year.
3. calculate percentage growth rates and classify performance tiers.
granularity: year_month
*/
with monthly_sales as (
-- step 1: aggregate core metrics by month
select
c.year,
c.month_number,
c.month_name,
count(distinct o.order_id) as total_orders,
sum(p.payment_value) as total_revenue
from
orders o
inner join
payments p on o.order_id = p.order_id
inner join
Calendar c on o.purchase_datekey = c.datekey
where
o.order_status not in ('canceled', 'unavailable')
group by
c.year, c.month_number, c.month_name
),
growth_metrics as (
-- step 2: use lag() to fetch values from the previous month and previous year
select
*,
-- previous month values
prev_month_orders = lag(total_orders) over (order by year, month_number),
prev_month_revenue = lag(total_revenue) over (order by year, month_number),
-- previous year values (offset of 12 rows)
prev_year_orders = lag(total_orders, 12) over (order by year, month_number),
prev_year_revenue = lag(total_revenue, 12) over (order by year, month_number)
from
monthly_sales
),
final_calculations as (
-- step 3: calculate percentage growth
select
*,
mom_revenue_growth_pct = cast(100.0 * (total_revenue - prev_month_revenue) / nullif(prev_month_revenue, 0) as decimal(10,2)),
yoy_revenue_growth_pct = cast(100.0 * (total_revenue - prev_year_revenue) / nullif(prev_year_revenue, 0) as decimal(10,2))
from
growth_metrics
)
-- final output: categorizing growth performance levels and seasonality
select
year,
month_name,
total_orders,
total_revenue,
mom_revenue_growth_pct,
-- identifying growth tiers as requested
case
when mom_revenue_growth_pct > 20 then 'Hyper-Growth (>20%)'
when mom_revenue_growth_pct > 0 then 'Positive Growth'
when mom_revenue_growth_pct <= 0 then 'Stagnation / Contraction'
else 'Baseline (No Prior Month)'
end as growth_performance_tag,
-- identifying seasonal spikes
case
when month_number = 11 then 'Black Friday Period'
when month_number = 12 then 'Holiday Season'
when month_number = 1 then 'New Year Peak'
else 'Standard Period'
end as seasonality_tag
from
final_calculations
order by
year, month_number;
Results:
| year | month_name | total_orders | total_revenue | mom_revenue_growth_pct | growth_performance_tag | seasonality_tag |
|---|---|---|---|---|---|---|
| 2016 | September | 1 | 136.22999572753906 | Baseline (No Prior Month) | Standard Period | |
| 2016 | October | 293 | 51657.52999472618 | 37819.35 | Hyper-Growth (>20%) | Standard Period |
| 2016 | December | 1 | 19.6200008392334 | -99.96 | Stagnation / Contraction | Holiday Season |
| 2017 | January | 787 | 137006.76003867388 | 698201.50 | Hyper-Growth (>20%) | New Year Peak |
| 2017 | February | 1718 | 283621.93943831325 | 107.01 | Hyper-Growth (>20%) | Standard Period |
| 2017 | March | 2617 | 425656.3995541632 | 50.08 | Hyper-Growth (>20%) | Standard Period |
| 2017 | April | 2377 | 405988.3795110285 | -4.62 | Stagnation / Contraction | Standard Period |
| 2017 | May | 3640 | 582926.160167003 | 43.58 | Hyper-Growth (>20%) | Standard Period |
| 2017 | June | 3205 | 499827.47035080194 | -14.26 | Stagnation / Contraction | Standard Period |
| 2017 | July | 3946 | 578858.5795211494 | 15.81 | Positive Growth | Standard Period |
| 2017 | August | 4272 | 662071.7698084246 | 14.38 | Positive Growth | Standard Period |
| 2017 | September | 4227 | 717195.2203616947 | 8.33 | Positive Growth | Standard Period |
| 2017 | October | 4547 | 764785.2796836384 | 6.64 | Positive Growth | Standard Period |
| 2017 | November | 7423 | 1172639.2297811043 | 53.33 | Hyper-Growth (>20%) | Black Friday Period |
| 2017 | December | 5620 | 861914.4599985629 | -26.50 | Stagnation / Contraction | Holiday Season |
| 2018 | January | 7187 | 1102639.4091129452 | 27.93 | Hyper-Growth (>20%) | New Year Peak |
| 2018 | February | 6625 | 979966.2302345932 | -11.13 | Stagnation / Contraction | Standard Period |
| 2018 | March | 7168 | 1152736.7396906763 | 17.63 | Positive Growth | Standard Period |
| 2018 | April | 6919 | 1156303.9107048158 | 0.31 | Positive Growth | Standard Period |
| 2018 | May | 6833 | 1145748.6310662013 | -0.91 | Stagnation / Contraction | Standard Period |
| 2018 | June | 6145 | 1020494.2907593064 | -10.93 | Stagnation / Contraction | Standard Period |
| 2018 | July | 6233 | 1039880.1590387616 | 1.90 | Positive Growth | Standard Period |
| 2018 | August | 6421 | 996896.1512343884 | -4.13 | Stagnation / Contraction | Standard Period |
| 2018 | September | 1 | 166.4600067138672 | -99.98 | Stagnation / Contraction | Standard Period |
Insights:
- The 2017 Startup Boom: The first half of 2017 is defined by aggressive “Hyper-Growth.” Months like February (+107%), March (+50%), and May (+43%) show huge revenue jumps, proving this was the phase where Olist rapidly acquired its early market share.
- The Black Friday Rollercoaster: November 2017 was an absolute game-changer. Revenue spiked by 53.3% due to Black Friday sales. However, this created a predictable hangover in December, resulting in a 26.5% contraction as the holiday shopping rush ended.
- The 2018 Plateau: After recovering with a strong January 2018 (+27.9%), the platform’s growth essentially flattened out. The rest of 2018 is filled with slight contractions (like -11% in February and -10% in June) and very mild growth (like +0.3% in April). The business stopped doubling its size and stabilized at around 1.15M per month.
- Ignoring the Noise: As analysts, we need to ignore the massive percentages at the very start (late 2016) and very end (Sept 2018) of the table. These months only have 1 recorded order, meaning they represent the setup phase and the cutoff date of our dataset, not actual business performance.
Seasonality Effect
30. How does the revenue of specific months compare across different years (e.g., Jan 2017 vs. Jan 2018)? Can we observe a consistent ‘Seasonality Effect’ where certain months (like November) consistently outperform others regardless of the year?
Solution:
-
To analyze the seasonality effect, revenue must first be aggregated by year and month. This should be done in the first CTE by joining the orders, payments, and calendar tables. The total revenue for each month of every year must be calculated while filtering out canceled or unavailable orders to ensure accuracy.
-
Next, the lag window function should be used to compare the revenue of the same month across different years. This will allow for a direct year-over-year comparison for each month, such as January 2017 vs January 2018. Additionally, the average monthly revenue for each year should be calculated to serve as a benchmark for identifying seasonal trends.
-
Once the current month revenue, previous year same month revenue, and yearly average are all aligned, a seasonality index can be calculated. This index will be derived by dividing the current month’s revenue by the yearly average. An index greater than 1.0 will indicate that the month outperformed the year’s average, suggesting a strong seasonal effect.
-
Finally, the seasonality index should be categorized to clearly identify which months are peak performers, standard performers, or low season periods. A case statement will be used to classify months with a seasonality index above 1.2 as ‘Peak Months’, those between 0.9 and 1.2 as ‘Standard Performance’, and those below 0.9 as ‘Low Season / Slack Period’. This final output will help determine if certain months consistently outperform others across different years, confirming the presence of a seasonality effect.
Show SQL Code
/* growth and seasonality analysis
objective:
1. compare specific months across years (e.g., Jan 2017 vs Jan 2018).
2. calculate a 'seasonality index' to identify recurring monthly trends.
logic:
- aggregate revenue by year and month.
- use window functions to compare same-month performance across years.
- calculate the deviation of each month from its year's average to find the 'seasonal push'.
*/
with monthly_revenue_base as (
-- step 1: aggregate total revenue by year and month
select
c.year,
c.month_number,
c.month_name,
sum(p.payment_value) as monthly_revenue
from
orders o
inner join
payments p on o.order_id = p.order_id
inner join
Calendar c on o.purchase_datekey = c.datekey
where
o.order_status not in ('canceled', 'unavailable')
group by
c.year, c.month_number, c.month_name
),
seasonal_indexing as (
-- step 2: calculate year-over-year same-month comparisons and yearly averages
select
*,
-- direct comparison: revenue of the same month in the previous year
prev_year_same_month_rev = lag(monthly_revenue) over (
partition by month_number
order by year
),
-- average monthly revenue for that specific year (benchmark)
yearly_avg_monthly_rev = avg(monthly_revenue) over (
partition by year
)
from
monthly_revenue_base
)
-- final output: quantifying the seasonality effect
select
year,
month_name,
cast(monthly_revenue as decimal(15,2)) as current_month_revenue,
-- yoy comparison for the specific month
cast(100.0 * (monthly_revenue - prev_year_same_month_rev) / nullif(prev_year_same_month_rev, 0) as decimal(10,2)) as month_specific_yoy_growth_pct,
-- seasonality index: revenue / yearly average
-- index > 1.0 means the month outperforms the year's average
cast(monthly_revenue / nullif(yearly_avg_monthly_rev, 0) as decimal(10,2)) as seasonality_index,
-- classification of the month's role in the business cycle
case
when (monthly_revenue / yearly_avg_monthly_rev) >= 1.2 then 'Peak Month (Strong Seasonality)'
when (monthly_revenue / yearly_avg_monthly_rev) between 0.9 and 1.2 then 'Standard Performance'
when (monthly_revenue / yearly_avg_monthly_rev) < 0.9 then 'Low Season / Slack Period'
else 'Baseline'
end as seasonal_classification
from
seasonal_indexing
order by
year, month_number;
Results:
| year | month_name | current_month_revenue | month_specific_yoy_growth_pct | seasonality_index | seasonal_classification |
|---|---|---|---|---|---|
| 2016 | September | 136.23 | 0.01 | Low Season / Slack Period | |
| 2016 | October | 51657.53 | 2.99 | Peak Month (Strong Seasonality) | |
| 2016 | December | 19.62 | 0.00 | Low Season / Slack Period | |
| 2017 | January | 137006.76 | 0.23 | Low Season / Slack Period | |
| 2017 | February | 283621.94 | 0.48 | Low Season / Slack Period | |
| 2017 | March | 425656.40 | 0.72 | Low Season / Slack Period | |
| 2017 | April | 405988.38 | 0.69 | Low Season / Slack Period | |
| 2017 | May | 582926.16 | 0.99 | Standard Performance | |
| 2017 | June | 499827.47 | 0.85 | Low Season / Slack Period | |
| 2017 | July | 578858.58 | 0.98 | Standard Performance | |
| 2017 | August | 662071.77 | 1.12 | Standard Performance | |
| 2017 | September | 717195.22 | 526359.11 | 1.21 | Peak Month (Strong Seasonality) |
| 2017 | October | 764785.28 | 1380.49 | 1.29 | Peak Month (Strong Seasonality) |
| 2017 | November | 1172639.23 | 1.98 | Peak Month (Strong Seasonality) | |
| 2017 | December | 861914.46 | 4392939.87 | 1.46 | Peak Month (Strong Seasonality) |
| 2018 | January | 1102639.41 | 704.81 | 1.15 | Standard Performance |
| 2018 | February | 979966.23 | 245.52 | 1.03 | Standard Performance |
| 2018 | March | 1152736.74 | 170.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | April | 1156303.91 | 184.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | May | 1145748.63 | 96.55 | 1.20 | Standard Performance |
| 2018 | June | 1020494.29 | 104.17 | 1.07 | Standard Performance |
| 2018 | July | 1039880.16 | 79.64 | 1.09 | Standard Performance |
| 2018 | August | 996896.15 | 50.57 | 1.04 | Standard Performance |
| 2018 | September | 166.46 | -99.98 | 0.00 | Low Season / Slack Period |
Insights:
- Massive Scaling: Comparing the same months across years shows the true scale of growth. January 2018 revenue was 704% higher than January 2017. Every month in 2018 outperformed the previous year by a huge margin.
- November is the Undisputed King: With a Seasonality Index of 1.98, November performs nearly twice as well as the average month. This confirms that Black Friday is the single most important event for the platform’s annual revenue.
- Shift to Year-Round Demand: In 2017, the first half of the year was considered “Low Season.” By 2018, months like March and April became “Peak Months,” showing that Olist successfully transitioned into a year-round shopping destination.
- Predictable Maturity: The Seasonality Index for 2018 stayed very stable (between 1.03 and 1.21). This indicates that the business became much more predictable as it matured, with less chaotic fluctuations than the previous year.
- The High-Revenue Window: A clear “Peak” cluster exists from September to January. This five-month window is the most critical period for the business, likely combining Black Friday, Christmas, and New Year sales.
Distribution of Order Volume by Day and Hour
31. What is the distribution of order volume across different Days of the Week and Hours of the Day? Specifically, do we see a ‘Lunchtime Spike’ (12 PM - 2 PM) or an ‘Evening Spike’ (8 PM - 10 PM)?
Solution:
-
To analyze the distribution of order volume across different days of the week and hours of the day, the order data must first be aggregated by extracting the day of the week and hour from the order purchase timestamp. This should be done in a CTE by using date functions to derive these time components. The total number of orders for each combination of day and hour must be calculated while filtering out canceled or unavailable orders.
-
Then we can analyze the aggregated data to identify specific patterns. A case statement should be used to classify hours between 12 PM and 2 PM as ‘Lunchtime Spike Window’ and hours between 8 PM and 10 PM as ‘Evening Spike Window’. All other hours can be classified as ‘Standard Time’.
-
Finally, the results should be ordered by the day of the week and hour to clearly visualize the distribution of order volume throughout the week and identify any consistent spikes during lunchtime or evening hours. This analysis will help determine if there are specific times when customer engagement is higher, which can inform marketing strategies and operational planning.
Show SQL Code
/* growth and seasonality analysis
objective:
1. compare specific months across years (e.g., Jan 2017 vs Jan 2018).
2. calculate a 'seasonality index' to identify recurring monthly trends.
3. analyze order distribution by Day of the Week and Hour of Day.
logic:
- aggregate revenue by year and month for high-level trends.
- use window functions for year-over-year same-month comparisons.
- extract time-of-day and day-of-week metrics to find peak engagement windows.
*/
with monthly_revenue_base as (
-- step 1: aggregate total revenue by year and month
select
c.year,
c.month_number,
c.month_name,
sum(p.payment_value) as monthly_revenue
from
orders o
inner join
payments p on o.order_id = p.order_id
inner join
Calendar c on o.purchase_datekey = c.datekey
where
o.order_status not in ('canceled', 'unavailable')
group by
c.year, c.month_number, c.month_name
),
seasonal_indexing as (
-- step 2: calculate year-over-year same-month comparisons and yearly averages
select
*,
-- direct comparison: revenue of the same month in the previous year
prev_year_same_month_rev = lag(monthly_revenue) over (
partition by month_number
order by year
),
-- average monthly revenue for that specific year (benchmark)
yearly_avg_monthly_rev = avg(monthly_revenue) over (
partition by year
)
from
monthly_revenue_base
),
-- step 3: analyzing hourly and weekly volume distribution
time_of_day_stats as (
select
datename(dw, order_purchase_timestamp) as day_of_week,
datepart(dw, order_purchase_timestamp) as day_num, -- used for sorting
datepart(hour, order_purchase_timestamp) as hour_of_day,
count(order_id) as total_orders
from
orders
where
order_status not in ('canceled', 'unavailable')
group by
datename(dw, order_purchase_timestamp),
datepart(dw, order_purchase_timestamp),
datepart(hour, order_purchase_timestamp)
)
-- final output: combining the original seasonality analysis with the new time-of-day insights
-- part 1: monthly seasonality (existing)
select
year,
month_name,
cast(monthly_revenue as decimal(15,2)) as current_month_revenue,
-- yoy comparison for the specific month
cast(100.0 * (monthly_revenue - prev_year_same_month_rev) / nullif(prev_year_same_month_rev, 0) as decimal(10,2)) as month_specific_yoy_growth_pct,
-- seasonality index
cast(monthly_revenue / nullif(yearly_avg_monthly_rev, 0) as decimal(10,2)) as seasonality_index,
case
when (monthly_revenue / yearly_avg_monthly_rev) >= 1.2 then 'Peak Month (Strong Seasonality)'
when (monthly_revenue / yearly_avg_monthly_rev) between 0.9 and 1.2 then 'Standard Performance'
when (monthly_revenue / yearly_avg_monthly_rev) < 0.9 then 'Low Season / Slack Period'
else 'Baseline'
end as seasonal_classification
from
seasonal_indexing
union all
-- part 2: placeholder/separator row for clear data interpretation if exported
select
null, '--- INTRA-DAY ANALYSIS ---', null, null, null, null
union all
-- part 3: day/hour distribution
select
day_num as year, -- repurposing columns for the union's schema
concat(day_of_week, ' at ', hour_of_day, ':00') as month_name,
total_orders as current_month_revenue,
null as month_specific_yoy_growth_pct,
null as seasonality_index,
case
when hour_of_day between 12 and 14 then 'Lunchtime Spike Window'
when hour_of_day between 20 and 22 then 'Evening Spike Window'
else 'Standard Time'
end as seasonal_classification
from
time_of_day_stats
order by
year, month_name;
Results:
| year | month_name | current_month_revenue | month_specific_yoy_growth_pct | seasonality_index | seasonal_classification |
|---|---|---|---|---|---|
| --- INTRA-DAY ANALYSIS --- | |||||
| 1 | Sunday at 0:00 | 260.00 | Standard Time | ||
| 1 | Sunday at 1:00 | 140.00 | Standard Time | ||
| 1 | Sunday at 10:00 | 524.00 | Standard Time | ||
| 1 | Sunday at 11:00 | 715.00 | Standard Time | ||
| 1 | Sunday at 12:00 | 669.00 | Lunchtime Spike Window | ||
| 1 | Sunday at 13:00 | 714.00 | Lunchtime Spike Window | ||
| 1 | Sunday at 14:00 | 676.00 | Lunchtime Spike Window | ||
| 1 | Sunday at 15:00 | 707.00 | Standard Time | ||
| 1 | Sunday at 16:00 | 706.00 | Standard Time | ||
| 1 | Sunday at 17:00 | 792.00 | Standard Time | ||
| 1 | Sunday at 18:00 | 888.00 | Standard Time | ||
| 1 | Sunday at 19:00 | 892.00 | Standard Time | ||
| 1 | Sunday at 2:00 | 68.00 | Standard Time | ||
| 1 | Sunday at 20:00 | 956.00 | Evening Spike Window | ||
| 1 | Sunday at 21:00 | 876.00 | Evening Spike Window | ||
| 1 | Sunday at 22:00 | 864.00 | Evening Spike Window | ||
| 1 | Sunday at 23:00 | 603.00 | Standard Time | ||
| 1 | Sunday at 3:00 | 43.00 | Standard Time | ||
| 1 | Sunday at 4:00 | 27.00 | Standard Time | ||
| 1 | Sunday at 5:00 | 27.00 | Standard Time | ||
| 1 | Sunday at 6:00 | 33.00 | Standard Time | ||
| 1 | Sunday at 7:00 | 105.00 | Standard Time | ||
| 1 | Sunday at 8:00 | 203.00 | Standard Time | ||
| 1 | Sunday at 9:00 | 343.00 | Standard Time | ||
| 2 | Monday at 0:00 | 322.00 | Standard Time | ||
| 2 | Monday at 1:00 | 134.00 | Standard Time | ||
| 2 | Monday at 10:00 | 1020.00 | Standard Time | ||
| 2 | Monday at 11:00 | 1068.00 | Standard Time | ||
| 2 | Monday at 12:00 | 961.00 | Lunchtime Spike Window | ||
| 2 | Monday at 13:00 | 1013.00 | Lunchtime Spike Window | ||
| 2 | Monday at 14:00 | 1082.00 | Lunchtime Spike Window | ||
| 2 | Monday at 15:00 | 1065.00 | Standard Time | ||
| 2 | Monday at 16:00 | 1076.00 | Standard Time | ||
| 2 | Monday at 17:00 | 974.00 | Standard Time | ||
| 2 | Monday at 18:00 | 915.00 | Standard Time | ||
| 2 | Monday at 19:00 | 933.00 | Standard Time | ||
| 2 | Monday at 2:00 | 64.00 | Standard Time | ||
| 2 | Monday at 20:00 | 1016.00 | Evening Spike Window | ||
| 2 | Monday at 21:00 | 1103.00 | Evening Spike Window | ||
| 2 | Monday at 22:00 | 982.00 | Evening Spike Window | ||
| 2 | Monday at 23:00 | 708.00 | Standard Time | ||
| 2 | Monday at 3:00 | 36.00 | Standard Time | ||
| 2 | Monday at 4:00 | 21.00 | Standard Time | ||
| 2 | Monday at 5:00 | 22.00 | Standard Time | ||
| 2 | Monday at 6:00 | 65.00 | Standard Time | ||
| 2 | Monday at 7:00 | 159.00 | Standard Time | ||
| 2 | Monday at 8:00 | 475.00 | Standard Time | ||
| 2 | Monday at 9:00 | 765.00 | Standard Time | ||
| 3 | Tuesday at 0:00 | 304.00 | Standard Time | ||
| 3 | Tuesday at 1:00 | 157.00 | Standard Time | ||
| 3 | Tuesday at 10:00 | 950.00 | Standard Time | ||
| 3 | Tuesday at 11:00 | 1040.00 | Standard Time | ||
| 3 | Tuesday at 12:00 | 889.00 | Lunchtime Spike Window | ||
| 3 | Tuesday at 13:00 | 1036.00 | Lunchtime Spike Window | ||
| 3 | Tuesday at 14:00 | 1106.00 | Lunchtime Spike Window | ||
| 3 | Tuesday at 15:00 | 1026.00 | Standard Time | ||
| 3 | Tuesday at 16:00 | 1074.00 | Standard Time | ||
| 3 | Tuesday at 17:00 | 956.00 | Standard Time | ||
| 3 | Tuesday at 18:00 | 868.00 | Standard Time | ||
| 3 | Tuesday at 19:00 | 909.00 | Standard Time | ||
| 3 | Tuesday at 2:00 | 80.00 | Standard Time | ||
| 3 | Tuesday at 20:00 | 970.00 | Evening Spike Window | ||
| 3 | Tuesday at 21:00 | 1017.00 | Evening Spike Window | ||
| 3 | Tuesday at 22:00 | 955.00 | Evening Spike Window | ||
| 3 | Tuesday at 23:00 | 685.00 | Standard Time | ||
| 3 | Tuesday at 3:00 | 28.00 | Standard Time | ||
| 3 | Tuesday at 4:00 | 28.00 | Standard Time | ||
| 3 | Tuesday at 5:00 | 24.00 | Standard Time | ||
| 3 | Tuesday at 6:00 | 71.00 | Standard Time | ||
| 3 | Tuesday at 7:00 | 219.00 | Standard Time | ||
| 3 | Tuesday at 8:00 | 515.00 | Standard Time | ||
| 3 | Tuesday at 9:00 | 853.00 | Standard Time | ||
| 4 | Wednesday at 0:00 | 392.00 | Standard Time | ||
| 4 | Wednesday at 1:00 | 175.00 | Standard Time | ||
| 4 | Wednesday at 10:00 | 1029.00 | Standard Time | ||
| 4 | Wednesday at 11:00 | 1046.00 | Standard Time | ||
| 4 | Wednesday at 12:00 | 912.00 | Lunchtime Spike Window | ||
| 4 | Wednesday at 13:00 | 1009.00 | Lunchtime Spike Window | ||
| 4 | Wednesday at 14:00 | 1039.00 | Lunchtime Spike Window | ||
| 4 | Wednesday at 15:00 | 971.00 | Standard Time | ||
| 4 | Wednesday at 16:00 | 1024.00 | Standard Time | ||
| 4 | Wednesday at 17:00 | 958.00 | Standard Time | ||
| 4 | Wednesday at 18:00 | 837.00 | Standard Time | ||
| 4 | Wednesday at 19:00 | 833.00 | Standard Time | ||
| 4 | Wednesday at 2:00 | 81.00 | Standard Time | ||
| 4 | Wednesday at 20:00 | 892.00 | Evening Spike Window | ||
| 4 | Wednesday at 21:00 | 953.00 | Evening Spike Window | ||
| 4 | Wednesday at 22:00 | 872.00 | Evening Spike Window | ||
| 4 | Wednesday at 23:00 | 612.00 | Standard Time | ||
| 4 | Wednesday at 3:00 | 31.00 | Standard Time | ||
| 4 | Wednesday at 4:00 | 33.00 | Standard Time | ||
| 4 | Wednesday at 5:00 | 26.00 | Standard Time | ||
| 4 | Wednesday at 6:00 | 93.00 | Standard Time | ||
| 4 | Wednesday at 7:00 | 210.00 | Standard Time | ||
| 4 | Wednesday at 8:00 | 508.00 | Standard Time | ||
| 4 | Wednesday at 9:00 | 817.00 | Standard Time | ||
| 5 | Thursday at 0:00 | 354.00 | Standard Time | ||
| 5 | Thursday at 1:00 | 167.00 | Standard Time | ||
| 5 | Thursday at 10:00 | 970.00 | Standard Time | ||
| 5 | Thursday at 11:00 | 945.00 | Standard Time | ||
| 5 | Thursday at 12:00 | 963.00 | Lunchtime Spike Window | ||
| 5 | Thursday at 13:00 | 968.00 | Lunchtime Spike Window | ||
| 5 | Thursday at 14:00 | 968.00 | Lunchtime Spike Window | ||
| 5 | Thursday at 15:00 | 916.00 | Standard Time | ||
| 5 | Thursday at 16:00 | 1066.00 | Standard Time | ||
| 5 | Thursday at 17:00 | 893.00 | Standard Time | ||
| 5 | Thursday at 18:00 | 776.00 | Standard Time | ||
| 5 | Thursday at 19:00 | 814.00 | Standard Time | ||
| 5 | Thursday at 2:00 | 74.00 | Standard Time | ||
| 5 | Thursday at 20:00 | 829.00 | Evening Spike Window | ||
| 5 | Thursday at 21:00 | 832.00 | Evening Spike Window | ||
| 5 | Thursday at 22:00 | 850.00 | Evening Spike Window | ||
| 5 | Thursday at 23:00 | 541.00 | Standard Time | ||
| 5 | Thursday at 3:00 | 39.00 | Standard Time | ||
| 5 | Thursday at 4:00 | 31.00 | Standard Time | ||
| 5 | Thursday at 5:00 | 28.00 | Standard Time | ||
| 5 | Thursday at 6:00 | 83.00 | Standard Time | ||
| 5 | Thursday at 7:00 | 217.00 | Standard Time | ||
| 5 | Thursday at 8:00 | 497.00 | Standard Time | ||
| 5 | Thursday at 9:00 | 750.00 | Standard Time | ||
| 6 | Friday at 0:00 | 418.00 | Standard Time | ||
| 6 | Friday at 1:00 | 210.00 | Standard Time | ||
| 6 | Friday at 10:00 | 942.00 | Standard Time | ||
| 6 | Friday at 11:00 | 983.00 | Standard Time | ||
| 6 | Friday at 12:00 | 843.00 | Lunchtime Spike Window | ||
| 6 | Friday at 13:00 | 989.00 | Lunchtime Spike Window | ||
| 6 | Friday at 14:00 | 950.00 | Lunchtime Spike Window | ||
| 6 | Friday at 15:00 | 969.00 | Standard Time | ||
| 6 | Friday at 16:00 | 960.00 | Standard Time | ||
| 6 | Friday at 17:00 | 807.00 | Standard Time | ||
| 6 | Friday at 18:00 | 716.00 | Standard Time | ||
| 6 | Friday at 19:00 | 772.00 | Standard Time | ||
| 6 | Friday at 2:00 | 70.00 | Standard Time | ||
| 6 | Friday at 20:00 | 730.00 | Evening Spike Window | ||
| 6 | Friday at 21:00 | 714.00 | Evening Spike Window | ||
| 6 | Friday at 22:00 | 695.00 | Evening Spike Window | ||
| 6 | Friday at 23:00 | 507.00 | Standard Time | ||
| 6 | Friday at 3:00 | 48.00 | Standard Time | ||
| 6 | Friday at 4:00 | 40.00 | Standard Time | ||
| 6 | Friday at 5:00 | 35.00 | Standard Time | ||
| 6 | Friday at 6:00 | 96.00 | Standard Time | ||
| 6 | Friday at 7:00 | 204.00 | Standard Time | ||
| 6 | Friday at 8:00 | 493.00 | Standard Time | ||
| 6 | Friday at 9:00 | 758.00 | Standard Time | ||
| 7 | Saturday at 0:00 | 312.00 | Standard Time | ||
| 7 | Saturday at 1:00 | 171.00 | Standard Time | ||
| 7 | Saturday at 10:00 | 648.00 | Standard Time | ||
| 7 | Saturday at 11:00 | 701.00 | Standard Time | ||
| 7 | Saturday at 12:00 | 681.00 | Lunchtime Spike Window | ||
| 7 | Saturday at 13:00 | 702.00 | Lunchtime Spike Window | ||
| 7 | Saturday at 14:00 | 670.00 | Lunchtime Spike Window | ||
| 7 | Saturday at 15:00 | 716.00 | Standard Time | ||
| 7 | Saturday at 16:00 | 688.00 | Standard Time | ||
| 7 | Saturday at 17:00 | 694.00 | Standard Time | ||
| 7 | Saturday at 18:00 | 707.00 | Standard Time | ||
| 7 | Saturday at 19:00 | 746.00 | Standard Time | ||
| 7 | Saturday at 2:00 | 66.00 | Standard Time | ||
| 7 | Saturday at 20:00 | 723.00 | Evening Spike Window | ||
| 7 | Saturday at 21:00 | 651.00 | Evening Spike Window | ||
| 7 | Saturday at 22:00 | 543.00 | Evening Spike Window | ||
| 7 | Saturday at 23:00 | 425.00 | Standard Time | ||
| 7 | Saturday at 3:00 | 42.00 | Standard Time | ||
| 7 | Saturday at 4:00 | 25.00 | Standard Time | ||
| 7 | Saturday at 5:00 | 23.00 | Standard Time | ||
| 7 | Saturday at 6:00 | 52.00 | Standard Time | ||
| 7 | Saturday at 7:00 | 104.00 | Standard Time | ||
| 7 | Saturday at 8:00 | 248.00 | Standard Time | ||
| 7 | Saturday at 9:00 | 426.00 | Standard Time | ||
| 2016 | December | 19.62 | 0.00 | Low Season / Slack Period | |
| 2016 | October | 51657.53 | 2.99 | Peak Month (Strong Seasonality) | |
| 2016 | September | 136.23 | 0.01 | Low Season / Slack Period | |
| 2017 | April | 405988.38 | 0.69 | Low Season / Slack Period | |
| 2017 | August | 662071.77 | 1.12 | Standard Performance | |
| 2017 | December | 861914.46 | 4392939.87 | 1.46 | Peak Month (Strong Seasonality) |
| 2017 | February | 283621.94 | 0.48 | Low Season / Slack Period | |
| 2017 | January | 137006.76 | 0.23 | Low Season / Slack Period | |
| 2017 | July | 578858.58 | 0.98 | Standard Performance | |
| 2017 | June | 499827.47 | 0.85 | Low Season / Slack Period | |
| 2017 | March | 425656.40 | 0.72 | Low Season / Slack Period | |
| 2017 | May | 582926.16 | 0.99 | Standard Performance | |
| 2017 | November | 1172639.23 | 1.98 | Peak Month (Strong Seasonality) | |
| 2017 | October | 764785.28 | 1380.49 | 1.29 | Peak Month (Strong Seasonality) |
| 2017 | September | 717195.22 | 526359.11 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | April | 1156303.91 | 184.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | August | 996896.15 | 50.57 | 1.04 | Standard Performance |
| 2018 | February | 979966.23 | 245.52 | 1.03 | Standard Performance |
| 2018 | January | 1102639.41 | 704.81 | 1.15 | Standard Performance |
| 2018 | July | 1039880.16 | 79.64 | 1.09 | Standard Performance |
| 2018 | June | 1020494.29 | 104.17 | 1.07 | Standard Performance |
| 2018 | March | 1152736.74 | 170.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | May | 1145748.63 | 96.55 | 1.20 | Standard Performance |
| 2018 | September | 166.46 | -99.98 | 0.00 | Low Season / Slack Period |
Insights:
- Monday is the Main Event: Monday is the busiest day of the week for Olist. It hits peak volumes at 2:00 PM and 9:00 PM. Overall, sales are significantly higher during the workweek (Monday–Friday) than on weekends, showing that customers prefer shopping during work hours or their commute.
- The Lunchtime Surge: There is a very consistent “Lunchtime Spike” every single weekday. Between 12:00 PM and 2:00 PM, order counts surge as people shop on their breaks. On Tuesdays, for example, volume jumps from 853 at 9:00 AM to a peak of 1,106 by 2:00 PM.
- The Evening Peak: Regardless of the day, the highest activity happens during the “Evening Spike” between 8:00 PM and 10:00 PM. Monday at 9:00 PM is the busiest single hour of the entire week with 1,103 orders.
- Weekend Drop-off: Shopping activity cools down on Saturdays and Sundays. While the evening spike still exists on Sunday (956 orders at 8:00 PM), the overall baseline is much lower. Saturday is the quietest day on the platform across almost every hour.
- Late-Night Dormancy: Across all days, shopping almost completely stops between 2:00 AM and 6:00 AM, with most hours dropping below 40 orders. This is the ideal window for system maintenance since traffic is at its lowest.
Cumulative Revenue and Growth Trajectory
32. What is the cumulative revenue generated over time since the inception of the platform? By plotting the running total of sales, can we visually confirm the business’s transition from ‘Linear Growth’ to ‘Exponential Growth’ (The Hockey Stick Curve)?
Solution:
-
To track cumulative revenue and observe the hockey stick growth curve, the total revenue must first be aggregated by year and month. This should be done in the first CTE by joining the orders, payments, and calendar tables, ensuring that canceled or unavailable orders are strictly filtered out.
-
Next, a running total of the monthly revenue must be calculated. Window functions should be applied here to continuously sum the monthly revenue over time, ordered chronologically by year and month. This cumulative revenue metric will clearly show the overall growth trajectory of the business from its inception. In the exact same step, historical year-over-year comparisons and yearly averages will also be established to maintain the baseline seasonality analysis.
-
To find the peak customer engagement windows, the order distribution by day of the week and hour of the day must be analyzed. A separate CTE should be created to extract the specific day name and hour from the order purchase timestamps. The total number of orders must then be counted for each specific hour of every day to find intraday shopping patterns.
-
Finally, these distinct sets of insights must be combined into a single final output. The monthly seasonality and cumulative growth metrics should be selected first. Then, a union all statement must be used to append the day and hour distribution below the monthly data. A case statement will be applied here to tag specific times as lunchtime or evening spike windows. This unified approach will allow the business to view both high-level exponential growth and granular daily shopping habits in one comprehensive report.
Show SQL Code
/* growth and seasonality analysis
objective:
1. compare specific months across years (e.g., Jan 2017 vs Jan 2018).
2. calculate a 'seasonality index' to identify recurring monthly trends.
3. analyze order distribution by Day of the Week and Hour of Day.
4. calculate cumulative revenue to track "The Hockey Stick" growth curve.
logic:
- aggregate revenue by year and month for high-level trends.
- use window functions for year-over-year and running total calculations.
- extract time-of-day and day-of-week metrics to find peak engagement windows.
*/
with monthly_revenue_base as (
-- step 1: aggregate total revenue by year and month
select
c.year,
c.month_number,
c.month_name,
sum(p.payment_value) as monthly_revenue
from
orders o
inner join
payments p on o.order_id = p.order_id
inner join
Calendar c on o.purchase_datekey = c.datekey
where
o.order_status not in ('canceled', 'unavailable')
group by
c.year, c.month_number, c.month_name
),
seasonal_indexing as (
-- step 2: calculate YoY comparisons, yearly averages, and cumulative running total
select
*,
-- direct comparison: revenue of the same month in the previous year
prev_year_same_month_rev = lag(monthly_revenue) over (
partition by month_number
order by year
),
-- average monthly revenue for that specific year (benchmark)
yearly_avg_monthly_rev = avg(monthly_revenue) over (
partition by year
),
-- cumulative revenue: running total since the platform inception
running_total_revenue = sum(monthly_revenue) over (
order by year, month_number
)
from
monthly_revenue_base
),
-- step 3: analyzing hourly and weekly volume distribution
time_of_day_stats as (
select
datename(dw, order_purchase_timestamp) as day_of_week,
datepart(dw, order_purchase_timestamp) as day_num, -- used for sorting
datepart(hour, order_purchase_timestamp) as hour_of_day,
count(order_id) as total_orders
from
orders
where
order_status not in ('canceled', 'unavailable')
group by
datename(dw, order_purchase_timestamp),
datepart(dw, order_purchase_timestamp),
datepart(hour, order_purchase_timestamp)
)
-- final output: combining the original seasonality analysis with the new running total and time-of-day insights
-- part 1: monthly seasonality and cumulative growth
select
year,
month_name,
cast(monthly_revenue as decimal(15,2)) as current_month_revenue,
cast(running_total_revenue as decimal(15,2)) as cumulative_revenue,
-- yoy comparison for the specific month
cast(100.0 * (monthly_revenue - prev_year_same_month_rev) / nullif(prev_year_same_month_rev, 0) as decimal(10,2)) as month_specific_yoy_growth_pct,
-- seasonality index
cast(monthly_revenue / nullif(yearly_avg_monthly_rev, 0) as decimal(10,2)) as seasonality_index,
case
when (monthly_revenue / yearly_avg_monthly_rev) >= 1.2 then 'Peak Month (Strong Seasonality)'
when (monthly_revenue / yearly_avg_monthly_rev) between 0.9 and 1.2 then 'Standard Performance'
when (monthly_revenue / yearly_avg_monthly_rev) < 0.9 then 'Low Season / Slack Period'
else 'Baseline'
end as seasonal_classification
from
seasonal_indexing
union all
-- part 2: placeholder/separator row
select
null, '--- INTRA-DAY ANALYSIS ---', null, null, null, null, null
union all
-- part 3: day/hour distribution
select
day_num as year,
concat(day_of_week, ' at ', hour_of_day, ':00') as month_name,
total_orders as current_month_revenue,
null as cumulative_revenue,
null as month_specific_yoy_growth_pct,
null as seasonality_index,
case
when hour_of_day between 12 and 14 then 'Lunchtime Spike Window'
when hour_of_day between 20 and 22 then 'Evening Spike Window'
else 'Standard Time'
end as seasonal_classification
from
time_of_day_stats
order by
year, month_name;
Results:
| year | month_name | current_month_revenue | cumulative_revenue | month_specific_yoy_growth_pct | seasonality_index | seasonal_classification |
|---|---|---|---|---|---|---|
| --- INTRA-DAY ANALYSIS --- | ||||||
| 1 | Sunday at 0:00 | 260.00 | Standard Time | |||
| 1 | Sunday at 1:00 | 140.00 | Standard Time | |||
| 1 | Sunday at 10:00 | 524.00 | Standard Time | |||
| 1 | Sunday at 11:00 | 715.00 | Standard Time | |||
| 1 | Sunday at 12:00 | 669.00 | Lunchtime Spike Window | |||
| 1 | Sunday at 13:00 | 714.00 | Lunchtime Spike Window | |||
| 1 | Sunday at 14:00 | 676.00 | Lunchtime Spike Window | |||
| 1 | Sunday at 15:00 | 707.00 | Standard Time | |||
| 1 | Sunday at 16:00 | 706.00 | Standard Time | |||
| 1 | Sunday at 17:00 | 792.00 | Standard Time | |||
| 1 | Sunday at 18:00 | 888.00 | Standard Time | |||
| 1 | Sunday at 19:00 | 892.00 | Standard Time | |||
| 1 | Sunday at 2:00 | 68.00 | Standard Time | |||
| 1 | Sunday at 20:00 | 956.00 | Evening Spike Window | |||
| 1 | Sunday at 21:00 | 876.00 | Evening Spike Window | |||
| 1 | Sunday at 22:00 | 864.00 | Evening Spike Window | |||
| 1 | Sunday at 23:00 | 603.00 | Standard Time | |||
| 1 | Sunday at 3:00 | 43.00 | Standard Time | |||
| 1 | Sunday at 4:00 | 27.00 | Standard Time | |||
| 1 | Sunday at 5:00 | 27.00 | Standard Time | |||
| 1 | Sunday at 6:00 | 33.00 | Standard Time | |||
| 1 | Sunday at 7:00 | 105.00 | Standard Time | |||
| 1 | Sunday at 8:00 | 203.00 | Standard Time | |||
| 1 | Sunday at 9:00 | 343.00 | Standard Time | |||
| 2 | Monday at 0:00 | 322.00 | Standard Time | |||
| 2 | Monday at 1:00 | 134.00 | Standard Time | |||
| 2 | Monday at 10:00 | 1020.00 | Standard Time | |||
| 2 | Monday at 11:00 | 1068.00 | Standard Time | |||
| 2 | Monday at 12:00 | 961.00 | Lunchtime Spike Window | |||
| 2 | Monday at 13:00 | 1013.00 | Lunchtime Spike Window | |||
| 2 | Monday at 14:00 | 1082.00 | Lunchtime Spike Window | |||
| 2 | Monday at 15:00 | 1065.00 | Standard Time | |||
| 2 | Monday at 16:00 | 1076.00 | Standard Time | |||
| 2 | Monday at 17:00 | 974.00 | Standard Time | |||
| 2 | Monday at 18:00 | 915.00 | Standard Time | |||
| 2 | Monday at 19:00 | 933.00 | Standard Time | |||
| 2 | Monday at 2:00 | 64.00 | Standard Time | |||
| 2 | Monday at 20:00 | 1016.00 | Evening Spike Window | |||
| 2 | Monday at 21:00 | 1103.00 | Evening Spike Window | |||
| 2 | Monday at 22:00 | 982.00 | Evening Spike Window | |||
| 2 | Monday at 23:00 | 708.00 | Standard Time | |||
| 2 | Monday at 3:00 | 36.00 | Standard Time | |||
| 2 | Monday at 4:00 | 21.00 | Standard Time | |||
| 2 | Monday at 5:00 | 22.00 | Standard Time | |||
| 2 | Monday at 6:00 | 65.00 | Standard Time | |||
| 2 | Monday at 7:00 | 159.00 | Standard Time | |||
| 2 | Monday at 8:00 | 475.00 | Standard Time | |||
| 2 | Monday at 9:00 | 765.00 | Standard Time | |||
| 3 | Tuesday at 0:00 | 304.00 | Standard Time | |||
| 3 | Tuesday at 1:00 | 157.00 | Standard Time | |||
| 3 | Tuesday at 10:00 | 950.00 | Standard Time | |||
| 3 | Tuesday at 11:00 | 1040.00 | Standard Time | |||
| 3 | Tuesday at 12:00 | 889.00 | Lunchtime Spike Window | |||
| 3 | Tuesday at 13:00 | 1036.00 | Lunchtime Spike Window | |||
| 3 | Tuesday at 14:00 | 1106.00 | Lunchtime Spike Window | |||
| 3 | Tuesday at 15:00 | 1026.00 | Standard Time | |||
| 3 | Tuesday at 16:00 | 1074.00 | Standard Time | |||
| 3 | Tuesday at 17:00 | 956.00 | Standard Time | |||
| 3 | Tuesday at 18:00 | 868.00 | Standard Time | |||
| 3 | Tuesday at 19:00 | 909.00 | Standard Time | |||
| 3 | Tuesday at 2:00 | 80.00 | Standard Time | |||
| 3 | Tuesday at 20:00 | 970.00 | Evening Spike Window | |||
| 3 | Tuesday at 21:00 | 1017.00 | Evening Spike Window | |||
| 3 | Tuesday at 22:00 | 955.00 | Evening Spike Window | |||
| 3 | Tuesday at 23:00 | 685.00 | Standard Time | |||
| 3 | Tuesday at 3:00 | 28.00 | Standard Time | |||
| 3 | Tuesday at 4:00 | 28.00 | Standard Time | |||
| 3 | Tuesday at 5:00 | 24.00 | Standard Time | |||
| 3 | Tuesday at 6:00 | 71.00 | Standard Time | |||
| 3 | Tuesday at 7:00 | 219.00 | Standard Time | |||
| 3 | Tuesday at 8:00 | 515.00 | Standard Time | |||
| 3 | Tuesday at 9:00 | 853.00 | Standard Time | |||
| 4 | Wednesday at 0:00 | 392.00 | Standard Time | |||
| 4 | Wednesday at 1:00 | 175.00 | Standard Time | |||
| 4 | Wednesday at 10:00 | 1029.00 | Standard Time | |||
| 4 | Wednesday at 11:00 | 1046.00 | Standard Time | |||
| 4 | Wednesday at 12:00 | 912.00 | Lunchtime Spike Window | |||
| 4 | Wednesday at 13:00 | 1009.00 | Lunchtime Spike Window | |||
| 4 | Wednesday at 14:00 | 1039.00 | Lunchtime Spike Window | |||
| 4 | Wednesday at 15:00 | 971.00 | Standard Time | |||
| 4 | Wednesday at 16:00 | 1024.00 | Standard Time | |||
| 4 | Wednesday at 17:00 | 958.00 | Standard Time | |||
| 4 | Wednesday at 18:00 | 837.00 | Standard Time | |||
| 4 | Wednesday at 19:00 | 833.00 | Standard Time | |||
| 4 | Wednesday at 2:00 | 81.00 | Standard Time | |||
| 4 | Wednesday at 20:00 | 892.00 | Evening Spike Window | |||
| 4 | Wednesday at 21:00 | 953.00 | Evening Spike Window | |||
| 4 | Wednesday at 22:00 | 872.00 | Evening Spike Window | |||
| 4 | Wednesday at 23:00 | 612.00 | Standard Time | |||
| 4 | Wednesday at 3:00 | 31.00 | Standard Time | |||
| 4 | Wednesday at 4:00 | 33.00 | Standard Time | |||
| 4 | Wednesday at 5:00 | 26.00 | Standard Time | |||
| 4 | Wednesday at 6:00 | 93.00 | Standard Time | |||
| 4 | Wednesday at 7:00 | 210.00 | Standard Time | |||
| 4 | Wednesday at 8:00 | 508.00 | Standard Time | |||
| 4 | Wednesday at 9:00 | 817.00 | Standard Time | |||
| 5 | Thursday at 0:00 | 354.00 | Standard Time | |||
| 5 | Thursday at 1:00 | 167.00 | Standard Time | |||
| 5 | Thursday at 10:00 | 970.00 | Standard Time | |||
| 5 | Thursday at 11:00 | 945.00 | Standard Time | |||
| 5 | Thursday at 12:00 | 963.00 | Lunchtime Spike Window | |||
| 5 | Thursday at 13:00 | 968.00 | Lunchtime Spike Window | |||
| 5 | Thursday at 14:00 | 968.00 | Lunchtime Spike Window | |||
| 5 | Thursday at 15:00 | 916.00 | Standard Time | |||
| 5 | Thursday at 16:00 | 1066.00 | Standard Time | |||
| 5 | Thursday at 17:00 | 893.00 | Standard Time | |||
| 5 | Thursday at 18:00 | 776.00 | Standard Time | |||
| 5 | Thursday at 19:00 | 814.00 | Standard Time | |||
| 5 | Thursday at 2:00 | 74.00 | Standard Time | |||
| 5 | Thursday at 20:00 | 829.00 | Evening Spike Window | |||
| 5 | Thursday at 21:00 | 832.00 | Evening Spike Window | |||
| 5 | Thursday at 22:00 | 850.00 | Evening Spike Window | |||
| 5 | Thursday at 23:00 | 541.00 | Standard Time | |||
| 5 | Thursday at 3:00 | 39.00 | Standard Time | |||
| 5 | Thursday at 4:00 | 31.00 | Standard Time | |||
| 5 | Thursday at 5:00 | 28.00 | Standard Time | |||
| 5 | Thursday at 6:00 | 83.00 | Standard Time | |||
| 5 | Thursday at 7:00 | 217.00 | Standard Time | |||
| 5 | Thursday at 8:00 | 497.00 | Standard Time | |||
| 5 | Thursday at 9:00 | 750.00 | Standard Time | |||
| 6 | Friday at 0:00 | 418.00 | Standard Time | |||
| 6 | Friday at 1:00 | 210.00 | Standard Time | |||
| 6 | Friday at 10:00 | 942.00 | Standard Time | |||
| 6 | Friday at 11:00 | 983.00 | Standard Time | |||
| 6 | Friday at 12:00 | 843.00 | Lunchtime Spike Window | |||
| 6 | Friday at 13:00 | 989.00 | Lunchtime Spike Window | |||
| 6 | Friday at 14:00 | 950.00 | Lunchtime Spike Window | |||
| 6 | Friday at 15:00 | 969.00 | Standard Time | |||
| 6 | Friday at 16:00 | 960.00 | Standard Time | |||
| 6 | Friday at 17:00 | 807.00 | Standard Time | |||
| 6 | Friday at 18:00 | 716.00 | Standard Time | |||
| 6 | Friday at 19:00 | 772.00 | Standard Time | |||
| 6 | Friday at 2:00 | 70.00 | Standard Time | |||
| 6 | Friday at 20:00 | 730.00 | Evening Spike Window | |||
| 6 | Friday at 21:00 | 714.00 | Evening Spike Window | |||
| 6 | Friday at 22:00 | 695.00 | Evening Spike Window | |||
| 6 | Friday at 23:00 | 507.00 | Standard Time | |||
| 6 | Friday at 3:00 | 48.00 | Standard Time | |||
| 6 | Friday at 4:00 | 40.00 | Standard Time | |||
| 6 | Friday at 5:00 | 35.00 | Standard Time | |||
| 6 | Friday at 6:00 | 96.00 | Standard Time | |||
| 6 | Friday at 7:00 | 204.00 | Standard Time | |||
| 6 | Friday at 8:00 | 493.00 | Standard Time | |||
| 6 | Friday at 9:00 | 758.00 | Standard Time | |||
| 7 | Saturday at 0:00 | 312.00 | Standard Time | |||
| 7 | Saturday at 1:00 | 171.00 | Standard Time | |||
| 7 | Saturday at 10:00 | 648.00 | Standard Time | |||
| 7 | Saturday at 11:00 | 701.00 | Standard Time | |||
| 7 | Saturday at 12:00 | 681.00 | Lunchtime Spike Window | |||
| 7 | Saturday at 13:00 | 702.00 | Lunchtime Spike Window | |||
| 7 | Saturday at 14:00 | 670.00 | Lunchtime Spike Window | |||
| 7 | Saturday at 15:00 | 716.00 | Standard Time | |||
| 7 | Saturday at 16:00 | 688.00 | Standard Time | |||
| 7 | Saturday at 17:00 | 694.00 | Standard Time | |||
| 7 | Saturday at 18:00 | 707.00 | Standard Time | |||
| 7 | Saturday at 19:00 | 746.00 | Standard Time | |||
| 7 | Saturday at 2:00 | 66.00 | Standard Time | |||
| 7 | Saturday at 20:00 | 723.00 | Evening Spike Window | |||
| 7 | Saturday at 21:00 | 651.00 | Evening Spike Window | |||
| 7 | Saturday at 22:00 | 543.00 | Evening Spike Window | |||
| 7 | Saturday at 23:00 | 425.00 | Standard Time | |||
| 7 | Saturday at 3:00 | 42.00 | Standard Time | |||
| 7 | Saturday at 4:00 | 25.00 | Standard Time | |||
| 7 | Saturday at 5:00 | 23.00 | Standard Time | |||
| 7 | Saturday at 6:00 | 52.00 | Standard Time | |||
| 7 | Saturday at 7:00 | 104.00 | Standard Time | |||
| 7 | Saturday at 8:00 | 248.00 | Standard Time | |||
| 7 | Saturday at 9:00 | 426.00 | Standard Time | |||
| 2016 | December | 19.62 | 51813.38 | 0.00 | Low Season / Slack Period | |
| 2016 | October | 51657.53 | 51793.76 | 2.99 | Peak Month (Strong Seasonality) | |
| 2016 | September | 136.23 | 136.23 | 0.01 | Low Season / Slack Period | |
| 2017 | April | 405988.38 | 1304086.86 | 0.69 | Low Season / Slack Period | |
| 2017 | August | 662071.77 | 3627770.84 | 1.12 | Standard Performance | |
| 2017 | December | 861914.46 | 7144305.03 | 4392939.87 | 1.46 | Peak Month (Strong Seasonality) |
| 2017 | February | 283621.94 | 472442.08 | 0.48 | Low Season / Slack Period | |
| 2017 | January | 137006.76 | 188820.14 | 0.23 | Low Season / Slack Period | |
| 2017 | July | 578858.58 | 2965699.07 | 0.98 | Standard Performance | |
| 2017 | June | 499827.47 | 2386840.49 | 0.85 | Low Season / Slack Period | |
| 2017 | March | 425656.40 | 898098.48 | 0.72 | Low Season / Slack Period | |
| 2017 | May | 582926.16 | 1887013.02 | 0.99 | Standard Performance | |
| 2017 | November | 1172639.23 | 6282390.57 | 1.98 | Peak Month (Strong Seasonality) | |
| 2017 | October | 764785.28 | 5109751.34 | 1380.49 | 1.29 | Peak Month (Strong Seasonality) |
| 2017 | September | 717195.22 | 4344966.06 | 526359.11 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | April | 1156303.91 | 11535951.32 | 184.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | August | 996896.15 | 15738970.55 | 50.57 | 1.04 | Standard Performance |
| 2018 | February | 979966.23 | 9226910.67 | 245.52 | 1.03 | Standard Performance |
| 2018 | January | 1102639.41 | 8246944.44 | 704.81 | 1.15 | Standard Performance |
| 2018 | July | 1039880.16 | 14742074.40 | 79.64 | 1.09 | Standard Performance |
| 2018 | June | 1020494.29 | 13702194.24 | 104.17 | 1.07 | Standard Performance |
| 2018 | March | 1152736.74 | 10379647.41 | 170.81 | 1.21 | Peak Month (Strong Seasonality) |
| 2018 | May | 1145748.63 | 12681699.95 | 96.55 | 1.20 | Standard Performance |
| 2018 | September | 166.46 | 15739137.01 | -99.98 | 0.00 | Low Season / Slack Period |
Insights:
- Hockey Stick Growth Confirmed: The cumulative revenue curve shows a classic “hockey stick” shape. The platform started extremely slowly in 2016, but the slope turned sharply upward in mid-2017. It took 7 months to reach the first 1M milestone every single month.
- Rapid Scaling Milestones: The business reached a 10M by March 2018. This acceleration proves that Olist successfully transitioned from a struggling startup phase into a high-growth scale-up.
- The November 2017 Catalyst: November 2017 wasn’t just a good month; it was a permanent game-changer. The massive revenue spike during that period permanently steepened the cumulative growth line. After this point, the business never returned to its lower 2017 baselines, maintaining a much higher trajectory throughout 2018.
- Final Revenue Snapshot: By the end of the recorded data in August 2018, the platform had generated over $15.7M in total revenue. The trajectory remained strong and steep right until the data cutoff, suggesting that the business was still in a healthy expansion phase.
Conclusion
What We Did:
- Analyzed Month-over-Month (MoM) revenue growth to pinpoint hyper-growth phases versus periods of stabilization.
- Calculated a Seasonality Index to determine the baseline importance of each month in the yearly cycle.
- Performed a Year-over-Year (YoY) comparison to measure the scale of expansion between 2017 and 2018.
- Conducted an Intra-day Analysis of order volumes by hour and day to identify peak shopping windows.
- Tracked Cumulative Revenue to visualize the business’s overall growth trajectory.
What We Obtained (Key Findings):
- The “Hockey Stick” Effect: Olist successfully moved from a slow startup phase in 2016 to a high-speed scale-up in 2017. The cumulative revenue trajectory turned sharply exponential in late 2017, reaching a total of over $15.7M by the end of the study period.
- Maturity & Stability: While 2017 was a year of chaotic growth, 2018 showed a mature plateau. Revenue stabilized at a predictable baseline of ~$1M per month, indicating that the platform successfully retained its market share.
- November is the Revenue Engine: November is the undisputed peak of the year. With a seasonality index of 1.98, it brings in nearly double the revenue of an average month, confirming that Black Friday is the most critical event for Olist’s financial health.
- The Weekday Workflow: Shopping is a “workweek” activity. Monday is the highest-volume day, and sales consistently dip during the weekends.
- Dual-Spike Daily Behavior: Customers follow a predictable daily routine across all states, with a primary Lunchtime Spike (12 PM - 2 PM) and a secondary Evening Spike (8 PM - 10 PM). Monday at 9 PM represents the absolute peak of customer activity.