こんな人にオススメ
plotly
で棒グラフを描きたいんだけど、どうやったらいい?
ということで、今回はplotly
のgo.Bar
を使用して棒グラフを作成する方法について解説する。matplotlib.pyplot
、plt
とplotly.express
、px
で棒グラフを作成した際の記事は以下。
-
-
【plt&棒グラフ】pythonのmatplotlibで棒グラフを作成してみる
続きを見る
-
-
【plolty&棒グラフ】px.barでバーチャートを作成
続きを見る
棒グラフはメジャーなグラフであり、色んなところで使われるだろう。今回の記事で基礎を学んでいただけると幸いだ。
なお、本記事は公式の棒グラフと水平棒グラフのページをベースにしている。
python環境は以下。
- Python 3.10.0
- numpy 1.21.4
- plotly 5.4.0
- plotly-orca 3.4.2
作成したコード全文
下準備
import numpy as np import plotly.graph_objects as go import plotly.io as pio
まずは下準備としてのimport
関連。numpy
はグラフを作成する過程で使用する。
グラフ作成のための関数の定義
ここでは棒グラフを作成するために使用する関数を定義しておく。予め関数を定義しておくことで処置がスッキリ書けるし何か問題が起きた時にその切り分けが簡単になる。
今回は基礎的な内容ということで、グラフの保存とグラフ描画から保存までの2関数を定義する。
グラフ保存用の関数
# グラフ保存用の関数 def save(fig, config, save_name): pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html", config=config,) pio.write_image(fig, f"{save_name}.png")
まずはグラフ保存用の関数。保存形式はhtmlとpng。config
はグラフの右上に存在するツールバーのようなもの。
config
を設定しておくとこのツールバーの内容を追加したり削除したりできる。htmlで使用することで、保存後のhtmlファイルでもツールバーの設定が引き継がれる。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
今回は基礎的な内容ということで、config
の設定はデフォルトのNone
にしておく。
グラフ描画用の関数の定義
# グラフ化するための関数 def graph(plot, save_name, layout={}): fig = go.Figure(data=plot, layout=layout) fig.show() prefix = 'go-bar' save(fig=fig, config=None, save_name=f"{prefix}_{save_name}")
先程のグラフ保存用の関数であるsave
関数を使用してグラフ描画用の関数を定義。引数のplot
にgo.Bar
で作成したプロット情報を入れ、save_name
にはグラフ保存時のファイル名を入れる。
layout
はデフォルトでは空のdict
にしてデフォルトを適用するようにした。レイアウトを設定したい時はgo.Layout
の形式で引数に入れたらいい。
これらの引数を入れると関数内でグラフが作成されて描画・表示・保存される。
シンプルな棒グラフ
まずはシンプルに棒グラフを作成する。go
の棒グラフはgo.Bar
で作る事ができ、ここでは横軸を動物の名前animals
、縦軸を何かしらの数値配列y
にしている。
go.Bar
で作成したプロットデータはlist
などの配列にして変数plot
に入れ、あとは先ほど定義したgraph
関数に入れたら完成。
関数を使用することで、1つ1つのグラフにいちいちfig.show
とか書かなくてよくなる。
# 横軸 animals = ['giraffes', 'orangutans', 'monkeys'] # 縦軸 y = [20, 14, 23] # プロットデータを作成 # 複数データならリストに追加する plot = [go.Bar(x=animals, y=y)] # グラフ化 # レイアウトはデフォルトで設定なし graph(plot=plot, save_name='standard')
複数の棒グラフ
複数のグラフを描くにはgo.Bar
を入れた変数plot
に追加でgo.Bar
を入れたらいい。ここでは動物園ごとに棒グラフを作成している。
デフォルトだと複数の棒グラフは横並びに配置されるが、レイアウトの引数barmode='stack'
にすることで積み上げ式の棒グラフに変更する事ができる。
animals = ['giraffes', 'orangutans', 'monkeys'] # nameで棒グラフの名称を決められる plot = [ go.Bar(name='SF Zoo', x=animals, y=[20, 14, 23]), go.Bar(name='LA Zoo', x=animals, y=[12, 18, 29]), ] # デフォルトだとグループとして横並びになる graph(plot=plot, save_name='group') # レイアウトをstackに変更すると積み上げ式になる layout = go.Layout(barmode='stack') graph(plot=plot, layout=layout, save_name='stack')
棒グラフの色やホバー内容を変更
棒グラフの色を変更するにはgo.Bar
で引数marker
とline
を使用する。marker
の方が棒グラフの塗りつぶしに該当し、line
が枠線に該当する。
ここでは塗りつぶしと枠線の色color
、そして枠線の太さwidth
を変更している。さらにopacity
で不透明度を0.6にしている。1
が完全な塗りつぶしで0
になると透明になる。
マウスオーバー時のホバー内容の追加は引数hovertext
で行う。ここではマーケットシェアをホバー内容として追加している。
x = ['Product A', 'Product B', 'Product C'] y = [20, 14, 23] plot = [ go.Bar( x=x, y=y, hovertext=[ '27% market share', # Aのホバーの追記分 '24% market share', # Bのホバーの追記分 '19% market share' # Cのホバーの追記分 ], marker=dict( color='rgb(158,202,225)', # 棒自体の色 line=dict(color='rgb(8,48,107)', width=1.5), # 枠線の色 ), opacity=0.6, # 棒の不透明度 ) ] # グラフタイトル追加 layout = go.Layout(title_text='January 2013 Sales Report') graph(plot=plot, layout=layout, save_name='color_hover')
テキストと軸ラベルの設定
go.Bar
の引数text
とtextposition
で棒グラフのバー部分にテキストを追加する事が可能。text
はそのまま表示する内容を決め、textposition
はその位置を決める。textposition
のデフォルトはauto
だが選択肢はinside
, outside
, auto
, none
がある。
また、ここではレイアウトにてグラフタイトルを設定している。グラフタイトルに関してはtitle_text
もしくはtitle=dict(text=)
の形式ではなく、単にtitle=
としても反映される。
x = ['Product A', 'Product B', 'Product C'] y = [20, 14, 23] # テキストを配置する位置 # デフォルトはauto textpositions = ('inside', 'outside', 'auto', 'none') for textposition in textpositions: plot = [go.Bar( x=x, y=y, text=y, # 棒グラフに表示したい内容 textposition=textposition, # テキストの位置 )] layout = go.Layout(title=textposition) graph(plot=plot, layout=layout, save_name=f"position_{textposition}")
横軸ラベルを回転
テキスト関連で他に使えるのは軸ラベルの回転。グラフを表示する画面の大きさによっては横軸ラベルが詰まって見づらくなる事がある。
そんな時はレイアウトの引数xaxis
のtickangle
で軸ラベルの回転角度を設定する事ができる。ここでは-45度に設定して斜めにした。
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] y1 = [20, 14, 25, 16, 18, 22, 19, 15, 12, 16, 14, 17] y2 = [19, 14, 22, 14, 16, 19, 15, 14, 10, 12, 12, 16] plot = [ go.Bar( x=months, y=y1, name='Primary Product', marker_color='indianred' ), go.Bar( x=months, y=y2, name='Secondary Product', marker_color='lightsalmon' ) ] # tickangleで横軸の値を回転させる layout = go.Layout(xaxis=dict(tickangle=-45)) graph(plot=plot, layout=layout, save_name='tickangle')
棒グラフの色と太さを変更
棒グラフの色の変更はすでに上のコードで書いたけど、go.Bar
の引数marker_color
で設定可能。先程は1つの色を設定して一括で変更したけど、配列にして設定することで棒ごとに色を変更することも可能。
ここでは一旦全ての棒グラフの色をlightslategray
にしたのち、Feature Bの棒グラフだけ色をcrimson
に変更している。
colors = ['lightslategray', ] * 5 colors[1] = 'crimson' # 一箇所だけ変更 print(colors) # ['lightslategray', 'crimson', 'lightslategray', 'lightslategray', 'lightslategray'] x = ['Feature A', 'Feature B', 'Feature C', 'Feature D', 'Feature E'] y = [20, 14, 23, 25, 22] plot = [go.Bar(x=x, y=y, marker_color=colors)] layout = go.Layout(title_text='Least Used Feature') graph(plot=plot, layout=layout, save_name='color')
棒グラフの太さを変更
棒グラフの太さを変更するにはgo.Ba
rの引数width
を使用する。width
では配列を渡すと棒ごとに太さを変更し、数値だけを渡すと一括で棒の太さを変更する事ができる。
# 各棒グラフで太さを変更 plot = [ go.Bar( x=[1, 2, 3, 5.5, 10], y=[10, 8, 6, 4, 2], width=[0.8, 0.8, 0.8, 3.5, 4] # 各棒で太さを変更 ) ] graph(plot=plot, save_name='width_each') # 一括で某の太さを変更 plot = [ go.Bar( x=[1, 2, 3, 5.5, 10], y=[10, 8, 6, 4, 2], width=0.1 # 各棒で太さを変更 ) ] graph(plot=plot, save_name='width_1')
2次元積み上げ(マリメッコグラフ)
2次元の積み上げグラフ、マリメッコグラフ(marimekko charts, mosaic plots, variwide charts)を作成するにはちょっと骨が折れる。
合計100となる変数widthsを使用して累積和をnp.cumsum
で作成、これを横軸とすることで最終的に0から100の範囲のグラフを作成している。
具体的な作成方法は以下。なお、公式の解説では色々と引数が追加されていたけど、ややこしくなるのでここでは排除した。
- 横軸のラベルとなる配列
labels
とwidth
を定義 - 縦軸になる
dict
のdata
を定義 - 横軸の位置を決める配列
x
を、累積和で計算 data
の2種類のkeys
でグラフ化、棒の太さはwidth
を参照- 横軸ラベルと2行にするために変数
ticktext
を計算(brタグで改行) - 引数
tickvals
で軸目盛の表示位置を計算して変更 - 縦・横軸の表示範囲を0-100に変更
- グラフ化
ticktext
など細かいことは置いといて、とりあえず0-100に範囲制限をすることで2次元の積み上げグラフを作成する事ができる。
labels = ['apples', 'oranges', 'pears', 'bananas'] widths = np.array([10, 20, 20, 50]) data = { 'South': [50, 80, 60, 70], 'North': [50, 20, 40, 30] } # 累積和 x = np.cumsum(widths) - widths print(x) # [ 0 10 30 50] plot = [ go.Bar( name='South', x=x, y=data['South'], width=widths, offset=0, # 横軸をシフトさせていい感じの位置にする ), go.Bar( name='North', x=x, y=data['North'], width=widths, offset=0, ), ] # brタグで改行 ticktext = [f"{l}<br>{w}" for l, w in zip(labels, widths)] print(ticktext) # ['apples<br>10', 'oranges<br>20', 'pears<br>20', 'bananas<br>50'] layout = go.Layout( xaxis=dict( tickvals=np.cumsum(widths) - widths / 2, # 軸目盛の位置 ticktext=ticktext, # 軸目盛の内容 range=[0, 100], # 表示範囲 ), yaxis_range=[0, 100], # 縦軸の表示範囲 barmode='stack', ) graph(plot=plot, layout=layout, save_name='marimekko_charts')
base
で縦の値をシフトさせる
引数baseを使用することで、棒グラフを上下にシフトさせる事が可能。上のグラフでは赤色の棒グラフをバーごとにシフト量を変更、灰色の棒グラフを一括でシフトさせている。
years = ['2016', '2017', '2018'] plot = [ go.Bar( x=years, y=[500, 600, 700], base=[-500, 100, 0], # 縦軸のシフト量 marker_color='crimson', name='expenses' ), go.Bar( x=years, y=[300, 400, 700], base=50, # 一括でシフト marker_color='lightslategrey', name='revenue' ) ] graph(plot=plot, save_name='base')
フォントサイズやバーの間隔をカスタム
軸のフォントサイズを変更したりバー同士の間隔を変更はレイアウトで行う。go.Layout
のxaxis
, yaxis
の引数tickfont
の引数size
でフォントサイズが変更可能。
間隔についてはgo.Layout
の引数bargap
でバー同士の間隔、引数bargroupgap
でバーグループ(ここではRest of worldとChinaの組み合わせ)同士の感覚を変更可能。
なお、bargap
, bargroupgap
ともに配列を渡すことはできず、0から1の間の数値しか受け付けない。
years = [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012] y1 = [ 219, 146, 112, 127, 124, 180, 236, 207, 236, 263, 350, 430, 474, 526, 488, 537, 500, 439 ] y2 = [ 16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270, 299, 340, 403, 549, 499 ] plot = [ go.Bar( x=years, y=y1, name='Rest of world', marker_color='rgb(55, 83, 109)' ), go.Bar( x=years, y=y2, name='China', marker_color='rgb(26, 118, 255)' ) ] layout = go.Layout( title='US Export of Plastic Scrap', xaxis_tickfont_size=14, # 目盛のフォントサイズ yaxis=dict( title='USD (millions)', # 縦軸タイトル titlefont_size=16, # タイトルのフォントサイズ tickfont_size=14, ), legend=dict( x=0, y=1.0, # 凡例の位置 bgcolor='rgba(255, 255, 255, 0)', # 凡例の背景色 bordercolor='rgba(255, 255, 255, 0)' # 凡例の枠線色 ), bargap=0.15, # バー同士の間隔 bargroupgap=0.1 # バーグループ同士の間隔 ) graph(plot=plot, layout=layout, save_name='custom')
barmode
を変更
引数barmode
についてはすでに開設したけど、ここでは使用できる引数について紹介。使用できるのは4種類。
stack
: 各バーを積み重ね。負の値はマイナス分として重ねて計上group
: 各バーを横に並べるoverlay
; 各バーを重ね書きrelative
: 各バーを積み重ね。負の値でも重ねずに計上
group
がシンプルでわかりやすいけど、それ以外についてはややこしいかもしれない。しかし実際にグラフを書いて比較してみると結構わかりやすい。
ドキュメントについてはここに書いてあるので、詳しく知りたい人は参照いただきたい。
x = [1, 2, 3, 4] # barmode一覧 barmodes = ['stack', 'group', 'overlay', 'relative'] plot = [ go.Bar(x=x, y=[1, 4, 9, 16], opacity=0.5), # trace0 go.Bar(x=x, y=[6, -8, -4.5, 8], opacity=0.5), # trace1 go.Bar(x=x, y=[-15, -3, 4.5, -8], opacity=0.5), # trace2 go.Bar(x=x, y=[-1, 3, -3, -4], opacity=0.5), # trace3 ] for barmode in barmodes: layout = go.Layout(barmode=barmode, title_text=f"{barmode} Barmode") graph(plot=plot, layout=layout, save_name=f"barmode_{barmode}")
categoryorder
を変更
レイアウトの引数categoryorder
はデータの並び方の法則を決める。法則は公式で書いてあるように17種類もあるが、ここでは3種類で作成。
1つ目は横軸をアルファベット順で表す。ここで使用するxはアルファベット順ではないが、categoryorder
で指定することでアルファベット順に指定する事ができる。
2つ目は順番をこちらで指定する方法、そして最後が合計値を降順で表している。
x = ['b', 'a', 'c', 'd'] plot = [ go.Bar(x=x, y=[2, 5, 1, 9], name='Montreal'), go.Bar(x=x, y=[1, 4, 9, 16], name='Ottawa'), go.Bar(x=x, y=[6, 8, 4.5, 8], name='Toronto'), ] # データの並び方 xaxes = [ # 横軸の昇順 {'categoryorder': 'category ascending'}, # 指定で順番決め {'categoryorder': 'array', 'categoryarray': ['d', 'a', 'c', 'b']}, # 合計値で順番決め {'categoryorder': 'total descending'} ] for num, xaxis in enumerate(xaxes): layout = go.Layout( barmode='stack', title=xaxis['categoryorder'], # グラフタイトルはcategoryorderの種類 xaxis=xaxis, # ここにcategoryorderの情報を入れる ) graph(plot=plot, layout=layout, save_name=f"categoryorder{num}")
水平の棒グラフ
水平の棒グラフに関しては別ページに書いてある。やり方は至って簡単で、go.Bar
の引数orientation
を水平(horizontal)の'h'
に変更したらいい。
plot = [ go.Bar( x=[20, 14, 23], y=['giraffes', 'orangutans', 'monkeys'], orientation='h' # 方向をhにすると水平に ) ] graph(plot=plot, save_name='horizontal')
マルチカテゴリー(横軸が2行)
横軸を2次元配列にすることで自動的に横軸を2行にする事が可能。ただし、2行の配列だといけるけど3行にしてもうまくいかない。
多分、配列の作り方とかデータの分配方法とかが関係しているんだろうけど、2行作れたらあとは使う時のデータに応じてbrタグでなんとかなりそう。
# 2次元配列にすると自動で軸反映できる x = [ ["BB+", "BB+", "BB+", "BB", "BB", "BB"], [16, 17, 18, 16, 17, 18, ], ] plot = [ go.Bar(x=x, y=[1, 2, 3, 4, 5, 6]), go.Bar(x=x, y=[6, 5, 4, 3, 2, 1]) ] layout = go.Layout(barmode="relative") graph(plot=plot, layout=layout, save_name='2darray')
棒グラフのパターン
棒グラフには色や不透明度だけではなくとパターンも指定可能。指定できるのは8種類で空白文字列にするとバーが消える。
指定はgo.Bar
の引数marker
の引数pattern
の引数shape
で可能。引数pattern
には他にも色々とあるから是非とも検索してほしい。
今回はそれぞれのパターンを1本のバーとして横並びにした。
patterns = ['', '/', '\\\\', 'x', '-', '|', '+', '.'] plot = [] for num, pattern in enumerate(patterns): d = go.Bar(x=[1], y=[num], name=pattern, marker_pattern_shape=pattern) plot.append(d) graph(plot=plot, save_name='pattern')
solidity
で塗りつぶし具合を設定
go.Bar
の引数marker
の引数pattern
の引数solidity
で塗りつぶし具合を設定可能。デフォルトは0.3。
0にするとバーが消え、1にするとパターンが消えてただの塗りつぶしとして表示される。
patterns = ['+'] * 8 plot = [] for num in range(11): solidity = num * 0.1 d = go.Bar( x=[1], y=[num], name=solidity, marker_pattern_shape='+', marker_pattern_solidity=solidity ) plot.append(d) graph(plot=plot, save_name='solidity')
エラーバーをつける
最後はエラーバー。エラーバーはいわゆる誤差で、一般的に言われる誤差とはニュアンスが違う。細かいことはここでは置いておく。
指定方法はgo.Bar
の引数error_x
, error_y
のarray
。ここでは左右(上下)同じ値にしたけど、引数arrayminus
で片方ずつ指定することも可能。
x = (0, 1, 2, 3) y1 = (1, 3, 5, 7) y2 = (2, 4, 6, 8) array = (0.1, 0.2, 0.3, 0.4) plot = [ go.Bar(name='error x', x=x, y=y1, error_x=dict(array=array)), go.Bar(name='error y', x=x, y=y2, error_y=dict(array=array)), ] graph(plot=plot, save_name='errorbar')
棒グラフは基礎
今回はplotly
のgo.Bar
を使った棒グラフの描き方について解説した。棒グラフは基礎的な内容だからこそできる事が多い。
折れ線グラフとの組み合わせもよく使われているので、この機会に是非とも基礎を固めていただければ幸いだ。
なお、px
と使用した場合はデータフレームを作るのが面倒だけど、グラフを描くのは楽というのが個人的な感想。
関連記事
-
-
【plotly&go.Scatter】plotlyの散布図グラフの描き方
続きを見る
-
-
【plolty&棒グラフ】px.barでバーチャートを作成
続きを見る
-
-
【plotly&add_vrect, hrect】グラフに垂直・水平の塗りつぶし
続きを見る
-
-
【plotly&fill】goで領域を塗りつぶし
続きを見る