{"id":246,"date":"2024-08-20T15:40:08","date_gmt":"2024-08-20T15:40:08","guid":{"rendered":"http:\/\/bentus\/?p=246"},"modified":"2024-08-21T12:01:40","modified_gmt":"2024-08-21T12:01:40","slug":"gsoc-2024-bivariate-colormaps-summary","status":"publish","type":"post","link":"http:\/\/bentus\/gsoc-2024-bivariate-colormaps-summary\/","title":{"rendered":"GSOC 2024 – Bivariate Colormaps – Summary"},"content":{"rendered":"\n

Color mapping with multiple channels has long been a requested feature<\/a> in Matplotlib, one this Google Summer of Code project (GSOC) has attempted to address, the figures below shows the desired functionality:<\/p>\n\n\n

\n
\"\"<\/figure><\/div>\n\n\n
\"\"<\/figure>\n\n\n\n\n\n\n\n

While a complete solution for bivariate color mapping has not been merged to main at the time of writing, the intention is to have it in place for the 3.10 release of Matplotlib (~October 2024). This represents a delay of the project as compared to the original timeline, caused by a change in the scope of the project. <\/p>\n\n\n\n

Originally, this GSOC project was based on replacing cm.ScalarMappable<\/code> with a vector version,cm.VectorMappable<\/code>, (relevant PR<\/a>). However, with the involvement of the API lead @timhoffm<\/a> and project lead @tacasawell<\/a> it was decided to rework the data\u2192color<\/code> pipeline before implementing bivariate color mapping.<\/p>\n\n\n\n

Color by numbers (advanced version)<\/h2>\n\n\n\n

Representing data with color lies at the root of many of the plotting methods in Matplotlib. In practice, this is handled by the class cm.ScalarMappable<\/code> which handles normalization (converting data to a 0-1 scale through colors.Normalize<\/code>) and color mapping (converting the 0-1 encoding to colors through colors.Colormap<\/code>). cm.ScalarMappable<\/code> is therefore at the root of the inheritance tree, together with `artist.Artist`. The figure below shows all the top-level plotting methods that use the data\u2192color transformation.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

What @timhoffm<\/a> argued<\/a> was that the data\u2192color<\/code> pipeline may be shared by multiple elements in the same figure, but the current class hierarchy (subclassing cm.ScallarMappable<\/code>) is not conducive to this use case, i.e. the user must manually add callbacks<\/a> between different graphical objects to obtain functionality such as syncing the colormaps across plots<\/p>\n\n\n\n

Changing the class hierarchy would have major positive implications:<\/p>\n\n\n\n

    \n
  1. Increased functionality when working with multiple graphical elements that share a data\u2192color<\/code> pipeline<\/li>\n\n\n\n
  2. Simplify code maintenance separating the artist (graphical element) from the data\u2192color<\/code> pipeline\u00b9 (i.e. more modular code)<\/li>\n\n\n\n
  3. Simplify the introduction of bivariate and multivariate color mapping<\/li>\n<\/ol>\n\n\n\n

    The proposed architecture change is as follows:<\/p>\n\n\n

    \n
    \"\"<\/figure><\/div>\n\n\n

    Where the data\u2192color<\/code> transformation is contained in the new colorizer.Colorizer<\/code> module, and one colorizer can control the data\u2192color<\/code> pipeline for multiple plots:
    <\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    These changes are implemented in the New data \u2192 color pipeline<\/a> PR, and the (slightly outdated) video shows how multiple images that share a colorizer behave.<\/p>\n\n\n\n

    Colormap classes for bivariate (and multivariate) colormaps<\/h2>\n\n\n\n

    A separate PR, MultivarColormap and BivarColormap<\/a>, introduces classes that hold bivariate and multivariate colormaps.<\/p>\n\n\n\n

    The bivariate colormaps come in different shapes:
    <\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    while the multivariate colormaps come with different combination modes:<\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    Exposing bivariate (and multivariate) colormaps to plotting methods<\/h2>\n\n\n\n

    Using bivariate or multivariate colormaps requires a multivariate normalization, so before the new colormaps can be exposed to users, a colors.MultiNorm<\/code> class<\/a> must be implemented. By using a colors.MultiNorm<\/code> and a colors.BivarColormap<\/code> with a colorizer.Colorizer<\/code> object, the new functionality can be exposed to the top level plotting functions<\/a> (i.e. ax.imshow()<\/code>). <\/p>\n\n\n\n

    Both of these features exist as commits (links above), and will be part of a PR after the two<\/a> other<\/a> PRs are merged. <\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    Combined with the functionality of the colorizer.Colorizer<\/code>, this solution allows for coupled plots where a single axes from a bivariate or multivariate colormap can be used together with the full colormap:<\/p>\n\n\n\n

    Choosing bivariate and multivariate colormaps<\/h2>\n\n\n\n

    Once the functionality is implemented, there needs to be colormaps for the user to choose from. I have written two blogposts describing the design considerations relevant for their design:<\/p>\n\n\n\n

    Designing 2D colormaps<\/a><\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    Multivariate colormaps for n dimensions<\/a><\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n
    \"\"<\/figure>\n\n\n\n

    In an effort to separate different concerns, these have not been a part of PR#28454 MultivarColormap and BivarColormap<\/a>, and will need to be introduced in a separate PR.<\/p>\n\n\n\n

    This should also feature a guide to how the new functionality<\/a> can be used<\/a>, and how to choose<\/a> a colormap<\/a>.<\/p>\n\n\n\n

    Colorbars<\/h2>\n\n\n\n

    To complete the figure, a multidimensional colorbar must be added. There is a draft for this here<\/a>, but some work still remains. Firstly, a BivarColorbar<\/code> class must be created (in the multivariate case – a list of Colorbar<\/code> objects will work). Secondly, the automatic positioning of the colorbars in the figure needs some improvement. <\/p>\n\n\n\n

    Summary<\/h2>\n\n\n\n

    This project has grown in scope and now includes:<\/p>\n\n\n\n

      \n
    1. Classes for bivariate and multivariate colormaps #28454<\/a><\/li>\n\n\n\n
    2. Reworking the data\u2192color<\/code> pipeline#28658<\/a> <\/li>\n\n\n\n
    3. Exposing bivariate and multivariate colormaps to the plotting methods<\/li>\n\n\n\n
    4. A selection of bivariate and multivariate colormaps<\/li>\n\n\n\n
    5. “Colorbar” objects for the bivariate and multivariate case<\/li>\n<\/ol>\n\n\n\n

      Of these, step 1 has been approved, and step 2 is (hopefully) near the end of code review. Step 3 waits for step 1 and 2 to be merged, but the PR will be made soon. Step 4 is similarly ready, but the PR should be made after the code review of step 3 has started. The PR for step 5 will similarly follow once step 4 is merged.<\/p>\n\n\n\n

      In other words, there is still some work to be done, but as there exists drafts for all the remaining functionality the remaining workloads are less code-intensive, and instead lean towards the collaborative aspects of open source. This project involves re-organizing the data\u2192color<\/code> pipeline for all relevant plotting methods, and it is therefore important that all the Matplotlib maintainers get to chime in at every step of the way. This process takes time, but ensures a better outcome. <\/p>\n\n\n\n

      Thank you to Hannah and Kyle for guiding me through GSOC, and to Elliot, Tom, Tim and Jody for feedback on the PRs.<\/p>\n\n\n\n


      \n\n\n\n

      \u00b9 The distinction between the Colorizer<\/code> and ColorizingArtist<\/code> objects are as follows:<\/p>\n\n\n\n

        \n
      • ColorizingArtist<\/code> subclasses Artist<\/code>, i.e. it refers to a specific graphical element in a plot. It has the following atributes:\n
          \n
        • data<\/li>\n\n\n\n
        • alpha (opacity)<\/li>\n\n\n\n
        • colorizer<\/code><\/li>\n\n\n\n
        • A .draw(renderer)<\/code> method to render (display) the graphical elements. <\/li>\n\n\n\n
        • …\u00b2<\/li>\n\n\n\n
        • …\u00b3<\/li>\n<\/ul>\n<\/li>\n\n\n\n
        • Colorizer<\/code> holds the data\u2192color<\/code> pipeline. It consists of:\n
            \n
          • norm<\/code><\/li>\n\n\n\n
          • cmap<\/code><\/li>\n\n\n\n
          • A .to_rgba(data, alpha)<\/code> method that assignes color (red, green, blue, alpha) to data using the norm<\/code> and cmap<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n

            When rendering a ColorizingArtist<\/code>, it will invoke self.colorizer.to_rgba(data, alpha)<\/code> to determine what colors to render.<\/p>\n\n\n\n

            \u00b2 ColorizingArtist<\/code> also has attributes required of an Artist<\/code>, such as position, size, etc. relating to rendering of the artist in an Axes<\/code>. <\/p>\n\n\n\n

            \u00b3 The norm<\/code> and cmap<\/code> are accessible as attributes on a ColorizingArtist<\/code> through the use of @property<\/code>. For many users, the distinction between the ColorizingArtist<\/code> and Colorizer<\/code> will therefore not be felt. <\/p>\n\n\n\n

            <\/p>\n","protected":false},"excerpt":{"rendered":"

            Color mapping with multiple channels has long been a requested feature in Matplotlib, one this Google Summer of Code project (GSOC) has attempted to address, the figures below shows the desired functionality:<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-246","post","type-post","status-publish","format-standard","hentry","category-uncategorized","clear"],"_links":{"self":[{"href":"http:\/\/bentus\/wp-json\/wp\/v2\/posts\/246"}],"collection":[{"href":"http:\/\/bentus\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bentus\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/comments?post=246"}],"version-history":[{"count":15,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/posts\/246\/revisions"}],"predecessor-version":[{"id":275,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/posts\/246\/revisions\/275"}],"wp:attachment":[{"href":"http:\/\/bentus\/wp-json\/wp\/v2\/media?parent=246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/categories?post=246"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bentus\/wp-json\/wp\/v2\/tags?post=246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}