当值间隔不均匀时,在matplotlib中绘制分类数据

时间:2019-07-29 07:37:21

标签: python matplotlib python-xarray

我需要创建栅格数据的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()

simple imshow

因此,我尝试使用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)

My attempt at mapping the values to the labels

但是此代码强制第二类别(40type_2的值)不以tick排列的颜色显示。因此,颜色条不能有效地反映图像中的数据。

(im == 40).mean()

Out[]:
0.17347301136363635

1 个答案:

答案 0 :(得分:1)

在您的第一个绘图中没有颜色映射到type_2颜色的原因是在23到35之间没有值,这大约是分配给红色的范围。

您可以尝试使用5

ListedColormap

这给出了以下输出: Plot

为了使标签位于颜色条区域的中心,您只需要确保刻度值(在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是隐式分类的,因此它理解为每个类别使颜色条的大小相等。

希望这会有所帮助!