Skip to content

Commit 99af87d

Browse files
feat(plotnine): implement spectrogram-basic (#2949)
## Implementation: `spectrogram-basic` - plotnine Implements the **plotnine** version of `spectrogram-basic`. **File:** `plots/spectrogram-basic/implementations/plotnine.py` **Parent Issue:** #2927 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20612807474)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 936b883 commit 99af87d

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
""" pyplots.ai
2+
spectrogram-basic: Spectrogram Time-Frequency Heatmap
3+
Library: plotnine 0.15.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from plotnine import aes, element_text, geom_tile, ggplot, labs, scale_fill_gradientn, theme, theme_minimal
10+
from scipy import signal
11+
12+
13+
# Data - Generate a chirp signal with increasing frequency
14+
np.random.seed(42)
15+
sample_rate = 1000 # Hz
16+
duration = 2.0 # seconds
17+
t = np.linspace(0, duration, int(sample_rate * duration))
18+
19+
# Create chirp signal: frequency increases from 50 Hz to 200 Hz
20+
f0, f1 = 50, 200
21+
chirp_signal = signal.chirp(t, f0=f0, f1=f1, t1=duration, method="linear")
22+
chirp_signal += 0.3 * np.random.randn(len(chirp_signal)) # Add noise
23+
24+
# Compute spectrogram using Short-Time Fourier Transform
25+
nperseg = 128
26+
noverlap = nperseg // 2
27+
frequencies, times, Sxx = signal.spectrogram(chirp_signal, fs=sample_rate, nperseg=nperseg, noverlap=noverlap)
28+
29+
# Convert power to dB scale
30+
Sxx_db = 10 * np.log10(Sxx + 1e-10)
31+
32+
# Create DataFrame for plotnine (convert 2D grid to long format)
33+
time_grid, freq_grid = np.meshgrid(times, frequencies)
34+
df = pd.DataFrame({"Time": time_grid.ravel(), "Frequency": freq_grid.ravel(), "Power": Sxx_db.ravel()})
35+
36+
# Filter to relevant frequency range (0-500 Hz)
37+
df = df[df["Frequency"] <= 500]
38+
39+
# Create spectrogram plot using geom_tile (heatmap)
40+
plot = (
41+
ggplot(df, aes(x="Time", y="Frequency", fill="Power"))
42+
+ geom_tile()
43+
+ scale_fill_gradientn(colors=["#000033", "#306998", "#4A90A4", "#FFD43B", "#FFFFFF"], name="Power (dB)")
44+
+ labs(x="Time (s)", y="Frequency (Hz)", title="spectrogram-basic · plotnine · pyplots.ai")
45+
+ theme_minimal()
46+
+ theme(
47+
figure_size=(16, 9),
48+
text=element_text(size=14),
49+
axis_title=element_text(size=20),
50+
axis_text=element_text(size=16),
51+
plot_title=element_text(size=24),
52+
legend_text=element_text(size=14),
53+
legend_title=element_text(size=16),
54+
)
55+
)
56+
57+
# Save
58+
plot.save("plot.png", dpi=300, verbose=False)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: plotnine
2+
specification_id: spectrogram-basic
3+
created: '2025-12-31T05:33:26Z'
4+
updated: '2025-12-31T05:44:32Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612807474
7+
issue: 2927
8+
python_version: 3.13.11
9+
library_version: 0.15.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/spectrogram-basic/plotnine/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/spectrogram-basic/plotnine/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Clear visualization of chirp signal with frequency increasing over time
17+
- Proper use of dB scale for power representation
18+
- Well-formatted title following spec format exactly
19+
- Good axis labels with units as specified
20+
- Reproducible with fixed random seed
21+
- Clean, readable KISS code structure
22+
- Appropriate colormap that is perceptually reasonable
23+
weaknesses:
24+
- Custom color gradient could use a more perceptually uniform colormap like viridis
25+
(spec recommends viridis/inferno)
26+
- No grid lines to aid in reading specific time-frequency values
27+
- Frequency axis filtered to 500 Hz when chirp only reaches 200 Hz - could be tighter
28+
range for better visualization

0 commit comments

Comments
 (0)