Calculate angle (degrees) in Python between line (with slope x) and horizontal
Asked Answered
H

2

13

I need to calculate the angle between a line and the horizontal. My high school maths seems to be failing me.

import matplotlib.pyplot as plt
import numpy as np

x = [8450.0, 8061.0, 7524.0, 7180.0, 8247.0, 8929.0, 8896.0, 9736.0, 9658.0, 9592.0]
y = range(len(x))

best_fit_line = np.poly1d(np.polyfit(y, x, 1))(y)

slope = (y[-1] - y[0]) / (x[-1] - x[0])
angle = np.arctan(slope)

print 'slope: ' + str(slope)
print 'angle: ' + str(angle)

plt.figure(figsize=(8,6))
plt.plot(x)
plt.plot(best_fit_line, '--', color='r')
plt.show()

The results are as follow:

slope: 0.00788091068301
angle: 0.00788074753125

slope of best-fit line

I need the angle between the horizontal and the red dotted line. Just by looking at it, it should probably be something between 30-45 degrees. What am I doing wrong?

* regarding slope = (y[-1] - y[0]) / (x[-1] - x[0]), I have also tried numpy.diff and scipy.stats.linregress, but no success either.

Hluchy answered 6/3, 2016 at 9:50 Comment(3)
You've forgotten to account for the scale of your chart.Intermarriage
Also, please use arctan2!Intermarriage
hi jtbandes, how do I account for the scale of my chart?Hluchy
M
30

The line goes in x-direction from 0 to 9 and in y-direction from 7500 to 9500. Therefore your slope is only 0.00788091068301 and not 0.57 for about 30°. Your calculations are correct, but better use arctan2:

angle = np.rad2deg(np.arctan2(y[-1] - y[0], x[-1] - x[0]))
Mountaineering answered 6/3, 2016 at 10:4 Comment(0)
G
3

Not so much an answer, but more of an explanation of the problems you faced.

  1. In your slope calculation, you are using the first and last data points in your list (x=[]), not the start and end of the line (best_fit_line).

so, you are using x[-1] = 9592.0, and x[0] = 8450.0 for the end and start of your vertical 'run' (y-axis / variable 'x').

In fact, your (red-dotted) line starts at 7500 and ends at 9500 as stated by Daniel above. this is shown in your variable 'best_fit_line' when calculated using your 'x' and 'y' data points.

>>>print('best_fit_line = ',best_fit_line)
>>>best_fit_line = [7581.47272727 7813.87878788 8046.28484848 8278.69090909 8511.0969697 8743.5030303  8975.90909091 9208.31515152 9440.72121212 9673.12727273]

to calculate the rise,run and slope of the 'red dotted line':

rise = best_fit_line[-1] - best_fit_line[0]
run = y[-1] - y[0]  # note: this is your variable for the x-axis
slope = rise / run
angle = np.rad2deg(np.arctan2(rise,run)) # in degrees from horizontal

which gives the following values when run using your data points and calculating the line slope:

best_fit_line = [7581.47272727 7813.87878788 8046.28484848 8278.69090909 8511.0969697 8743.5030303  8975.90909091 9208.31515152 9440.72121212 9673.12727273]
rise =  2091.654545454541
run = 9
slope:  232.40606060606012
angle =  89.75346845184545 degrees

which brings to the 2nd problem jtbandes pointed out when figuring out slope.

  1. Slope is meant to be determined using same units of measurement.

eg. slope of a ramp (best_fit_line) is the amount of rise (change in vertical height: y-axis) / the amount of run (length of horizontal distance ramp covers: x-axis)

A ramp which rises 1m over a length of 10m has a slope of 1/10 or 0.1 This is the same as saying a 10% incline or grade (0.1 * 100) Or 5.71 degrees up from the horizontal.

The problem with using the number of datapoints for the horizontal (run) is that you are not using the same units in the vertical (y-axis, your variable 'x') as the horizontal (x-axis, your variable 'y')

So, the calculated slope of 89.7 degrees of your line using your data points (rise) and number of data points (run) is correct, but the scale of the graph is off.

Run the code above again and look at the graph. the graph is roughly square when matplotlib displays it. On my monitor, the x-axis of your graph which is a total of 9 units (0 -> 9) long, measures 15cm. The y-axis which has a total of 3,000 units (7,000 -> 10,000) long, measures 12cm.

If the units were the same (days, $, km, etc) on both axis, then the line would have a slope of 232, or 89.7 degrees.

but the units are not the same, and matplotlib does not know this, it just displays a pretty graph within the confines of the figsize you asked it to print.

plt.figure(figsize=(8,6))

change the figsize numbers around and see what happens to the slope of the line when you run the code again.

8,1 displays an almost horizontal line.

1,6 displays an almost vertical line.

change back to 8,6 and re-size the graph manually using your mouse. Changing the aspect ratio of the graph changes the displayed slope of the line from almost vertical to almost horizontal and everything in between.

Answers what you did wrong, but probably doesn't help.

Need to find a way to compare 'apples' to 'apples' to have the slope of a line mean anything.

Ginny answered 21/6, 2022 at 9:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.