如何在长度不等的轴上添加等间距刻度?[Python]
为了简化我的问题(虽然实际情况可能不完全这样,但我更喜欢简单明了的答案):
我有几个二维地图,显示的是矩形区域。我想在地图的坐标轴上加上刻度线,以显示地图上的距离(使用matplotlib,因为旧代码是用这个写的),但问题是这些区域的大小不一样。我想在坐标轴上放上清晰的刻度线,但地图的宽度和高度可以是任意的……
我来试着解释一下我的意思:假设我有一张区域的地图,大小是4.37公里 * 6.42公里。我希望在x轴上有0、1、2、3和4公里的刻度,在y轴上有0、1、2、3、4、5和6公里的刻度。然而,图像和坐标轴的范围稍微超出了4公里和6公里,因为这个区域的实际大小大于4公里 * 6公里。
刻度之间的间隔可以是固定的,比如1公里。然而,地图的大小变化很大(假设在5到15公里之间),而且这些都是浮动值。我的当前脚本知道区域的大小,并可以将图像缩放到正确的高宽比,但我该如何告诉它在哪里放置刻度呢?
可能已经有解决这个问题的方法,但因为我找不到合适的搜索词,所以不得不在这里问……
3 个回答
好的,我试了你们的版本,但不幸的是,我没能让它们工作,因为有一些缩放和PDF定位的东西让我(还有你们的代码建议)感到很困惑。不过,通过测试这些代码,我又学到了很多Python的知识,谢谢你们!
最后,我找到了一种解决方案,虽然不是特别精确,但满足了我的需求。下面是我怎么做的。
在我的版本中,一公里被一个合适的整数常量叫做STEP_PART来划分。STEP_PART越大,坐标轴的数值就越准确(但如果太大,坐标轴就会变得难以阅读)。比如说,如果STEP_PART是5,那么准确度就是1公里 / 5 = 200米,刻度就会标在每200米的位置。
STEP_PART = 5 # In the start of the program.
height = 6.42 # These are actually given elsewhere,
width = 4.37 # but just as example...
vHeight = range(0, int(STEP_PART*height), 1) # Make tick vectors, now in format
# 0, 1, 2... instead of 0, 0.2...
vWidth = range(0, int(STEP_PART*width), 1) # Should be divided by STEP_PART
# later to get right values.
为了避免生成太多的坐标轴标签(0, 1, 2...就够了,0, 0.2, 0.4...就太多了),我们把非整数的公里值替换成空字符串""。同时,我们把整数的公里值除以STEP_PART来得到正确的值。
for j in range(len(vHeight)):
if (j % STEP_PART != 0):
vHeight[j] = ""
else:
vHeight[j] = int(vHeight[j]/STEP_PART)
for i in range(len(vWidth)):
if (i % STEP_PART != 0):
vWidth[i] = ""
else:
vWidth[i] = int(vWidth[i]/STEP_PART)
之后,在创建图表和坐标轴后,刻度是这样放置的(以x轴为例)。这里,x是图片的实际宽度,通过shape()命令获得(我不太明白具体是怎么回事...我修改的代码里有很多缩放的内容)。
xt = np.linspace(0,x-1,len(vWidth)+1) # For locating those ticks on the same distances.
locs, labels = mpl.xticks(xt, vWidth, fontsize=9)
y轴也是这样重复处理。结果是一个图表,每200米有一个刻度,但数据标签只在整数公里值上。总之,这些坐标轴的准确度是200米,虽然不完全精确,但对我来说已经足够了。如果我能找到办法让整数刻度的大小变大,这个脚本会更好...
这段代码会让你在当前x轴的范围内,所有整数值的位置上都显示刻度。
from matplotlib import pylab as plt
import math
# get values for the axis limits (unless you already have them)
xmin,xmax = plt.xlim()
# get the outermost integer values using floor and ceiling
# (I need to convert them to int to avoid a DeprecationWarning),
# then get all the integer values between them using range
new_xticks = range(int(math.ceil(xmin)),int(math.floor(xmax)+1))
plt.xticks(new_xticks,new_xticks)
# passing the same argment twice here because the first gives the tick locations
# and the second gives the tick labels, which should just be the numbers
对y轴也可以用同样的方法。
顺便问一下:默认情况下,你会看到什么样的刻度呢?
只需要把刻度定位器设置为 matplotlib.ticker.MultipleLocator(x)
,其中 x
是你想要的间隔(比如在你上面的例子中是 1.0)。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
x = np.arange(20)
y = x * 0.1
fig, ax = plt.subplots()
ax.plot(x, y)
ax.xaxis.set_major_locator(MultipleLocator(1.0))
ax.yaxis.set_major_locator(MultipleLocator(1.0))
# Forcing the plot to be labeled with "plain" integers instead of scientific notation
ax.xaxis.set_major_formatter(FormatStrFormatter('%i'))
plt.show()
这样做的好处是,无论我们如何缩放或与图表互动,刻度标签始终会保持每隔 1 个单位标记一次。
