matplotlib 和 numpy - 直方图条形颜色和标准化
matplotlib and numpy - histogram bar color and normalization
所以我有两个问题:
1- 我有一个 2D 直方图,沿 x 和 y 轴有 1D 直方图。这些直方图汇总了它们各自的 x 和 y 值,而主直方图汇总了对数 x-y bin 中的值。代码如下。我使用 pcolormesh 生成 2D 直方图...并且生成了 vmin=1、vmax=14 范围内的颜色条...我将这些设置为常数,因为我在 a 上生成了一组这些图广泛的数据范围 -- 我希望颜色在它们之间保持一致。
我还想根据相同的归一化为一维直方图条着色。我已经设置了一个函数来进行映射,但它是顽固的线性——即使我为映射指定了 LogNorm。
我附上了一些图表,它们显示了我认为是一维直方图的线性比例。查看 10^4(或 10^6)左右的 x 轴直方图值...它们在颜色条中的 1/2 方向点着色,而不是在对数刻度点。
我做错了什么?
2- 我还想最终通过 bin 宽度(xrange 或 yrange)对 1D 直方图进行归一化。但是,我认为我不能直接在 matplotlib.hist 中进行。也许我应该使用 np hist,但我不知道如何使用对数刻度和彩色条绘制 matplotlib.bar 图(同样,映射我用于 2D hist 的颜色)。
代码如下:
#
# 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}$")
这里有几张图显示了一维轴直方图的着色问题
0) 您的代码非常好(而且很有帮助!)记录在案,但如果您将 trim 降至 最小值[=25],将会非常有帮助=] 工作示例。
1) colorHistOnHeight
中的fracs
数组不包含1e2的下界。
2)不同 LogNorm
颜色图的边界在整个代码中发生变化(例如 [1,8] 与 [2,9])。将这些参数设置为变量,并根据需要传递这些变量。
3)创建标量可映射对象matplotlib.cm.ScalarMappable
对象,使用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
这就是我如何能够通过对数 bin 宽度(以及我的模拟体积——一个标量值)对 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)
我通过 bin 宽度 (dbx) 和模拟的共动体积 (cmvol) 对直方图矩形的高度进行归一化。我认为这样做!
所以我有两个问题:
1- 我有一个 2D 直方图,沿 x 和 y 轴有 1D 直方图。这些直方图汇总了它们各自的 x 和 y 值,而主直方图汇总了对数 x-y bin 中的值。代码如下。我使用 pcolormesh 生成 2D 直方图...并且生成了 vmin=1、vmax=14 范围内的颜色条...我将这些设置为常数,因为我在 a 上生成了一组这些图广泛的数据范围 -- 我希望颜色在它们之间保持一致。
我还想根据相同的归一化为一维直方图条着色。我已经设置了一个函数来进行映射,但它是顽固的线性——即使我为映射指定了 LogNorm。
我附上了一些图表,它们显示了我认为是一维直方图的线性比例。查看 10^4(或 10^6)左右的 x 轴直方图值...它们在颜色条中的 1/2 方向点着色,而不是在对数刻度点。
我做错了什么?
2- 我还想最终通过 bin 宽度(xrange 或 yrange)对 1D 直方图进行归一化。但是,我认为我不能直接在 matplotlib.hist 中进行。也许我应该使用 np hist,但我不知道如何使用对数刻度和彩色条绘制 matplotlib.bar 图(同样,映射我用于 2D hist 的颜色)。
代码如下:
#
# 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}$")
这里有几张图显示了一维轴直方图的着色问题
0) 您的代码非常好(而且很有帮助!)记录在案,但如果您将 trim 降至 最小值[=25],将会非常有帮助=] 工作示例。
1) colorHistOnHeight
中的fracs
数组不包含1e2的下界。
2)不同 LogNorm
颜色图的边界在整个代码中发生变化(例如 [1,8] 与 [2,9])。将这些参数设置为变量,并根据需要传递这些变量。
3)创建标量可映射对象matplotlib.cm.ScalarMappable
对象,使用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
这就是我如何能够通过对数 bin 宽度(以及我的模拟体积——一个标量值)对 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)
我通过 bin 宽度 (dbx) 和模拟的共动体积 (cmvol) 对直方图矩形的高度进行归一化。我认为这样做!