Matplotlib離散カラーバー


94

私はmatplotlibで散布図の離散カラーバーを作ろうとしています

私は自分のx、yデータを持ち、各ポイントに一意の色で表したい整数タグ値があります。たとえば、

plt.scatter(x, y, c=tag)

通常、タグは0〜20の範囲の整数ですが、正確な範囲は変わる可能性があります

これまでのところ、私はデフォルト設定を使用しました、例えば

plt.colorbar()

連続した色の範囲を与えます。理想的には、n個の個別の色(この例ではn = 20)のセットが欲しいです。タグ値を0にして灰色にし、1〜20をカラフルにするのがさらに良いでしょう。

「クックブック」スクリプトをいくつか見つけましたが、それらは非常に複雑であり、一見単純な問題を解決するための正しい方法であるとは思えません


1
ないこのまたはこのヘルプは?
Francesco Montesano 2013

第一リンクが便利です-リンクへの感謝が、第二の例では、私は(一見)些細なタスクを実行するのに約非常overcomplicated手段を意味するものである
BPH

1
このリンクは、既存のカラーマップの離散化に非常に役立ちました。gist.github.com
BallpointBen

回答:


91

散布のノーマライザとしてBoundaryNormを使用すると、カスタムの離散カラーバーを非常に簡単に作成できます。風変わりなビット(私の方法で)は灰色として0の表示をしています。

画像の場合、私はしばしばcmap.set_bad()を使用して、データを派手なマスクされた配列に変換します。0をグレーにする方がはるかに簡単ですが、スキャッターまたはカスタムcmapでこれを機能させることはできませんでした。

別の方法として、独自のcmapを最初から作成するか、既存のcmapを読み出して、一部の特定のエントリのみをオーバーライドできます。

import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt

fig, ax = plt.subplots(1, 1, figsize=(6, 6))  # setup the plot

x = np.random.rand(20)  # define the data
y = np.random.rand(20)  # define the data
tag = np.random.randint(0, 20, 20)
tag[10:12] = 0  # make sure there are some 0 values to show up as grey

cmap = plt.cm.jet  # define the colormap
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# force the first color entry to be grey
cmaplist[0] = (.5, .5, .5, 1.0)

# create the new map
cmap = mpl.colors.LinearSegmentedColormap.from_list(
    'Custom cmap', cmaplist, cmap.N)

# define the bins and normalize
bounds = np.linspace(0, 20, 21)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# make the scatter
scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20),
                  cmap=cmap, norm=norm)

# create a second axes for the colorbar
ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8])
cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm,
    spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i')

ax.set_title('Well defined discrete colors')
ax2.set_ylabel('Very custom cbar [-]', size=12)

ここに画像の説明を入力してください

個人的には、20種類の色があるため、特定の値を読み取るのは少し難しいと思いますが、それはもちろんあなた次第です。


これが許可されているかどうかはわかりませんが、私の質問をここで見ていただけますか?
vwos 2015

6
plt.colorbar.ColorbarBaseエラーをスローします。使用mpl.colorbar.ColorbarBase
ジーシャンカーン

この回答をありがとうございます。本当にドキュメントから逃してください。パーセンタイルのウインドローズ用に転置しようとしたところ、カラーマッピングにバグがありました。これは別のユースケースですが、にあると考えられる場合がN-1ありcmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N-1)ます。そうでない場合、色がビン内で均等に分散されておらず、フェンスバリアの問題があります。
jlandercy

1
:ここに均等に分配マッピングを再現するためのコードであるq=np.arange(0.0, 1.01, 0.1) cmap = mpl.cm.get_cmap('jet') cmaplist = [cmap(x) for x in q] cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, len(q)-1) norm = mpl.colors.BoundaryNorm(q, cmap.N)
jlandercy

についてはN-1わかりませんが、多分正しいかもしれませんが、私の例では再現できません。を使用することでLinearSegmentedColormap(およびそのN引数)を回避できListedColormapます。ドキュメントは'13以来、多くのことを改善している、例えば以下を参照してくださいmatplotlib.org/3.1.1/tutorials/colors/...
ルトガーKassies

62

あなたはこの例に従うことができます:

#!/usr/bin/env python
"""
Use a pcolor or imshow with a custom colormap to make a contour plot.

Since this example was initially written, a proper contour routine was
added to matplotlib - see contour_demo.py and
http://matplotlib.sf.net/matplotlib.pylab.html#-contour.
"""

from pylab import *


delta = 0.01
x = arange(-3.0, 3.0, delta)
y = arange(-3.0, 3.0, delta)
X,Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1 # difference of Gaussians

cmap = cm.get_cmap('PiYG', 11)    # 11 discrete colors

im = imshow(Z, cmap=cmap, interpolation='bilinear',
            vmax=abs(Z).max(), vmin=-abs(Z).max())
axis('off')
colorbar()

show()

次の画像が生成されます。

貧乏人


14
cmap = cm.get_cmap( 'jet'、20)で、その後scatter(x、y、c = tags、cmap = cmap)を実行すると、matplotlibの有用なドキュメントを見つけるのが非常に困難になります
bph

リンクが壊れているようです。
Quinn Culver

42

上記の回答は、カラーバーに適切な目盛りが配置されていない場合を除き、適切です。ティックを色の中央に配置して、番号->カラーマッピングをより明確にするのが好きです。この問題は、matshow呼び出しの制限を変更することで解決できます。

import matplotlib.pyplot as plt
import numpy as np

def discrete_matshow(data):
    #get discrete colormap
    cmap = plt.get_cmap('RdBu', np.max(data)-np.min(data)+1)
    # set limits .5 outside true range
    mat = plt.matshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5)
    #tell the colorbar to tick at integers
    cax = plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1))

#generate data
a=np.random.randint(1, 9, size=(10, 10))
discrete_matshow(a)

離散カラーバーの例


1
目盛りを対応する色の中央に配置すると、離散データを見るときに非常に役立つことに同意します。2番目の方法は正しいです。ただし、最初の方法は、一般的には間違っています。カラーバー上の配置と一致しない値で目盛りにラベルを付けています。set_ticklabels(...)ラベルのフォーマットを制御するためにのみ使用する必要があります(例:10進数など)。データが完全に離散的である場合、問題に気付かない場合があります。システムにノイズがある場合(たとえば、2-> 1.9)、この一貫性のないラベル付けは、誤解を招く不適切なカラーバーになります。
E.デイビス

E.、あなたは正しいと思います。制限を変更することは優れたソリューションなので、もう1つは削除しましたが、どちらも「ノイズ」をうまく処理できません。継続的なデータを処理するには、いくつかの調整が必要になります。
ben.dichter 2015年

37

カラーマップの範囲の上下に値を設定するには、カラーマップのset_overおよびset_underメソッドを使用する必要があります。特定の値にフラグを付けたい場合は、値をマスクして(つまり、マスクされた配列を作成して)、set_badメソッドを使用します。(基本カラーマップクラスのドキュメントをご覧くださいhttp : //matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap

あなたはこのようなものが欲しいようです:

import matplotlib.pyplot as plt
import numpy as np

# Generate some data
x, y, z = np.random.random((3, 30))
z = z * 20 + 0.1

# Set some values in z to 0...
z[:5] = 0

cmap = plt.get_cmap('jet', 20)
cmap.set_under('gray')

fig, ax = plt.subplots()
cax = ax.scatter(x, y, c=z, s=100, cmap=cmap, vmin=0.1, vmax=z.max())
fig.colorbar(cax, extend='min')

plt.show()

ここに画像の説明を入力してください


それは本当に良いです-私はset_underを使ってみましたが、vminが含まれていなかったので、何もしていなかったと思います
bph

8

このトピックはすでに十分にカバーされていますが、もっと具体的なものを追加したいと思います。特定の値がその色にマップされることを確認したいのです(どの色にもマップされません)。

複雑ではありませんが、少し時間がかかったので、他の人が私ほど時間を無駄にしていないのを助けるかもしれません:)

import matplotlib
from matplotlib.colors import ListedColormap

# Let's design a dummy land use field
A = np.reshape([7,2,13,7,2,2], (2,3))
vals = np.unique(A)

# Let's also design our color mapping: 1s should be plotted in blue, 2s in red, etc...
col_dict={1:"blue",
          2:"red",
          13:"orange",
          7:"green"}

# We create a colormar from our list of colors
cm = ListedColormap([col_dict[x] for x in col_dict.keys()])

# Let's also define the description of each category : 1 (blue) is Sea; 2 (red) is burnt, etc... Order should be respected here ! Or using another dict maybe could help.
labels = np.array(["Sea","City","Sand","Forest"])
len_lab = len(labels)

# prepare normalizer
## Prepare bins for the normalizer
norm_bins = np.sort([*col_dict.keys()]) + 0.5
norm_bins = np.insert(norm_bins, 0, np.min(norm_bins) - 1.0)
print(norm_bins)
## Make normalizer and formatter
norm = matplotlib.colors.BoundaryNorm(norm_bins, len_lab, clip=True)
fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: labels[norm(x)])

# Plot our figure
fig,ax = plt.subplots()
im = ax.imshow(A, cmap=cm, norm=norm)

diff = norm_bins[1:] - norm_bins[:-1]
tickz = norm_bins[:-1] + diff / 2
cb = fig.colorbar(im, format=fmt, ticks=tickz)
fig.savefig("example_landuse.png")
plt.show()

ここに画像の説明を入力してください


これを複製しようとしましたが、「tmp」が定義されていないため、コードは実行されません。また、ラムダ関数の「pos」が何であるかは不明です。ありがとう!
George Liu

@GeorgeLiu確かにあなたは書いていた!コピー/貼り付けの間違いを犯しましたが、修正されました!コードのスニペットが実行されています!pos私はそれがここにいる理由を完全にわからないが、それはFuncFormatter()によって要求された...たぶん他の誰かがそれについての私達を啓発することができます!
Enzoupi

7

私はこれらのアイデアを調査しており、ここに私の5セントの価値があります。およびへの引数BoundaryNormとしての指定と同様に、呼び出しを回避します。しかし、私はへのかなり長い時間のかかる呼び出しを排除する方法を見つけていません。normscattercolorbarmatplotlib.colors.LinearSegmentedColormap.from_list

背景には、matplotlibが離散データで使用することを目的とした、いわゆる質的カラーマップを提供するというものがあります。Set1たとえば、9つの簡単に区別できる色がありtab20、20色に使用できます。これらのマップでは、次の例のように、最初のn色を使用してnカテゴリの散布図に色を付けるのが自然です。この例では、適切にラベル付けされたn個の個別の色を持つカラーバーも作成します。

import matplotlib, numpy as np, matplotlib.pyplot as plt
n = 5
from_list = matplotlib.colors.LinearSegmentedColormap.from_list
cm = from_list(None, plt.cm.Set1(range(0,n)), n)
x = np.arange(99)
y = x % 11
z = x % n
plt.scatter(x, y, c=z, cmap=cm)
plt.clim(-0.5, n-0.5)
cb = plt.colorbar(ticks=range(0,n), label='Group')
cb.ax.tick_params(length=0)

以下の画像が生成されます。のn呼び出しでのは、そのカラーマップのSet1最初のn色を指定し、呼び出しの最後のnは、色でfrom_list マップを作成することを指定しnます(デフォルトは256)。でcmデフォルトのカラーマップとして設定するにはplt.set_cmap、名前を付けて登録する必要があることがわかりました。つまり、

cm = from_list('Set15', plt.cm.Set1(range(0,n)), n)
plt.cm.register_cmap(None, cm)
plt.set_cmap(cm)
...
plt.scatter(x, y, c=z)

離散色の散布図


1

Colors.ListedColormapを見てカラーマップを生成するか、静的なカラーマップだけが必要な場合は、役立つアプリ開発してきました。


それはかっこいい、多分私のニーズにはやり過ぎです-グレイ値を既存のカラーマップにタグ付けする方法を提案できますか?だから0の値は灰色になり、他の値は色として出ますか?
bph 2013

@Hiett y値に基づいてRGB配列color_listを生成し、それをListedColormapに渡すのはどうですか?color_list [y == value_to_tag] = gray_colorで値にタグを付けることができます。
ChrisC 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.