Published
Edited
Jul 19, 2021
1 star
Also listed in…
Selected works
Data Notes
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = FileAttachment("2015-10-2021-04-combined-data.csv").csv({
typed: true
})
Insert cell
colors = new Map([
["Brandy", "#f16913"],
["Rum", "#fd8d3c"],
["Vodka", "#fdae6b"],
["Whisky", "#fdd0a2"],
["Gin", "#fee6ce"],
["Beer", "#807dba"],
["Wine", "#bcbddc"]
])
Insert cell
abvs = new Map([
["Brandy", 0.36],
["Rum", 0.36],
["Vodka", 0.36],
["Whisky", 0.36],
["Gin", 0.36],
["Beer", 0.049],
["Wine", 0.125]
])
Insert cell
Insert cell
color = d3
.scaleOrdinal()
.domain(activeColors.keys())
.range(activeColors.values())
Insert cell
dataByDrink = {
const drinks = Array.from(colors.keys());

const keySuffices = ["_excise_duty", "_bulk_litre"];

function initialiseDateObj() {
const obj = {};
const emptyDrinkObj = keySuffices.reduce((acc, key) => {
acc[changeCase.camelCase(key)] = 0;
return acc;
}, {});

drinks.forEach((drink) => {
obj[drink] = { ...emptyDrinkObj };
});

return obj;
}

function sumData(dataObj, rawData) {
const obj = { ...dataObj };
drinks.forEach((drink) => {
keySuffices.forEach((suffix) => {
if (obj[drink]) {
obj[drink][changeCase.camelCase(suffix)] +=
rawData[`${drink.toLowerCase()}${suffix}`];
}
});
});
return obj;
}

const dataByDate = rows.reduce((acc, d) => {
const { date } = d;

if (acc[date] === undefined) {
acc[date] = { date, ...initialiseDateObj() };
}

acc[date] = { ...sumData(acc[date], d) };

return acc;
}, {});

let flattenedByDate = Object.keys(dataByDate).map((k) => dataByDate[k]);

flattenedByDate = flattenedByDate.map((d) => {
const onDate = { ...d };
drinks.forEach((drink) => {
d[drink].standardLitre = d[drink].bulkLitre * abvs.get(drink);
});
return onDate;
});

return flattenedByDate;
}
Insert cell
chartData = {
const data = dataByDrink.map((d) => {
const obj = { date: d.date };

Object.keys(d).forEach((key) => {
if (key !== "date") {
obj[key] =
stat.value === "volume"
? d[key].bulkLitre
: stat.value === "stdVolume"
? d[key].standardLitre
: d[key].exciseDuty;
}
});

return obj;
});

return {
data,
yTickFormat: stat.value === "tax" ? formatRevenue : formatNumbers
};
}
Insert cell
typeOfStats = [
{ label: "Tax Revenue", value: "tax" },
{ label: "Volume", value: "volume" },
{
label: "Alcohol Volume",
value: "stdVolume"
}
]
Insert cell
series = {
const stack = d3
.stack()
.keys(activeColors.keys())
.value((group, key) => group[key])
.order(d3.stackOrderReverse);

return stack(chartData.data).map(
(s) => (
s.forEach((d) => {
d.data = { ...d.data };
d.data.key = s.key;
}),
s
)
);
}
Insert cell
x = d3
.scaleBand()
.domain(chartData.data.map((d) => d.date))
.rangeRound([margin.left, width - margin.right])
Insert cell
y = d3
.scaleLinear()
.domain([0, d3.max(series, (d) => d3.max(d, (d) => d[1]))])
.range([height - margin.bottom, margin.top])
Insert cell
formatDate = d3.timeFormat("%b, %y")
Insert cell
xAxis = (g) =>
g.attr("transform", `translate(0,${height - margin.bottom})`).call(
d3.axisBottom(x).tickFormat((date, i) => {
return i % 3 !== 0 ? null : formatDate(date);
})
)
Insert cell
yAxis = (g) =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickFormat((x) => chartData.yTickFormat(x)))
.call((g) =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(stat.value !== "tax" ? "Litres" : "")
)
Insert cell
formatRevenue = x => {
if (currencyRounding === "Crores") {
return +(x / 1e7).toFixed(2) >= 1
? `₹${(x / 1e7).toFixed(2)}Cr`
: +(x / 1e5).toFixed(2) >= 1
? `₹${(x / 1e5).toFixed(2)}L`
: `₹${(x / 1e3).toFixed(2)}K`;
}
return +(x / 1e9).toFixed(2) >= 1
? `₹${(x / 1e9).toFixed(2)}B`
: +(x / 1e6).toFixed(2) >= 1
? `₹${(x / 1e6).toFixed(2)}M`
: `₹${(x / 1e3).toFixed(2)}K`;
}
Insert cell
formatNumbers = (x) => {
return +(x / 1e9).toFixed(2) >= 1
? `${(x / 1e9).toFixed(2)}B`
: +(x / 1e6).toFixed(2) >= 1
? `${(x / 1e6).toFixed(2)}M`
: `${(x / 1e3).toFixed(2)}K`;
}
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