如何在密度图中使用matplotlib设置对数坐标轴?
我正在尝试使用matplotlib制作一个二维密度图(来自一些模拟数据)。我的x和y数据是一些量的以10为底的对数。请问我该如何设置坐标轴为对数坐标(并且有对数的次刻度)呢?
下面是我的代码示例:
import numpy as np
import matplotlib.pyplot as plt
Data = np.genfromtxt("data") # A 2-column data file
x = np.log10(Data[:,0])
y = np.log10(Data[:,1])
xmin = x.min()
xmax = x.max()
ymin = y.min()
ymax = y.max()
fig = plt.figure()
ax = fig.add_subplot(111)
hist = ax.hexbin(x,y,bins='log', gridsize=(30,30), cmap=cm.Reds)
ax.axis([xmin, xmax, ymin, ymax])
plt.savefig('plot.pdf')
3 个回答
1
@gcalmettes的回答提到了pyplot.hist
,而pyplot.hexbin
的用法稍微有点不同:
hexbin(x, y, C = None, gridsize = 100, bins = None,
xscale = 'linear', yscale = 'linear',
cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None,
edgecolors='none', reduce_C_function = np.mean, mincnt=None, marginals=True,
**kwargs)
你需要关注的是xscale
这个参数:
*xscale*: [ 'linear' | 'log' ]
Use a linear or log10 scale on the horizontal axis.
4
根据matplotlib.pyplot.hist的说明,如果你想在坐标轴上使用对数刻度,可以把'log'这个参数设置为'True'。
hist(x, bins=10, range=None, normed=False, cumulative=False,
bottom=None, histtype='bar', align='mid',
orientation='vertical', rwidth=None, log=False, **kwargs)
log:
If True, the histogram axis will be set to a log scale. If log is True and x is a 1D
array, empty bins will be filtered out and only the non-empty (n, bins, patches) will be
returned.
另外,还有一个pyplot.loglog函数,可以用来绘制x轴和y轴都使用对数刻度的图。
3
非常感谢大家的建议。
下面我分享一下我自己的解决方案。虽然这不算是一个“最小可工作示例”,但我已经把我的代码简化了不少!
简单来说,我使用了 imshow 来绘制“图像”(一个带有对数区间的二维直方图),并且我去掉了坐标轴。然后,我在第一个图的正上方画了一个第二个空白(透明)的图,只是为了得到对数坐标轴,因为 imshow 似乎不支持这个功能。说实话,这样做挺复杂的!
我的代码可能还远远不够好,因为我刚开始学习 python 和 matplotlib……
顺便提一下,我不使用 hexbin 有两个原因: 1) 对于像我这样的超大数据文件,它运行得太慢。 2) 在我使用的版本中,六边形的大小稍微有点大,也就是说它们会重叠,导致“像素”形状和大小不规则。 另外,我还想把直方图的数据以文本格式写入文件。
#!/usr/bin/python
# How to get log axis with a 2D colormap (i.e. an "image") ??
#############################################################
#############################################################
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import math
# Data file containing 2D data in log-log coordinates.
# The format of the file is 3 columns : x y v
# where v is the value to plotted for coordinate (x,y)
# x and y are already log values
# For instance, this can be a 2D histogram with log bins.
input_file="histo2d.dat"
# Parameters to set space for the plot ("bounding box")
x1_bb, y1_bb, x2_bb, y2_bb = 0.125, 0.12, 0.8, 0.925
# Parameters to set space for colorbar
cb_fraction=0.15
cb_pad=0.05
# Return unique values from a sorted list, will be required later
def uniq(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result
# Read data from file. The format of the file is 3 columns : x y v
# where v is the value to plotted for coordinate (x,y)
Data = np.genfromtxt(input_file)
x = Data[:,0]
y = Data[:,1]
v = Data[:,2]
# Determine x and y limits and resolution of data
x_uniq = np.array(uniq(np.sort(x)))
y_uniq = np.array(uniq(np.sort(y)))
x_resolution = x_uniq.size
y_resolution = y_uniq.size
x_interval_length = x_uniq[1]-x_uniq[0]
y_interval_length = y_uniq[1]-y_uniq[0]
xmin = x.min()
xmax = x.max()+0.5*x_interval_length
ymin = y.min()
ymax = y.max()+0.5*y_interval_length
# Reshape 1D data to turn it into a 2D "image"
v = v.reshape([x_resolution, y_resolution])
v = v[:,range(y_resolution-1,-1,-1)].transpose()
# Plot 2D "image"
# ---------------
# I use imshow which only work with linear axes.
# We will have to change the axes later...
axis_lim=[xmin, xmax, ymin, ymax]
fig = plt.figure()
ax = fig.add_subplot(111)
extent = [xmin, xmax, ymin, ymax]
img = plt.imshow(v, extent=extent, interpolation='nearest', cmap=cm.Reds, aspect='auto')
ax.axis(axis_lim)
# Make space for the colorbar
x2_bb_eff = (x2_bb-(cb_fraction+cb_pad)*x1_bb)/(1.0-(cb_fraction+cb_pad))
ax.set_position([x1_bb, y1_bb, x2_bb_eff-x1_bb, y2_bb-y1_bb])
position = ax.get_position()
# Remove axis ticks so that we can put log ticks on top
ax.set_xticks([])
ax.set_yticks([])
# Add colorbar
cb = fig.colorbar(img,fraction=cb_fraction,pad=cb_pad)
cb.set_label('Value [unit]')
# Add logarithmic axes
# --------------------
# Empty plot on top of previous one. Only used to add log axes.
ax = fig.add_subplot(111,frameon=False)
ax.set_xscale('log')
ax.set_yscale('log')
plt.plot([])
ax.set_position([x1_bb, y1_bb, x2_bb-x1_bb, y2_bb-y1_bb])
axis_lim_log=map(lambda x: 10.**x, axis_lim)
ax.axis(axis_lim_log)
plt.grid(b=True, which='major', linewidth=1)
plt.ylabel('Some quantity [unit]')
plt.xlabel('Another quantity [unit]')
plt.show()