  # numpy.arctan () in Python

arctan | NumPy | Python Methods and Functions

Parameters :

`  array:  [array_like] elements are in radians.  out:  [array_like] array of same shape as x. `

Note:

The convention is to return the angle z whose real part lies in [- pi / 2, pi / 2].

Return:

` An array with inverse tangent of x for all x ie array elements. The values ​​are in the closed interval [-pi / 2, pi / 2]. `

Code # 1: Work

 ` # Python program explaining ` ` # arctan () function `   ` import ` ` numpy as np ` ` `  ` in_array ` ` = ` ` [` ` 0 , 1 , 0.3 , - 1 ] `` print ( "Input array: " , in_array)    arctan_Values ​​ = np.arctan (in_array ) print ( "Inverse Tangent values : " ,   arctan_Values) `

Output:

` Input array: [0, 1, 0.3, -1] Inverse Tangent values: [0. 0.78539816 0.29145679 -0.78539816] `

Code # 2: Graphical representation

 ` # Show Python program ` ` # Graphical view ` ` # arctan () functions `   ` import ` ` numpy as np ` ` import ` ` matplotlib.pyplot as plt `   ` in_array ` ` = ` ` np.linspace (` ` - ` ` np.pi, np.pi, 12 ) `` out_array1 = np.tan (in_array) out_array2 = np.arctan (in_array)   print ( "in_array:" , in_array) print ( "out_array with tan:" , out_ar ray1) print ( "out_arraywith arctan : " , out_array1)    # red for numpy.arccos () plt.plot (in_array, out_array1, color = `blue` , marker = "*" )   plt.plot (in_array, out_array2, color = ` red` , marker = "o" )   plt.title ( "blue: numpy.tan () red: numpy.arctan ()" ) plt.xlabel ( "X" ) plt.ylabel ( "Y" ) plt.show () `

Output:

` in_array: [-3.14159265 -2.57039399 -1.99919533 -1.42799666 -0.856798 -0.28559933 0.28559933 0.856798 1.42799666 1.99919533 2.57039399 3.14159265] out_array with tan6.4680 01 2.18969456e + 00 -6.95515277e + 00 -1.15406152e + 00 -2.93626493e-01 2.93626493e-01 1.15406152e + 00 6.95515277e + 00 -2.18969456e + 00 -6.42660977e-01 -1.22464680e-16] out_array : [1.22464680e-16 6.42660977e-01 2.18969456e + 00 -6.95515277e + 00 -1.15406152e + 00 -2.93 626493e-01 2.93626493e-01 1.15406152e + 00 6.95515277e + 00 -2.18969456e + 00 -6.42660977e-01 -1.22464680e-16] `

arcsin.html#numpy.arccos>https://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.arcsin. html # numpy.arccos
,

## numpy.arctan () in Python: StackOverflow Questions

Update: User cphyc has kindly created a Github repository for the code in this answer (see here), and bundled the code into a package which may be installed using `pip install matplotlib-label-lines`.

Pretty Picture: In `matplotlib` it"s pretty easy to label contour plots (either automatically or by manually placing labels with mouse clicks). There does not (yet) appear to be any equivalent capability to label data series in this fashion! There may be some semantic reason for not including this feature which I am missing.

Regardless, I have written the following module which takes any allows for semi-automatic plot labelling. It requires only `numpy` and a couple of functions from the standard `math` library.

## Description

The default behaviour of the `labelLines` function is to space the labels evenly along the `x` axis (automatically placing at the correct `y`-value of course). If you want you can just pass an array of the x co-ordinates of each of the labels. You can even tweak the location of one label (as shown in the bottom right plot) and space the rest evenly if you like.

In addition, the `label_lines` function does not account for the lines which have not had a label assigned in the `plot` command (or more accurately if the label contains `"_line"`).

Keyword arguments passed to `labelLines` or `labelLine` are passed on to the `text` function call (some keyword arguments are set if the calling code chooses not to specify).

## Issues

• Annotation bounding boxes sometimes interfere undesirably with other curves. As shown by the `1` and `10` annotations in the top left plot. I"m not even sure this can be avoided.
• It would be nice to specify a `y` position instead sometimes.
• It"s still an iterative process to get annotations in the right location
• It only works when the `x`-axis values are `float`s

## Gotchas

• By default, the `labelLines` function assumes that all data series span the range specified by the axis limits. Take a look at the blue curve in the top left plot of the pretty picture. If there were only data available for the `x` range `0.5`-`1` then then we couldn"t possibly place a label at the desired location (which is a little less than `0.2`). See this question for a particularly nasty example. Right now, the code does not intelligently identify this scenario and re-arrange the labels, however there is a reasonable workaround. The labelLines function takes the `xvals` argument; a list of `x`-values specified by the user instead of the default linear distribution across the width. So the user can decide which `x`-values to use for the label placement of each data series.

Also, I believe this is the first answer to complete the bonus objective of aligning the labels with the curve they"re on. :)

label_lines.py:

``````from math import atan2,degrees
import numpy as np

#Label line with line2D label data
def labelLine(line,x,label=None,align=True,**kwargs):

ax = line.axes
xdata = line.get_xdata()
ydata = line.get_ydata()

if (x < xdata) or (x > xdata[-1]):
print("x label location is outside data range!")
return

#Find corresponding y co-ordinate and angle of the line
ip = 1
for i in range(len(xdata)):
if x < xdata[i]:
ip = i
break

y = ydata[ip-1] + (ydata[ip]-ydata[ip-1])*(x-xdata[ip-1])/(xdata[ip]-xdata[ip-1])

if not label:
label = line.get_label()

if align:
#Compute the slope
dx = xdata[ip] - xdata[ip-1]
dy = ydata[ip] - ydata[ip-1]
ang = degrees(atan2(dy,dx))

#Transform to screen co-ordinates
pt = np.array([x,y]).reshape((1,2))
trans_angle = ax.transData.transform_angles(np.array((ang,)),pt)

else:
trans_angle = 0

#Set a bunch of keyword arguments
if "color" not in kwargs:
kwargs["color"] = line.get_color()

if ("horizontalalignment" not in kwargs) and ("ha" not in kwargs):
kwargs["ha"] = "center"

if ("verticalalignment" not in kwargs) and ("va" not in kwargs):
kwargs["va"] = "center"

if "backgroundcolor" not in kwargs:
kwargs["backgroundcolor"] = ax.get_facecolor()

if "clip_on" not in kwargs:
kwargs["clip_on"] = True

if "zorder" not in kwargs:
kwargs["zorder"] = 2.5

ax.text(x,y,label,rotation=trans_angle,**kwargs)

def labelLines(lines,align=True,xvals=None,**kwargs):

ax = lines.axes
labLines = []
labels = []

#Take only the lines which have labels other than the default ones
for line in lines:
label = line.get_label()
if "_line" not in label:
labLines.append(line)
labels.append(label)

if xvals is None:
xmin,xmax = ax.get_xlim()
xvals = np.linspace(xmin,xmax,len(labLines)+2)[1:-1]

for line,x,label in zip(labLines,xvals,labels):
labelLine(line,x,label,align,**kwargs)
``````

Test code to generate the pretty picture above:

``````from matplotlib import pyplot as plt
from scipy.stats import loglaplace,chi2

from labellines import *

X = np.linspace(0,1,500)
A = [1,2,5,10,20]
funcs = [np.arctan,np.sin,loglaplace(4).pdf,chi2(5).pdf]

plt.subplot(221)
for a in A:
plt.plot(X,np.arctan(a*X),label=str(a))

labelLines(plt.gca().get_lines(),zorder=2.5)

plt.subplot(222)
for a in A:
plt.plot(X,np.sin(a*X),label=str(a))

labelLines(plt.gca().get_lines(),align=False,fontsize=14)

plt.subplot(223)
for a in A:
plt.plot(X,loglaplace(4).pdf(a*X),label=str(a))

xvals = [0.8,0.55,0.22,0.104,0.045]
labelLines(plt.gca().get_lines(),align=False,xvals=xvals,color="k")

plt.subplot(224)
for a in A:
plt.plot(X,chi2(5).pdf(a*X),label=str(a))

lines = plt.gca().get_lines()
l1=lines[-1]
labelLine(l1,0.6,label=r"\$Re=\${}".format(l1.get_label()),ha="left",va="bottom",align = False)
labelLines(lines[:-1],align=False)

plt.show()
``````

First find the difference between the start point and the end point (here, this is more of a directed line segment, not a "line", since lines extend infinitely and don"t start at a particular point).

``````deltaY = P2_y - P1_y
deltaX = P2_x - P1_x
``````

Then calculate the angle (which runs from the positive X axis at `P1` to the positive Y axis at `P1`).

``````angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
``````

But `arctan` may not be ideal, because dividing the differences this way will erase the distinction needed to distinguish which quadrant the angle is in (see below). Use the following instead if your language includes an `atan2` function:

``````angleInDegrees = atan2(deltaY, deltaX) * 180 / PI
``````

EDIT (Feb. 22, 2017): In general, however, calling `atan2(deltaY,deltaX)` just to get the proper angle for `cos` and `sin` may be inelegant. In those cases, you can often do the following instead:

1. Treat `(deltaX, deltaY)` as a vector.
2. Normalize that vector to a unit vector. To do so, divide `deltaX` and `deltaY` by the vector"s length (`sqrt(deltaX*deltaX+deltaY*deltaY)`), unless the length is 0.
3. After that, `deltaX` will now be the cosine of the angle between the vector and the horizontal axis (in the direction from the positive X to the positive Y axis at `P1`).
4. And `deltaY` will now be the sine of that angle.
5. If the vector"s length is 0, it won"t have an angle between it and the horizontal axis (so it won"t have a meaningful sine and cosine).

EDIT (Feb. 28, 2017): Even without normalizing `(deltaX, deltaY)`:

• The sign of `deltaX` will tell you whether the cosine described in step 3 is positive or negative.
• The sign of `deltaY` will tell you whether the sine described in step 4 is positive or negative.
• The signs of `deltaX` and `deltaY` will tell you which quadrant the angle is in, in relation to the positive X axis at `P1`:
• `+deltaX`, `+deltaY`: 0 to 90 degrees.
• `-deltaX`, `+deltaY`: 90 to 180 degrees.
• `-deltaX`, `-deltaY`: 180 to 270 degrees (-180 to -90 degrees).
• `+deltaX`, `-deltaY`: 270 to 360 degrees (-90 to 0 degrees).

An implementation in Python using radians (provided on July 19, 2015 by Eric Leschinski, who edited my answer):

``````from math import *
def angle_trunc(a):
while a < 0.0:
a += pi * 2
return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
deltaY = y_landmark - y_orig
deltaX = x_landmark - x_orig
return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
``````

All tests pass. See https://en.wikipedia.org/wiki/Unit_circle

Sorry EMS, but I actually just got another response from the matplotlib mailling list (Thanks goes out to Benjamin Root).

The code I am looking for is adjusting the savefig call to:

``````fig.savefig("samplefigure", bbox_extra_artists=(lgd,), bbox_inches="tight")
#Note that the bbox_extra_artists must be an iterable
``````

This is apparently similar to calling tight_layout, but instead you allow savefig to consider extra artists in the calculation. This did in fact resize the figure box as desired.

``````import matplotlib.pyplot as plt
import numpy as np

plt.gcf().clear()
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
fig = plt.figure(1)
ax.plot(x, np.sin(x), label="Sine")
ax.plot(x, np.cos(x), label="Cosine")
ax.plot(x, np.arctan(x), label="Inverse tan")
handles, labels = ax.get_legend_handles_labels()
lgd = ax.legend(handles, labels, loc="upper center", bbox_to_anchor=(0.5,-0.1))
text = ax.text(-0.2,1.05, "Aribitrary text", transform=ax.transAxes)
ax.set_title("Trigonometry")
ax.grid("on")
fig.savefig("samplefigure", bbox_extra_artists=(lgd,text), bbox_inches="tight")
``````

This produces: The intent of this question was to completely avoid the use of arbitrary coordinate placements of arbitrary text as was the traditional solution to these problems. Despite this, numerous edits recently have insisted on putting these in, often in ways that led to the code raising an error. I have now fixed the issues and tidied the arbitrary text to show how these are also considered within the bbox_extra_artists algorithm.

Last time I checked it, the scipy `__init__` method executes a

``````from numpy import *
``````

so that the whole numpy namespace is included into scipy when the scipy module is imported.

The `log10` behavior you are describing is interesting, because both versions are coming from numpy. One is a `ufunc`, the other is a `numpy.lib` function. Why scipy is preferring the library function over the `ufunc`, I don"t know off the top of my head.

EDIT: In fact, I can answer the `log10` question. Looking in the scipy `__init__` method I see this:

``````# Import numpy symbols to scipy name space
import numpy as _num
from numpy import oldnumeric
from numpy import *
from numpy.random import rand, randn
from numpy.fft import fft, ifft
from numpy.lib.scimath import *
``````

The `log10` function you get in scipy comes from `numpy.lib.scimath`. Looking at that code, it says:

``````"""
Wrapper functions to more user-friendly calling of certain math functions
whose output data-type is different than the input data-type in certain
domains of the input.

For example, for functions like log() with branch cuts, the versions in this
module provide the mathematically valid answers in the complex plane:

>>> import math
>>> from numpy.lib import scimath
>>> scimath.log(-math.exp(1)) == (1+1j*math.pi)
True

Similarly, sqrt(), other base logarithms, power() and trig functions are
correctly handled.  See their respective docstrings for specific examples.
"""
``````

It seems that module overlays the base numpy ufuncs for `sqrt`, `log`, `log2`, `logn`, `log10`, `power`, `arccos`, `arcsin`, and `arctanh`. That explains the behavior you are seeing. The underlying design reason why it is done like that is probably buried in a mailing list post somewhere.