Thanh màu rời rạc Matplotlib


95

Tôi đang cố tạo một thanh màu riêng biệt cho một biểu đồ phân tán trong matplotlib

Tôi có dữ liệu x, y của mình và cho mỗi điểm một giá trị thẻ số nguyên mà tôi muốn được thể hiện bằng một màu duy nhất, ví dụ:

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

thường thẻ sẽ là một số nguyên nằm trong khoảng từ 0-20, nhưng phạm vi chính xác có thể thay đổi

cho đến nay tôi chỉ sử dụng cài đặt mặc định, ví dụ:

plt.colorbar()

mang lại một dải màu liên tục. Lý tưởng nhất là tôi muốn một bộ n màu rời rạc (trong ví dụ này là n = 20). Thậm chí tốt hơn là nhận giá trị thẻ 0 để tạo ra màu xám và 1-20 là màu sặc sỡ.

Tôi đã tìm thấy một số kịch bản 'sách dạy nấu ăn' nhưng chúng rất phức tạp và tôi không thể nghĩ rằng chúng là cách phù hợp để giải quyết một vấn đề có vẻ đơn giản


1
làm này hay này giúp đỡ?
Francesco Montesano

nhờ các liên kết nhưng ví dụ thứ 2 là những gì tôi có nghĩa là khoảng vô cùng overcomplicated phương tiện để thực hiện một (dường như) nhiệm vụ tầm thường - Liên kết 1 là hữu ích
bph

1
Tôi thấy liên kết này rất hữu ích trong việc discretizing một bản đồ màu hiện có: gist.github.com/jakevdp/91077b0cae40f8f8244a
BallpointBen

Câu trả lời:


91

Bạn có thể tạo một thanh màu rời rạc tùy chỉnh khá dễ dàng bằng cách sử dụng BoundaryNorm làm bộ chuẩn hóa cho phân tán của bạn. Bit kỳ quặc (trong phương pháp của tôi) đang làm cho 0 hiển thị thành màu xám.

Đối với hình ảnh, tôi thường sử dụng cmap.set_bad () và chuyển đổi dữ liệu của tôi thành một mảng ẩn có mặt nạ. Điều đó sẽ dễ dàng hơn nhiều để tạo ra 0 màu xám, nhưng tôi không thể làm cho điều này hoạt động với phân tán hoặc cmap tùy chỉnh.

Thay vào đó, bạn có thể tạo cmap của riêng mình từ đầu hoặc đọc ra một bản đồ hiện có và chỉ ghi đè một số mục cụ thể.

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)

nhập mô tả hình ảnh ở đây

Cá nhân tôi nghĩ rằng với 20 màu sắc khác nhau, hơi khó để đọc giá trị cụ thể, nhưng điều đó tất nhiên là tùy thuộc vào bạn.


Tôi không chắc liệu điều này có được phép hay không, nhưng bạn có thể xem câu hỏi của tôi ở đây không?
vwos

6
plt.colorbar.ColorbarBaseném Lỗi. Sử dụngmpl.colorbar.ColorbarBase
zeeshan khan

Cảm ơn bạn vì câu trả lời này, thực sự nhớ nó từ tài liệu. Tôi đã cố gắng chuyển đổi nó cho các bức xạ gió của tỷ lệ phần trăm và tôi đã gặp lỗi với ánh xạ màu. Đây là một trường hợp sử dụng khác, nhưng nó có thể gợi ý rằng nó N-1ở trong cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N-1). Nếu không, màu sắc không được phân bổ đều trong các thùng và bạn có vấn đề về hàng rào.
jlandercy

1
Đây là mã để tái tạo ánh xạ phân tán đều: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

Tôi không chắc về N-1, bạn có thể đúng nhưng tôi không thể lặp lại nó bằng ví dụ của mình. Bạn có thể tránh LinearSegmentedColormap(và Nđối số của nó ) bằng cách sử dụng a ListedColormap. Tài liệu đã được cải thiện rất nhiều kể từ năm '13, xem ví dụ: matplotlib.org/3.1.1/tutorials/colors/…
Rutger Kassies

62

Bạn có thể làm theo ví dụ sau:

#!/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()

tạo ra hình ảnh sau:

poormans_contour


14
cmap = cm.get_cmap ('jet', 20) rồi phân tán (x, y, c = tags, cmap = cmap) đưa tôi đến đó - rất khó tìm tài liệu hữu ích cho matplotlib
bph

Liên kết dường như bị phá vỡ, FYI.
Quinn Culver

44

Các câu trả lời trên là tốt, ngoại trừ chúng không có vị trí đánh dấu thích hợp trên thanh màu. Tôi thích có dấu tích ở giữa màu để ánh xạ số -> màu rõ ràng hơn. Bạn có thể giải quyết vấn đề này bằng cách thay đổi giới hạn của lệnh gọi chiếu:

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)

ví dụ về thanh màu rời rạc


1
Tôi đồng ý rằng việc đặt dấu tích ở giữa màu tương ứng sẽ rất hữu ích khi xem dữ liệu rời rạc. Phương pháp thứ hai của bạn là đúng. Tuy nhiên, phương pháp đầu tiên của bạn nói chung là sai : bạn đang gắn nhãn các dấu tích với các giá trị không phù hợp với vị trí của chúng trên thanh màu. set_ticklabels(...)chỉ nên được sử dụng để kiểm soát định dạng nhãn (ví dụ: số thập phân, v.v.). Nếu dữ liệu thực sự rời rạc, bạn có thể không nhận thấy bất kỳ vấn đề nào. Nếu có nhiễu trong hệ thống (ví dụ: 2 -> 1,9), việc ghi nhãn không nhất quán này sẽ dẫn đến thanh màu sai lệch và không chính xác.
E. Davis

E., Tôi nghĩ bạn đúng khi thay đổi các giới hạn là giải pháp ưu việt nên tôi đã loại bỏ cái còn lại-- mặc dù cả hai đều không xử lý tốt "tiếng ồn". Một số điều chỉnh sẽ cần thiết để xử lý dữ liệu liên tục.
ben.dichter

37

Để đặt giá trị trên hoặc dưới phạm vi của bản đồ màu, bạn sẽ muốn sử dụng set_overset_undercác phương thức của bản đồ màu. Nếu bạn muốn gắn cờ một giá trị cụ thể, hãy tạo mặt nạ cho nó (tức là tạo một mảng có mặt nạ) và sử dụng set_badphương thức này. (Hãy xem tài liệu cho lớp bản đồ màu cơ sở: http://matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap )

Có vẻ như bạn muốn một cái gì đó như thế này:

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()

nhập mô tả hình ảnh ở đây


điều đó thực sự tốt - tôi đã thử sử dụng set_under nhưng không bao gồm vmin nên tôi không nghĩ nó có tác dụng gì
bph

9

Chủ đề này đã được đề cập kỹ nhưng tôi muốn thêm một cái gì đó cụ thể hơn: Tôi muốn chắc chắn rằng một giá trị nhất định sẽ được ánh xạ tới màu đó (không phải bất kỳ màu nào).

Nó không phức tạp nhưng vì tôi đã mất một chút thời gian, nó có thể giúp những người khác không mất nhiều thời gian như tôi đã làm :)

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()

nhập mô tả hình ảnh ở đây


Đã cố gắng sao chép điều này, tuy nhiên mã không chạy vì 'tmp' không được xác định. Cũng không rõ 'pos' là gì trong hàm lambda. Cảm ơn!
George Liu

@GeorgeLiu Quả thực bạn đã viết! Tôi đã thực hiện một lỗi sao chép / dán và nó bây giờ đã được chỉnh sửa! Đoạn mã hiện đang chạy! Về vấn đề postôi không hoàn toàn chắc chắn về lý do tại sao nó ở đây nhưng nó được yêu cầu bởi FuncFormatter () ... Có thể ai đó khác có thể khai sáng cho chúng tôi về nó!
Enzoupi

7

Tôi đã tìm hiểu những ý tưởng này và đây là giá trị năm xu của tôi. Nó tránh gọi BoundaryNormcũng như chỉ định normlàm đối số cho scattercolorbar. Tuy nhiên, tôi không tìm thấy cách nào để loại bỏ cuộc gọi khá dài dòng tới matplotlib.colors.LinearSegmentedColormap.from_list.

Một số cơ sở là matplotlib cung cấp cái gọi là bản đồ màu định tính, nhằm mục đích sử dụng với dữ liệu rời rạc. Set1, ví dụ, có 9 màu dễ phân biệt và tab20có thể được sử dụng cho 20 màu. Với những bản đồ này, có thể tự nhiên sử dụng n màu đầu tiên của chúng để tô màu cho các ô phân tán với n loại, như ví dụ sau đây. Ví dụ này cũng tạo ra một thanh màu với n màu rời rạc được dán nhãn chính xác.

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)

tạo ra hình ảnh bên dưới. Lệnh ngọi trong lệnh Set1chỉ định các nmàu đầu tiên của bản đồ màu đó và lệnh cuối cùng ntrong lệnh gọi để from_list chỉ định xây dựng bản đồ với các nmàu (mặc định là 256). Để đặt cmlàm bản đồ màu mặc định plt.set_cmap, tôi thấy cần phải đặt tên và đăng ký cho nó, viz:

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)

scatterplot với màu sắc rời rạc


1

Tôi nghĩ bạn sẽ muốn xem xét màu sắc. Biểu đồ mã liệt kê để tạo bản đồ màu của bạn, hoặc nếu bạn chỉ cần một bản đồ màu tĩnh, tôi đang làm việc trên một ứng dụng có thể hữu ích.


trông bắt mắt, có thể là quá mức cần thiết cho nhu cầu của tôi - bạn có thể đề xuất cách gắn thẻ giá trị màu xám vào sơ đồ màu hiện có không? để các giá trị 0 có màu xám và các giá trị khác có màu?
bph,

@Hiett thì sao về việc tạo một color_list mảng RGB dựa trên các giá trị y của bạn và chuyển nó vào ListedColormap? Bạn có thể gắn thẻ một giá trị bằng color_list [y == value_to_tag] = gray_color.
ChrisC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.