Confusion with Matplotlib - python

I have a code:
import matplotlib.pyplot as plt
import numpy as np
# create 1000 equally spaced points between -10 and 10
x = np.linspace(-10, 10, 1000)
# calculate the y value for each element of the x vector
y = 2*x**2 - 3*x + 5
# draw a tangent line to parabola at x = -1
a = -7*x + 3
# draw a normal line to parabola at x = -1
b = 1/7*x + 71/7
fig, ax = plt.subplots()
ax.plot(x, y)
ax.plot(x, a)
ax.plot(x, b)
With this code I am trying to plot a parabola, a tangent line to parabola at x = -1 and a line that is normal to the tangent line at x = -1. The parabola and the the tangent line seemingly are plotting correctly, but the normal line always plots parallel to the x axis. What am I doing wrong? First picture(top) is what i get with matplotlib and the second picture is what it should look like (graphing calc.)

There are two things going wrong here. The first was answered Jody in his comment, the aspect ratio is off. The second problem is the fact that the y-axis value of the parabola goes all the way up to 250. This essentially "flattens" the look of the normal line. You can fix this by cutting of the y-axis using ax.set_ylim(). Try the following:
ax.set_aspect(1)
ax.set_ylim([-5, 20])
This will produce an image with that represents the lines correctly (i.e. you can see the right angles) but the effective aspect ratio of the output figure is not square or of the desired aspect ratio you might want. You can adjust this further by changing the linspace, for instance to x=np.linspace(-20,20,1000).

Related

plotting straight line over a scatterplot

y = dsleep["TotalTimeInBed"]
x = dsleep["TotalMinutesAsleep"]
plt.scatter(x,y,color = "red")
plt.plot([0,0],[961,961])
plt.title("relation between total minutes asleep and total time in bed")
plt.ylabel("total time in bed")
plt.xlabel("total minutes asleep")
I tried this code but it ignored the line and output was simply a scatterplot. How should I plot a straight line of slope 45 degree over my scatter plot for comparing my data points
Given your task, I presume you want the unity line. Then you should code plt.plot([0, 961], [0, 961])
Your line, as it is now, has zero length
What you are actually searching for is a trendline on your scatter plot. You can achieve that with np.polyfit()
import numpy as np
x = np.arange(20)
y = [num + random.choice(range(-5, 5)) for num in x]
plt.scatter(x, y)
z = np.poly1d(np.polyfit(x, y, deg=1)) # deg -> 1 - linear, 2 - quadratic ...
plt.plot(x,z(x),"r--")
plt.show()

Move the bottom of a curve without changing both ends

I'm having a curve as follows:
The curve is generated with the following code:
import matplotlib.pyplot as plt
import numpy as np
# normalize array
def min_max_scale_array(arr):
arr = np.array(arr)
return (arr - arr.min())/(arr.max()-arr.min())
x = np.linspace(-50,48,100)
y = x**2 + 2*x + 2
x = min_max_scale_array(x)
y = min_max_scale_array(y)
fig, ax = plt.subplots()
ax.plot(x, y)
How can I generate a new curve by moving only the bottom left (or right) and keeping both ends the same like this? Thank you!
Edit: any curve generating algorithm is appreciated, as long as it works!
One way of doing so is defining x,y as before, but applying a shift. The dotted line shows if you just shift it. But now at the top most y we don't want to shift it, so we'd like to weight the shifted version on the bottom (y=0) by 1 but on the top (y=1) by 0 such that we get a gradual interpolation. We can do this by multiplying the shift by (1-y):
a = 0.25 # how far to shift left
plt.plot(x, y, 'k')
plt.plot(x-a, y, 'k:')
plt.plot(x-a*(1-y), y, 'r')
plt.show()
Easiest solution: apply a sublinear transformation to x - a quadratic function will work.
x = x**2 # works because x is scaled to 0-1
ax.plot(x, y)
UPD: as requested, a scaling factor would look something like:
scaling_factor = 0.7
x = scaling_factor*(x**2) + (1-scaling_factor)*x

Plot and function with three variables in python

An equation which is represent as below
sin(x)*sin(y)*sin(z)+cos(x)*sin(y)*cos(z)=0
I know the code to plot function for z=f(x,y) using matplotlib but to plot above function I don’t know the code, but I tried MATLAB MuPad code which is as follows
Plot(sin(x)*sin(y)*sin(z)+cos(x)*sin(y)*cos(z),#3d)
This will be much easier if you can isolate z. Your equation is the same as sin(z)/cos(z) = -cos(x)*sin(y)/(sin(x)*sin(y)) so z = atan(-cos(x)*sin(y)/(sin(x)*sin(y))).
Please don't mistake me, but I think your given equation to plot can be reduced to a simple 2D plot.
sin(x)*sin(y)*sin(z)+cos(x)*sin(y)*cos(z) = 0
sin(y)[sin(x)*sin(z)+cos(x)*cos(z)] = 0
sin(y)*cos(x-z) = 0
Hence sin(y) = 0 or cos(x-z)=0
Hence y = n*pi (1) or x-z=(2*n + 1)pi/2
Implies, x = z + (2*n + 1)pi/2 (2)
For (1), it will be a straight line (the plot of y vs n) and in second case, you will get parallel lines which cuts x-axis at (2*n + 1)pi/2 and distance between two parallel lines would be pi. (Assuming you keep n constant).
Assuming, your y can't be zero, you could simplify the plot to a 2D plot with just x and z.
And answering your original question, you need to use mplot3d to plot 3D plots. But as with any graphing tool, you need values or points of x, y, z. (You can compute the possible points by programming). Then you feed those points to the plot, like below.
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(projection="3d")
xs = [] # X values
ys = [] # Y values
zs = [] # Z values
ax.plot3D(xs, ys, zs)
plt.show()

Matplotlib plot has slanted lines

I'm trying to plot projections of coordinates onto a line, but for some reason, Matplotlib is plotting the projections in a slightly slanted manner. Ideally, I would like the (blue) projections to be perpendicular to the (green) line. Here's an image of how it looks with sample data:
As you can see, the angles between the blue lines and the green line are slightly obtuse instead of right. I tried playing around with the rotation parameter to the annotate function, but this did not help. The code for this plot is below, although the data might look a bit different since the random generator is not seeded:
import numpy as np
import matplotlib.pyplot as plt
prefs = {'color':'purple','edgecolors':'black'}
X = np.dot(np.random.rand(2,2), np.random.rand(2,50)).T
pts = np.linspace(-1,1)
v1_m = 0.8076549717643662
plt.scatter(X[:,0],X[:,1],**prefs)
plt.plot(pts, [v1_m*x for x in pts], color='lightgreen')
for x,y in X:
# slope of connecting line
# y = mx+b
m = -np.reciprocal(v1_m)
b = y-m*x
# find intersecting point
zx = b/(v1_m-m)
zy = v1_m*zx
# draw line
plt.annotate('',(zx,zy),(x,y),arrowprops=dict(linewidth=2,arrowstyle='-',color='lightblue'))
plt.show()
The problem lies in the unequal axes which makes it look like they are not at a right angle. Use plt.axis('equal') to have equal axis spans on x- and y-axis and a square figure with equal height and width. plt.axis('scaled') works the same way. As pointed out by #CedricZoppolo, you should set the equal aspect ratios before plt.show(). As per docs, setting the aspect ratio to "equal" means
same scaling from data to plot units for x and y
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8))
# Your code here
plt.axis('equal')
plt.show()
Choosing a square figure is not necessary as it works also with rectangular figures as
fig = plt.figure(figsize=(8,6))
# Your code here
plt.axis('equal')
plt.show()
The blue lines not being perpendicular is due to axis not being equal.
You just need to add below line before plt.show()
plt.gca().set_aspect('equal')
Below you can see the resulted graph:

How to get the length of the entire 3d linear function in Python?

I create a graph of a logarithmic spiral in the form of a spring. I'm using the below parametric equations:
x=a*exp(b*th)*cos(th)
y=a*exp(b*th)*sin(th)
Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['legend.fontsize'] = 10
fig = plt.figure(figsize=(10,10))
ax = fig.gca(projection='3d')
a=0.6
b=0.2
th=np.linspace(0, 25, 1000)
x=a*np.exp(b*th)*np.cos(th)
y=a*np.exp(b*th)*np.sin(th)
z = np.linspace(0, 2, len(th))
ax.plot(x, y, z)
ax.plot(x, y, zdir='z', zs=0)
ax.plot(x, z, zdir='y', zs=100)
ax.set_xlim([-100, 100])
ax.set_ylim([-100, 100])
ax.set_zlim([-0, 2.5])
plt.show()
This gives me the following output:
Can I get the length of the entire spiral? Is it possible to mark the position of a point on the graph, which lies in the distance (for example, 5), starting from the beginning of the graph in point (x,y)=(0,0), and pull out these coordinates? I will be grateful for any tips.
Pythagoras works as well in 3 dimensions. Any line segment si has a length of
si = sqrt(xi**2+yi**2+zi**2)
Hence,
a=0.6
b=0.2
th=np.linspace(0, 25, 1000)
x=a*np.exp(b*th)*np.cos(th)
y=a*np.exp(b*th)*np.sin(th)
z = np.linspace(0, 2, len(th))
diffs = np.sqrt(np.diff(x)**2+np.diff(y)**2+np.diff(z)**2)
length = diffs.sum()
print length # prints 451.011712939
the length of the line is 451.
Note that the extention of the line in z direction is much smaller than in x and y, so we might as well leave out z completely, the error when doing so is 0.025 or 0.006%.
The other aim is to find the point on the line where the line is some length l=5 long. Of course since we work with numerical data, we do not find the point where it is exactly 5 units long, but e.g. rather that point where the length is smaller than 5 but closest to it. We can calculate the index at which point that happens,
l = 5 # length to find coordinate of
cumlenth = np.cumsum(diffs)
s = np.abs(np.diff(np.sign(cumlenth-l))).astype(bool)
c = np.argwhere(s)[0][0]
and then find that index in the original arrays.
print c # index of coordinate, here 192
print x[c], y[c], z[c] # 0.144750230412 -1.56183108038 0.384384384384
We might then label that point with a scatter,
ax.scatter([x[c]], [y[c]], [z[c]], color="crimson")

Categories