質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.13%

seabornを使ったグラフで、任意のy軸上の位置に横線を引く

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,545

yuyu82

score 22

seabornのbarplotを使い、以下のようなグラフを作成したのですが、
![イメージ説明](c25ce8f350c527f1c72dacaba9a49a2c.png)

このグラフ中に、任意のpriceの位置に横棒を引きたいのですが、調べてもよくわからなかったので質問致します。

イメージ説明
イメージとしては上のような感じにしたいです。

どなたかご教授の程、よろしくお願いします。


以下追記

今回動かしているコードは以下になります。(ここで使っている配列は実際使っているものを簡略化したもの)

from io import StringIO

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(style="whitegrid")

# データフレーム作成
num_samples = 100
data = pd.DataFrame(
    {
        "price": np.arange(855000, 855000 + num_samples),
        "sell": np.random.uniform(0, 10, num_samples),
        "buy": np.random.uniform(0, 10, num_samples),
    }
)

# 描画する。
fig, ax = plt.subplots(figsize=(7, 5))

# 棒グラフのデフォルトは右方向→
# sell の値は符号を反転させることで左方向←に棒グラフが作成されるようにする。
copied = data.copy()  # 元のデータを変更しないようにコピーしておく。
copied["sell"] *= -1

colors = ["#60D394", "#FFD97D"]  # 色
names = ["sell", "buy"]  # 列名

for name, color in zip(names, colors):
    sns.barplot(
        x=name,
        y="price",
        data=copied,
        color=color,
        label=name,
        orient="h",
        order=copied["price"].iloc[::-1],
        ax=ax,
    )

ax.set_xlabel("")  # x 軸のラベル
ax.set_ylabel("Price", fontsize=12)  # y 軸のラベル
# x 軸の範囲を左右対称になるように調整する。
max_val = data[["sell", "buy"]].values.max() * 1.1
ax.set_xlim(-max_val, max_val)

# y 軸の目盛りの間隔を10個おきに調整する。
yticks = ax.get_yticks()
yticklabels = ax.get_yticklabels()
ax.set_yticks(yticks[::10])
ax.set_yticklabels(yticklabels[::10])

ax.axhline(2000, c="r") # 新しく追加

ax.legend()  # 凡例追加

plt.show()

これにより出力されるグラフが以下のようになり、(使っているデータが違いのでイメージ図)
イメージ説明
引きたい箇所からずれたところに線が引かれてしまっています。
(ax.axhline(1100000, c="r")とすると大きくずれたところに横線が引かれグラフが崩れる。
ax.axhline(2000, c="r")とすると上のグラフのように描画している範囲に横線が引かれる。)
axhlineで指定しているyとbarplotのyがずれているようなのですが、どのように解決したらよいでしょうか?
ご教授頂けると幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

横線は hayataka2049 さんが記述されている通り axhline()で書けます。

で、横線を引くポジションが合わないのは、実際のY軸の座標とラベル名の数値が別に設定されており合致してしていないからですね。
(seabornのbarplotの仕様かな?)

ラベルが書かれている実際の座標は 質問内のコードでも使用されている、

ax.get_yticks()


で取得でき、同じくラベル名も質問内のコードでも使用されている、

ax.get_yticklabels()


にて取得できますので、これらのデータを使用して、ラベル名に対応する実際のy座標を取得することにより意図した場所線を引くことが
できるかと思います。

以下は質問にあるサンプルコードを修正したものです。
とりあえず、55050 の箇所に横線を引いております。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(style="whitegrid")

# データフレーム作成
num_samples = 100
data = pd.DataFrame(
    {
        "price": np.arange(855000, 855000 + num_samples),
        "sell": np.random.uniform(0, 10, num_samples),
        "buy": np.random.uniform(0, 10, num_samples),
    }
)

# 描画する。
fig, ax = plt.subplots(figsize=(7, 5))

# 棒グラフのデフォルトは右方向→
# sell の値は符号を反転させることで左方向←に棒グラフが作成されるようにする。
copied = data.copy()  # 元のデータを変更しないようにコピーしておく。
copied["sell"] *= -1

colors = ["#60D394", "#FFD97D"]  # 色
names = ["sell", "buy"]  # 列名

for name, color in zip(names, colors):
    sns.barplot(
        x=name,
        y="price",
        data=copied,
        color=color,
        label=name,
        orient="h",
        order=copied["price"].iloc[::-1],
        ax=ax,
    )

ax.set_xlabel("")  # x 軸のラベル
ax.set_ylabel("Price", fontsize=12)  # y 軸のラベル
# x 軸の範囲を左右対称になるように調整する。
max_val = data[["sell", "buy"]].values.max() * 1.1
ax.set_xlim(-max_val, max_val)

# y 軸の目盛りの間隔を10個おきに調整する。
yticks = ax.get_yticks()
yticklabels = ax.get_yticklabels()

# ラベル名に対応した、Y座標を取得するためのdict
# 10個置きに間引かれる前のデータを使い、ラベルはIntに変換しておく
conv_tbl = {int(label.get_text()): pos for label, pos in zip(yticklabels, yticks)}

ax.set_yticks(yticks[::10])
ax.set_yticklabels(yticklabels[::10])

# 追加 (変換テーブルにてY座標に変換)
ax.axhline(conv_tbl[855050], c="r")

ax.legend()  # 凡例追加

plt.show()

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/25 12:17 編集

    厄介な仕様ですね。まあget_yticksで取れるならいいか(get系のあまり使わないので把握していませんでした)

    キャンセル

  • 2019/06/25 13:25

    調べておりませんので想像なのですが、Y軸が数値ではなく文字列のリストとして扱われているのではないですかね。

    キャンセル

  • 2019/06/25 14:51

    ありがとうございます!
    さっそくやってみます

    キャンセル

+2

使ったコードはこちらのものでしょうか。

Matplotlib - Python3のmatplotlibを使ったヒストグラムの作図|teratail

axに対して線を引くメソッドを呼べば良いです。

matplotlib.pyplot.axhline — Matplotlib 3.1.0 documentation

これを好きな位置(axを作ってからplt.show()を呼ぶまでの間)に入れてください。

ax.axhline(4, -40, 40, c="r")

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/24 23:44

    回答ありがとうございます。いろいろ試したのですが、以下のような問題が発生してしまいました。
    (質問文中にも追記)
    ax.axhline(1100000, c="r")とすると大きくずれたところに横線が引かれグラフが崩れる。
    ax.axhline(2000, c="r")とすると上のグラフのように描画している範囲に横線が引かれる。
    axhlineで指定しているyとbarplotのyがずれているようなのですが、どのように解決したらよいでしょうか?
    ご教授頂けると幸いです。

    キャンセル

  • 2019/06/24 23:54 編集

    ラベルに表示されている値と実際に内部で持っている座標が違うのでしょう。最初から合致するようにプロットするか、それが難しければ適当に換算する必要があります。取り急ぎこれだけ。追ってまたなにかコメントします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる