简体   繁体   English

通过在matplotlib中使用Latex,xticklabel中的正值和负值之间的差异

[英]Difference between positive and negative values in xticklabel by using Latex in matplotlib

I set usetext=True in matplotlib to use Latex for managing the font layout in my plot. 我在matplotlib中设置了usetext = True来使用Latex来管理我的绘图中的字体布局。 Now the space between the x-axis and the xticklabel is different for positive and negative values as shown in the picture. 现在,x轴和xticklabel之间的空间对于正值和负值是不同的,如图所示。

Is there a possibility to get the same space? 是否有可能获得相同的空间?

Example: 例:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-10.0, 10.0, 100)
s = np.cos(t)

plt.rc('text', usetex=True)

plt.rc('font', family='serif', size=30)
plt.plot(t, s)

plt.show()

在此输入图像描述

It's unfortunate that this bug has been around for so long, and that most answers here thought the question was about horizontal alignment rather than vertical spacing. 不幸的是,这个bug已经存在了很长时间,而且这里的大多数答案都认为问题是关于水平对齐而不是垂直间距。

The bug is known, and there is a patch to fix it, but it hasn't been merged, and simply been listed as needing review for the last year and a half. 该错误是已知的,并且有一个补丁来修复它,但它尚未合并,并且在过去的一年半中被列为需要审核。

Interestingly, it's a bug that is rather confusing. 有趣的是,这是一个令人困惑的错误。 The problem initially arises in that, for some reason, TeX gives a minus sign (and several other math symbols) a descender value, suggesting that it extends goes below the baseline. 最初出现的问题是,由于某些原因,TeX给出一个减号(和其他几个数学符号)的下降值,表明它延伸到了基线以下。 dvipng, used to create the labels in raster backends, just crops to the visible, so this wouldn't matter. dvipng,用于在光栅后端创建标签,只是裁剪到可见,所以这没关系。 But to keep things aligned that actually do have descenders, matplotlib has a separate system, dviread, that reads values from the dvi file itself. 但为了保持对齐实际上具有下行的东西,matplotlib有一个单独的系统dviread,它从dvi文件本身读取值。 This picks up the odd descender value. 这会获得奇怪的下降值。 Then matplotlib's alignment system, thinking that part of the png is supposed to be below the baseline, moves it down. 然后matplotlib的对齐系统,认为png的一部分应该低于基线,将其向下移动。

The fix in the bug report is very simple, but I'm not entirely sure how it works. 错误报告中的修复非常简单,但我不完全确定它是如何工作的。 I've tried it, however, and it does work. 不过,我已经尝试过了,它确实有效。

Of course, this question was asked in 2013, so it doesn't seem like the patch is going to be applied any time soon. 当然,这个问题是在2013年提出的,所以看起来这个补丁似乎不会很快应用。 So what are the alternatives? 那有什么选择呢?

One easy option is to just ignore the alignment issue when working, but, when doing presentation output, use the pdf backend. 一个简单的选择是在工作时忽略对齐问题,但是,在进行演示输出时,请使用pdf后端。 If you want images, you can always use other software to convert. 如果您想要图像,可以随时使用其他软件进行转换。 The pdf backend does not suffer from the same problem, as it handles TeX portions completely differently. pdf后端不会遇到同样的问题,因为它完全不同地处理TeX部分。

The other option is to just tweak the position of negative xticks. 另一种选择是调整负xticks的位置。 In theory, you could pull the exact tweak out from _get_layout, which will give you the descender value, but I'm not sure how to convert the values. 从理论上讲,你可以从_get_layout中拉出精确的调整,这将给你下降值,但我不知道如何转换值。 So here's an example of just eyeing the alignment: 所以这是一个只关注对齐的例子:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-10.0, 10.0, 100)
s = np.cos(t)

i = [-10,-5,0,5,10]

plt.rc('text', usetex=True)

plt.rc('font', family='serif', size=30)
plt.plot(t, s)

for n,l in zip(*plt.xticks()):
    if n<0: l.set_position((0,0.014))

This is kind of a "hacky" way of doing it and I'm not sure why it fixes it, but using FormatStrFormatter from matplotlib.ticker seems to fix it. 这是一种“hacky”方式,我不知道为什么要修复它,但是使用matplotlib.ticker FormatStrFormatter似乎可以修复它。 Only difference is the font weight looks bold. 唯一的区别是字体粗细看起来很粗。 I can't seem to change that, but maybe you can work with that. 我似乎无法改变这一点,但也许你可以解决这个问题。

import numpy as np
import matplotlib.ticker as mtick
import matplotlib.pyplot as plt

t = np.linspace(-10.0, 10.0, 100)
s = np.cos(t)

plt.rc('text', usetex=True)
plt.rc('font', family='serif', size=30)

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(t, s)

fmtx = '%.0f%%'
fmty = '%.1f%%'
xticks = mtick.FormatStrFormatter(fmtx)
yticks = mtick.FormatStrFormatter(fmty)
ax1.xaxis.set_major_formatter(xticks)
ax1.yaxis.set_major_formatter(yticks)
plt.show()

在此输入图像描述

The way I fix minor problems like this is to save the plot as an SVG and then edit it in my favourite Vector Graphics program (such as InkScape ). 我修复这样的小问题的方法是将绘图保存为SVG,然后在我最喜欢的矢量图形程序(如InkScape )中编辑它。 This will allow you to select the individual parts of the plot and move them while preserving the good quality vector graphics. 这将允许您选择绘图的各个部分并移动它们,同时保留优质矢量图形。

so (sadly) I only managed to come up with another "hacky" solution but I thought I'd share: 所以(遗憾的是)我只设法提出另一个“hacky”解决方案,但我想我会分享:

import numpy as np
import matplotlib as mplib
import matplotlib.pyplot as plt
import types

# set up few plot options
plt.rc('text', usetex=True)
plt.rc('font', family='serif', size=30)

# get some data to plot
t = np.linspace(-10.0, 10.0, 100)
s = np.cos(t)

# plot things
fig = plt.figure()
ax  = fig.add_subplot(111)
ax.plot(t, s)

# define and register onresize callback to handle new ticks that appear on resize
def onresize(event):
    SHIFT = 0.5
    for label in ax.get_xticklabels():
        #print(label.get_text())  # test to see if new ticks appear as expected
        label.set_ha('right')
        label.customShiftValue = SHIFT
        label.set_x = types.MethodType(
                lambda self, x: mplib.text.Text.set_x(self, x+self.customShiftValue),
                label)

cid = fig.canvas.mpl_connect('resize_event', onresize)

# hold plot open
plt.show(block=True)

produces the following output 产生以下输出

alt_img


So let me explain what I did here. 那么让我解释一下我在这里做了什么。 Initially, I iterated the xticklabels in the for -loop and used label.set_ha('right') to horizontally align all xticklabels to the right. 最初,我迭代for -loop中的xticklabels并使用label.set_ha('right')将所有xticklabels水平对齐到右边。 However, the labels were all padded and I failed to shrink their bounding box (even not sure if it works that way!). 然而,标签都是填充的,我没有缩小它们的边界框(甚至不确定它是否以这种方式工作!)。 Hence, I decided to register an onresize callback function because on resize new xticklabels may appear. 因此,我决定注册一个onresize回调函数,因为在调整大小时可能会出现新的xticklabels。 Then, I augmented the set_x function (code is borrowed from: https://stackoverflow.com/a/33688795/2402281 , credits to them). 然后,我增加了set_x函数(代码来自: httpsset_x ,归功于他们)。 The only thing that could be done to not manually tweak the SHIFT variable is to get the canvas size on do some meaningful computation that is in range [0.4, 0.6] which appear to be good offset values (empirically tested...). 不能手动调整SHIFT变量唯一可以做的就是让画布大小进行一些有意义的计算,这些计算在[0.4,0.6]范围内,这似乎是好的偏移值(经验测试......)。

Even though it is just another "hacky" solution proposal, it may help in finding a suitable answer! 即使它只是另一个“hacky”解决方案提案,它可能有助于找到合适的答案! I hope it helps. 我希望它有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM