如何向极坐标图(玫瑰图)添加颜色条?

2023-11-22

In 这个例子颜色与每个条形的半径相关。如何向该图添加颜色条?

matplotlib example

我的代码模仿“玫瑰图”投影,它本质上是极坐标投影上的条形图。

这是其中的一部分:

angle = radians(10.)
patches = radians(360.)/angle
theta = np.arange(0,radians(360.),angle)
count = [0]*patches
for i, item in enumerate(some_array_of_azimuth_directions):
    temp = int((item - item%angle)/angle)
    count[temp] += 1
width = angle * np.ones(patches)

# force square figure and square axes looks better for polar, IMO
fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True)

rmax = max(count) + 1

ax.set_rlim(0,rmax)
ax.set_theta_offset(np.pi/2)
ax.set_thetagrids(np.arange(0,360,10))
ax.set_theta_direction(-1)

# project strike distribution as histogram bars
bars = ax.bar(theta, count, width=width)
r_values = []
colors = []
for r,bar in zip(count, bars):
    r_values.append(r/float(max(count)))
    colors.append(cm.jet(r_values[-1], alpha=0.5))
    bar.set_facecolor(colors[-1])
    bar.set_edgecolor('grey')
    bar.set_alpha(0.5)
    
# Add colorbar, make sure to specify tick locations to match desired ticklabels
colorlist = []
r_values.sort()
values = []
for val in r_values:
    if val not in values:
        values.append(val*float(max(count)))
            
    color = cm.jet(val, alpha=0.5)
    if color not in colorlist:
        colorlist.append(color)

cpt = mpl.colors.ListedColormap(colorlist)
bounds = range(max(count)+1)
norm = mpl.colors.BoundaryNorm(values, cpt.N-1)

cax = fig.add_axes([0.97, 0.3, 0.03, 0.4])
cb = mpl.colorbar.ColorbarBase(cax, cmap=cpt,
                                     norm=norm,
                                     boundaries=bounds,
                                     # Make the length of each extension
                                     # the same as the length of the
                                     # interior colors:
                                     extendfrac='auto',
                                     ticks=[bounds[i] for i in range(0, len(bounds), 2)],
                                     #ticks=bounds,
                                     spacing='uniform')

and here is the resulting plot: enter image description here

正如您所看到的,颜色条不太正确。如果仔细观察,在 16 和 17 之间,会缺少一种颜色(深橙色),并且根据颜色条,黄色达到值 15,这在玫瑰图(或数据)中是不正确的。

我已经对代码进行了很多尝试,但我只是不知道如何正确规范化颜色条。


最简单的方法是使用PatchCollection并传入您的“z”(即您想要着色的值)作为array kwarg.

举个简单的例子:

import itertools
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.collections import PatchCollection
import numpy as np

def main():
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='polar')
    x = np.radians(np.arange(0, 360, 10))
    y = np.random.random(x.size)
    z = np.random.random(y.size)
    cmap = plt.get_cmap('cool')
    coll = colored_bar(x, y, z, ax=ax, width=np.radians(10), cmap=cmap)
    fig.colorbar(coll)
    ax.set_yticks([0.5, 1.0])
    plt.show()   

def colored_bar(left, height, z=None, width=0.8, bottom=0, ax=None, **kwargs):
    if ax is None:
        ax = plt.gca()
    width = itertools.cycle(np.atleast_1d(width))
    bottom = itertools.cycle(np.atleast_1d(bottom))
    rects = []
    for x, y, w, h in zip(left, bottom, width, height):
        rects.append(Rectangle((x,y), w, h))
    coll = PatchCollection(rects, array=z, **kwargs)
    ax.add_collection(coll)
    ax.autoscale()
    return coll

if __name__ == '__main__':
    main()

enter image description here

如果您想要离散颜色图,最简单的方法是在调用时指定您想要的间隔数plt.get_cmap。例如,在上面的代码中,如果将行替换为cmap = plt.get_cmap('cool') with:

cmap = plt.get_cmap('cool', 5)

然后您将获得一个具有 5 个间隔的离散颜色图。 (或者,您可以传递ListedColormap您在示例中创建的。)

enter image description here

如果你想要一个“全功能”的玫瑰图函数,你可以这样做:

import itertools
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.collections import PatchCollection
import numpy as np

def main():
    azi = np.random.normal(20, 30, 100)
    z = np.cos(np.radians(azi + 45))

    plt.figure(figsize=(5,6))
    plt.subplot(111, projection='polar')
    coll = rose(azi, z=z, bidirectional=True)
    plt.xticks(np.radians(range(0, 360, 45)), 
               ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'])
    plt.colorbar(coll, orientation='horizontal')
    plt.xlabel('A rose diagram colored by a second variable')
    plt.rgrids(range(5, 20, 5), angle=290)

    plt.show()

def rose(azimuths, z=None, ax=None, bins=30, bidirectional=False, 
         color_by=np.mean, **kwargs):
    """Create a "rose" diagram (a.k.a. circular histogram).  

    Parameters:
    -----------
        azimuths: sequence of numbers
            The observed azimuths in degrees.
        z: sequence of numbers (optional)
            A second, co-located variable to color the plotted rectangles by.
        ax: a matplotlib Axes (optional)
            The axes to plot on. Defaults to the current axes.
        bins: int or sequence of numbers (optional)
            The number of bins or a sequence of bin edges to use.
        bidirectional: boolean (optional)
            Whether or not to treat the observed azimuths as bi-directional
            measurements (i.e. if True, 0 and 180 are identical).
        color_by: function or string (optional)
            A function to reduce the binned z values with. Alternately, if the
            string "count" is passed in, the displayed bars will be colored by
            their y-value (the number of azimuths measurements in that bin).
        Additional keyword arguments are passed on to PatchCollection.

    Returns:
    --------
        A matplotlib PatchCollection
    """
    azimuths = np.asanyarray(azimuths)
    if color_by == 'count':
        z = np.ones_like(azimuths)
        color_by = np.sum
    if ax is None:
        ax = plt.gca()
    ax.set_theta_direction(-1)
    ax.set_theta_offset(np.radians(90))
    if bidirectional:
        other = azimuths + 180
        azimuths = np.concatenate([azimuths, other])
        if z is not None:
            z = np.concatenate([z, z])
    # Convert to 0-360, in case negative or >360 azimuths are passed in.
    azimuths[azimuths > 360] -= 360
    azimuths[azimuths < 0] += 360
    counts, edges = np.histogram(azimuths, range=[0, 360], bins=bins)
    if z is not None:
        idx = np.digitize(azimuths, edges)
        z = np.array([color_by(z[idx == i]) for i in range(1, idx.max() + 1)])
        z = np.ma.masked_invalid(z)
    edges = np.radians(edges)
    coll = colored_bar(edges[:-1], counts, z=z, width=np.diff(edges), 
                       ax=ax, **kwargs)
    return coll

def colored_bar(left, height, z=None, width=0.8, bottom=0, ax=None, **kwargs):
    """A bar plot colored by a scalar sequence."""
    if ax is None:
        ax = plt.gca()
    width = itertools.cycle(np.atleast_1d(width))
    bottom = itertools.cycle(np.atleast_1d(bottom))
    rects = []
    for x, y, h, w in zip(left, bottom, height, width):
        rects.append(Rectangle((x,y), w, h))
    coll = PatchCollection(rects, array=z, **kwargs)
    ax.add_collection(coll)
    ax.autoscale()
    return coll

if __name__ == '__main__':
    main()

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何向极坐标图(玫瑰图)添加颜色条? 的相关文章

随机推荐