2017-02-12 21 views
0

matplotlibと苦労して、これまで使っていた図書館の中でも何かをやってみたいと思っていたのですが、私はStackiverseにいくつかの洞察を求めました。簡単に言えば、私が必要とするのは、x軸を共有する複数の横棒グラフを作成し、y軸上の異なる数の値と同じ高さを持つすべての棒を作成することです。グラフ自体は、エントリ。私はプロットするために必要なものの単純化されたデータ構造は次のようになります:Matplotlib - データに基づく動的(棒グラフ)のグラフの高さですか?

[ 
    {"name": "Category 1", "entries": [ 
     {"name": "Entry 1", "value": 5}, 
     {"name": "Entry 2", "value": 2}, 
    ]}, 
    {"name": "Category 2", "entries": [ 
     {"name": "Entry 1", "value": 1}, 
    ]}, 
    {"name": "Category 3", "entries": [ 
     {"name": "Entry 1", "value": 1}, 
     {"name": "Entry 2", "value": 10}, 
     {"name": "Entry 3", "value": 4}, 
    ]},  
] 

そして、私は結果として私が好きなものになったclosesestが使用している:

import matplotlib.pyplot as plt 

def plot_data(data): 
    total_categories = len(data) # holds how many charts to create 
    max_values = 1 # holds the maximum number of bars to create 
    for category in data: 
     max_values = max(max_values, len(category["entries"])) 
    fig = plt.figure(1) 
    ax = None 
    for index, category in enumerate(data): 
     entries = [] 
     values = [] 
     for entry in category["entries"]: 
      entries.append(entry["name"]) 
      values.append(entry["value"]) 
     if not entries: 
      continue # do not create empty charts 
     y_ticks = range(1, len(entries) + 1) 
     ax = fig.add_subplot(total_categories, 1, index + 1, sharex=ax) 
     ax.barh(y_ticks, values) 
     ax.set_ylim(0, max_values + 1) # limit the y axis for fixed height 
     ax.set_yticks(y_ticks) 
     ax.set_yticklabels(entries) 
     ax.invert_yaxis() 
     ax.set_title(category["name"], loc="left") 
    fig.tight_layout() 

をこれが維持されますどのように多くの項目が特定のカテゴリを持っていても、データの棒の数が最大に設定されているy制限(set_ylim())のおかげで、しかし、それはまた、エントリの最大数よりも少ないカテゴリに厄介なギャップを残すでしょう。私は数に依存してgridspecと異なるスケールを通じてギャップを削除しようとした

IMG LINK

:またはVisual視点ですべてのものを置くために、私は実際期待にからそれを取得しようとしていますしかし、それはさらに「歪曲して」矛盾しているように見えてしまった。私は複数のチャートを生成し、Figureのサイズを操作してポストプロセスで一緒にステッチすることを試みましたが、バーの高さをデータにかかわらず同じままにする方法を見つけることができませんでした。 matplotlibのあいまいなオブジェクトから正確なスケーリングのために必要なメトリックを抽出する方法があると確信していますが、この時点で私は描画手順全体をトレースしようとすると別の野生のガチョウの追跡に行きます。

さらに、個々のサブプロットをデータの周りに折りたたむことができる場合、そのデータに基づいてFigureを拡大するにはどうすればよいですか?例えば、上記のデータに4番目のカテゴリを追加する場合、別のグラフによって高さが「伸びる」のではなく、実際にはすべてのグラフがデフォルトのFigureサイズに収まるように縮小されます。さて、私は軸の単位とすべてのものを持つmatplotlibの背後にある論理を理解していると思う、私は全体の高さを増やすためにFigureのサイズを設定することができますが、どのようにチャート全体にわたって一貫性を保つか、バーの高さはデータに関係なく正確に同じですか?

私が望むものを手に入れるためには、すべて手動でプロットする必要がありますか?もしそうなら、matplotlibパッケージ全体をダンプして自分のSVGを最初から作成するかもしれません。私がこれに費やした時間を考えれば、最初はそれをやっていたはずですが、今私はそれをあきらめるにはあまりにも頑固です(あるいは私は恐怖の沈んだコストの誤りの犠牲者です)。

アイデア?

おかげ

答えて

0

私は同時に同じバー幅(垂直方向の幅)と異なるsubplotsizesで持っている唯一の方法は、手動で、図に軸を置くことは本当にあると思います。

この目的のために、棒グラフの大きさと棒グラフ間の間隔を棒グラフの単位で指定することができます。これらの数値とプロットするデータの量から、合計フィギュアの高さをインチ単位で計算できます。 各サブプロットは、前のサブプロットのデータ量とスペーシングに応じて(fig.add_axesを介して)配置されます。それによって、素敵にプロットを埋めることができます。 新しいデータセットを追加すると、Figureが大きくなります。

data = [ 
    {"name": "Category 1", "entries": [ 
     {"name": "Entry 1", "value": 5}, 
     {"name": "Entry 2", "value": 2}, 
    ]}, 
    {"name": "Category 2", "entries": [ 
     {"name": "Entry 1", "value": 1}, 
    ]}, 
    {"name": "Category 3", "entries": [ 
     {"name": "Entry 1", "value": 1}, 
     {"name": "Entry 2", "value": 10}, 
     {"name": "Entry 3", "value": 4}, 
    ]}, 
    {"name": "Category 4", "entries": [ 
     {"name": "Entry 1", "value": 6}, 
    ]}, 
] 

import matplotlib.pyplot as plt 
import numpy as np 

def plot_data(data, 
       barwidth = 0.2, # inch per bar 
       spacing = 3, # spacing between subplots in units of barwidth 
       figx = 5,  # figure width in inch 
       left = 4,  # left margin in units of bar width 
       right=2):  # right margin in units of bar width 

    tc = len(data) # "total_categories", holds how many charts to create 
    max_values = [] # holds the maximum number of bars to create 
    for category in data: 
     max_values.append(len(category["entries"])) 
    max_values = np.array(max_values) 

    # total figure height: 
    figy = ((np.sum(max_values)+tc) + (tc+1)*spacing)*barwidth #inch 

    fig = plt.figure(figsize=(figx,figy)) 
    ax = None 
    for index, category in enumerate(data): 
     entries = [] 
     values = [] 
     for entry in category["entries"]: 
      entries.append(entry["name"]) 
      values.append(entry["value"]) 
     if not entries: 
      continue # do not create empty charts 
     y_ticks = range(1, len(entries) + 1) 
     # coordinates of new axes [left, bottom, width, height] 
     coord = [left*barwidth/figx, 
       1-barwidth*((index+1)*spacing+np.sum(max_values[:index+1])+index+1)/figy, 
       1-(left+right)*barwidth/figx, 
       (max_values[index]+1)*barwidth/figy ] 

     ax = fig.add_axes(coord, sharex=ax) 
     ax.barh(y_ticks, values) 
     ax.set_ylim(0, max_values[index] + 1) # limit the y axis for fixed height 
     ax.set_yticks(y_ticks) 
     ax.set_yticklabels(entries) 
     ax.invert_yaxis() 
     ax.set_title(category["name"], loc="left") 


plot_data(data) 
plt.savefig(__file__+".png") 
plt.show() 

enter image description here

+0

おかげで、それは働きます!私は、私が必要とするものを示す最も単純なチャートを提示していたので、collapse_emptyオプションなどを望んでいました。もし私が本質的にSVGに全力を尽くして描く必要があれば。そのような一般的なプロットライブラリはすでにユーザーフレンドリーな方法で整理された基本レイアウトを持っていたと思うでしょう。 – zwer

+0

上記のスクリプトで何かを測定する必要はなく、すべて自動的に行われます。私は、あなたの要件は本当に非常にそのためのいくつかのオートメーション機能を期待するように具体的だと思う。 – ImportanceOfBeingErnest

+0

私はあなたのコードを実際に解決するyスペースの問題を解決しようとしていましたが、現実には私がやっていることはやや複雑ですが、barh() 'と' text() 'は一元的に連携して、x軸上のすべてを調整します。 この場合、 'tight_layout()'は手動で設定された軸では機能しないため、エントリ名が大きい場合、軸を右(左のパラメータ)に移動してから測定する必要がない限り画面から外れます最長のエントリ名がどれくらいのスペースを取るか。 – zwer

関連する問題