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

标签 python matplotlib python-xarray

我需要创建具有不均匀间隔值的网格数据的二维图像。我正在绘制一个分类数据集,其中类别使用与特定标签相对应的数值进行编码。

我需要能够使用格式化程序为数据集中的每个类别分配不同的颜色。这最好应该是灵活的,因为真实的数据集有大约 30 个我正在绘制的独特类别。因此,当值为 1040 时,我应该有一个独特的颜色。

制作示例数据进行演示

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 所对应的颜色显示。因此,颜色条无法有效反射(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()

这给出了这个输出: Plot

为了使标签到达颜色条区域的中心,您只需确保刻度值(在 plt.colorbar 参数中)恰好位于相关 bounds 之间的中间。 。我对这些进行了硬编码,但您可以轻松地自动计算这些!我相信边界不等距并不重要,因为 ListedColormap是隐式分类的,因此它理解使每个类别的颜色条大小相等。

希望这有帮助!

关于python - 当值间隔不均匀时,在 matplotlib 中绘制分类数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57249138/

相关文章:

python - 获取一维结果列表并将其转换为 N 维 xarray.DataArray

python - 查找标识符的值

python - Mercurial 修订树的可视化

python - 在 Pandas 中使用特定的图形处理程序

python - Seaborn light_palette 自定义长度

python - 代码中的 matplotlib 原始字符串换行符

python - 如何使用Dask.Array.From_Zarr在Dask上打开zarr文件?

python-xarray - 将 xarray.Dataset 变量重新分类为新变量

python - SQLAlchemy - 连接表的 order_by 关系

python - 如何改进评估列表以确定其是否包含特定连续项目的方法?