Skip to content

docs: update outputs, notebooks, and GIFs for 0.835 flat detection threshold#246

Merged
RussellSB merged 10 commits into
fix/gradual-up-truncatedfrom
docs/update-baselines-for-flat-threshold
Jul 20, 2026
Merged

docs: update outputs, notebooks, and GIFs for 0.835 flat detection threshold#246
RussellSB merged 10 commits into
fix/gradual-up-truncatedfrom
docs/update-baselines-for-flat-threshold

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Updates documentation outputs, notebook tutorials, and GIFs to reflect the new 8-segment result from PR #224's 0.835x flat detection threshold.

Changes

  • README.md and docs/index.md: Updated print_summary() output block (9→8 segments, removed Flat Mar 15-17, extended Up to Mar 17) and filter_segments table with corrected dates/metrics
  • gradual.ipynb: Re-executed to regenerate all output cells with new segment boundaries
  • noise.ipynb: Re-executed + fixed markdown cell ("9 segments" → "10 segments")
  • abrupt.ipynb: Normalised via scripts/normalize_notebooks.sh (no content changes)
  • Ran scripts/normalize_notebooks.sh across all notebooks for JupyterLite compatibility
  • Programmatic GIF generation: Added scripts/generate_gifs/ with shared utilities and scripts for all four GIFs (Abrupt, Gradual, Noise-Random, Noise-Spikes)
  • Regenerated plots: plots/pytrendy-gradual.png updated with current pytrendy behaviour

Scope

  • CHANGELOG.md and docs/whats-new.md are not touched (automation targets)

Verification

All 101 tests pass on this branch (74 core + 27 non-core).

Closes #233

- Update print_summary() output in README.md and docs/index.md to reflect
  8 segments (removed Flat Mar 15-17, extended Up to Mar 17)
- Update filter_segments table with new Up segment dates and metrics
- Re-execute gradual.ipynb and noise.ipynb with new threshold
- Fix noise.ipynb markdown: 9 → 10 noise segments
- Run normalize_notebooks.sh for JupyterLite compatibility
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

📚 Docs preview removed

The docs preview for this PR has been cleaned up.

github-actions Bot added a commit that referenced this pull request Jul 17, 2026
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@RussellSB
RussellSB changed the base branch from develop to fix/gradual-up-truncated July 17, 2026 09:25
github-actions Bot added a commit that referenced this pull request Jul 18, 2026
github-actions Bot added a commit that referenced this pull request Jul 19, 2026
github-actions Bot added a commit that referenced this pull request Jul 19, 2026
github-actions Bot added a commit that referenced this pull request Jul 19, 2026
github-actions Bot added a commit that referenced this pull request Jul 19, 2026
@RussellSB RussellSB changed the title docs: update outputs and notebooks for 0.835 flat detection threshold docs: update outputs, notebooks, and GIFs for 0.835 flat detection threshold Jul 19, 2026
@RussellSB
RussellSB marked this pull request as ready for review July 19, 2026 20:48
@RussellSB
RussellSB self-requested a review as a code owner July 19, 2026 20:48
@RussellSB RussellSB self-assigned this Jul 20, 2026
@RussellSB

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 444365fb-4aa2-488d-81ce-922a91394af0

📥 Commits

Reviewing files that changed from the base of the PR and between c24ca8b and 7a60293.

⛔ Files ignored due to path filters (5)
  • plots/Abrupt.gif is excluded by !**/*.gif
  • plots/Gradual.gif is excluded by !**/*.gif
  • plots/Noise-Random.gif is excluded by !**/*.gif
  • plots/Noise-Spikes.gif is excluded by !**/*.gif
  • plots/pytrendy-gradual.png is excluded by !**/*.png
📒 Files selected for processing (10)
  • README.md
  • docs/examples/fundamentals/abrupt.ipynb
  • docs/examples/fundamentals/gradual.ipynb
  • docs/examples/fundamentals/noise.ipynb
  • docs/index.md
  • scripts/generate_gifs/abrupt.py
  • scripts/generate_gifs/gradual.py
  • scripts/generate_gifs/noise_random.py
  • scripts/generate_gifs/noise_spikes.py
  • scripts/generate_gifs/utils.py
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/examples/fundamentals/noise.ipynb
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

MkDocs Material + mkdocstrings docs should use Google-style docstrings, and any new documentation page must be registered under nav: in mkdocs.yml.

Files:

  • docs/examples/fundamentals/noise.ipynb
  • docs/examples/fundamentals/abrupt.ipynb
  • docs/index.md
  • docs/examples/fundamentals/gradual.ipynb
docs/examples/**/*.ipynb

📄 CodeRabbit inference engine (AGENTS.md)

Run scripts/normalize_notebooks.sh before building docs for JupyterLite notebooks in docs/examples/.

Files:

  • docs/examples/fundamentals/noise.ipynb
  • docs/examples/fundamentals/abrupt.ipynb
  • docs/examples/fundamentals/gradual.ipynb
🪛 Ruff (0.15.21)
docs/examples/fundamentals/noise.ipynb

[error] 35-35: Undefined name display

(F821)


[error] 37-37: Undefined name display

(F821)


[warning] 47-47: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

scripts/generate_gifs/gradual.py

[warning] 50-50: Missing return type annotation for private function R

Add return type annotation: None

(ANN202)


[warning] 50-50: Boolean default positional argument in function definition

(FBT002)


[warning] 53-53: Missing return type annotation for private function hold

Add return type annotation: None

(ANN202)


[error] 60-60: Multiple statements on one line (semicolon)

(E702)


[error] 64-64: Multiple statements on one line (semicolon)

(E702)


[error] 67-67: Multiple statements on one line (semicolon)

(E702)


[error] 72-72: Multiple statements on one line (semicolon)

(E702)


[error] 75-75: Multiple statements on one line (semicolon)

(E702)


[error] 80-80: Multiple statements on one line (semicolon)

(E702)


[error] 85-85: Multiple statements on one line (semicolon)

(E702)


[error] 88-88: Multiple statements on one line (semicolon)

(E702)

scripts/generate_gifs/noise_spikes.py

[warning] 58-58: Missing return type annotation for private function R

Add return type annotation: None

(ANN202)


[warning] 58-58: Boolean default positional argument in function definition

(FBT002)


[warning] 61-61: Missing return type annotation for private function hold

Add return type annotation: None

(ANN202)


[error] 68-68: Multiple statements on one line (semicolon)

(E702)


[error] 72-72: Multiple statements on one line (semicolon)

(E702)


[error] 75-75: Multiple statements on one line (semicolon)

(E702)


[error] 80-80: Multiple statements on one line (semicolon)

(E702)


[error] 83-83: Multiple statements on one line (semicolon)

(E702)


[error] 88-88: Multiple statements on one line (semicolon)

(E702)


[error] 93-93: Multiple statements on one line (semicolon)

(E702)


[error] 96-96: Multiple statements on one line (semicolon)

(E702)

scripts/generate_gifs/abrupt.py

[warning] 42-42: Too many statements (61 > 50)

(PLR0915)


[warning] 55-55: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


[warning] 62-62: Missing return type annotation for private function R

Add return type annotation: None

(ANN202)


[warning] 62-62: Boolean default positional argument in function definition

(FBT002)


[warning] 65-65: Missing return type annotation for private function hold

Add return type annotation: None

(ANN202)


[error] 72-72: Multiple statements on one line (semicolon)

(E702)


[error] 76-76: Multiple statements on one line (semicolon)

(E702)


[error] 79-79: Multiple statements on one line (semicolon)

(E702)


[error] 84-84: Multiple statements on one line (semicolon)

(E702)


[error] 87-87: Multiple statements on one line (semicolon)

(E702)


[error] 92-92: Multiple statements on one line (semicolon)

(E702)


[error] 108-108: Multiple statements on one line (semicolon)

(E702)


[error] 112-112: Multiple statements on one line (semicolon)

(E702)


[error] 115-115: Multiple statements on one line (semicolon)

(E702)


[error] 120-120: Multiple statements on one line (semicolon)

(E702)


[error] 123-123: Multiple statements on one line (semicolon)

(E702)


[error] 128-128: Multiple statements on one line (semicolon)

(E702)

scripts/generate_gifs/noise_random.py

[warning] 87-87: Missing return type annotation for private function hold

Add return type annotation: None

(ANN202)

scripts/generate_gifs/utils.py

[error] 67-67: Test for membership should be not in

Convert to not in

(E713)


[error] 72-72: Test for membership should be not in

Convert to not in

(E713)


[warning] 82-82: Self-assignment of variable start

(PLW0127)


[error] 105-105: Test for membership should be not in

Convert to not in

(E713)


[warning] 109-109: Self-assignment of variable end

(PLW0127)


[warning] 125-125: Boolean-typed positional argument in function definition

(FBT001)


[warning] 125-125: Boolean default positional argument in function definition

(FBT002)


[warning] 189-189: Boolean positional value in function call

(FBT003)


[warning] 239-239: Unnecessary assignment to size_kb before return statement

Remove unnecessary assignment

(RET504)

docs/examples/fundamentals/gradual.ipynb

[warning] 36-36: Found useless expression. Either assign it to a variable or remove it.

(B018)

🔇 Additional comments (19)
docs/examples/fundamentals/abrupt.ipynb (2)

47-47: LGTM!

Also applies to: 96-97, 125-126, 133-134, 168-169, 176-177, 232-233, 240-241, 291-292, 299-300, 358-359, 405-406, 413-414, 440-441, 448-449, 533-534


497-498: 🎯 Functional Correctness

No change needed here The table is sorted by descending total_change; the change_rank values are just the underlying ranks, so the markdown and output already agree.

			> Likely an incorrect or invalid review comment.
scripts/generate_gifs/utils.py (4)

87-104: 🎯 Functional Correctness | ⚡ Quick win

Truthiness check on numeric boundary values is unsafe for a value of exactly 0.

valid_up_start/valid_down_start/valid_up_end/valid_down_end gate on (value_new_start) / (value_new_end) directly. If the signal legitimately equals 0.0 at the neighbouring point, this evaluates as falsy even though the value exists in the index, so the boundary won't be extended even when it should be. Use an explicit is not None check instead.

🐛 Proposed fix
-            valid_up_start = (value_new_start) and (seg["direction"] == "Up") and (value_new_start < value)
-            valid_down_start = (value_new_start) and (seg["direction"] == "Down") and (value_new_start > value)
+            valid_up_start = (value_new_start is not None) and (seg["direction"] == "Up") and (value_new_start < value)
+            valid_down_start = (value_new_start is not None) and (seg["direction"] == "Down") and (value_new_start > value)
-            valid_up_end = (value_new_end) and (seg["direction"] == "Up") and (value_new_end > value)
-            valid_down_end = (value_new_end) and (seg["direction"] == "Down") and (value_new_end < value)
+            valid_up_end = (value_new_end is not None) and (seg["direction"] == "Up") and (value_new_end > value)
+            valid_down_end = (value_new_end is not None) and (seg["direction"] == "Down") and (value_new_end < value)

60-113: 📐 Maintainability & Code Quality | 💤 Low value

A few readability nits in adjust_segment_boundaries.

  • Lines 67, 72, 105: Ruff's not ("x" in y)"x" not in y (E713) applies three times.
  • Line 105 recomputes is_not_trend identically to line 72 — same seg, same expression, no need to redo it right before the "adjust ends" block.
  • Line 82: start = start is a self-assignment no-op; the comment above it ("Conditional logic for making abrupt visually tighter") reads like intent that never got implemented. Worth a plain pass or just removing the branch's body if there truly is nothing to do here.

193-198: 📐 Maintainability & Code Quality | ⚡ Quick win

Legend colours are hardcoded rather than sourced from COLOR_MAP.

The four mpatches.Patch(color=..., ...) calls duplicate the values already defined in COLOR_MAP (lines 36-41). If COLOR_MAP ever changes, the legend silently drifts out of sync with the actual fills.

♻️ Proposed fix
-    legend_handles = [
-        mpatches.Patch(color="lightgreen", alpha=0.4, label="Up"),
-        mpatches.Patch(color="lightcoral", alpha=0.4, label="Down"),
-        mpatches.Patch(color="lightblue", alpha=0.4, label="Flat"),
-        mpatches.Patch(color="lightgray", alpha=0.4, label="Noise"),
-    ]
+    legend_handles = [
+        mpatches.Patch(color=COLOR_MAP[direction], alpha=0.4, label=direction)
+        for direction in ("Up", "Down", "Flat", "Noise")
+    ]

1-24: LGTM!

Also applies to: 119-217, 220-239

scripts/generate_gifs/abrupt.py (2)

26-28: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Animation boilerplate (_crossfade, R/hold, sweep/rank/fade cycle) is duplicated across every gif script.

This same shape reappears in gradual.py and noise_spikes.py (and _crossfade verbatim in noise_random.py). Flagging here; see consolidated comment for the cross-file picture and fix.

Also applies to: 59-137


1-24: LGTM!

Also applies to: 42-58, 138-150

scripts/generate_gifs/gradual.py (2)

50-88: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Same duplicated animation-cycle boilerplate as abrupt.py / noise_spikes.py.

See the consolidated comment for the cross-file fix.


1-49: LGTM!

Also applies to: 89-101

scripts/generate_gifs/noise_random.py (2)

40-42: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

_crossfade and hold duplicate the copies in abrupt.py/gradual.py/noise_spikes.py.

See the consolidated comment for the cross-file fix.

Also applies to: 87-88


1-39: LGTM!

Also applies to: 48-86, 89-137

scripts/generate_gifs/noise_spikes.py (2)

58-96: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Same duplicated animation-cycle boilerplate as gradual.py / abrupt.py.

See the consolidated comment for the cross-file fix.


1-57: LGTM!

Also applies to: 97-109

README.md (2)

80-84: 🎯 Functional Correctness | ⚡ Quick win

Stale time_index in the "strongest uptrends" example — row 1 wasn't updated for the new 8-segment numbering.

Per the regenerated Full Results table above, time_index=6 is now the Up 2025-04-02 → 2025-05-08 segment (rank 2), and time_index=7 is the Down 2025-05-09 → 2025-06-17 segment. Line 82 still shows 7 for the Up row — a leftover from the old 9-segment numbering before the Mar 15–17 flat was removed. The notebook's own results.df/filter_segments output confirms 6 is correct. Row 2 (line 83) was updated correctly, so this looks like a missed edit rather than an intentional discrepancy.

🐛 Proposed fix
-| 7 | Up | 2025-04-02 | 2025-05-08 | gradual | 72.61 | 367.50% | 36 | 72.61 | 21.70 | 2 |
+| 6 | Up | 2025-04-02 | 2025-05-08 | gradual | 72.61 | 367.50% | 36 | 72.61 | 21.70 | 2 |

36-79: LGTM!

Also applies to: 85-91

docs/index.md (2)

100-104: 🎯 Functional Correctness | ⚡ Quick win

Same stale time_index issue as README.md — row 1 of "strongest uptrends" still says 7 instead of 6.

Identical root cause to the README.md instance; see the consolidated comment.

🐛 Proposed fix
-| 7 | Up | 2025-04-02 | 2025-05-08 | gradual | 72.61 | 367.50% | 36 | 72.61 | 21.70 | 2 |
+| 6 | Up | 2025-04-02 | 2025-05-08 | gradual | 72.61 | 367.50% | 36 | 72.61 | 21.70 | 2 |

59-98: LGTM!

Also applies to: 105-114

docs/examples/fundamentals/gradual.ipynb (1)

33-49: LGTM!

Also applies to: 68-71, 95-105, 124-142, 169-179, 226-229, 245-255, 269-279, 297-307, 325-334, 368-378, 404-414, 444-454, 487-497


📝 Walkthrough

Summary by CodeRabbit

  • Documentation

    • Updated Quickstart and fundamentals examples with refreshed trend-detection results.
    • Corrected segment counts, boundaries, dates, metrics, rankings, and filtered-result tables.
    • Updated examples now show two flat segments instead of three.
  • New Features

    • Added animated visualisations demonstrating abrupt and gradual trend detection.
    • Added animations showing how trend detection responds to random noise and spikes.

Walkthrough

The PR adds shared GIF-rendering utilities and four executable animation generators. README, documentation, and the gradual tutorial notebook now show trend results produced with two flat segments and revised boundaries and metrics.

Changes

Trend visualisation and documentation

Layer / File(s) Summary
Shared GIF rendering foundation
scripts/generate_gifs/utils.py
Adds segment-boundary adjustment, matplotlib frame rendering, colour configuration, and palette-quantised looping GIF output.
Animation generators
scripts/generate_gifs/*.py
Adds generators for abrupt, gradual, random-noise, and spike scenarios, including trend detection, frame sequencing, fades, and executable entry points.
Updated trend examples
README.md, docs/index.md, docs/examples/fundamentals/gradual.ipynb
Refreshes displayed segmentation counts, boundaries, metrics, ranks, filtered tables, and notebook outputs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Generator as Animation generator
  participant PyTrendy as pytrendy
  participant Renderer as render_frame
  participant Saver as save_gif
  Generator->>PyTrendy: Load data and detect trends
  PyTrendy-->>Generator: Return segments
  Generator->>Renderer: Render frames
  Renderer-->>Generator: Return images
  Generator->>Saver: Save frames and durations
Loading

Possibly related PRs

Suggested labels: documentation, automated

Suggested reviewers: russellsb

Poem

Trends sweep softly, frames align,
Flats fall from three to two in line.
Fades cross noise, spikes arise,
GIFs loop beneath refreshed skies.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR adds Abrupt GIF generation and a shared GIF utility module, which go beyond the gradual/noise documentation update requested in #233. Split the Abrupt GIF script and shared generation refactor into a separate PR unless they are required for #233.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The docs and notebook updates match #233, but the regenerated GIF artefacts are in ignored .gif files, so that requirement cannot be verified here. Remove the !**/*.gif filter or provide the regenerated GIF files in reviewable form so the updated assets can be checked.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title uses Conventional Commits docs: and accurately describes the documentation, notebook, and GIF output refresh.
Description check ✅ Passed The description matches the diff and stays on-topic, covering the docs, notebooks, GIF scripts, and regenerated assets.
📋 Issue Planner

Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).

View plan for ticket: #233

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch docs/update-baselines-for-flat-threshold

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the documentation Improvements or additions to documentation label Jul 20, 2026
@RussellSB
RussellSB merged commit 777ad33 into fix/gradual-up-truncated Jul 20, 2026
10 checks passed
@RussellSB
RussellSB deleted the docs/update-baselines-for-flat-threshold branch July 20, 2026 06:03
github-actions Bot added a commit that referenced this pull request Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant