こんな人にオススメ
RGB形式で書かれたmatplotlib.pyplot
、plt
の色を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
下準備
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
とかのこと。
rgb(R, G, B)
、rgba(R, G, B, a)
:plotly.colors.unlabel_rgb(color)
(R, G, B)
: そのまま- 色名の
str
、全英語の16進数の場合:matplotlib.colors.ColorConverter.to_rgb(color)
- 数字まじりの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()
統一してくれたらいいのにな
今回はplotly
とplt
における色の認識の違いをなくすための関数を作成した。この関数を使用すると大体の色の入力はまかなえる気がする。でも、そもそもこんな仕様じゃなかったら困ることはない。
なんで0-255にしたのかは知らないけど、メジャーどころと変なところで差別化されると対応する側も大変。幸いあっさり解決する内容だったから良かったけど、これかも何かしら引っかかりそうで怖い。
関連記事
-
-
【plotly&マーカー】plotlyのマーカーのシンボル
続きを見る
-
-
【plt vs plotly】matplotlibとgoでグラフの比較
続きを見る
-
-
【plt&fill_between】matplotlibで領域を塗りつぶし
続きを見る
-
-
【plotly&バブルチャート】plotlyで各国の収入と平均寿命をバブルチャートで描く
続きを見る