Skip to content

Commit 30c36a9

Browse files
committed
roc_curve.typ + sierpinski-triangle.typ and their assets
- increment title from "117 Scientific Diagrams" to "118 Scientific Diagrams" in readme - update badge links for Typst from "96" to "98" - convert complex-sign-function.tex to Typst Cetz
1 parent 392835e commit 30c36a9

14 files changed

+953
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#import "@preview/cetz:0.3.4": canvas, draw, vector, matrix
2+
#import draw: set-transform, scale, content, line, rect, group
3+
4+
#set page(width: auto, height: auto, margin: 8pt)
5+
#set text(size: 8pt)
6+
7+
#canvas({
8+
draw.set-style(line: (stroke: none))
9+
// Set up the transformation matrix for 3D perspective
10+
set-transform(matrix.transform-rotate-dir((1, 1, -2), (0, 2, .3)))
11+
scale(x: 1.5, z: -1)
12+
13+
let arrow-style = (mark: (end: "stealth", fill: black, scale: 0.5))
14+
15+
// Add vertical z-lines at corners and origin
16+
for (x, y) in ((-1, -1), (1, -1), (-1, 1), (1, 1)) {
17+
draw.line((x, y, -1.2), (x, y, 1.2), stroke: gray + .3pt)
18+
}
19+
draw.line((0, 0, -1.2), (0, 0, 1.2), stroke: gray + .3pt, ..arrow-style)
20+
21+
22+
// Draw the zero plane (gray, semi-transparent)
23+
group({
24+
draw.rect((-1, -1, 0), (1, 1, 0), fill: rgb(128, 128, 128, 20), stroke: none)
25+
})
26+
27+
// Draw the blue quadrants (s = -1)
28+
group({
29+
draw.on-layer(
30+
-1,
31+
{
32+
draw.line(
33+
(-1, 0, -1),
34+
(0, 0, -1),
35+
(0, 1, -1),
36+
(-1, 1, -1),
37+
fill: rgb(173, 216, 230),
38+
)
39+
draw.line(
40+
(0, -1, -1),
41+
(1, -1, -1),
42+
(1, 0, -1),
43+
(0, 0, -1),
44+
fill: rgb(173, 216, 230),
45+
)
46+
},
47+
)
48+
})
49+
50+
// Draw the orange quadrants (s = 1)
51+
group({
52+
draw.line(
53+
(0, 0, 1),
54+
(1, 0, 1),
55+
(1, 1, 1),
56+
(0, 1, 1),
57+
fill: rgb(255, 165, 0),
58+
)
59+
draw.line(
60+
(-1, -1, 1),
61+
(0, -1, 1),
62+
(0, 0, 1),
63+
(-1, 0, 1),
64+
fill: rgb(255, 165, 0),
65+
)
66+
})
67+
68+
// Draw grid lines
69+
for x in range(-1, 2) {
70+
let style = if x == 0 { arrow-style } else { () }
71+
draw.line((x, -1, 0), (x, 1, 0), stroke: gray + .3pt, ..style)
72+
}
73+
for y in range(-1, 2) {
74+
let style = if y == 0 { arrow-style } else { () }
75+
draw.line((-1, y, 0), (1, y, 0), stroke: gray + .3pt, ..style)
76+
}
77+
78+
content((1.45, .1, 0), [$"Re"(p_0)$])
79+
content((0, 1.6, 0), [$"Im"(p_0)$])
80+
content((0, 0, 1.5), [$s(p_0)$])
81+
})

assets/heatmap/heatmap.typ

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#canvas({
77
let cell-size = .7 // Size of each heatmap cell
8-
let data = (
8+
let cell-data = (
99
(74, 25, 39, 20, 3, 3, 3, 3, 3),
1010
(25, 53, 31, 17, 7, 7, 2, 3, 2),
1111
(39, 31, 37, 24, 3, 3, 3, 3, 3),
@@ -16,7 +16,7 @@
1616
(3, 3, 3, 5, 0, 0, 1, 23, 1),
1717
(3, 2, 3, 5, 0, 0, 1, 1, 78),
1818
)
19-
let row-labels = ("a", "b", "c", "d", "e", "f", "g", "h", "i")
19+
let row-labels = "abcdefghi".split("").slice(1)
2020

2121
// Draw column labels (1-9)
2222
for col in range(9) {
@@ -39,7 +39,7 @@
3939
// Draw heatmap cells
4040
for row in range(9) {
4141
for col in range(9) {
42-
let value = data.at(row).at(col)
42+
let value = cell-data.at(row).at(col)
4343
rect(
4444
((col + 1) * cell-size - cell-size / 2, -(row + 1) * cell-size - cell-size / 2),
4545
((col + 1) * cell-size + cell-size / 2, -(row + 1) * cell-size + cell-size / 2),

assets/roc-curve/roc-curve-hd.png

60.6 KB
Loading

assets/roc-curve/roc-curve.pdf

14.8 KB
Binary file not shown.

assets/roc-curve/roc-curve.png

26.4 KB
Loading

assets/roc-curve/roc-curve.svg

+602
Loading

assets/roc-curve/roc-curve.typ

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#import "@preview/cetz:0.3.4": canvas, draw
2+
#import "@preview/cetz-plot:0.1.1": plot
3+
4+
#set page(width: auto, height: auto, margin: 8pt)
5+
6+
// ROC curve functions for different classifiers
7+
#let perfect_classifier(x) = {
8+
if x == 0 { return 0 }
9+
if x == 1 { return 1 }
10+
if x > 0 { return 0.99 }
11+
return 0
12+
}
13+
14+
#let excellent_classifier(x) = {
15+
if x <= 0 { return 0 }
16+
if x >= 1 { return 1 }
17+
return calc.pow(x, 0.15)
18+
}
19+
20+
#let good_classifier(x) = {
21+
if x <= 0 { return 0 }
22+
if x >= 1 { return 1 }
23+
return calc.pow(x, 0.3)
24+
}
25+
26+
#let fair_classifier(x) = {
27+
if x <= 0 { return 0 }
28+
if x >= 1 { return 1 }
29+
return calc.pow(x, 0.6)
30+
}
31+
32+
#let poor_classifier(x) = {
33+
if x <= 0 { return 0 }
34+
if x >= 1 { return 1 }
35+
return 0.2 * x + 0.8 * x * x
36+
}
37+
38+
#let random_classifier(x) = x
39+
40+
#canvas({
41+
let mark = (end: "stealth", fill: black, scale: 0.7)
42+
draw.set-style(
43+
axes: (
44+
y: (label: (anchor: "south-east", offset: 1.2, angle: 90deg), mark: mark),
45+
x: (label: (anchor: "south-east", offset: 1.2), mark: mark),
46+
),
47+
)
48+
49+
plot.plot(
50+
size: (8, 8),
51+
x-label: "False Positive Rate (1-Specificity)",
52+
y-label: "True Positive Rate (Sensitivity)",
53+
x-min: 0,
54+
x-max: 1,
55+
y-min: 0,
56+
y-max: 1,
57+
x-tick-step: 0.25,
58+
y-tick-step: 0.25,
59+
x-grid: true,
60+
y-grid: true,
61+
axis-style: "left",
62+
legend: "inner-north",
63+
legend-style: (item: (spacing: 0.15), padding: 0.15, stroke: none, offset: (7.8, 0.3)),
64+
{
65+
plot.add(
66+
style: (stroke: gray),
67+
domain: (0, 1),
68+
samples: 2,
69+
random_classifier,
70+
label: "Random Guess (AUC = 0.5)",
71+
)
72+
73+
plot.add(
74+
style: (stroke: green),
75+
domain: (0, 1),
76+
samples: 50,
77+
perfect_classifier,
78+
label: "Near-Perfect Classifier (AUC = 0.99)",
79+
)
80+
81+
plot.add(
82+
style: (stroke: blue),
83+
domain: (0, 1),
84+
samples: 100,
85+
excellent_classifier,
86+
label: "Excellent Classifier (AUC = 0.93)",
87+
)
88+
89+
plot.add(
90+
style: (stroke: purple),
91+
domain: (0, 1),
92+
samples: 100,
93+
good_classifier,
94+
label: "Good Classifier (AUC = 0.85)",
95+
)
96+
97+
plot.add(
98+
style: (stroke: orange),
99+
domain: (0, 1),
100+
samples: 100,
101+
fair_classifier,
102+
label: "Fair Classifier (AUC = 0.73)",
103+
)
104+
105+
plot.add(
106+
style: (stroke: red),
107+
domain: (0, 1),
108+
samples: 100,
109+
poor_classifier,
110+
label: "Poor Classifier (AUC = 0.65)",
111+
)
112+
},
113+
)
114+
})

assets/roc-curve/roc-curve.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
title: ROC Curve
2+
tags:
3+
- statistics
4+
- classification
5+
- machine learning
6+
- performance metrics
7+
- data science
8+
- model evaluation
9+
description: |
10+
The Receiver Operating Characteristic (ROC) curve is a widely used evaluation tool in statistics that plots the True Positive Rate (Sensitivity) against the False Positive Rate (1-Specificity) at various classification thresholds.
11+
12+
This visualization compares classifiers of different performance levels, from near-perfect (AUC = 0.99) to poor (AUC = 0.65), with a random classifier (AUC = 0.5) as baseline. The Area Under the Curve (AUC) serves as a threshold-independent measure of classifier performance, with values closer to 1.0 indicating better discrimination ability.
13+
14+
ROC curves are particularly valuable in domains requiring careful trade-off between sensitivity and specificity, such as medical diagnostics (balancing false negatives vs. false positives), fraud detection (minimizing false alerts while catching true fraud), and information retrieval (evaluating ranking algorithms). They provide insights into model behavior across all possible decision thresholds, allowing practitioners to select operating points that best align with application requirements and costs of different types of errors.
Loading
Binary file not shown.
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#import "@preview/fractusist:0.3.0": lsystem, lsystem-use
2+
3+
#set page(width: auto, height: auto, margin: 0pt)
4+
#set text(fill: white, size: 6pt)
5+
6+
// Sierpiński triangle with Au(100) surface background
7+
#box({
8+
let triangle_size = 128pt
9+
let margin = 12pt
10+
let canvas_width = triangle_size + 2 * margin
11+
let canvas_height = triangle_size * calc.pow(3, 0.5) / 2 + 2 * margin
12+
13+
rect(
14+
width: canvas_width,
15+
height: canvas_height,
16+
fill: rgb(50, 50, 50),
17+
{
18+
// Gold atoms in hexagonal close-packed arrangement
19+
let horizontal_spacing = 2.5pt
20+
let vertical_spacing = 2.2pt
21+
let dot_radius = 1pt
22+
let rows = int(canvas_height / vertical_spacing)
23+
let cols = int(canvas_width / horizontal_spacing)
24+
25+
for y in range(-3, rows + 3) {
26+
let offset = if calc.odd(y) { horizontal_spacing / 2 } else { 0pt }
27+
28+
for x in range(-3, cols + 3) {
29+
place(
30+
dx: x * horizontal_spacing + offset,
31+
dy: y * vertical_spacing,
32+
circle(
33+
radius: dot_radius,
34+
fill: rgb(200, 50, 50),
35+
stroke: none,
36+
),
37+
)
38+
}
39+
}
40+
41+
// Sierpiński triangle
42+
place(
43+
dx: margin / 2,
44+
dy: margin / 2,
45+
{
46+
lsystem(
47+
..lsystem-use("Sierpinski Triangle"),
48+
order: 5,
49+
step-size: 4,
50+
start-angle: 1,
51+
fill: rgb(255, 230, 100),
52+
stroke: 0.5pt + rgb(200, 140, 0),
53+
)
54+
},
55+
)
56+
57+
// 10nm scale bar
58+
let scale_bar_width = horizontal_spacing * 8
59+
let (scale_bar_height, scale_bar_margin) = (2pt, 12pt)
60+
61+
place(
62+
dx: canvas_width - scale_bar_width - scale_bar_margin,
63+
dy: scale_bar_margin / 5,
64+
rect(width: scale_bar_width, height: scale_bar_height, fill: white),
65+
)
66+
67+
place(
68+
dx: canvas_width - scale_bar_width - scale_bar_margin + scale_bar_width / 2 - 7pt,
69+
dy: scale_bar_margin / 5 + scale_bar_height + 2pt,
70+
[10 nm],
71+
)
72+
73+
// Legend
74+
let color_square_size = 4pt
75+
76+
place(
77+
block(
78+
inset: (x: 3pt, y: 2pt),
79+
radius: 2pt,
80+
fill: rgb(30, 30, 30),
81+
{
82+
grid(
83+
columns: (color_square_size, auto),
84+
column-gutter: 2.5pt,
85+
row-gutter: 3pt,
86+
rect(width: color_square_size, height: color_square_size, fill: rgb(200, 50, 50), radius: 1pt), [Au(100)],
87+
rect(width: color_square_size, height: color_square_size, fill: rgb(255, 230, 100), radius: 1pt),
88+
[Fe/C3PC],
89+
90+
rect(width: color_square_size, height: color_square_size, fill: rgb(200, 140, 0), radius: 1pt), [BPyB],
91+
)
92+
},
93+
),
94+
)
95+
},
96+
)
97+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
title: Sierpiński Triangle
2+
description: |
3+
A Sierpiński triangle is a fractal with the overall shape of an equilateral triangle, subdivided recursively into smaller equilateral triangles. The diagram shows a fifth-order Sierpiński triangle, which was experimentally realized out of Fe/C3PC molecules on a reconstructed Au(100)-(hex) in https://doi.org/10.1021/jacs.7b05720.
4+
5+
The visualization shows a yellow Sierpiński triangle (representing Fe/C3PC molecules) with a gold border (representing BPyB molecules) on a background of red dots arranged in a hexagonal close-packed pattern (representing the Au(100) substrate atoms). Previous molecular Sierpiński triangle fractals were limited to fourth-order due to kinetic growth constraints.
6+
7+
references:
8+
- li_construction_2017:
9+
title: Construction of Sierpiński Triangles up to the Fifth Order
10+
authors: Chao Li, Xue Zhang, Na Li, Yawei Wang, Jiajia Yang, Gaochen Gu, Yajie Zhang, Shimin Hou, Lianmao Peng, Kai Wu, Damian Nieckarz, Paweł Szabelski, Hao Tang, Yongfeng Wang
11+
year: 2017
12+
publisher: Journal of the American Chemical Society
13+
doi: 10.1021/jacs.7b05720
14+
page: 13701-13707
15+
volume: 139
16+
issue: 39
17+
18+
tags:
19+
- fractal
20+
- self-assembly
21+
- surface science
22+
- nanotechnology
23+
- materials science
24+
- molecular assembly
25+
- self-similar structure
26+
- mathematical pattern
27+
- Au(100) surface
28+
- ultrahigh vacuum
29+
- recursive pattern

0 commit comments

Comments
 (0)