我需要创建具有不均匀间隔值的网格数据的二维图像。我正在绘制一个分类数据集,其中类别使用与特定标签相对应的数值进行编码。
我需要能够使用格式化程序为数据集中的每个类别分配不同的颜色。这最好应该是灵活的,因为真实的数据集有大约 30 个我正在绘制的独特类别。因此,当值为 10
和 40
时,我应该有一个独特的颜色。
制作示例数据进行演示
import xarray as xr
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
time = pd.date_range('2010-01-31', '2015-12-31', freq='M')
lat = np.linspace(0, 1, 224)
lon = np.linspace(0, 1, 176)
valid_vals = [10., 40., 50., 60.]
labels = ['type_1', 'type_2', 'type_3', 'type_4']
lookup = dict(zip(valid_vals, labels))
values = np.random.choice(valid_vals, size=(len(time), len(lat), len(lon)))
rand_nans = np.random.random(size=(len(time), len(lat), len(lon))) < 0.3
values[rand_nans] = np.nan
coords = {'time': time, 'lat': lat, 'lon': lon}
dims = ['time', 'lat', 'lon']
ds = xr.Dataset({'lc_code': (dims, values)}, coords=coords)
# convert to numpy array (only the first timestep)
im = ds.isel(time=0).lc_code.values
ds
Out[]:
<xarray.Dataset>
Dimensions: (lat: 224, lon: 176, time: 72)
Coordinates:
* time (time) datetime64[ns] 2010-01-31 2010-02-28 ... 2015-12-31
* lat (lat) float64 0.0 0.004484 0.008969 0.01345 ... 0.991 0.9955 1.0
* lon (lon) float64 0.0 0.005714 0.01143 0.01714 ... 0.9886 0.9943 1.0
Data variables:
lc_code (time, lat, lon) float64 50.0 nan 60.0 50.0 ... 40.0 10.0 40.0 10.0
仅绘制图像数据有两个问题:
1) 刻度标签不是labels
中定义的字符串
2) 颜色条间隔均匀,但值不均匀。这样我们的值就是 10, 40, 50, 60
plt.imshow(im, cmap=plt.cm.get_cmap('tab10', len(valid_vals)))
plt.colorbar()
所以我尝试使用FuncFormatter
。然而,该图像仍然存在问题,即尽管刻度标签排列在颜色条的中心,但没有值映射到 type_2
颜色。
fig, ax = plt.subplots(figsize=(12, 8))
plt.imshow(im, cmap=plt.cm.get_cmap('tab10', len(valid_vals)))
# calculate the POSITION of the tick labels
min_ = min(valid_vals)
max_ = max(valid_vals)
positions = np.linspace(min_, max_, len(valid_vals))
val_lookup = dict(zip(positions, labels))
def formatter_func(x, pos):
'The two args are the value and tick position'
val = val_lookup[x]
return val
formatter = plt.FuncFormatter(formatter_func)
# We must be sure to specify the ticks matching our target names
plt.colorbar(ticks=positions, format=formatter, spacing='proportional');
# set the colorbar limits so that the ticks are evenly spaced
plt.clim(0, 70)
但是此代码强制第二个类别(40
、type_2
的值)不以tick
所对应的颜色显示。因此,颜色条无法有效反射(reflect)图像中的数据。
(im == 40).mean()
Out[]:
0.17347301136363635
最佳答案
第一个图中没有颜色映射到 type_2 颜色的原因是 23 到 35 之间没有值,这大致是分配给红色的范围。
您可以尝试使用 ListedColormap
。
import xarray as xr
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
import pandas as pd
time = pd.date_range('2010-01-31', '2015-12-31', freq='M')
lat = np.linspace(0, 1, 224)
lon = np.linspace(0, 1, 176)
valid_values = [10., 40., 50., 60.]
labels = ['type_1', 'type_2', 'type_3', 'type_4']
lookup = dict(zip(valid_values, labels))
values = np.random.choice(valid_values, size=(len(time), len(lat), len(lon)))
rand_nans = np.random.random(size=(len(time), len(lat), len(lon))) < 0.3
values[rand_nans] = np.nan
coords = {'time': time, 'lat': lat, 'lon': lon}
dims = ['time', 'lat', 'lon']
ds = xr.Dataset({'lc_code': (dims, values)}, coords=coords)
# convert to numpy array (only the first timestep)
im = ds.isel(time=0).lc_code.values
# Build a listed colormap.
c_map = colors.ListedColormap(['white', 'red', 'blue', 'green'])
bounds = [-15, 35, 45, 55, 65]
norm = colors.BoundaryNorm(bounds, c_map.N)
# Plot the image with a color bar
im = plt.imshow(im, cmap=c_map, norm=norm)
c_bar = plt.colorbar(
im, cmap=c_map, norm=norm, boundaries=bounds, ticks=[10, 40, 50, 60])
c_bar.ax.set_xticklabels(['type_1', 'type_2', 'type_3', 'type_4'])
plt.show()
为了使标签到达颜色条区域的中心,您只需确保刻度值(在 plt.colorbar
参数中)恰好位于相关 bounds
之间的中间。 。我对这些进行了硬编码,但您可以轻松地自动计算这些!我相信边界不等距并不重要,因为 ListedColormap
是隐式分类的,因此它理解使每个类别的颜色条大小相等。
希望这有帮助!
关于python - 当值间隔不均匀时,在 matplotlib 中绘制分类数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57249138/