Plotting an intersection when graph touches an x-axis - python

So I'm making a Graphical Calculator, which shows an intersection between graphs and axes. I found the method from Intersection of two graphs in Python, find the x value to work most of the time, however trying to plot the x-axis intersection of x**2 as such
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-5, 5, 0.01)
g = (x) ** 2
plt.plot(x, g, '-')
idx = np.argwhere(np.diff(np.sign(g))).flatten()
plt.plot(x[idx], g[idx], 'ro')
plt.show()
doesn't put the dot at (0,0) point. I assumed it has something to do with the fact that 0 is not in g, so the grpah it doesn't actually pass through the point exactly and instead gets really close to it. So I experimented with changing idx to
epsilon = 0.0001
# or another real small number
idx = g < epsilon
Unfortunately, that only seemed to make a lot of points near the actual x-intercept, instead of just one.

You are close, instead, I just search for where the absolute value of the derivative is at a minimum such that
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-5, 5, 0.01)
g = x**2
plt.plot(np.abs(np.diff(g)))
plt.show()
which shows that the minimum should be at index 500:
Then all you need to do is return the index of the minimum value with argmin and plot that point
idx = np.argmin(np.abs(np.diff(g)))
plt.plot(x, g, '-')
plt.scatter(x[idx],g[idx])
plt.show()
You'll need to modify the idx variable to return multiple roots, but for the question you posted, this should be sufficient.

Related

Adjusting Plotted Values of Contour Plots

I'm making contour plots which are basically analytical or numerical solutions to a fluid dynamic system. I don't think the technical stuff really matters too much, but here's my plots. The first plot is the numerical (Matrix system) solution, and the second plot is the nice closed form (single forumla) solution.
As can be seen, my second plot has the bubbles on the right hand side. Looking at the legend/scale, I have negative values. I'd like to not have negative values, or not plot them, although I'm not sure how to adjust this within my code. I've spent some time looking into how to adjust the z values to being positive only, but I can't seem to get it. I'll drop my plot code, and then my nice closed form function that is used in the plot.
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.special as sp1
from mpl_toolkits.mplot3d import Axes3D
def v(r,z,gamma):
a=r*(1-z/gamma)
sums = 0
for n in range(1,26):
sums += ((sp1.iv(1(n*np.pi*r)/gamma))/(n*sp1.iv(1(n*np.pi)/gamma)))*np.sin(n*np.pi*z/gamma)
return a-(2/np.pi)*sums
def plot_contour(a, filename=None, zlabel='v(r,z)',cmap=plt.cm.gnuplot):
fig = plt.figure(figsize=(5,4))
ax = fig.add_subplot(111)
x = np.arange(a.shape[0])
y = np.arange(a.shape[1])
X, Y = np.meshgrid(x, y)
Z = a[X, Y]
cset = ax.contourf(X, Y, Z, 20, cmap=cmap)
ax.set_xlabel('r')
ax.set_ylabel('z')
ax.set_title('\u0393=2.5')
ax.axis('off')
ax.set_aspect(1)
cb = fig.colorbar(cset, shrink=0.5, aspect=5)
cb.set_label(zlabel)
if filename:
fig.savefig(filename,dpi=1600)
plt.close(fig)
return filename
else:
return ax
...
plot_contour(v1, 'gamma25e+1')
This is all the necessary code. The rest of it is the matrix solution stuff, which is just a bunch of linear algebra. Any help on what I need to add or adjust to prevent negative values from showing up on the second plot. It should look exactly like the first.
I've spent some time looking into how to adjust the z values to being positive only
what you can do depends greatly on what you want to do with the results below zero, if your sole purpose is to make the points below zero show as zero, you can simply make them zero, however that would be showing a wrong result.
x = np.arange(a.shape[0])
y = np.arange(a.shape[1])
X, Y = np.meshgrid(x, y)
Z = a[X, Y]
Z[Z < 0] = 0
another solution is to subtract the minimum value of you data so that the minimum value of the result is 0.
x = np.arange(a.shape[0])
y = np.arange(a.shape[1])
X, Y = np.meshgrid(x, y)
Z = a[X, Y]
Z -= np.amin(Z)

Plot stochastic trajectories deviations from 'real' path using a colormesh in matplotlib (Python)

Hi I created a program that will create deviations from a real trajectory, it is complicated and I do not have a simple example unfortunately.
It calculates a path with stochastic initial conditions from the real path and does this for x iterations, the goal is to show that the deviations become larger at greater times.
The real path and the deviations are showed below.
However I want to show that the deviations become greater the longer in time we are. Ofcourse I could just calculate the variance and plot mean+var and mean-var at each time step but I was wondering if I could plot something like this, using hist2d
You see that the blocks are not as smooth as a like and this is not that great to use.
Then I went and looked at python's kde and created the following.
This is also not preferable as I think it bins more points at the minima and maxima. Also it is 'too smeared out'. Especially in the beginning, all the points are the same so I want there just to be a straight line to really show that the deviations start later on.
I guess my question is; is what I want even possible and what package/command should I use. I haven't found what I am looking for on other questions. Or has anyone a suggestion to nicely show what I want in a any other way?
Here is an idea plotting multiple curves with transparency on top of each other:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 200)
for _ in range(1000):
plt.plot(x, np.sin(x * np.random.normal(1, 0.1)) * np.random.normal(1, 0.1), color='r', alpha=0.02)
plt.plot(x, np.sin(x), color='b')
plt.margins(x=0)
plt.show()
Another option creates a 2d histogram:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 200)
all_curves = np.array([np.sin(x * np.random.normal(1, 0.1)) * np.random.normal(1, 0.1) for _ in range(100)])
plt.hist2d(x=np.tile(x, all_curves.shape[0]), y=all_curves.ravel(), bins=(100, 100), cmap='inferno')
plt.show()
Still another approach would use fill_between (as suggested by #bramb) between confidence intervals:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 200)
all_curves = np.array([np.sin(x * np.random.normal(1, 0.1)) * np.random.normal(1, 0.1) for _ in range(1000)])
confidence_interval1 = 95
confidence_interval2 = 80
confidence_interval3 = 50
for ci in [confidence_interval1, confidence_interval2, confidence_interval3]:
low = np.percentile(all_curves, 50 - ci / 2, axis=0)
high = np.percentile(all_curves, 50 + ci / 2, axis=0)
plt.fill_between(x, low, high, color='r', alpha=0.2)
plt.plot(x, np.sin(x), color='b')
plt.margins(x=0)
plt.show()
You could use something like the matplotlib.pyplot.fill_between method. It fills everything between y1 (max) and y2 (min) for a given (common) x array. You would then be able to accentuate that the filled region keeps enlarging with increasing x value.
However, this would require you to find the minimal and maximal value of your deviations at each time point and save these to two separate arrays. The exact method of doing this will depend on how you are storing these individual runs.
In case they are separate lists / arrays, you can convert these to a numpy matrix / pandas dataframe and use the minimum / maximum methods along the relevant axis.

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()

How to eliminate connecting line when plotting unwrapped function

I am trying to figure out how to eliminate the spurious "connecting line" that occurs when a function is "chopped" up so that it is plotted only in a single interval. For example, suppose I have an angular function that extends from zero to 10 pi (or perhaps even larger) and I want to plot this function only in the range 0 to 2 pi. I can use a modulo operation to fix the data, but if I plot it I get a line that connects from 2 pi back to zero, which I do not want to plot. Here is some code that shows what I am talking about.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 10*np.pi, 1000)
y = t + np.sin(t)
t2 = t%(2*np.pi)
plt.plot(t2, y)
plt.show()
The resulting plot has a series of horizontal lines that I don't want (see image below). I have done some research on this and have not found any simple way of dealing with this situation, but it seems like this would be somewhat common.
Any ideas?
By the way, I am dealing with a pretty large data set, so I can't very well do anything "by hand."
In general, you can use a NaN to insert a break into a point. In the particular case that you have shown, you can use np.diff to identify the discontinuities and set the t2 value at those locations to NaN resulting in the desired breaks
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 10*np.pi, 1000)
y = t + np.sin(t)
t2 = t % (2*np.pi)
# Compute the difference between successive t2 values
diffs = np.append(np.diff(t2), 0)
# Find the differences that are greater than pi
discont_indices = np.abs(diffs) > np.pi
# Set those t2 values to NaN
t2[discont_indices] = np.nan
plt.plot(t2, y)
plt.show()
You can approach the same problem in a slightly different way: Create a x-mesh from 0 to 2*pi and then add an offset for y for plotting the five different curves. The key here is to exclude the last point of t using the index [0:-1] in order to avoid the continuation line.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 2*np.pi, 1000)
t = (t%(2*np.pi))[0:-1]
for i in range(5):
y = t+ np.sin(t) + i*2*np.pi
plt.plot(t, y, 'b')

Non-overlapping scatter plot labels using matplotlib

I have a scatter plot with a number of points. Each point has a string associated with it (varying in length) that I'd like to supply a label, but I can't fit them all. So I'd like to iterating through my data points from most to least important, and in each case apply a label only if it would not overlap as existing label. The strings vary in length. One of the commenters mentions solving a knapsack problem to find an optimal solution. In my case the greedy algorithm (always label the most important remaining point that can be labeled without overlap) would be a good start and might suffice.
Here's a toy example. Could I get Python to label only as many points as it can without overlapping?
import matplotlib.pylab as plt, numpy as np
npoints = 100
xs = np.random.rand(npoints)
ys = np.random.rand(npoints)
plt.scatter(xs, ys)
labels = iter(dir(np))
for x, y, in zip(xs, ys):
# Ideally I'd condition the next line on whether or not the new label would overlap with an existing one
plt.annotate(labels.next(), xy = (x, y))
plt.show()
You can draw all the annotates first, and then use a mask array to check the overlap and use set_visible() to hide. Here is an example:
import numpy as np
import pylab as pl
import random
import string
import math
random.seed(0)
np.random.seed(0)
n = 100
labels = ["".join(random.sample(string.ascii_letters, random.randint(4, 10))) for _ in range(n)]
x, y = np.random.randn(2, n)
fig, ax = pl.subplots()
ax.scatter(x, y)
ann = []
for i in range(n):
ann.append(ax.annotate(labels[i], xy = (x[i], y[i])))
mask = np.zeros(fig.canvas.get_width_height(), bool)
fig.canvas.draw()
for a in ann:
bbox = a.get_window_extent()
x0 = int(bbox.x0)
x1 = int(math.ceil(bbox.x1))
y0 = int(bbox.y0)
y1 = int(math.ceil(bbox.y1))
s = np.s_[x0:x1+1, y0:y1+1]
if np.any(mask[s]):
a.set_visible(False)
else:
mask[s] = True
the output:
Just as an additional note: for my code to work, I had to add and additional renderer=fig.canvas.get_renderer() parameter to the get_window_extent() method rather than the default get_window_extent(renderer=None). I think the necessity of this additional parameter specification depends on the operating system. https://github.com/matplotlib/matplotlib/issues/10874

Categories