|
| 1 | +""" pyplots.ai |
| 2 | +scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis |
| 3 | +Library: bokeh 3.9.0 | Python 3.14.3 |
| 4 | +Quality: 85/100 | Created: 2026-04-12 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +from bokeh.io import export_png, save |
| 9 | +from bokeh.models import ColorBar, ColumnDataSource, Label, LinearColorMapper |
| 10 | +from bokeh.palettes import Viridis256 |
| 11 | +from bokeh.plotting import figure |
| 12 | +from bokeh.resources import CDN |
| 13 | +from bokeh.transform import linear_cmap |
| 14 | + |
| 15 | + |
| 16 | +# Data - AR(1) process with moderate positive autocorrelation |
| 17 | +np.random.seed(42) |
| 18 | +n_obs = 500 |
| 19 | +phi = 0.85 |
| 20 | +noise = np.random.normal(0, 1, n_obs) |
| 21 | +series = np.zeros(n_obs) |
| 22 | +series[0] = noise[0] |
| 23 | +for i in range(1, n_obs): |
| 24 | + series[i] = phi * series[i - 1] + noise[i] |
| 25 | + |
| 26 | +lag = 1 |
| 27 | +y_t = series[:-lag] |
| 28 | +y_t_lag = series[lag:] |
| 29 | +time_index = np.arange(len(y_t)) |
| 30 | + |
| 31 | +correlation = np.corrcoef(y_t, y_t_lag)[0, 1] |
| 32 | + |
| 33 | +source = ColumnDataSource(data={"y_t": y_t, "y_t_lag": y_t_lag, "time_index": time_index}) |
| 34 | + |
| 35 | +# Plot |
| 36 | +color_mapper = LinearColorMapper(palette=Viridis256, low=time_index.min(), high=time_index.max()) |
| 37 | + |
| 38 | +p = figure( |
| 39 | + width=4800, height=2700, title="scatter-lag · bokeh · pyplots.ai", x_axis_label="y(t)", y_axis_label="y(t + 1)" |
| 40 | +) |
| 41 | + |
| 42 | +p.scatter( |
| 43 | + x="y_t", |
| 44 | + y="y_t_lag", |
| 45 | + source=source, |
| 46 | + size=20, |
| 47 | + fill_color=linear_cmap("time_index", palette=Viridis256, low=time_index.min(), high=time_index.max()), |
| 48 | + line_color="white", |
| 49 | + line_width=0.8, |
| 50 | + fill_alpha=0.8, |
| 51 | +) |
| 52 | + |
| 53 | +# Diagonal reference line (y = x) |
| 54 | +axis_min = min(y_t.min(), y_t_lag.min()) - 0.5 |
| 55 | +axis_max = max(y_t.max(), y_t_lag.max()) + 0.5 |
| 56 | +p.line( |
| 57 | + [axis_min, axis_max], [axis_min, axis_max], line_color="#AAAAAA", line_dash="dashed", line_width=3, line_alpha=0.5 |
| 58 | +) |
| 59 | + |
| 60 | +# Color bar for time index |
| 61 | +color_bar = ColorBar( |
| 62 | + color_mapper=color_mapper, |
| 63 | + title="Time Index", |
| 64 | + title_text_font_size="22pt", |
| 65 | + title_standoff=20, |
| 66 | + major_label_text_font_size="18pt", |
| 67 | + label_standoff=12, |
| 68 | + width=50, |
| 69 | + padding=40, |
| 70 | +) |
| 71 | +p.add_layout(color_bar, "right") |
| 72 | + |
| 73 | +# Correlation annotation |
| 74 | +corr_label = Label( |
| 75 | + x=axis_min + 0.3, y=axis_max - 0.5, text=f"r = {correlation:.3f}", text_font_size="24pt", text_color="#333333" |
| 76 | +) |
| 77 | +p.add_layout(corr_label) |
| 78 | + |
| 79 | +# Style |
| 80 | +p.title.text_font_size = "32pt" |
| 81 | +p.title.text_font_style = "normal" |
| 82 | +p.xaxis.axis_label_text_font_size = "24pt" |
| 83 | +p.yaxis.axis_label_text_font_size = "24pt" |
| 84 | +p.xaxis.major_label_text_font_size = "20pt" |
| 85 | +p.yaxis.major_label_text_font_size = "20pt" |
| 86 | + |
| 87 | +p.xaxis.minor_tick_line_color = None |
| 88 | +p.yaxis.minor_tick_line_color = None |
| 89 | +p.xaxis.major_tick_line_color = None |
| 90 | +p.yaxis.major_tick_line_color = None |
| 91 | + |
| 92 | +p.xgrid.grid_line_alpha = 0.2 |
| 93 | +p.ygrid.grid_line_alpha = 0.2 |
| 94 | +p.xgrid.grid_line_width = 1 |
| 95 | +p.ygrid.grid_line_width = 1 |
| 96 | + |
| 97 | +p.outline_line_color = None |
| 98 | +p.background_fill_color = "#FFFFFF" |
| 99 | +p.border_fill_color = "#FFFFFF" |
| 100 | + |
| 101 | +p.toolbar_location = None |
| 102 | + |
| 103 | +# Save |
| 104 | +export_png(p, filename="plot.png") |
| 105 | +save(p, filename="plot.html", resources=CDN, title="scatter-lag · bokeh · pyplots.ai") |
0 commit comments