【问题标题】:Access Color from Plotly Color Scale从 Plotly 色标访问颜色
【发布时间】:2020-10-23 20:41:39
【问题描述】:

Plotly 中有没有一种方法可以访问其范围内任何值的颜色图颜色?

我知道我可以从

访问色阶的定义颜色
plotly.colors.PLOTLY_SCALES["Viridis"]

但我无法找到如何访问中间值/插值。

Matplotlib 中的等效项显示为in this question。还有another question 解决了来自colorlover 库的类似问题,但都没有提供很好的解决方案。

【问题讨论】:

    标签: python plotly-python


    【解决方案1】:

    官方参考解释。 Here

    import plotly.express as px
    
    print(px.colors.sequential.Viridis)
    ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725']
    
    print(px.colors.sequential.Viridis[0])
    #440154
    

    【讨论】:

    • 正如我在原始帖子中所说,我知道如何访问各个颜色。我正在寻找的是 plotly 是否允许访问连续色标上的任意点(如果上面的颜色在两个之间插值)。
    【解决方案2】:

    Plotly好像没有这样的方法,所以写了一个:

    import plotly.colors
    
    def get_continuous_color(colorscale, intermed):
        """
        Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
        color for any value in that range.
    
        Plotly doesn't make the colorscales directly accessible in a common format.
        Some are ready to use:
        
            colorscale = plotly.colors.PLOTLY_SCALES["Greens"]
    
        Others are just swatches that need to be constructed into a colorscale:
    
            viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
            colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)
    
        :param colorscale: A plotly continuous colorscale defined with RGB string colors.
        :param intermed: value in the range [0, 1]
        :return: color in rgb string format
        :rtype: str
        """
        if len(colorscale) < 1:
            raise ValueError("colorscale must have at least one color")
    
        if intermed <= 0 or len(colorscale) == 1:
            return colorscale[0][1]
        if intermed >= 1:
            return colorscale[-1][1]
    
        for cutoff, color in colorscale:
            if intermed > cutoff:
                low_cutoff, low_color = cutoff, color
            else:
                high_cutoff, high_color = cutoff, color
                break
    
        # noinspection PyUnboundLocalVariable
        return plotly.colors.find_intermediate_color(
            lowcolor=low_color, highcolor=high_color,
            intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
            colortype="rgb")
    

    挑战在于内置的 Plotly 色标并非始终如一地暴露。有些已经定义为色阶,而另一些则只是必须首先转换为色阶的色板列表。

    Viridis 色阶是用十六进制值定义的,Plotly 颜色操作方法不喜欢这种值,因此最简单的方法是从这样的色板构造它:

    viridis_colors, _ = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
    colorscale = plotly.colors.make_colorscale(viridis_colors)
    
    get_continuous_color(colorscale, intermed=0.25)
    # rgb(58.75, 80.75, 138.25)
    

    【讨论】:

      【解决方案3】:

      这个答案扩展了亚当提供的已经很好的答案。特别是,它处理 Plotly 色标的不一致问题。

      在 Plotly 中,您可以通过编写 colorscale="name_of_the_colorscale" 来指定内置色标。这表明 Plotly 已经有一个内置工具,可以以某种方式将色标转换为适当的值,并且能够处理这些不一致。通过搜索 Plotly 的源代码,我们找到了有用的 ColorscaleValidator 类。让我们看看如何使用它:

      def get_color(colorscale_name, loc):
          from _plotly_utils.basevalidators import ColorscaleValidator
          # first parameter: Name of the property being validated
          # second parameter: a string, doesn't really matter in our use case
          cv = ColorscaleValidator("colorscale", "")
          # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...] 
          colorscale = cv.validate_coerce(colorscale_name)
          
          if hasattr(loc, "__iter__"):
              return [get_continuous_color(colorscale, x) for x in loc]
          return get_continuous_color(colorscale, loc)
              
      
      # Identical to Adam's answer
      import plotly.colors
      from PIL import ImageColor
      
      def get_continuous_color(colorscale, intermed):
          """
          Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
          color for any value in that range.
      
          Plotly doesn't make the colorscales directly accessible in a common format.
          Some are ready to use:
          
              colorscale = plotly.colors.PLOTLY_SCALES["Greens"]
      
          Others are just swatches that need to be constructed into a colorscale:
      
              viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
              colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)
      
          :param colorscale: A plotly continuous colorscale defined with RGB string colors.
          :param intermed: value in the range [0, 1]
          :return: color in rgb string format
          :rtype: str
          """
          if len(colorscale) < 1:
              raise ValueError("colorscale must have at least one color")
      
          hex_to_rgb = lambda c: "rgb" + str(ImageColor.getcolor(c, "RGB"))
      
          if intermed <= 0 or len(colorscale) == 1:
              c = colorscale[0][1]
              return c if c[0] != "#" else hex_to_rgb(c)
          if intermed >= 1:
              c = colorscale[-1][1]
              return c if c[0] != "#" else hex_to_rgb(c)
      
          for cutoff, color in colorscale:
              if intermed > cutoff:
                  low_cutoff, low_color = cutoff, color
              else:
                  high_cutoff, high_color = cutoff, color
                  break
      
          if (low_color[0] == "#") or (high_color[0] == "#"):
              # some color scale names (such as cividis) returns:
              # [[loc1, "hex1"], [loc2, "hex2"], ...]
              low_color = hex_to_rgb(low_color)
              high_color = hex_to_rgb(high_color)
      
          return plotly.colors.find_intermediate_color(
              lowcolor=low_color,
              highcolor=high_color,
              intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
              colortype="rgb",
          )
      

      此时,您所要做的就是:

      get_color("phase", 0.5)
      # 'rgb(123.99999999999999, 112.00000000000001, 236.0)'
      
      import numpy as np
      get_color("phase", np.linspace(0, 1, 256))
      # ['rgb(167, 119, 12)',
      #  'rgb(168.2941176470588, 118.0078431372549, 13.68235294117647)',
      #  ...
      

      编辑:处理特殊情况的改进。

      【讨论】:

      • 在哪里可以找到`_plotly_utils` 模块?
      • Plotly 自带!只需安装 plotly,_plotly_utils 将可用。
      • 您对这种方法返回 Viridis、viridis、岩浆等色标的错误的原因有任何线索吗?
      • 是的,一些色标返回十六进制格式的颜色。我刚刚更新了答案。请尝试一下并告诉我!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 2020-03-22
      • 1970-01-01
      相关资源
      最近更新 更多