Skip to content

Commit f80b0ee

Browse files
feat(altair): implement spectrum-basic (#2966)
## Implementation: `spectrum-basic` - altair Implements the **altair** version of `spectrum-basic`. **File:** `plots/spectrum-basic/implementations/altair.py` **Parent Issue:** #2926 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20612831682)* --------- 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 f53c9b4 commit f80b0ee

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
""" pyplots.ai
2+
spectrum-basic: Frequency Spectrum Plot
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 98/100 | Created: 2025-12-31
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Data - Synthetic signal with multiple frequency components
13+
np.random.seed(42)
14+
15+
# Sampling parameters
16+
sample_rate = 1000 # Hz
17+
duration = 1.0 # seconds
18+
n_samples = int(sample_rate * duration)
19+
20+
# Create time-domain signal with multiple frequency components
21+
t = np.linspace(0, duration, n_samples, endpoint=False)
22+
23+
# Signal components: 50 Hz (dominant), 120 Hz (harmonic), 200 Hz (weak), plus noise
24+
signal = (
25+
1.0 * np.sin(2 * np.pi * 50 * t) # 50 Hz - dominant frequency
26+
+ 0.5 * np.sin(2 * np.pi * 120 * t) # 120 Hz - secondary component
27+
+ 0.25 * np.sin(2 * np.pi * 200 * t) # 200 Hz - weak component
28+
+ 0.1 * np.random.randn(n_samples) # Noise
29+
)
30+
31+
# Compute FFT
32+
fft_result = np.fft.fft(signal)
33+
frequencies = np.fft.fftfreq(n_samples, 1 / sample_rate)
34+
35+
# Take only positive frequencies
36+
positive_mask = frequencies >= 0
37+
frequencies = frequencies[positive_mask]
38+
amplitude = np.abs(fft_result[positive_mask]) * 2 / n_samples # Normalize
39+
40+
# Convert to dB scale for better visualization
41+
amplitude_db = 20 * np.log10(amplitude + 1e-10) # Add small value to avoid log(0)
42+
43+
# Create DataFrame
44+
df = pd.DataFrame({"Frequency (Hz)": frequencies, "Amplitude (dB)": amplitude_db})
45+
46+
# Filter to show relevant frequency range (0-300 Hz)
47+
df = df[df["Frequency (Hz)"] <= 300]
48+
49+
# Create chart
50+
chart = (
51+
alt.Chart(df)
52+
.mark_line(color="#306998", strokeWidth=2)
53+
.encode(
54+
x=alt.X("Frequency (Hz):Q", title="Frequency (Hz)", scale=alt.Scale(domain=[0, 300])),
55+
y=alt.Y("Amplitude (dB):Q", title="Amplitude (dB)", scale=alt.Scale(domain=[-80, 10])),
56+
tooltip=[alt.Tooltip("Frequency (Hz):Q", format=".1f"), alt.Tooltip("Amplitude (dB):Q", format=".1f")],
57+
)
58+
.properties(
59+
width=1600, height=900, title=alt.Title("spectrum-basic · altair · pyplots.ai", fontSize=28, anchor="middle")
60+
)
61+
.configure_axis(labelFontSize=18, titleFontSize=22, grid=True, gridColor="#cccccc", gridOpacity=0.3)
62+
.configure_view(strokeWidth=0)
63+
.configure_title(fontSize=28)
64+
)
65+
66+
# Save as PNG and HTML
67+
chart.save("plot.png", scale_factor=3.0)
68+
chart.save("plot.html")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: altair
2+
specification_id: spectrum-basic
3+
created: '2025-12-31T05:35:59Z'
4+
updated: '2025-12-31T05:48:53Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20612831682
7+
issue: 2926
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/spectrum-basic/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/spectrum-basic/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/spectrum-basic/altair/plot.html
13+
quality_score: 98
14+
review:
15+
strengths:
16+
- Excellent FFT computation with proper normalization and dB conversion
17+
- Clear visualization of multiple frequency components (50 Hz dominant, 120 Hz secondary,
18+
200 Hz weak)
19+
- Appropriate use of Altair declarative encoding with typed columns
20+
- Interactive tooltips with number formatting
21+
- Proper scale domains ensure all data is visible
22+
- Clean KISS code structure
23+
weaknesses:
24+
- Could add mark_rule() or mark_point() to highlight peak frequencies
25+
- No use of Altair selection/interactivity features (zoom/pan)

0 commit comments

Comments
 (0)