|
| 1 | +""" pyplots.ai |
| 2 | +line-realtime: Real-Time Updating Line Chart |
| 3 | +Library: seaborn 0.13.2 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-31 |
| 5 | +""" |
| 6 | + |
| 7 | +import matplotlib.pyplot as plt |
| 8 | +import numpy as np |
| 9 | +import pandas as pd |
| 10 | +import seaborn as sns |
| 11 | +from matplotlib.dates import DateFormatter |
| 12 | + |
| 13 | + |
| 14 | +# Data - Simulated CPU usage with realistic patterns |
| 15 | +np.random.seed(42) |
| 16 | +n_points = 100 |
| 17 | +visible_points = 60 # Sliding window shows last 60 points |
| 18 | + |
| 19 | +# Create timestamps (100ms intervals) |
| 20 | +base_time = pd.Timestamp("2025-12-31 14:30:00") |
| 21 | +timestamps = pd.date_range(start=base_time, periods=n_points, freq="100ms") |
| 22 | + |
| 23 | +# Generate realistic CPU usage (with spikes and trends) |
| 24 | +base_usage = 45 + np.cumsum(np.random.randn(n_points) * 0.5) |
| 25 | +spikes = np.random.choice([0, 15, 25], size=n_points, p=[0.85, 0.10, 0.05]) |
| 26 | +cpu_usage = np.clip(base_usage + spikes + np.random.randn(n_points) * 2, 5, 95) |
| 27 | + |
| 28 | +# Create DataFrame |
| 29 | +df = pd.DataFrame({"timestamp": timestamps, "cpu_usage": cpu_usage}) |
| 30 | + |
| 31 | +# Select visible window (sliding window effect) |
| 32 | +df_visible = df.iloc[-visible_points:].copy() |
| 33 | + |
| 34 | +# Create alpha gradient for fade effect (older points more transparent) |
| 35 | +alpha_values = np.linspace(0.3, 1.0, visible_points) |
| 36 | + |
| 37 | +# Create plot |
| 38 | +fig, ax = plt.subplots(figsize=(16, 9)) |
| 39 | + |
| 40 | +# Plot the main line with gradient effect (segment by segment for fade) |
| 41 | +for i in range(len(df_visible) - 1): |
| 42 | + sns.lineplot( |
| 43 | + data=df_visible.iloc[i : i + 2], |
| 44 | + x="timestamp", |
| 45 | + y="cpu_usage", |
| 46 | + ax=ax, |
| 47 | + color="#306998", |
| 48 | + linewidth=3, |
| 49 | + alpha=alpha_values[i], |
| 50 | + legend=False, |
| 51 | + ) |
| 52 | + |
| 53 | +# Add scatter points at key locations (latest points more visible) |
| 54 | +scatter_indices = [0, len(df_visible) // 4, len(df_visible) // 2, -10, -5, -1] |
| 55 | +for idx in scatter_indices: |
| 56 | + point = df_visible.iloc[idx] |
| 57 | + alpha = alpha_values[idx] if idx >= 0 else 1.0 |
| 58 | + size = 150 if idx == -1 else 80 |
| 59 | + ax.scatter( |
| 60 | + point["timestamp"], |
| 61 | + point["cpu_usage"], |
| 62 | + s=size, |
| 63 | + color="#306998", |
| 64 | + alpha=alpha, |
| 65 | + zorder=5, |
| 66 | + edgecolors="white", |
| 67 | + linewidth=1.5, |
| 68 | + ) |
| 69 | + |
| 70 | +# Highlight the latest value with annotation |
| 71 | +latest_value = df_visible.iloc[-1]["cpu_usage"] |
| 72 | +latest_time = df_visible.iloc[-1]["timestamp"] |
| 73 | +ax.annotate( |
| 74 | + f"Latest: {latest_value:.1f}%", |
| 75 | + xy=(latest_time, latest_value), |
| 76 | + xytext=(15, 25), |
| 77 | + textcoords="offset points", |
| 78 | + fontsize=18, |
| 79 | + fontweight="bold", |
| 80 | + color="#306998", |
| 81 | + bbox={"boxstyle": "round,pad=0.4", "facecolor": "#FFD43B", "edgecolor": "#306998", "linewidth": 2}, |
| 82 | + arrowprops={"arrowstyle": "->", "color": "#306998", "linewidth": 2}, |
| 83 | +) |
| 84 | + |
| 85 | +# Add arrow indicating scroll direction (old data scrolls off left) |
| 86 | +ax.annotate( |
| 87 | + "", |
| 88 | + xy=(df_visible.iloc[0]["timestamp"], 92), |
| 89 | + xytext=(df_visible.iloc[8]["timestamp"], 92), |
| 90 | + arrowprops={"arrowstyle": "<-", "color": "#666666", "linewidth": 2.5, "mutation_scale": 15}, |
| 91 | +) |
| 92 | +ax.text( |
| 93 | + df_visible.iloc[4]["timestamp"], |
| 94 | + 96, |
| 95 | + "← Old data scrolls off", |
| 96 | + fontsize=13, |
| 97 | + color="#666666", |
| 98 | + ha="center", |
| 99 | + style="italic", |
| 100 | +) |
| 101 | + |
| 102 | +# Styling |
| 103 | +ax.set_xlabel("Time (HH:MM:SS)", fontsize=20) |
| 104 | +ax.set_ylabel("CPU Usage (%)", fontsize=20) |
| 105 | +ax.set_title("line-realtime · seaborn · pyplots.ai", fontsize=24, fontweight="bold") |
| 106 | +ax.tick_params(axis="both", labelsize=16) |
| 107 | + |
| 108 | +# Format x-axis time labels (cleaner format) |
| 109 | +ax.xaxis.set_major_formatter(DateFormatter("%H:%M:%S")) |
| 110 | +plt.xticks(rotation=25, ha="right") |
| 111 | + |
| 112 | +# Set y-axis limits with padding |
| 113 | +ax.set_ylim(0, 100) |
| 114 | + |
| 115 | +# Grid styling |
| 116 | +ax.grid(True, alpha=0.3, linestyle="--") |
| 117 | +ax.set_facecolor("#fafafa") |
| 118 | + |
| 119 | +# Add live indicator with pulsing effect simulation |
| 120 | +ax.text( |
| 121 | + 0.02, |
| 122 | + 0.96, |
| 123 | + "● LIVE", |
| 124 | + transform=ax.transAxes, |
| 125 | + fontsize=18, |
| 126 | + fontweight="bold", |
| 127 | + color="#e74c3c", |
| 128 | + verticalalignment="top", |
| 129 | +) |
| 130 | + |
| 131 | +# Add window info |
| 132 | +ax.text( |
| 133 | + 0.98, |
| 134 | + 0.02, |
| 135 | + f"Sliding window: {visible_points} samples (6 sec)", |
| 136 | + transform=ax.transAxes, |
| 137 | + fontsize=13, |
| 138 | + color="#666666", |
| 139 | + ha="right", |
| 140 | + va="bottom", |
| 141 | +) |
| 142 | + |
| 143 | +plt.tight_layout() |
| 144 | +plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
0 commit comments