Line chart¶
Load data
# Source : https://observablehq.com/@d3/line-chart/2
import detroit as d3
import polars as pl # for data manipulation
from collections import namedtuple
URL = (
"https://static.observableusercontent.com/files/de259092d525c13bd10926eaf7add45b15f"
"2771a8b39bc541a5bba1e0206add4880eb1d876be8df469328a85243b7d813a91feb8cc4966de582dc"
"02e5f8609b7?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27aapl."
"csv"
)
Margin = namedtuple("Margin", ["top", "right", "bottom", "left"])
aapl = pl.read_csv(URL).select(
pl.col("date").str.to_datetime("%Y-%m-%d"),
pl.col("close"),
)
shape: (1_280, 2)
┌─────────────────────┬────────┐
│ date ┆ close │
│ --- ┆ --- │
│ datetime[μs] ┆ f64 │
╞═════════════════════╪════════╡
│ 2007-04-23 00:00:00 ┆ 93.24 │
│ 2007-04-24 00:00:00 ┆ 95.35 │
│ 2007-04-25 00:00:00 ┆ 98.84 │
│ 2007-04-26 00:00:00 ┆ 99.92 │
│ 2007-04-29 00:00:00 ┆ 99.8 │
│ … ┆ … │
│ 2012-04-24 00:00:00 ┆ 610.0 │
│ 2012-04-25 00:00:00 ┆ 607.7 │
│ 2012-04-26 00:00:00 ┆ 603.0 │
│ 2012-04-29 00:00:00 ┆ 583.98 │
│ 2012-05-01 00:00:00 ┆ 582.13 │
└─────────────────────┴────────┘
Make the line chart
# Declare the chart dimensions and margins.
width = 928
height = 500
margin = Margin(20, 30, 30, 40)
# Declare the x (horizontal position) scale.
x = d3.scale_time(
[aapl["date"].min(), aapl["date"].max()], [margin.left, width - margin.right]
)
# Declare the y (vertical position) scale.
y = d3.scale_linear([0, aapl["close"].max()], [height - margin.bottom, margin.top])
# Declare the line generator.
line = d3.line().x(lambda d: x(d[0].timestamp())).y(lambda d: y(d[1]))
# 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; height: intrinsic;")
)
# Add the x-axis.
svg.append("g").attr("transform", f"translate(0, {height - margin.bottom})").call(
d3.axis_bottom(x).set_ticks(width / 80).set_tick_size_outer(0)
)
# Add the y-axis, remove the domain line, add grid lines and a label.
(
svg.append("g")
.attr("transform", f"translate({margin.left}, 0)")
.call(d3.axis_left(y).set_ticks(height / 40))
.call(lambda g: g.select(".domain").remove())
.call(lambda g: g.select_all(".tick")
.select_all("line")
.clone()
.attr("x2", width - margin.left - margin.right)
.attr("stroke-opacity", 0.1)
)
.call(
lambda g: (
g.append("text")
.attr("x", -margin.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Daily close ($)")
)
)
)
# Append a path for the line.
(
svg.append("path")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", line(aapl.iter_rows()))
)
Save your chart
with open("line.svg", "w") as file:
file.write(str(svg))