Python matplotlib和numpy-直方图条颜色和规格化

Python matplotlib和numpy-直方图条颜色和规格化,python,numpy,matplotlib,histogram,histogram2d,Python,Numpy,Matplotlib,Histogram,Histogram2d,所以我有两个问题: 1-我有一个沿x&y轴的二维直方图,其中包含1D直方图。这些直方图将各自的x和y值相加,而主直方图将对数x-y箱中的值相加。代码如下。我已经使用pcolormesh生成了2D直方图。。。我已经生成了一个范围为vmin=1,vmax=14的颜色条。。。我将这些设置为常量,因为我在一个大的数据范围内生成了一组这些图——我希望它们的颜色保持一致 我还想根据相同的标准化给1D直方图条上色。我已经设置了一个函数来进行映射,但是它是顽固的线性的——即使我为映射指定了LogNorm 我附上

所以我有两个问题:

1-我有一个沿x&y轴的二维直方图,其中包含1D直方图。这些直方图将各自的x和y值相加,而主直方图将对数x-y箱中的值相加。代码如下。我已经使用pcolormesh生成了2D直方图。。。我已经生成了一个范围为vmin=1,vmax=14的颜色条。。。我将这些设置为常量,因为我在一个大的数据范围内生成了一组这些图——我希望它们的颜色保持一致

我还想根据相同的标准化给1D直方图条上色。我已经设置了一个函数来进行映射,但是它是顽固的线性的——即使我为映射指定了LogNorm

我附上了两个图,显示了我认为一维直方图的线性比例。查看10^4(或10^6)左右的x轴直方图值。。。它们在颜色栏中的1/2方向点上色,而不是在对数刻度点上色

我做错了什么

2-我还希望最终通过箱子宽度(X范围或Y范围)对1D直方图进行规范化。但是,我不认为我可以直接在matplotlib.hist中执行此操作。也许我应该使用np hist,但是我不知道如何使用对数比例和彩色条(同样,映射我用于2D hist的颜色)进行matplotlib.bar打印

代码如下:

#
# 20 Oct 2015
# Rick Sarmento
#
# Purpose:
#  Reads star particle data and creates phase plots
#  Place histograms of x and y axis along axes
#  Uses pcolormesh norm=LogNorm(vmin=1,vmax=8)
#
# Method:
#  Main plot uses np.hist2d then takes log of result
#
# Revision history
#

# ##########################################################
# Generate colors for histogram bars based on height
# This is not right! 
# ##########################################################
def colorHistOnHeight(N, patches):
    # we need to normalize the data to 0..1 for the full
    # range of the colormap
    print("N max: %.2lf"%N.max())
    fracs = np.log10(N.astype(float))/9.0 # normalize colors to the top of our scale
    print("fracs max: %.2lf"%fracs.max())
    norm = mpl.colors.LogNorm(2.0, 9.0)
    # NOTE this color mapping is different from the one below.
    for thisfrac, thispatch in zip(fracs, patches):
        color = mpl.cm.jet(thisfrac)
        thispatch.set_facecolor(color)

    return

# ##########################################################
# Generate a combo contour/density plot
# ##########################################################
def genDensityPlot(x, y, mass, pf, z, filename, xaxislabel):
    """

    :rtype : none
    """
    nullfmt = NullFormatter()

    # Plot location and size
    fig = plt.figure(figsize=(20, 20))
    ax2dhist = plt.axes(rect_2dhist)
    axHistx = plt.axes(rect_histx)
    axHisty = plt.axes(rect_histy)

    # Fix any "log10(0)" points...
    x[x == np.inf] = 0.0
    y[y == np.inf] = 0.0
    y[y > 1.0] = 1.0 # Fix any minor numerical errors that could result in y>1

    # Bin data in log-space
    xrange = np.logspace(minX,maxX,xbins)
    yrange = np.logspace(minY,maxY,ybins)
    # Note axis order: y then x
    # H is the binned data... counts normalized by star particle mass
    # TODO -- if we're looking at x = log Z, don't weight by mass * f_p... just mass!
    H, xedges, yedges = np.histogram2d(y, x, weights=mass * (1.0 - pf), # We have log bins, so we take 
                                        bins=(yrange,xrange))

    # Use the bins to find the extent of our plot
    extent = [yedges[0], yedges[-1], xedges[0], xedges[-1]]

    # levels = (5, 4, 3) # Needed for contours only... 

    X,Y=np.meshgrid(xrange,yrange) # Create a mess over our range of bins

    # Take log of the bin data
    H = np.log10(H)
    masked_array = np.ma.array(H, mask=np.isnan(H))  # mask out all nan, i.e. log10(0.0)

    # Fix colors -- white for values of 1.0. 
    cmap = copy.copy(mpl.cm.jet)
    cmap.set_bad('w', 1.)  # w is color, for values of 1.0

    # Create a plot of the binned
    cax = (ax2dhist.pcolormesh(X,Y,masked_array, cmap=cmap, norm=LogNorm(vmin=1,vmax=8)))
    print("Normalized H max %.2lf"%masked_array.max())

    # Setup the color bar
    cbar = fig.colorbar(cax, ticks=[1, 2, 4, 6, 8])
    cbar.ax.set_yticklabels(['1', '2', '4', '6', '8'], size=24)
    cbar.set_label('$log\, M_{sp, pol,\odot}$', size=30)

    ax2dhist.tick_params(axis='x', labelsize=22)
    ax2dhist.tick_params(axis='y', labelsize=22)
    ax2dhist.set_xlabel(xaxislabel, size=30)
    ax2dhist.set_ylabel('$log\, Z_{pri}/Z$', size=30)

    ax2dhist.set_xlim([10**minX,10**maxX])
    ax2dhist.set_ylim([10**minY,10**maxY])
    ax2dhist.set_xscale('log')
    ax2dhist.set_yscale('log')
    ax2dhist.grid(color='0.75', linestyle=':', linewidth=2)

    # Generate the xy axes histograms
    ylims = ax2dhist.get_ylim()
    xlims = ax2dhist.get_xlim()

    ##########################################################
    # Create the axes histograms
    ##########################################################
    # Note that even with log=True, the array N is NOT log of the weighted counts
    # Eventually we want to normalize these value (in N) by binwidth and overall
    # simulation volume... but I don't know how to do that.
    N, bins, patches = axHistx.hist(x, bins=xrange, log=True, weights=mass * (1.0 - pf))
    axHistx.set_xscale("log")
    colorHistOnHeight(N, patches)
    N, bins, patches = axHisty.hist(y, bins=yrange, log=True, weights=mass * (1.0 - pf),
                                    orientation='horizontal')
    axHisty.set_yscale('log')
    colorHistOnHeight(N, patches)

    # Setup format of the histograms
    axHistx.set_xlim(ax2dhist.get_xlim())  # Match the x range on the horiz hist
    axHistx.set_ylim([100.0,10.0**9])       # Constant range for all histograms
    axHistx.tick_params(labelsize=22)
    axHistx.yaxis.set_ticks([1e2,1e4,1e6,1e8])
    axHistx.grid(color='0.75', linestyle=':', linewidth=2)

    axHisty.set_xlim([100.0,10.0**9])       # We're rotated, so x axis is the value
    axHisty.set_ylim([10**minY,10**maxY])  # Match the y range on the vert hist
    axHisty.tick_params(labelsize=22)
    axHisty.xaxis.set_ticks([1e2,1e4,1e6,1e8])
    axHisty.grid(color='0.75', linestyle=':', linewidth=2)

    # no labels
    axHistx.xaxis.set_major_formatter(nullfmt)
    axHisty.yaxis.set_major_formatter(nullfmt)

    if z[0] == '0': z = z[1:]
    axHistx.set_title('z=' + z, size=40)

    plt.savefig(filename + "-z_" + z + ".png", dpi=fig.dpi)
    #    plt.show()
    plt.close(fig) # Release memory assoc'd with the plot
    return


# ##########################################################
# ##########################################################
##
## Main program
##
# ##########################################################
# ##########################################################
import matplotlib as mpl
import matplotlib.pyplot as plt
#import matplotlib.colors as colors # For the colored 1d histogram routine
from matplotlib.ticker import NullFormatter
from matplotlib.colors import LogNorm
from matplotlib.ticker import LogFormatterMathtext
import numpy as np
import copy as copy

files = [
    "18.00",
    "17.00",
    "16.00",
    "15.00",
    "14.00",
    "13.00",
    "12.00",
    "11.00",
    "10.00",
    "09.00",
    "08.50",
    "08.00",
    "07.50",
    "07.00",
    "06.50",
    "06.00",
    "05.50",
    "05.09"
]
# Plot parameters - global
left, width = 0.1, 0.63
bottom, height = 0.1, 0.63
bottom_h = left_h = left + width + 0.01

xbins = ybins = 100

rect_2dhist = [left, bottom, width, height]
rect_histx = [left, bottom_h, width, 0.15]
rect_histy = [left_h, bottom, 0.2, height]

prefix = "./"
# prefix="20Sep-BIG/"
for indx, z in enumerate(files):
    spZ = np.loadtxt(prefix + "spZ_" + z + ".txt", skiprows=1)
    spPZ = np.loadtxt(prefix + "spPZ_" + z + ".txt", skiprows=1)
    spPF = np.loadtxt(prefix + "spPPF_" + z + ".txt", skiprows=1)
    spMass = np.loadtxt(prefix + "spMass_" + z + ".txt", skiprows=1)

    print ("Generating phase diagram for z=%s" % z)
    minY = -4.0
    maxY = 0.5
    minX = -8.0
    maxX = 0.5
    genDensityPlot(spZ, spPZ / spZ, spMass, spPF, z,
                   "Z_PMassZ-MassHistLogNorm", "$log\, Z_{\odot}$")
    minX = -5.0
    genDensityPlot((spZ) / (1.0 - spPF), spPZ / spZ, spMass, spPF, z,
                   "Z_PMassZ1-PGF-MassHistLogNorm", "$log\, Z_{\odot}/f_{pol}$")
这里有几个图显示了1D轴直方图的着色问题

0)您的代码有很好的文档记录(而且非常有用!),但是将代码精简为一个最小的工作示例将非常有帮助。
1)
colorHistOnHeight
中的
fracs
数组不包括1e2的下限。
2) 不同的
LogNorm
颜色映射的边界在代码中不断变化(例如[1,8]与[2,9])。将这些参数设置为变量,并根据需要传递这些变量。
3) 创建标量可映射对象,以使用
to_rgba
方法将标量值直接转换为颜色


希望其中一个能帮上忙

我想出了如何使用上面的建议:
matplotlib.sm.ScalarMappable
。就这样!贴图与我的颜色条比例匹配

# ##########################################################
# Generate colors for histogram bars based on height
# Method:
#  Take log of the histogram values (weighted counts)..
#  Create a LogNorm mapping between 1->9
#  Use the norm to map scalar values between 1 & 9 to rgb
# ##########################################################
def colorHistOnHeight(N, patches):
    cleanN = np.ma.masked_where(N == 0.0, N)
    fracs  = np.log10(cleanN) # normalize colors to the top of our scale
    norm   = mpl.colors.LogNorm(vmin=1.0, vmax=9.0)
    sm     = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
    sm.set_clim([1.0,9.0])
    for thisfrac, thispatch in zip(fracs, patches):
        color = sm.to_rgba(thisfrac)
        thispatch.set_facecolor(color)
    return 

下面是我如何通过对数箱子宽度(以及我的模拟量——标量值)来规范化matplotlib hist的。但是,有人请检查我的解决方案

yrange = np.logspace(minY,maxY,ybins)
N, bins, patches = axHisty.hist(y, bins=yrange, log=True, weights=mass * (1.0 - pf))

widths = np.diff(bins)
for item,dbx in zip(patches,widths):
    item.set_height(item.get_height()/dbx/cmvol)
我通过模拟的箱子宽度(dbx)和移动体积(cmvol)来规范化直方图矩形的高度。我想这就够了


是的。类似于,
(log10(N)-2.0)/9.0
啊,谢谢。。。我刚刚意识到我创造了规范,但不要使用它!1) 哼。。。对。我想我现在正在将范围的底部映射到区间2->9。2) 这是一个输入错误--我确实把范围设置为相同的。3) 我会调查的。谢谢我搞不懂如何使用ScalarMapable。。。如果你能给我举个例子,我会很感激的。。。创建了ScalarMapable对象,但我认为frac已经是一个标量了——不。我必须弄清楚如何将thisfrac对象转换为对数范围内的标量值,我想将颜色映射到…@earnric很抱歉让你挂断,但我很高兴你能工作!