|
1 | 1 | """ pyplots.ai |
2 | 2 | density-basic: Basic Density Plot |
3 | | -Library: matplotlib 3.10.8 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: matplotlib 3.10.8 | Python 3.14.3 |
| 4 | +Quality: 91/100 | Updated: 2026-02-23 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import matplotlib.pyplot as plt |
| 8 | +import matplotlib.ticker as ticker |
8 | 9 | import numpy as np |
| 10 | +from matplotlib.collections import EventCollection |
| 11 | +from matplotlib.colors import LinearSegmentedColormap |
| 12 | +from matplotlib.patches import PathPatch |
| 13 | +from matplotlib.path import Path |
9 | 14 | from scipy import stats |
10 | 15 |
|
11 | 16 |
|
12 | | -# Data - Simulating test scores with realistic distribution characteristics |
| 17 | +# Data - Response times (ms) showing bimodal server behavior |
13 | 18 | np.random.seed(42) |
14 | | -# Create a slightly left-skewed distribution typical of test scores |
15 | | -values = np.concatenate( |
16 | | - [ |
17 | | - np.random.normal(72, 12, 400), # Main group of students |
18 | | - np.random.normal(45, 8, 100), # Lower performing group (creates slight bimodality) |
19 | | - ] |
20 | | -) |
21 | | -values = np.clip(values, 0, 100) # Clip to valid test score range |
| 19 | +cached_responses = np.random.normal(45, 12, 350) # Fast cache-hit requests |
| 20 | +db_responses = np.random.normal(140, 25, 150) # Slower database-query requests |
| 21 | +response_times = np.concatenate([cached_responses, db_responses]) |
| 22 | +response_times = response_times[response_times > 0] |
22 | 23 |
|
23 | | -# Calculate kernel density estimation |
24 | | -kde = stats.gaussian_kde(values) |
25 | | -x_range = np.linspace(values.min() - 5, values.max() + 5, 500) |
| 24 | +# Compute KDE with Silverman bandwidth selection |
| 25 | +kde = stats.gaussian_kde(response_times, bw_method="silverman") |
| 26 | +x_range = np.linspace(0, response_times.max() + 30, 600) |
26 | 27 | density = kde(x_range) |
27 | 28 |
|
28 | | -# Create plot (4800x2700 px) |
| 29 | +# Plot |
29 | 30 | fig, ax = plt.subplots(figsize=(16, 9)) |
30 | 31 |
|
31 | | -# Plot density curve with fill |
32 | | -ax.fill_between(x_range, density, alpha=0.4, color="#306998") |
33 | | -ax.plot(x_range, density, linewidth=3, color="#306998") |
| 32 | +# Vertical gradient fill (dark at base, light at top) clipped to density curve |
| 33 | +cmap = LinearSegmentedColormap.from_list("blue_fade", ["#306998", "#c4daf0"]) |
| 34 | +gradient = np.linspace(0, 1, 256).reshape(-1, 1) |
| 35 | +ax.imshow( |
| 36 | + gradient, |
| 37 | + extent=[x_range[0], x_range[-1], 0, density.max()], |
| 38 | + aspect="auto", |
| 39 | + cmap=cmap, |
| 40 | + alpha=0.5, |
| 41 | + origin="lower", |
| 42 | + zorder=1, |
| 43 | +) |
| 44 | +verts = np.column_stack([np.concatenate([x_range, [x_range[-1], x_range[0]]]), np.concatenate([density, [0, 0]])]) |
| 45 | +codes = [Path.MOVETO] + [Path.LINETO] * (len(verts) - 1) |
| 46 | +clip_path = PathPatch(Path(verts, codes), transform=ax.transData, facecolor="none", edgecolor="none") |
| 47 | +ax.add_patch(clip_path) |
| 48 | +for artist in ax.get_images(): |
| 49 | + artist.set_clip_path(clip_path) |
| 50 | + |
| 51 | +# Density curve |
| 52 | +ax.plot(x_range, density, linewidth=3, color="#306998", zorder=3) |
34 | 53 |
|
35 | | -# Add rug plot showing individual observations |
36 | | -rug_height = -0.0015 |
37 | | -ax.plot(values, np.full_like(values, rug_height), "|", color="#306998", alpha=0.4, markersize=12) |
| 54 | +# Rug plot using EventCollection |
| 55 | +rug = EventCollection( |
| 56 | + response_times, lineoffset=-0.0006, linelength=0.001, linewidth=0.8, color="#306998", alpha=0.4, zorder=2 |
| 57 | +) |
| 58 | +ax.add_collection(rug) |
38 | 59 |
|
39 | | -# Labels and styling (scaled font sizes for 4800x2700) |
40 | | -ax.set_xlabel("Test Score", fontsize=20) |
| 60 | +# Style |
| 61 | +ax.set_xlabel("Response Time (ms)", fontsize=20) |
41 | 62 | ax.set_ylabel("Density", fontsize=20) |
42 | | -ax.set_title("density-basic · matplotlib · pyplots.ai", fontsize=24) |
43 | | -ax.tick_params(axis="both", labelsize=16) |
44 | | -ax.grid(True, alpha=0.3, linestyle="--") |
| 63 | +ax.set_title("density-basic \u00b7 matplotlib \u00b7 pyplots.ai", fontsize=24, fontweight="medium", pad=16) |
| 64 | +ax.tick_params(axis="both", labelsize=16, length=0) |
| 65 | +ax.xaxis.set_major_formatter(ticker.FormatStrFormatter("%g")) |
45 | 66 |
|
46 | | -# Remove top and right spines for cleaner look |
| 67 | +# Grid and spines |
| 68 | +ax.yaxis.grid(True, alpha=0.15, linewidth=0.8) |
47 | 69 | ax.spines["top"].set_visible(False) |
48 | 70 | ax.spines["right"].set_visible(False) |
| 71 | +ax.spines["left"].set_color("#888888") |
| 72 | +ax.spines["bottom"].set_color("#888888") |
49 | 73 |
|
50 | | -# Set y-axis to show rug plot |
51 | | -ax.set_ylim(bottom=-0.003) |
| 74 | +# Axis limits |
| 75 | +ax.set_xlim(x_range[0], x_range[-1]) |
| 76 | +ax.set_ylim(bottom=-0.0018) |
52 | 77 |
|
53 | 78 | plt.tight_layout() |
54 | 79 | plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
0 commit comments