Bar chart¶
Load data
# Source : https://observablehq.com/@d3/bar-chart/2
import detroit as d3
import polars as pl # for data manipulation
from collections import namedtuple
URL = (
"https://static.observableusercontent.com/files/09f63bb9ff086fef80717e2ea8c974f918a"
"996d2bfa3d8773d3ae12753942c002d0dfab833d7bee1e0c9cd358cd3578c1cd0f9435595e76901508"
"adc3964bbdc?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27alpha"
"bet.csv"
)
Margin = namedtuple("Margin", ["top", "right", "bottom", "left"])
alphabet = pl.read_csv(URL).sort(by="frequency", descending=True)
shape: (26, 2)
┌────────┬───────────┐
│ letter ┆ frequency │
│ --- ┆ --- │
│ str ┆ f64 │
╞════════╪═══════════╡
│ E ┆ 0.12702 │
│ T ┆ 0.09056 │
│ A ┆ 0.08167 │
│ O ┆ 0.07507 │
│ I ┆ 0.06966 │
│ … ┆ … │
│ K ┆ 0.00772 │
│ J ┆ 0.00153 │
│ X ┆ 0.0015 │
│ Q ┆ 0.00095 │
│ Z ┆ 0.00074 │
└────────┴───────────┘
Make the bar chart
width = 928
height = 500
margin = Margin(30, 0, 30, 40)
# Declare the x (horizontal position) scale.
# descending frequency
x = (
d3.scale_band()
.set_domain(alphabet["letter"].unique(maintain_order=True))
.set_range([margin.left, width - margin.right])
.set_padding(0.1)
)
# Declare the y (vertical position) scale.
y = (
d3.scale_linear()
.set_domain([0, alphabet["frequency"].max()])
.set_range([height - margin.bottom, margin.top])
)
# Create the SVG container.
svg = (
d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", f"0 0 {width} {height}")
.attr("style", "max-width: 100%; height: auto;")
)
# Add a rect for each bar.
(
svg.append("g")
.select_all()
.data(alphabet.iter_rows())
.join("rect")
.attr("x", lambda d: x(d[0]))
.attr("y", lambda d: y(d[1]))
.attr("height", lambda d: y(0) - y(d[1]))
.attr("width", x.get_bandwidth())
.attr("fill", "steelblue")
)
# Add the x-axis and label.
svg.append("g").attr("transform", f"translate(0, {height - margin.bottom})").call(
d3.axis_bottom(x).set_tick_size_outer(0)
)
# Add the y-axis and label, and remove the domain line.
(
svg.append("g")
.attr("transform", f"translate({margin.left}, 0)")
.call(d3.axis_left(y).set_tick_format(lambda y: str(int(y * 100))))
.call(lambda g: g.select(".domain").remove())
.call(
lambda g: g.append("text")
.attr("x", -margin.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Frequency (%)")
)
)
Save your chart
with open("bar.svg", "w") as file:
file.write(str(svg))