カテゴリー

当サイトはアフィリエイトプログラムによる収益を得ています〈景品表示法に基づく表記です)

go

【plotly&棒グラフ】go.Barでバーチャートを作成

2021年9月10日

こんな人にオススメ

plotlyで棒グラフを描きたいんだけど、どうやったらいい?

ということで、今回はplotlygo.Barを使用して棒グラフを作成する方法について解説する。matplotlib.pyplotpltplotly.expresspxで棒グラフを作成した際の記事は以下。

【plt&棒グラフ】pythonのmatplotlibで棒グラフを作成してみる

続きを見る

【plolty&棒グラフ】px.barでバーチャートを作成

続きを見る

棒グラフはメジャーなグラフであり、色んなところで使われるだろう。今回の記事で基礎を学んでいただけると幸いだ。

なお、本記事は公式の棒グラフ水平棒グラフのページをベースにしている。

python環境は以下。

  • Python 3.10.0
  • numpy 1.21.4
  • plotly 5.4.0
  • plotly-orca 3.4.2

運営者のメガネです。YouTubeTwitterInstagramも運営中。自己紹介お問い合わせページあります。

運営者メガネ

作成したコード全文

下準備

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関数を使用してグラフ描画用の関数を定義。引数のplotgo.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で引数markerlineを使用する。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の引数texttextpositionで棒グラフのバー部分にテキストを追加する事が可能。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}")

横軸ラベルを回転

テキスト関連で他に使えるのは軸ラベルの回転。グラフを表示する画面の大きさによっては横軸ラベルが詰まって見づらくなる事がある。

そんな時はレイアウトの引数xaxistickangleで軸ラベルの回転角度を設定する事ができる。ここでは-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.Barの引数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の範囲のグラフを作成している。

具体的な作成方法は以下。なお、公式の解説では色々と引数が追加されていたけど、ややこしくなるのでここでは排除した。

  1. 横軸のラベルとなる配列labelswidthを定義
  2. 縦軸になるdictdataを定義
  3. 横軸の位置を決める配列xを、累積和で計算
  4. dataの2種類のkeysでグラフ化、棒の太さはwidthを参照
  5. 横軸ラベルと2行にするために変数ticktextを計算(brタグで改行)
  6. 引数tickvalsで軸目盛の表示位置を計算して変更
  7. 縦・横軸の表示範囲を0-100に変更
  8. グラフ化

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.Layoutxaxis, 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_yarray。ここでは左右(上下)同じ値にしたけど、引数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')

棒グラフは基礎

今回はplotlygo.Barを使った棒グラフの描き方について解説した。棒グラフは基礎的な内容だからこそできる事が多い。

折れ線グラフとの組み合わせもよく使われているので、この機会に是非とも基礎を固めていただければ幸いだ。

なお、pxと使用した場合はデータフレームを作るのが面倒だけど、グラフを描くのは楽というのが個人的な感想。

関連記事

【plotly&go.Scatter】plotlyの散布図グラフの描き方

続きを見る

【plolty&棒グラフ】px.barでバーチャートを作成

続きを見る

【plotly&add_vrect, hrect】グラフに垂直・水平の塗りつぶし

続きを見る

【plotly&fill】goで領域を塗りつぶし

続きを見る

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-go
-, , , ,