カテゴリー

Plotly全般

【plotly&色の自動調節】入力したRGBをrgba(R, G, B, a)に自動変換する

2021年8月1日

こんな人にオススメ

RGB形式で書かれたmatplotlib.pyplotpltの色をplotlyに入れると黒になるんだけどなんで?どうしたら他の色に変えられるの?

ということで、今回はplotlyでRGB形式の色を指定する際に自動で必要な計算とか言葉づけとかをしてくれるコードについて紹介する。

pltを使い始めて2年ほどが経過した時plotlyに出会った。書き方やグラフなどの全てが全然異なって困惑したがさらに追い討ちをかけるのが色問題。pltのノリでコードを書くと全てのプロットが黒になる。

今回は色を色々イジくる回。python環境は以下。

  • Python 3.9.4
  • plotly-orca 3.4.2
  • plotly 4.14.3
  • matplotlib 3.4.2
  • numpy 1.20.3
スポンサーリンク
スポンサーリンク

運営者のメガネと申します。TwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

下準備

import numpy as np
import matplotlib
import plotly.colors
import plotly.graph_objects as go
import plotly.io as pio

x = np.array([1, 2, 3])
y = np.array([30, 50, 40])

まずは下準備としてのimport関連とデータの作成。今回はデータがどうこうって話じゃないから簡単なものを使用。ただし、今回は色を中心に話すので色関連の配列を定義。

colors = (
    (1, 0.5, 0), (255, 125, 0),  # (R, G, B)形式
    (1, 0.5, 0, 1), (255, 125, 0, 0.5),  # (R, G, B, a)形式
    '(1, 0.5, 0)', '(255, 125, 0)',  # 文字列の(R, G, B)形式
    '(1, 0.5, 0, 1)', '(255, 125, 0, 0.5)',  # 文字列の(R, G, B, a)形式
    'rgb(1, 0.5, 0)', 'rgb(255, 255, 255)',  # rgb(R, G, B)形式
    'rgba(1, 0.5, 0.1, 1)', 'rgba(255, 125, 0.1, 0.5)',  # rgba(R, G, B, a)形式
    'black', 'white', 'mediumvioletred',  # 色名
    '#ffffff', '#000000', '#fff', '#000',  # 16進数形式の黒と白(後ろ2つは短縮系)
    '#c71585', '#dcdcdc',  # 数字まじりと全英語の16進数形式
)

今回はGRB形式を中心に解説するのでhslなどは考えない。執筆者がまだ学習していない範囲だ。RGBというのはRed, Green, Blueを表していて、これら3色の組み合わせてあらゆる色を表現しようってこと。

全部が最小値なら黒で全部が最大値なら白になる。pltの場合は最小値が0で最大値が1。で、今回のplotlyは最小値が0で最大値が255。この値の違いが面倒なことを引き起こす。

ちなみにGRBaのaはalphaの略で、透明度を表す。alphaはplt, plotlyともに0から1の範囲で書き、0が薄く1が濃い。

plotlyで受け付ける色の書き方と色の範囲

今回はRGB形式に絞ってplotlyと色についての解説を行うが、実はRGB形式の中でも受け付けられる書き方と受け付けられない書き方がある。

まずはこれについて解説。その後、指定した色と実際のグラフを見てみて何が起きているのかを知る。

plotlyで受け付ける色の書き方・受け付けない書き方

# 大半の指定方法だと動くのは動く
# しかし、pltでは色の範囲は0-1なので、そのままだとほぼ黒
plot = []
for num, color in enumerate(colors):
    try:
        d = go.Scatter(
            x=x, y=y - num, name=color,
            line=dict(color=color),
        )
        plot.append(d)
        print(f"{color}: OK")
    except ValueError:
        print(f"{color}: error")

下準備で書いたcolors変数を次々とgo.Scatterに入れ、plotlyで受け付ける色と受け付けない色を判別した。すると以下のような結果となる。

# (1, 0.5, 0): error
# (255, 125, 0): error
# (1, 0.5, 0, 1): error
# (255, 125, 0, 0.5): error
# (1, 0.5, 0): error
# (255, 125, 0): error
# (1, 0.5, 0, 1): error
# (255, 125, 0, 0.5): error
# rgb(1, 0.5, 0): OK
# rgb(255, 255, 255): OK
# rgba(1, 0.5, 0.1, 1): OK
# rgba(255, 125, 0.1, 0.5): OK
# black: OK
# white: OK
# mediumvioletred: OK
# #ffffff: OK
# #000000: OK
# #fff: OK
# #000: OK
# #c71585: OK
# #dcdcdc: OK

要するに通常のtuple形式で書くのがダメそうだ。なのでtupleに焦点を絞って解決する必要があるんだけど、一応他の書き方も見る。詳細は後ほど。

plotlyでは色は0から255


で、今回のお悩みがpltと同じようにグラフを書いたらプロットが全て黒くなるというもの。それもそのはず、pltは0から1の範囲内で色を指定するかたわら、plotlyは0から255。そりゃ黒くなる。

実際にグラフを描くと上のようになり、rgb(1, 0.5, 0)のプロットがオレンジになるはずが黒になる。0から1の範囲内の1と0から255の範囲内の1では意味合いが全く異なる。

# 大半の指定方法だと動くのは動く
# しかし、pltでは色の範囲は0-1なので、そのままだとほぼ黒
plot = []
for num, color in enumerate(colors):
    try:
        d = go.Scatter(
            x=x, y=y - num, name=color,
            line=dict(color=color),
        )
        plot.append(d)
        print(f"{color}: OK")
    except ValueError:
        print(f"{color}: error")

fig = go.Figure(data=plot)
fig.show()

name = 'plot'
pio.write_html(fig, f"{name}.html", auto_open=False,)
pio.write_image(fig, f"{name}.png")

今回の記事はRGB中心

# 使用できる色
# A hex string (e.g. '#ff0000')
# An rgb/rgba string (e.g. 'rgb(255,0,0)')
# An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
# An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
# A named CSS color (e.g. 'red')

じゃあ面倒なRGBをやめて他の色にしたらいいじゃんっていうのはアリ。hex、すなわち16進数だとぱっと見わからないし、CSS colorの場合は透明度が指定できないし、hslとhsvも馴染みがない。

どれもわかりにくいけど、個人的に一番使いやすく、かつ、有名なのがRGB。なのでRGBを使うことにする。自分が慣れにいくスタイル。

色をrgba(R, G, B, a)に自動変換する

def check_rgb_print(color: str or tuple, alpha: float):
    """入力した色のstrやtupleをplotlyで使われる色の書式形式rgba(R, G, B, a)に変換

    Parameters
    ----------
    color : str or tuple
        書式を変換したい色
    alpha : float
        透明度

    Returns
    -------
    str
        書式を設定した色と透明度の文字列
    """
    try:  # rgb(R, G, B)、rgba(R, G, B, a)の場合
        ans = plotly.colors.unlabel_rgb(color)
        print('\\trgb(R, G, B)、rgba(R, G, B, a)に該当')
        print(f"halfway: {ans}")
    except TypeError:  # (R, G, B)の場合
        ans = color
        print('\\t(R, G, B)に該当')
        print(f"halfway: {ans}")
    except ValueError:  # 色名のstr、全英語の16進数の場合
        ans = matplotlib.colors.ColorConverter.to_rgb(color)
        print('\\t色のstr、全英語の16進数に該当')
        print(f"halfway: {ans}")
    except IndexError:  # 数字まじりの16進数の場合
        ans = plotly.colors.hex_to_rgb(color)
        print('\\t数字まじりの16進数に該当')
        print(f"halfway: {ans}")

    if max(ans) <= 1:  # 最大値が1以下の場合は0-1になっているはずだから0-255にする
        ans = plotly.colors.convert_to_RGB_255(ans)

    if len(ans) == 3:  # 要素数が3だと透明度の指定がないはずだから透明度を指定
        ans += (alpha,)

    ans = f"rgba{ans}"  # rgbaという文字を追加

    return ans

ということでplotlyに色を入れて適切な処理をしてくれるコードを作成した。結構複雑に見えるのはprintを大量にしているから。色の処理わけは以下。16進数っていうのは#ffffffとかのこと。

  1. rgb(R, G, B)rgba(R, G, B, a): plotly.colors.unlabel_rgb(color)
  2. (R, G, B): そのまま
  3. 色名のstr、全英語の16進数の場合: matplotlib.colors.ColorConverter.to_rgb(color)
  4. 数字まじりの16進数の場合: plotly.colors.hex_to_rgb(color)

上記の処理で入力された色を(r, g, b)の形式に変換できる。さらにmax(ans) <= 1で最大値が1以下の場合はplotly.colors.convert_to_RGB_255で全要素を0-255に変換。これでplt表記のrgbをplotly用に変換できる。

あとは長さがピッタリ3なら透明度alphaを追加し、rgbaの文字を追加して完成。初めに見たように、tupleの状態だとエラーが吐かれるがrgbaをつけると吐かれない。

自動変換を使用してグラフ化


最後に作成したcheck_rgb_print関数からprintを除いたcheck_rgb関数使用してcolorsに入っていた色を全て変換してグラフ化。圧倒的オレンジだが仕方ない。

def check_rgb(color: str or tuple, alpha: float):
    """入力した色のstrやtupleをplotlyで使われる色の書式形式rgba(R, G, B, a)に変換

    Parameters
    ----------
    color : str or tuple
        書式を変換したい色
    alpha : float
        透明度

    Returns
    -------
    str
        書式を設定した色と透明度の文字列
    """
    try:  # rgb(R, G, B)、rgba(R, G, B, a)
        ans = plotly.colors.unlabel_rgb(color)
    except TypeError:  # (R, G, B)の場合
        ans = color
    except ValueError:  # 色のstr、全英語の16進数の場合
        ans = matplotlib.colors.ColorConverter.to_rgb(color)
    except IndexError:  # 数字まじりの16進数の場合
        ans = plotly.colors.hex_to_rgb(color)

    if max(ans) <= 1:  # 最大値が1以下の場合は0-1になっているはずだから0-255にする
        ans = plotly.colors.convert_to_RGB_255(ans)

    if len(ans) == 3:  # 要素数が3だと透明度の指定がないはずだから透明度を指定
        ans += (alpha,)

    ans = f"rgba{ans}"  # rgbaという文字を追加

    return ans

check_rgb関数の残念なところは元の配列を変更してしまうので凡例が変わるところ。ここはlistとかを使って変換前後を残しておくことで回避できそう。面倒だから今回はしない。

def plot_color():
    plot = []

    for num, color in enumerate(colors):

        color = check_rgb(color, 0.8)
        d = go.Scatter(
            x=x, y=y - num, name=color,
            line=dict(color=color, width=3),
        )
        plot.append(d)

    fig = go.Figure(data=plot)
    fig.show()

    name = 'plot_color'
    pio.write_html(fig, f"{name}.html", auto_open=False,)
    pio.write_image(fig, f"{name}.png")

plot_color()

統一してくれたらいいのにな

今回はplotlypltにおける色の認識の違いをなくすための関数を作成した。この関数を使用すると大体の色の入力はまかなえる気がする。でも、そもそもこんな仕様じゃなかったら困ることはない。

なんで0-255にしたのかは知らないけど、メジャーどころと変なところで差別化されると対応する側も大変。幸いあっさり解決する内容だったから良かったけど、これかも何かしら引っかかりそうで怖い。

関連記事

自作したplotlyのマーカーのシンボル一覧
【plotly&マーカー】plotlyのマーカーのシンボル

続きを見る

【plt vs plotly】matplotlibとgoでグラフの比較

続きを見る

(9 < x y2)の条件に合った塗りつぶし
【plt&fill_between】matplotlibで領域を塗りつぶし

続きを見る

bubblechart2017_pop
【plotly&バブルチャート】plotlyで各国の収入と平均寿命をバブルチャートで描く

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-Plotly全般
-, ,