Public
Edited
Dec 14, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
daily_orders
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
stores
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
function getWeekNumber(d) {
// Copy date so don't modify original
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
// Get first day of year
var yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
// Calculate full weeks to nearest Thursday
var weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
// Return array of year and week number
return weekNo;
}

Insert cell
Insert cell
// Q1
// This will sum the revenue for each date
revenueByDateArray = {
const revenueByDate = daily_orders.reduce((acc, { order_date, revenue }) => {
// If the date isn't in the accumulator, add it with initial revenue
if (!acc[order_date]) {
acc[order_date] = 0;
}
// Add the revenue for the current item to the total for the date
acc[order_date] += revenue;
return acc;
}, {});

// Convert the result into an array suitable for plotting
const revenueByDateArray = Object.keys(revenueByDate).map(date => ({
date: new Date(date),
fakeDate: new Date(new Date(new Date(new Date(date).setFullYear(2024)).setMonth(0)).setDate(((new Date(date).getDay() + 4) % 7) + 1)),
year: new Date(date).getFullYear(),
month: new Date(date).getMonth(),
weekDay: (new Date(date).getDay() + 4) % 7,
weekNumber: getWeekNumber(new Date(date)),
totalRevenue: revenueByDate[date]
}));
// Sort the array by date if needed
revenueByDateArray.sort((a, b) => new Date(a.date) - new Date(b.date));

return revenueByDateArray;
}
Insert cell
// Q2
// This will sum the revenue for each store
// This function extracts the year from a date string
stackedData = {
const getYear = dateString => new Date(dateString).getFullYear();
// Group by store and year
const revenueByStoreAndYear = daily_orders.reduce((acc, { store_id, revenue, order_date }) => {
const year = getYear(order_date);
const key = `${store_id}-${year}`;
if (!acc[key]) {
acc[key] = { store_id, year, revenue: 0 };
}
acc[key].revenue += revenue;
return acc;
}, {});
// Convert the result into an array
const revenueArray = Object.values(revenueByStoreAndYear);
// Now we need to nest this array by store, to create arrays of revenue by year for each store
const nestedRevenueByStore = Array.from(
d3.group(revenueArray, d => d.store_id),
([storeId, values]) => ({ storeId, values })
);
// Prepare data for stacked bar chart
// We need to map each store to its revenues per year
const stackedData = nestedRevenueByStore.map(({ storeId, values }) => {
const revenuesByYear = values.reduce((obj, { year, revenue }) => {
obj[year] = revenue;
return obj;
}, {});
return { storeId, ...revenuesByYear };
});

const tidyData = Object.keys(stackedData[0]).slice(0, 3).flatMap((year) => stackedData.map(d => ({storeId: d.storeId, year, revenue: d[year]})))

return tidyData;
}
Insert cell
// Q3
// Calculate total revenue and orders for each store and each day
averageOrderValueArray = {
const revenueAndOrdersByStoreAndDay = daily_orders.reduce((acc, { store_id, order_date, revenue, orders }) => {
const key = `${store_id}-${order_date}`;
if (!acc[key]) {
acc[key] = { store_id, order_date, totalRevenue: 0, totalOrders: 0 };
}
acc[key].totalRevenue += revenue;
acc[key].totalOrders += orders;
return acc;
}, {});
// Convert the result into an array and calculate the average order value
const averageOrderValueArray = Object.values(revenueAndOrdersByStoreAndDay).map(({ store_id, order_date, totalRevenue, totalOrders }) => ({
store_id,
order_date,
averageOrderValue: totalOrders ? totalRevenue / totalOrders : 0,
totalOrders: totalOrders,
totalRevenue: totalRevenue
}));
// Now 'averageOrderValueArray' is ready to be used with a visualization library to plot the data

return averageOrderValueArray;
// If you want to calculate the average per store regardless of the date:
const averageOrderValueByStore = Array.from(d3.group(averageOrderValueArray, d => d.store_id), ([storeId, values]) => {
const totalRevenue = d3.sum(values, d => d.totalRevenue);
const totalOrders = d3.sum(values, d => d.totalOrders);
return {
storeId,
averageOrderValue: totalOrders ? totalRevenue / totalOrders : 0
};
});

return averageOrderValueByStore;
// If you want to calculate the average per day regardless of the store:
const averageOrderValueByDay = Array.from(d3.group(averageOrderValueArray, d => d.order_date), ([orderDate, values]) => {
const totalRevenue = d3.sum(values, d => d.totalRevenue);
const totalOrders = d3.sum(values, d => d.totalOrders);
return {
orderDate,
averageOrderValue: totalOrders ? totalRevenue / totalOrders : 0
};
});
// Sort by storeId or orderDate if necessary before plotting
averageOrderValueByStore.sort((a, b) => a.storeId.localeCompare(b.storeId));
averageOrderValueByDay.sort((a, b) => new Date(a.orderDate) - new Date(b.orderDate));

return averageOrderValueByStore;
};
Insert cell
Insert cell
Insert cell
Insert cell
//Your first plot here
Plot.plot({
title: "Q1: How does the daily revenue vary over a week?",
subtitle: "Grouped by day of week. We can see that overall, stores sell a lot more on Thursdays. We also can see that, in terms of revenue, 2020 is generally outperformed by both 2021 and 2022, which themselves perform about the same. However, for 2022, Thursday revenues are generally down compared to previous years.",
marginBottom: 60,
color: {type: "categorical", scheme: "Spectral", legend: "swatches", width: 340, label: "Year"},
marks: [
Plot.dotY(revenueByDateArray, {x: "fakeDate", y: "totalRevenue", z:"year", tip: true, stroke: "year", opacity: 0.5, dx: 20}),
Plot.axisX({ticks: "1 day", tickFormat: " %A", tickRotate: -45, tickSize: 6, dx: 20, label: "Week day"}),
Plot.axisY({label: "Revenue"}),
Plot.ruleY([0], {dx: 20})
]
})
Insert cell
Insert cell
Insert cell
//Your second plot here
// Second plot, part 1
plt = Plot.plot({
title: "Q2: Which store generates the most revenue?",
subtitle: "Split by year.",
marginBottom: 60,
width: width/2,
marginLeft: 60,
color: {type: "categorical", scheme: "Spectral", legend: "swatches", width: 340, label: "Year"},
marks: [
Plot.barY(stackedData, {x: "storeId", y: "revenue", tip: true, fill: "year", sort: {color: null, x: "-y"}}),
Plot.axisX({tickRotate: -45, tickSize: 6, label: "Store ID"}),
Plot.axisY({label: "Revenue"})
]
})
Insert cell
// Second plot, part 2
plt2 = Plot.plot({
title: ".", // This is just so that the subtitle appears lower
subtitle: "Y axis in log scale.",
marginBottom: 60,
width: width/2,
marginLeft: 60,
y: {type: "log"},
// y: {transform: (y) => Math.log(y)},
color: {type: "categorical", scheme: "Spectral", legend: "swatches", width: 340, label: "Year"},
marks: [
Plot.barY(stackedData, {x: "storeId", y: "revenue", tip: true, fill: "year", sort: {color: null, x: "-y"}}),
Plot.axisX({tickRotate: -45, tickSize: 6, label: "Store ID"}),
Plot.axisY({label: "log(Revenue)"}),
Plot.linearRegressionY(stackedData, {x: "storeId", y: "revenue"})
]
})
Insert cell
//Your third plot here
Plot.plot({
title: "Q3: Is there a correlation between the number of orders and the revenue generated?",
subtitle: "It appears there is an almost perfect correlation between # of orders and revenue on any given day, by any given store. This probably means that stores' main revenues come from the same set of products, so X% higher sales equals X% higher revenues.",
marginBottom: 50,
marginLeft: 60,
color: {type: "categorical", scheme: "Spectral", legend: "swatches", width: 340, label: "Store ID"},
marks: [
Plot.dotY(averageOrderValueArray, {x: "totalOrders", y: "totalRevenue", opacity: 0.5, stroke: "store_id", tip: true}),
Plot.axisX({tickRotate: -45, tickSize: 6, label: "# Orders"}),
Plot.linearRegressionY(averageOrderValueArray, {x: "totalOrders", y: "totalRevenue"}),
Plot.axisY({label: "Revenue"})
]
})
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more