Skip to content

fix: Trend indicator not working with date_range x-axis#8509

Open
SuMayaBee wants to merge 4 commits intoholoviz:mainfrom
SuMayaBee:fix/Trend-indicator-not-working-with-date_range-x-axis
Open

fix: Trend indicator not working with date_range x-axis#8509
SuMayaBee wants to merge 4 commits intoholoviz:mainfrom
SuMayaBee:fix/Trend-indicator-not-working-with-date_range-x-axis

Conversation

@SuMayaBee
Copy link
Copy Markdown
Contributor

Description

The Trend indicator was not rendering correctly when a datetime column was used as plot_x. Instead of a proper chart, the bars appeared as tiny invisible specks crammed into the bottom-right corner of the plot.

Before the fix:

2026-03-15.16-32-41.mp4

After the fix:

2026-03-15.16-28-37.mp4

Root cause:
ColumnDataSource.from_df() converts datetime columns to milliseconds since epoch (e.g. 2020-01-311580428800000.0). The VBar glyph in the frontend has a hardcoded width: 0.9, which works fine for integer x-values but becomes completely invisible when x-values are ms timestamps, the bars end up 0.9 ms wide while data points are ~30 days apart.

Fix:
In _get_data(), after calling ColumnDataSource.from_df(), we check if the plot_x column has a datetime dtype and replace the ms-timestamps with simple integer indices [0, 1, 2, ...]. This is safe because the Trend indicator never displays x-axis labels or ticks, only the relative positions matter for the visual shape.

Fixes #6158

How Has This Been Tested?

To reproduce the bug (before the fix), run:

import pandas as pd
import panel as pn

data = pd.DataFrame({
    "Date": pd.date_range(start="2020-01-01", periods=12, freq="ME"),
    "Sales": [100, 120, 150, 130, 110, 140, 160, 180, 200, 220, 250, 230],
})

pn.indicators.Trend(
    data=data,
    plot_x="Date",
    plot_y="Sales",
    height=200,
    width=400,
).servable().show()

After the fix the chart renders correctly.
A new unit test test_trend_date_range_x_axis was added to panel/tests/widgets/test_trend_indicator.py to cover this case.

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested all AI-generated content in my PR.

    • I take responsibility for all AI-generated content in my PR.

      Tools: Claude Sonnet 4.6 (Claude Code)

Checklist

  • Tests added and is passing
  • Added documentation

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.61%. Comparing base (e6c2916) to head (31f6bbf).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8509      +/-   ##
==========================================
- Coverage   86.24%   85.61%   -0.64%     
==========================================
  Files         349      349              
  Lines       55388    55406      +18     
==========================================
- Hits        47769    47435     -334     
- Misses       7619     7971     +352     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

np.issubdtype(self.data[self.plot_x].dtype, np.datetime64)
):
processed = dict(processed)
processed[self.plot_x] = np.arange(len(self.data), dtype=float)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the fix but this seems problematic since it doesn't handle unevenly spaced data very well. We should just handle datetimes in the Trend model.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philippjfr Thanks for the feedback! I've updated the fix to address the uneven spacing concern. Instead of using integer indices with np.arange, datetime values are now normalized by dividing by the minimum interval between consecutive points:

  x_ns = self.data[self.plot_x].astype(np.int64)
  diffs = np.diff(x_ns)
  min_diff = diffs[diffs > 0].min() if len(diffs) else 1
  processed[self.plot_x] = (x_ns - x_ns[0]) / min_diff

So unevenly spaced data like [Jan 1, Jan 2, Feb 1] becomes [0, 1, 31] rather than [0, 1, 2], preserving the relative gaps. The VBar width of 0.9 then works correctly for all cases. Tested manually and confirmed bars render correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trend indicator not working with date_range x-axis.

2 participants