Title: [ENH] Support for Multi-Color Line Legends in Plots · Issue #28304 · matplotlib/matplotlib · GitHub
Open Graph Title: [ENH] Support for Multi-Color Line Legends in Plots · Issue #28304 · matplotlib/matplotlib
X Title: [ENH] Support for Multi-Color Line Legends in Plots · Issue #28304 · matplotlib/matplotlib
Description: Need This enhancement proposes adding native support for multi-color lines in the legend of line (and other styles!) plots in Matplotlib. This feature will allow users to represent data series with gradients or multiple colors more intui...
Open Graph Description: Need This enhancement proposes adding native support for multi-color lines in the legend of line (and other styles!) plots in Matplotlib. This feature will allow users to represent data series with...
X Description: Need This enhancement proposes adding native support for multi-color lines in the legend of line (and other styles!) plots in Matplotlib. This feature will allow users to represent data series with...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/28304
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[ENH] Support for Multi-Color Line Legends in Plots","articleBody":"### Need\n\nThis enhancement proposes adding native support for multi-color lines in the legend of line (and other styles!) plots in Matplotlib. This feature will allow users to represent data series with gradients or multiple colors more intuitively within the plot legends.\r\n\n\n### Proposed solution\n\nHello, awesome matplotlib people!\r\n\r\n### Why This Enhancement is Needed\r\n\r\n- Improved Data Visualization: Multi-color lines in legends can significantly enhance the readability and interpretability of plots, especially in cases where data series represent a range of values or a transition over time.\r\n- Consistency: Currently, users need to implement custom handlers for multi-color lines, which can be cumbersome and inconsistent. Native support will standardize the implementation.\r\n- User Demand: As data visualizations become more complex and integral to analysis, the demand for more advanced and intuitive plotting features increases. This feature aligns with modern data visualization needs.\r\n\r\n### Benefits\r\n\r\n- Enhanced Clarity: Users can visually match plot lines with legend entries more easily when colors in the legend reflect the actual colors used in the plot.\r\n- Time-Saving: Eliminates the need for users to write and maintain custom legend handlers for multi-color lines.\r\n- Professional Presentation: Provides a more polished and professional look to data visualizations, which is crucial for presentations and publications.\r\n\r\n\r\n## Proposed Implementation\r\nAdd support in the Legend class for a new handler that can process and display multi-color lines. \r\nThis handler will generate a line segment with a gradient or series of colors, consistent with the corresponding plot line.\r\n\r\n### My current implementation:\r\n```\r\nimport matplotlib.pyplot as plt\r\nimport numpy as np\r\nfrom matplotlib.collections import LineCollection\r\nfrom matplotlib.legend_handler import HandlerLineCollection\r\nfrom matplotlib.colors import Normalize\r\nimport cmasher as cmr\r\n\r\nclass HandlerColorLineCollection(HandlerLineCollection):\r\n def __init__(self, cmap, **kwargs):\r\n self.cmap = cmap\r\n super().__init__(**kwargs)\r\n \r\n def create_artists(self, legend, artist, xdescent, ydescent, width, height, fontsize, trans):\r\n x = np.linspace(0, width, self.get_numpoints(legend) + 1)\r\n y = np.zeros(self.get_numpoints(legend) + 1) + height / 2. - ydescent\r\n points = np.array([x, y]).T.reshape(-1, 1, 2)\r\n segments = np.concatenate([points[:-1], points[1:]], axis=1)\r\n lc = LineCollection(segments, cmap=self.cmap, transform=trans)\r\n lc.set_array(x)\r\n lc.set_linewidth(artist.get_linewidth())\r\n return [lc]\r\n\r\ndef add_normalized_line_collection(ax, cmap, linewidth=3, linestyle='-'):\r\n norm = Normalize(vmin=0., vmax=1.)\r\n t = np.linspace(0, 1, 100) # Smooth gradient\r\n lc = LineCollection([np.column_stack([t, t * 0])], cmap=cmap, norm=norm, linewidth=linewidth, linestyle=linestyle)\r\n lc.set_array(np.linspace(0., 1, len(t))) # Ensure this spans 0 to 1 for correct normalization\r\n ax.add_collection(lc) # Add the LineCollection to the axis\r\n return lc\r\n\r\n# Sample data\r\nx_data = np.logspace(1, 3, 50)\r\ny_data = np.random.rand(3, len(x_data))\r\n\r\nfig, ax = plt.subplots(figsize=(8, 6))\r\n\r\ncolors = cmr.take_cmap_colors('cmr.rainforest', 3, cmap_range=(0.15, 0.85), return_fmt='hex')\r\nfor i in range(y_data.shape[0]):\r\n ax.plot(x_data, y_data[i], color=colors[i], lw=3)\r\n \r\n ax.set_xlabel('$x$', fontsize=18)\r\n ax.set_ylabel('$y$', fontsize=18)\r\n\r\n# Create color lines for legend\r\ncolor_line = add_normalized_line_collection(ax, cmap=\"cmr.rainforest\", linewidth=4)\r\n\r\n# Existing legend handles and labels\r\nhandles, labels = ax.get_legend_handles_labels()\r\nhandles.append(color_line)\r\nlabels.append(\"Colormap-based Line\")\r\n\r\nax.legend(handles, labels, handler_map={color_line: HandlerColorLineCollection(cmap=\"cmr.rainforest\", numpoints=30)}, loc=\"upper right\", frameon=True, fontsize=15)\r\n\r\nplt.show()\r\n\r\n```\r\n\r\n\u003cimg width=\"710\" alt=\"Screenshot 2024-05-26 at 17 40 26\" src=\"https://github.com/matplotlib/matplotlib/assets/32773944/61066b2b-418f-4daf-8326-359b494a021a\"\u003e\r\n\r\n\r\n### Proposed usage\r\n```\r\nimport matplotlib.pyplot as plt\r\nimport numpy as np\r\nimport cmasher as cmr\r\n\r\n# Sample data\r\nx_data = np.logspace(1, 3, 50)\r\ny_data = np.random.rand(3, len(x_data))\r\n\r\nfig, ax = plt.subplots(figsize=(8, 6))\r\n\r\ncolors = cmr.take_cmap_colors('cmr.rainforest', 3, cmap_range=(0.15, 0.85), return_fmt='hex')\r\nfor i in range(y_data.shape[0]):\r\n ax.plot(x_data, y_data[i], color=colors[i], lw=3)\r\n \r\n ax.set_xlabel('$x$', fontsize=18)\r\n ax.set_ylabel('$y$', fontsize=18)\r\n\r\n# New API for colormap-based lines in legend\r\nax.legend(use_colormap=True, loc=\"upper right\", frameon=True, fontsize=15, new_arg='cmr.rainforest', )\r\n\r\nplt.show()\r\n\r\n```\r\n\r\nPlease note that it would be beneficial to allow passing either a colormap or a list of colors (in HEX) to this new argument (new_arg) in the legend. Ideally, the legend object should be able to internally determine the colors used in the plot lines, making the process seamless and intuitive for the user.\r\n\r\nI am also [including a version](https://gist.github.com/JBorrow/4d1fed2abf275b9f7a4140f0099fd0b0) from a colleague.\r\n\r\n\r\nMany thanks!\r\nNiko\r\n","author":{"url":"https://github.com/nikosarcevic","@type":"Person","name":"nikosarcevic"},"datePublished":"2024-05-26T16:46:01.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":19},"url":"https://github.com/28304/matplotlib/issues/28304"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:298cb07b-790f-d824-3085-5e3004e8fb07 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | CD26:153E3D:D93845:1286A12:6A5215F6 |
| html-safe-nonce | 1a7a6eb289d3d6c1c11bdbb1c78eb72eda2351426a6cd95863af2782ba65ce92 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDRDI2OjE1M0UzRDpEOTM4NDU6MTI4NkExMjo2QTUyMTVGNiIsInZpc2l0b3JfaWQiOiI0NDcyODk2MzYzOTQ5MDY5ODE0IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | d4b4adc4dbd6f2fa629feccdc6d716776c41f5bc235c3059e7f497f6f88943f2 |
| hovercard-subject-tag | issue:2317842180 |
| github-keyboard-shortcuts | repository,issues,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/_view_fragments/issues/show/matplotlib/matplotlib/28304/issue_layout |
| twitter:image | https://opengraph.githubassets.com/94673db84078a1e4894a306deef029a8d24315b742e908d272e07d1ef2f5b30d/matplotlib/matplotlib/issues/28304 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/94673db84078a1e4894a306deef029a8d24315b742e908d272e07d1ef2f5b30d/matplotlib/matplotlib/issues/28304 |
| og:image:alt | Need This enhancement proposes adding native support for multi-color lines in the legend of line (and other styles!) plots in Matplotlib. This feature will allow users to represent data series with... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | nikosarcevic |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| 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 |
| disable-turbo | false |
| 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