%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22yohou%5Bplotting%5D%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.8%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Cross-Validation%20Splitters%20for%20Time%20Series%0A%0A%20%20%20%20Standard%20k-fold%20CV%20violates%20temporal%20order.%20Yohou%20provides%20time-respecting%0A%20%20%20%20splitters%20that%20always%20train%20on%20past%20data%20and%20test%20on%20future%20data.%0A%0A%20%20%20%20%23%23%20Prerequisites%0A%0A%20%20%20%20Basic%20understanding%20of%20cross-validation%20concepts.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20polars%20as%20pl%0A%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.model_selection%20import%20ExpandingWindowSplitter%2C%20SlidingWindowSplitter%0A%20%20%20%20from%20yohou.plotting%20import%20plot_splits%2C%20plot_time_series%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20SlidingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20plot_splits%2C%0A%20%20%20%20%20%20%20%20plot_time_series%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Prepare%20Data%0A%0A%20%20%20%20We%20create%20a%20time%20series%20dataset%20to%20demonstrate%20how%20each%20cross-validation%20splitter%20partitions%20the%20data%20into%20training%20and%20test%20folds.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly)%3A%0A%20%20%20%20y%20%3D%20fetch_tourism_monthly().frame.select(%22time%22%2C%20%22T1__tourists%22).drop_nulls().rename(%7B%22T1__tourists%22%3A%20%22tourists%22%7D)%0A%20%20%20%20print(f%22Total%20observations%3A%20%7Blen(y)%7D%22)%0A%20%20%20%20return%20(y%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20y)%3A%0A%20%20%20%20plot_time_series(y%2C%20title%3D%22Tourism%20Monthly%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20ExpandingWindowSplitter%0A%0A%20%20%20%20The%20training%20set%20**grows**%20with%20each%20split.%20The%20earliest%20data%20is%20always%20included.%0A%20%20%20%20%60n_splits%60%20controls%20how%20many%20train%2Ftest%20pairs%20are%20generated.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ExpandingWindowSplitter%2C%20y)%3A%0A%20%20%20%20expanding%20%3D%20ExpandingWindowSplitter(n_splits%3D4%2C%20test_size%3D12)%0A%0A%20%20%20%20print(f%22Number%20of%20splits%3A%20%7Bexpanding.get_n_splits(y)%7D%22)%0A%20%20%20%20for%20_i%2C%20(_train_idx%2C%20_test_idx)%20in%20enumerate(expanding.split(y))%3A%0A%20%20%20%20%20%20%20%20print(f%22%20%20Split%20%7B_i%7D%3A%20train%3D%7Blen(_train_idx)%7D%2C%20test%3D%7Blen(_test_idx)%7D%22)%0A%20%20%20%20return%20(expanding%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%5B%60plot_splits%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.model_selection.plot_splits%2F)%20renders%20each%20CV%20fold%20as%20a%20horizontal%20bar%2C%20with%20training%0A%20%20%20%20periods%20in%20one%20colour%20and%20test%20periods%20in%20another.%20This%20makes%20it%20easy%0A%20%20%20%20to%20verify%20that%20the%20splitter%20respects%20temporal%20order.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(expanding%2C%20plot_splits%2C%20y)%3A%0A%20%20%20%20plot_splits(y%2C%20expanding%2C%20title%3D%22Expanding%20Window%20Splitter%20(4%20splits%2C%20test%3D12)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20SlidingWindowSplitter%0A%0A%20%20%20%20The%20training%20window%20has%20a%20**fixed%20size**%20that%20slides%20forward.%0A%20%20%20%20Older%20data%20is%20dropped%2C%20keeping%20model%20training%20focused%20on%20recent%20patterns.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SlidingWindowSplitter%2C%20y)%3A%0A%20%20%20%20sliding%20%3D%20SlidingWindowSplitter(n_splits%3D5%2C%20test_size%3D12)%0A%0A%20%20%20%20print(f%22Number%20of%20splits%3A%20%7Bsliding.get_n_splits()%7D%22)%0A%20%20%20%20for%20_i%2C%20(_train_idx%2C%20_test_idx)%20in%20enumerate(sliding.split(y))%3A%0A%20%20%20%20%20%20%20%20print(f%22%20%20Split%20%7B_i%7D%3A%20train%3D%7Blen(_train_idx)%7D%2C%20test%3D%7Blen(_test_idx)%7D%22)%0A%20%20%20%20return%20(sliding%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%5B%60plot_splits%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.model_selection.plot_splits%2F)%20now%20shows%20the%20sliding%20window%20structure%2C%20where%20each%20fold%0A%20%20%20%20has%20a%20fixed-size%20training%20window%20that%20moves%20forward%20through%20time.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_splits%2C%20sliding%2C%20y)%3A%0A%20%20%20%20plot_splits(y%2C%20sliding%2C%20title%3D%22Sliding%20Window%20Splitter%20(5%20folds%2C%20test%3D12)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204.%20Controlling%20Window%20Size%0A%0A%20%20%20%20%60max_train_size%60%20on%20%5B%60ExpandingWindowSplitter%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.split.ExpandingWindowSplitter%2F)%20limits%20growth%2C%20effectively%0A%20%20%20%20becoming%20a%20sliding%20window%20that%20fills%20up%20from%20the%20expanding%20start.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ExpandingWindowSplitter%2C%20plot_splits%2C%20y)%3A%0A%20%20%20%20capped%20%3D%20ExpandingWindowSplitter(n_splits%3D4%2C%20test_size%3D12%2C%20max_train_size%3D80)%0A%0A%20%20%20%20for%20_i%2C%20(_train_idx%2C%20_test_idx)%20in%20enumerate(capped.split(y))%3A%0A%20%20%20%20%20%20%20%20print(f%22%20%20Split%20%7B_i%7D%3A%20train%3D%7Blen(_train_idx)%7D%20(capped%20at%2080)%2C%20test%3D%7Blen(_test_idx)%7D%22)%0A%0A%20%20%20%20plot_splits(y%2C%20capped%2C%20title%3D%22Expanding%20Window%20capped%20at%2080%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20SlidingWindowSplitter%20with%20Stride%0A%0A%20%20%20%20%60stride%60%20controls%20how%20many%20observations%20the%20window%20advances%20between%20splits.%0A%20%20%20%20Default%20is%20%60test_size%60%20(non-overlapping%20test%20sets).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SlidingWindowSplitter%2C%20plot_splits%2C%20y)%3A%0A%20%20%20%20strided%20%3D%20SlidingWindowSplitter(n_splits%3D5%2C%20test_size%3D12%2C%20stride%3D6)%0A%0A%20%20%20%20print(f%22Splits%20with%20stride%3D6%3A%20%7Bstrided.get_n_splits()%7D%22)%0A%20%20%20%20plot_splits(y%2C%20strided%2C%20title%3D%22Sliding%20Window%20with%20stride%3D6%20(overlapping%20tests)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Next%20Steps%0A%0A%20%20%20%20-%20**Hyperparameter%20search**%3A%20See%20the%20%5BForecasting%20Workflow%5D(%2Fpages%2Ftutorials%2Fforecasting-workflow%2F)%20tutorial%20for%20using%20splitters%20inside%20GridSearchCV%0A%20%20%20%20-%20**Conceptual%20background**%3A%20See%20%5BModel%20Selection%5D(%2Fpages%2Fexplanation%2Fmodel-selection%2F)%20for%20expanding%20vs.%20sliding%20windows%20and%20fold%20design%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
f43fdea122d484958f26a97972a3b3d7