简体   繁体   English

Matplotlib,添加多行文本。 添加可以跟随曲线的文本

[英]Matplotlib, adding text with more than one line. Adding text that can follow the curve

I have added text to a plot, coded in each line, and then adjusted it look decent, increase or decrease the width, or change the placement. 我已经将文本添加到绘图中,在每行中编码,然后调整它看起来像样,增加或减少宽度,或更改位置。 However, is there a way to have Python know where you want the text and how you want it set? 但是,有没有办法让Python知道你想要文本的位置以及你想要它的设置方式? Then I could add the text and Python would work out the details. 然后我可以添加文本,Python将计算出细节。

For example, take a look at the image below: 例如,看看下面的图片:

在此输入图像描述

In the figure, I have 3 lines of text in the upper left corner and one line above the line of the plot. 在图中,我在左上角有3行文字,在图的线上有一行。

I had to adjust the 3 lines to get a decent spacing. 我不得不调整3条线以获得合适的间距。 This wasnt a difficult task but it would be easy if I could say here is the text, here is the location, and then Python stacks it with proper spacing. 这不是一项艰巨的任务,但如果我能说这里是文本,这里就是位置,那将很容易,然后Python以适当的间距堆叠它。

For the lone line, I had to make adjustments so it wasn't on the line and lower the line. 对于单独的线路,我必须进行调整,以便它不在线上并降低线路。 For this case, is is possible to tell python I would like the text above the plot and 80% down the line? 对于这种情况,有可能告诉python我想在情节上方的文字和80%下线?

I am used to LaTeX where I can make this adjustments without hard coding the coordinates. 我习惯了LaTeX ,我可以在没有硬编码坐标的情况下进行调整。 The advantage are 优点是

(1) if I want to change the location, I can change the percentage shift and not the coordinate.

(2) if the line is angled, the text will adjust to the line.

The advantage to (2) is that I am trying to put text on the top portion of the figure that slopes upward with the line. (2)的优点是我试图在图的顶部放置文字,该文字的顶部向上倾斜。

Can this be done or am I asking to much? 可以这样做,还是我要求多少? If so, how do I do this? 如果是这样,我该怎么做?

Here is the code that implements the figure: 以下是实现该图的代码:

import numpy as np
import pylab

r1 = 1  #  AU Earth                                                                 
r2 = 1.524  #  AU Mars                                                              
deltanu = 75 * np.pi / 180  #  angle in radians                                     
mu = 38.86984154054163

c = np.sqrt(r1 ** 2 + r2 ** 2 - 2 * r1 * r2 * np.cos(deltanu))   
s = (r1 + r2 + c) / 2
am = s / 2


def g(a):
    alphag = 2* np.pi - 2 * np.arcsin(np.sqrt(s / (2 * a)))
    return (np.sqrt(a ** 3 / mu)
            * (alphag - betag - (np.sin(alphag) - np.sin(betag))))


def f(a):
    alpha = 2 * np.arcsin(np.sqrt(s / (2 * a)))
    beta = 2 * np.arcsin(np.sqrt((s - c) / (2 * a)))
    return (np.sqrt(a **3 / mu) * (alpha - betag - (np.sin(alpha)
                                                      - np.sin(betag))))


betag = -2 * np.arcsin(np.sqrt((s - c) / (2 * a)))
a = np.linspace(am, 2, 500000)

a = np.linspace(am, 2, 500000)

fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot(a, f(a), color = '#000000')
ax.plot(a, g(a), color = '#000000')
pylab.xlim((0.9, 2))
pylab.ylim((0, 2))

pylab.xlabel('Semi-major Axis $a$ in AU')
pylab.ylabel('Time of Flight in Years')
pylab.text(1, 1.8, '$r_1 = 1.0$ AU', fontsize = 11, color = 'r')
pylab.text(1, 1.7, '$r_2 = 1.524$ AU', fontsize = 11, color = 'r')
pylab.text(1, 1.6, '$\\Delta \\nu = 75^{\\circ}$', fontsize = 11,
           color = 'r')
pylab.text(1.75, 0.35, '$\\alpha = \\alpha_0$', fontsize = 11,
           color = 'r')
pylab.savefig('lamberttransferties.eps', format = 'eps')
pylab.show()

You can use line separators \\n : 您可以使用行分隔符\\n

 pylab.text(1, 1.5, '$r_1 = 1.0$ AU\n' +\
                    '$r_2 = 1.524$ AU\n' +\
                    '$\\Delta \\nu = 75^{\\circ}$', fontsize = 11, color = 'r')

pylab.text() uses data coordinates by default, but you can use relative positions (0,0) to the lower-left and (1,1) to the upper-right, passing the parameter transform . pylab.text()默认使用数据坐标,但您可以使用相对位置(0,0)到左下角和(1,1)到右上角,传递参数transform See this example: 看这个例子:

pylab.text(0.6, 0.75, 'using axis coords', transform=ax.transAxes)

The parameters: verticalalignment and horizontalalignment can also help you tremendously. 参数: verticalalignmenthorizontalalignment也可以帮助你。 Suppose you want to place a texts at the very corners: 假设您想要在角落放置文本:

pylab.text(1.,1.,'top-right', transform=ax.transAxes,
           horizontalalignment='right', verticalalignment='top')

pylab.text(0.,0.,'bottom-left', transform=ax.transAxes,
           horizontalalignment='left', verticalalignment='bottom')

在此输入图像描述

To automatically calculate an angle to the text depending on your data you can do the following approach: 要根据您的数据自动计算文本角度,您可以执行以下方法:

  • detect the data closest point 检测数据最近点
  • find the a sequence near the closest point and fit a curve using this sequence (the example below uses a fourth order curve) 找到最近点附近的序列并使用此序列拟合曲线(下面的示例使用四阶曲线)
  • calculate the derivative at the point where you want the text placed 计算您希望放置文本的点处的导数
  • correct the direvative with ax.get_data_ratio() OBS: not needed if ax.axis('scaled') is used, for example 使用ax.get_data_ratio() OBS更正指示:如果使用ax.axis('scaled')则不需要,例如

This algorithm can be implemented as follows: 该算法可以实现如下:

def rtext(line,x,y,s, **kwargs):
    from scipy.optimize import curve_fit
    xdata,ydata = line.get_data()
    dist = np.sqrt((x-xdata)**2 + (y-ydata)**2)
    dmin = dist.min()
    TOL_to_avoid_rotation = 0.3
    if dmin > TOL_to_avoid_rotation:
        r = 0.
    else:
        index = dist.argmin()
        xs = xdata[ [index-2,index-1,index,index+1,index+2] ]
        ys = ydata[ [index-2,index-1,index,index+1,index+2] ]
        def f(x,a0,a1,a2,a3):
            return a0 + a1*x + a2*x**2 + a3*x**3
        popt, pcov = curve_fit(f, xs, ys, p0=(1,1,1,1))
        a0,a1,a2,a3 = popt
        ax = pylab.gca()
        derivative = (a1 + 2*a2*x + 3*a3*x**2)
        derivative /= ax.get_data_ratio()
        r = np.arctan( derivative )
    return pylab.text(x, y, s, rotation=np.rad2deg(r), **kwargs)

The following test example shows how to use it: 以下测试示例显示了如何使用它:

ax = pylab.subplot(111)
thetas = np.linspace(0,6*np.pi,1000)
i = np.arange(len(thetas))
xdata = (1. + (3.-1.)*i/len(thetas))*np.cos(thetas)
ydata = (1. + (3.-1.)*i/len(thetas))*np.sin(thetas)
ax.plot(xdata, ydata, color = 'b')
pylab.xlabel('x')
pylab.ylabel('y')
for x, y in zip(xdata,ydata)[::25]:
    rtext(ax.lines[0], x, y,
          '$\\alpha = \\alpha_0$', fontsize = 14, color = 'r',
          horizontalalignment='center', verticalalignment='center')

在此输入图像描述

Changing verticalalignment='bottom' 改变verticalalignment='bottom'

在此输入图像描述

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

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