【注意】この記事は完成度70%くらいです。一部の図が張れていないので正しく表示されません。気が向いたら読める形にします。
seabornが好きだ。
特に指定しなくても、大抵の場合はきれいな色で美しいグラフを描画してくれるし、
matplotlibでは簡単に描けないような複雑なグラフも一発で作れる。
seabornを使って多数のグラフを別々に作った場合に、色を合わせたい場合がある。
例えば「1月のデータは赤、2月は青」という決まりでグラフを書きたい場合である。
一部のグラフでその決まりが崩れていると、読んで理解するのに時間がかかってしまうだろう。
そこで今回は、swarmplotで色を直接指定する方法について調べた。
seaborn公式ドキュメントのswamplot関数の説明はこちら
準備
import seaborn as sns import matplotlib # -------------------- Duplicate key in file PosixPath('/usr/local/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc'), line 258 ('font.family : Hiragino sans')
# 動作環境の確認 # print(pd.__version__) # print(np.__version__) print(sns.__version__) print(matplotlib.__version__) !python3 --version # -------------------- 0.11.0 3.3.1 Python 3.8.5
# https://seaborn.pydata.org/generated/seaborn.swarmplot.html # ★styleの設定どうする? tips = sns.load_dataset("tips") tips.dtypes # -------------------- total_bill float64 tip float64 sex category smoker category day category time category size int64 dtype: object
※上記のdtypesで、dayなどのカラムが文字列型ではなくカテゴリカル型であることに注意、
(文字列型だと異なる挙動をする可能性もある。今回は検証していない。)
seabornのswarmplotで、色を指定する方法は2つある。
1つが、xとyのうち片方に指定したカテゴリに合わせて色を指定する方法である。
もう1つが、軸に指定したものとは別のカテゴリに合わせて色を指定する方法である。
この順に紹介する。
1つ目:軸に指定したカテゴリに合わせて色を指定したい
ax = sns.swarmplot(x="day", y="total_bill", data=tips)
xにdayを指定すると、xに応じて点に色が付く。
この色を直接指定したい。
例えば、Thurを赤で、Friを黒で、……のように具体的な色の希望があった場合、どうすればよいのか?
色を単純に入れ替えたいだけならば、hue_orderで順序を指定すれば良い……かと思ったが、hue_orderを用いてもx軸上の並びは変わらなかった。
(たぶん元データの中でdayのデータ型がcategoryなので、hue_orderを指定しても無視されて、categoryが優先される?)
ax = sns.swarmplot(x="day", y="total_bill", hue_order=["Fri", "Sun", "Sat", "Thur"], data=tips)
正解は 引数のうち、paletteを指定すればよい。
palette palette name, list, or dict
Colors to use for the different levels of the hue variable. Should be something that can be interpreted by color_palette(), or a dictionary mapping hue levels to matplotlib colors.
https://seaborn.pydata.org/generated/seaborn.swarmplot.html より
palette引数に指定できるのは、paletteの名前かlistかdictである。
「paletteの名前」はpastelやbrightなどである。カラーパレットに関する公式ドキュメントの説明を参照。今回は具体的な色を指定したいので「paletteの名前」は使えない。
listを使って指定してみよう。以下のようになる。
listの中身である「具体的なそれぞれの色」の指定方法はいくつかあるが、今回は色の名前(文字列)を使う。
ax = sns.swarmplot(x="day", y="total_bill", palette=["purple", "green", "orange", "skyblue"], data=tips)
リストだと、どの値がどの色になるのか分かりづらい。dictも指定できて、この場合は対応関係が明確になる。
ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Fri": "green", "Sat": "orange", "Sun": "skyblue"}, data=tips)
ちなみにpaletteをリストで指定し、長さが足りない場合、繰り返しになる
ax = sns.swarmplot(x="day", y="total_bill", palette=["purple", "green"], data=tips)
辞書でkeyがない場合は、エラーになる。そのdayを何色で塗ればいいか分からないからね。
# 辞書のkeyにFriがないのでエラー ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Sat": "orange", "Sun": "skyblue"}, data=tips) # -------------------- ---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-9-c9f97ba5b082> in <module>
1 # 辞書のkeyにFriがないのでエラー
----> 2 ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Sat": "orange", "Sun": "skyblue"}, data=tips)
/usr/local/lib/python3.8/site-packages/seaborn/_decorators.py in inner_f(*args, **kwargs)
44 )
45 kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
---> 46 return f(**kwargs)
47 return inner_f
48
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in swarmplot(x, y, hue, data, order, hue_order, dodge, orient, color, palette, size, edgecolor, linewidth, ax, **kwargs)
2989 warnings.warn(msg, UserWarning)
2990
-> 2991 plotter = _SwarmPlotter(x, y, hue, data, order, hue_order,
2992 dodge, orient, color, palette)
2993 if ax is None:
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in __init__(self, x, y, hue, data, order, hue_order, dodge, orient, color, palette)
1171 """Initialize the plotter."""
1172 self.establish_variables(x, y, hue, data, orient, order, hue_order)
-> 1173 self.establish_colors(color, palette, 1)
1174
1175 # Set object attributes
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in establish_colors(self, color, palette, saturation)
304 else:
305 levels = self.hue_names
--> 306 palette = [palette[l] for l in levels]
307
308 colors = color_palette(palette, n_colors)
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in <listcomp>(.0)
304 else:
305 levels = self.hue_names
--> 306 palette = [palette[l] for l in levels]
307
308 colors = color_palette(palette, n_colors)
KeyError: 'Fri'
2つ目:軸に指定したものとは別のカテゴリに合わせて色を指定したい
# Color the points using a second categorical variable: # 2つ目のカテゴリカル変数を用いて、点の色を指定する ax = sns.swarmplot(x="day", y="total_bill", hue="sex", data=tips)
xにdayというカテゴリカル変数を指定して、色のパラメータhueにはsexという別のカテゴリカル変数を指定するパターン。 色を単純に入れ替えたいだけならば、hue_orderで順序を指定すれば良い。以下のようになる。
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", data=tips, hue_order=["Female", "Male"])
次に、「男性が緑、女性が紫」のように、直接色を指定したい場合はどうすればよいだろうか?
palette変数だよなー多分。
palette palette name, list, or dict
Colors to use for the different levels of the hue variable. Should be something that can be interpreted by color_palette(), or a dictionary mapping hue levels to matplotlib colors.
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette=["green", "purple"], data=tips)
リストだと、どの値がどの色になるのか分かりづらい。dictも指定できて、この場合は対応関係が明確になる。
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette={"Male": "green", "Female": "purple"}, data=tips)
# https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.to_hex.html print(matplotlib.colors.to_hex("green")) print(matplotlib.colors.to_hex("purple")) # -------------------- #008000 #800080
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette={"Male": "#008000", "Female": "#800080"}, data=tips)
余談:color変数の挙動
color matplotlib color, optional
Color for all of the elements, or seed for a gradient palette.
ax = sns.swarmplot(x="day", y="total_bill", color="red", data=tips)
なぜかFemaleの色だけを赤に指定するっぽい?
# Color the points using a second categorical variable: # 2つ目のカテゴリカル変数を用いて、点の色を指定する ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color="red", data=tips)
ではcolorとして色の配列を渡せば良さそうに見えるが、それではエラーになる。
# Color the points using a second categorical variable: # 2つ目のカテゴリカル変数を用いて、点の色を指定する ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color=["red", "gray"], data=tips) # -------------------- ---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-18-9e9e98d7907f> in <module>
2 # 2つ目のカテゴリカル変数を用いて、点の色を指定する
3
----> 4 ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color=["red", "gray"], data=tips)
/usr/local/lib/python3.8/site-packages/seaborn/_decorators.py in inner_f(*args, **kwargs)
44 )
45 kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
---> 46 return f(**kwargs)
47 return inner_f
48
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in swarmplot(x, y, hue, data, order, hue_order, dodge, orient, color, palette, size, edgecolor, linewidth, ax, **kwargs)
2989 warnings.warn(msg, UserWarning)
2990
-> 2991 plotter = _SwarmPlotter(x, y, hue, data, order, hue_order,
2992 dodge, orient, color, palette)
2993 if ax is None:
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in __init__(self, x, y, hue, data, order, hue_order, dodge, orient, color, palette)
1171 """Initialize the plotter."""
1172 self.establish_variables(x, y, hue, data, orient, order, hue_order)
-> 1173 self.establish_colors(color, palette, 1)
1174
1175 # Set object attributes
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in establish_colors(self, color, palette, saturation)
293 colors = light_palette(color, n_colors)
294 elif self.default_palette == "dark":
--> 295 colors = dark_palette(color, n_colors)
296 else:
297 raise RuntimeError("No default palette specified")
/usr/local/lib/python3.8/site-packages/seaborn/palettes.py in dark_palette(color, n_colors, reverse, as_cmap, input)
541
542 """
--> 543 rgb = _color_to_rgb(color, input)
544 h, s, l = husl.rgb_to_husl(*rgb)
545 gray_s, gray_l = .15 * s, 15
/usr/local/lib/python3.8/site-packages/seaborn/palettes.py in _color_to_rgb(color, input)
465 color = xkcd_rgb[color]
466
--> 467 return mpl.colors.to_rgb(color)
468
469
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in to_rgb(c)
344 def to_rgb(c):
345 """Convert *c* to an RGB color, silently dropping the alpha channel."""
--> 346 return to_rgba(c)[:3]
347
348
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in to_rgba(c, alpha)
187 rgba = None
188 if rgba is None: # Suppress exception chaining of cache lookup failure.
--> 189 rgba = _to_rgba_no_colorcycle(c, alpha)
190 try:
191 _colors_full_map.cache[c, alpha] = rgba
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in _to_rgba_no_colorcycle(c, alpha)
263 raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
264 if len(c) not in [3, 4]:
--> 265 raise ValueError("RGBA sequence should have length 3 or 4")
266 if not all(isinstance(x, Number) for x in c):
267 # Checks that don't work: `map(float, ...)`, `np.array(..., float)` and
ValueError: RGBA sequence should have length 3 or 4