【问题标题】:Manually draw log-spaced tick marks and labels in matplotlib在 matplotlib 中手动绘制对数间隔的刻度线和标签
【发布时间】:2018-08-24 08:55:36
【问题描述】:

我经常发现自己在绘图时使用对数单位,例如在对数据进行分箱或创建等高线图之前获取 np.log10(x) 的数据。问题是,当我想要使绘图呈现时,轴是丑陋的对数单位,并且刻度线是均匀间隔的。

如果我让 matplotlib 进行所有转换,即通过设置 ax.set_xaxis('log'),那么我会得到非常漂亮的轴,但是我不能对我的数据执行此操作,因为它是例如已经以日志单位分类。我可以手动更改刻度标签,但这不会使刻度间距成为对数。我想我也可以手动指定每个小刻度的位置,使其具有日志间距,但这是实现这一目标的唯一方法吗?有点繁琐,如果有更好的方法就好了。

为了具体起见,这里有一个情节:

我希望刻度标签为10^x10^y(所以'1' 是'10',2 是'100' 等等),并且我希望将小刻度绘制为ax.set_xaxis('log')会画他们。

编辑:为了更具体,假设情节是从图像生成的,如下所示:

import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()

x_range = [-5,3] # log10 units
y_range = [-55, -45] # log10 units

p = plt.imshow(img,extent=x_range+y_range)

plt.show()

我们要做的就是按照我的描述改变坐标轴的外观。

编辑 2:好的,ImportanceOfBeingErnest 的回答非常聪明,但它比我想要的更具体到图像。我还有另一个例子,这次是分箱数据。也许他们的技术仍然适用于此,尽管我不清楚是否是这种情况。

import numpy as np
import pandas as pd
import datashader as ds
from matplotlib import pyplot as plt
import scipy.stats as sps

v1 = sps.lognorm(loc=0, scale=3, s=0.8)
v2 = sps.lognorm(loc=0, scale=1, s=0.8)
x = np.log10(v1.rvs(100000))
y = np.log10(v2.rvs(100000))
x_range=[np.min(x),np.max(x)]
y_range=[np.min(y),np.max(y)]

df = pd.DataFrame.from_dict({"x": x, "y": y})

#------ Aggregate the data ------
cvs = ds.Canvas(plot_width=30, plot_height=30, x_range=x_range, y_range=y_range)
agg = cvs.points(df, 'x', 'y')

# Create contour plot
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(agg, extent=x_range+y_range)
ax.set_xlabel("x")
ax.set_ylabel("y")

plt.show()

【问题讨论】:

  • 这还不清楚。为什么要首先记录数据?将其保留并使用ax.set_xaxis('log'),据我所知,您将获得所需的情节。
  • 因为,例如,我已经使用其他工具以对数单位绘制了绘图。在这种特殊情况下,我有一个基本上来自数据着色器的图像,我需要在 matplotlib 中很好地绘制轴。也许一个好的技巧是获取未记录的数据范围并让 matplotlib 用set_xaxis('log') 绘制空轴,然后将图像添加到其中,或其他东西。
  • 您介意创建一个minimal reproducible example 的问题吗?否则这都是猜测。
  • 好的,我加了一个

标签: python matplotlib


【解决方案1】:

这个问题的一般答案可能在这篇文章中给出:

Can I mimic a log scale of an axis in matplotlib without transforming the associated data?

但是这里一个简单的选择可能是缩放轴的内容,然后将轴设置为对数刻度。

A.图片

您可以在对数刻度上绘制图像,但在对数单位中使所有像素大小相同。不幸的是,imshow 不允许使用这种图像 (any more),但可以使用 pcolormesh 来达到此目的。

import numpy as np
import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()

extx = [-5,3]     # log10 units
exty = [-45, -55] # log10 units
x = np.logspace(extx[0],extx[-1],img.shape[1]+1)
y = np.logspace(exty[0],exty[-1],img.shape[0]+1)
X,Y = np.meshgrid(x,y)

c =  img.reshape((img.shape[0]*img.shape[1],img.shape[2]))/255.0
m = plt.pcolormesh(X,Y,X[:-1,:-1], color=c, linewidth=0)
m.set_array(None)

plt.gca().set_xscale("log")
plt.gca().set_yscale("log")

plt.show()

B.轮廓

同样的概念也可以用于等高线图。

import numpy as np
from matplotlib import pyplot as plt

x = np.linspace(-1.1,1.9)
y = np.linspace(-1.4,1.55)
X,Y = np.meshgrid(x,y)
agg = np.exp(-(X**2+Y**2)*2)


fig, ax  = plt.subplots()

plt.gca().set_xscale("log")
plt.gca().set_yscale("log")


exp = lambda x: 10.**(np.array(x))

cf = ax.contourf(exp(X), exp(Y),agg, extent=exp([x.min(),x.max(),y.min(),y.max()]))

ax.set_xlabel("x")
ax.set_ylabel("y")

plt.show()

【讨论】:

  • 嗯好吧,这很酷,但比我想要的更具体到图像案例。我添加了另一个示例,将数据与除 numpy.histogram 之外的其他内容进行分箱(即,您不能只为其提供日志箱边缘),然后制作结果的等高线图。像你建议的东西还能在那里工作吗?
  • 哈哈,当然,一旦我为你解决了这个问题,你就会决定你还需要一些不同的东西。
  • 嗯,我希望有一个通用的解决方案来解决将轴更改为漂亮的对数单位的问题,而不是针对这种或那种类型的绘图进行微调。
  • 无论如何,我用轮廓解决方案更新了答案。我很好奇接下来会发生什么。 ;-)
  • 我实际上忘记了我已经回答了一个类似的问题。这可能是您对通用解决方案的理解(尽管实施起来也很麻烦);我更新了答案。
猜你喜欢
  • 2014-08-03
  • 2021-06-14
  • 2018-11-22
  • 2014-02-27
  • 2013-07-17
  • 1970-01-01
  • 2014-03-22
  • 2021-09-05
相关资源
最近更新 更多