Title: BUG: Fix IndexLocator.tick_values returning values greater than vmax by saakshigupta2002 · Pull Request #31091 · matplotlib/matplotlib · GitHub
Open Graph Title: BUG: Fix IndexLocator.tick_values returning values greater than vmax by saakshigupta2002 · Pull Request #31091 · matplotlib/matplotlib
X Title: BUG: Fix IndexLocator.tick_values returning values greater than vmax by saakshigupta2002 · Pull Request #31091 · matplotlib/matplotlib
Description: Pull Request: Fix IndexLocator.tick_values returning values greater than vmax Summary This PR fixes a bug in IndexLocator.tick_values() where tick values could exceed the specified vmax parameter. This issue manifested most notably when using colorbars with discrete colormaps and NoNorm normalization, where cbar.get_ticks() would return an incorrect array containing values outside the valid range. The Problem When creating a colorbar using a ScalarMappable with NoNorm normalization and a discrete colormap (e.g., viridis with a fixed number of colors), the ticks returned by colorbar.get_ticks() did not align with the visually displayed tick positions. Specifically, calling cbar.set_ticks(cbar.get_ticks()) would unexpectedly change the ticks because get_ticks() returned values outside the valid range. Example reproduction: import matplotlib.pyplot as plt from matplotlib import cm, colors data = [1, 2, 3, 4, 5] fig, ax = plt.subplots() cbar = fig.colorbar( cm.ScalarMappable(norm=colors.NoNorm(), cmap=plt.get_cmap("viridis", len(data))), ax=ax ) print(cbar.get_ticks()) # Actual (before fix): [0. 1. 2. 3. 4. 5.] # Expected (after fix): [0. 1. 2. 3. 4.] Root Cause The issue was in IndexLocator.tick_values() in lib/matplotlib/ticker.py: def tick_values(self, vmin, vmax): return self.raise_if_exceeds( np.arange(vmin + self.offset, vmax + 1, self._base)) The vmax + 1 in the np.arange call was intended to make the interval inclusive of vmax (since np.arange produces a semi-open interval [start, stop)). However, this overshot the target — combined with certain offset values, it could generate tick positions that exceed vmax. For instance, with offset=0.5 and vmax=4, the array [0.5, 1.5, 2.5, 3.5, 4.5] would be generated, where 4.5 > vmax. The Fix Replaced vmax + 1 with vmax + 1e-12 as the stop value for np.arange. This provides a minimal epsilon beyond vmax that ensures vmax itself is included in the closed interval [vmin, vmax], without overshooting to produce values beyond vmax: def tick_values(self, vmin, vmax): # We want tick values in the closed interval [vmin, vmax]. # Since np.arange(start, stop) returns values in the semi-open interval # [start, stop), we add a minimal offset so that stop = vmax + eps tick_values = np.arange(vmin + self.offset, vmax + 1e-12, self._base) return self.raise_if_exceeds(tick_values) This directly generates the correct number of ticks without any post-filtering, which is both cleaner and more efficient. Changes Modified Files lib/matplotlib/ticker.py Modified IndexLocator.tick_values() to use vmax + 1e-12 instead of vmax + 1 as the np.arange stop value, ensuring tick values stay within the [vmin, vmax] closed interval lib/matplotlib/tests/test_ticker.py Added test_tick_values_not_exceeding_vmax() test method to TestIndexLocator class Tests verify correct tick generation for: Integer offset (offset=0) Fractional offset (offset=0.5) Step size greater than 1 (base=2) Test Plan Added unit tests for the fix in test_ticker.py Run the full test suite: pytest lib/matplotlib/tests/test_ticker.py -v Verify the reproduction case from the issue is fixed Check that existing colorbar functionality is not affected New Test Cases def test_tick_values_not_exceeding_vmax(self): """ Test that tick_values does not return values greater than vmax. """ # Test case where offset=0 could cause vmax to be included incorrectly index = mticker.IndexLocator(base=1, offset=0) assert_array_equal(index.tick_values(0, 4), [0, 1, 2, 3, 4]) # Test case with fractional offset index = mticker.IndexLocator(base=1, offset=0.5) assert_array_equal(index.tick_values(0, 4), [0.5, 1.5, 2.5, 3.5]) # Test case with base > 1 index = mticker.IndexLocator(base=2, offset=0) assert_array_equal(index.tick_values(0, 5), [0, 2, 4]) Related Issues Fixes #31086 Checklist Code follows the project's style guidelines Changes are minimal and focused on the specific bug Added regression test to prevent future occurrences Commit message follows conventional format CI tests pass
Open Graph Description: Pull Request: Fix IndexLocator.tick_values returning values greater than vmax Summary This PR fixes a bug in IndexLocator.tick_values() where tick values could exceed the specified vmax parameter. ...
X Description: Pull Request: Fix IndexLocator.tick_values returning values greater than vmax Summary This PR fixes a bug in IndexLocator.tick_values() where tick values could exceed the specified vmax parameter. ...
Opengraph URL: https://github.com/matplotlib/matplotlib/pull/31091
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:3fc3cba4-ff88-cc3c-c383-be86269a280c |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | B78C:B51F5:4B87CE:653D9B:6A5233BC |
| html-safe-nonce | 7dbb99c14f3f442cef9099a25dd0f93a35751328f5c1e828190389e012c94db9 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCNzhDOkI1MUY1OjRCODdDRTo2NTNEOUI6NkE1MjMzQkMiLCJ2aXNpdG9yX2lkIjoiODE5MDYwMDIyMTIwNzcwNDUwOCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 25b91a2c45be5b6ea16f7ead501707c7d744f94cce04257d6fa2d371dd8defb8 |
| hovercard-subject-tag | pull_request:3251571412 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/matplotlib/matplotlib/pull/31091/files |
| twitter:image | https://avatars.githubusercontent.com/u/62831255?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/62831255?s=400&v=4 |
| og:image:alt | Pull Request: Fix IndexLocator.tick_values returning values greater than vmax Summary This PR fixes a bug in IndexLocator.tick_values() where tick values could exceed the specified vmax parameter. ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 7aed05249554b889eb33d002851a973eebcc7e91 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width