我需要创建栅格数据的2D图像,其值的间距不均匀。我正在绘制一个类别数据集,其中类别使用对应于特定标签的数值编码。
我需要能够使用格式化程序为数据集中的每个类别分配不同的颜色。这应该最好是灵活的,因为真实的数据集具有我要绘制的约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
排列的颜色显示。因此,颜色条不能有效地反映图像中的数据。
(im == 40).mean()
Out[]:
0.17347301136363635
答案 0 :(得分:1)
在您的第一个绘图中没有颜色映射到type_2颜色的原因是在23到35之间没有值,这大约是分配给红色的范围。
您可以尝试使用5
。
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
是隐式分类的,因此它理解为每个类别使颜色条的大小相等。
希望这会有所帮助!