Reputation: 938
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
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.
Upvotes: 13
Views: 30711
Reputation: 83
Not so much an answer, but more of an explanation of the problems you faced.
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.
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.
Upvotes: 3
Reputation: 42748
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]))
Upvotes: 30