This question already has answers here:
Omit joining lines in matplotlib plot e.g. y = tan(x)
(4 answers)
Closed 5 years ago.
Is there an easy way to plot a function which tends to infinity in the positive and negative as a single plot, without the plot joining both ends of the positive and negative?
For example, plotting y=1/x using this code gives the resulting plot:
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return 1/x
fx_name = r'$f(x)=\frac{1}{x}$'
x=np.setdiff1d(np.linspace(-10,10,100),[0]) #to remove the zero
y=f(x)
plt.plot(x, y, label=fx_name)
plt.legend(loc='upper left')
plt.show()
But I would like this output, which I achieve by plotting two separate domains:
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return 1/x
fx_name = r'$f(x)=\frac{1}{x}$'
xfn=np.setdiff1d(np.linspace(-10,0,100),[0])
xfp=np.setdiff1d(np.linspace(0,10,100),[0])
yfn=f(xfn)
yfp=f(xfp)
yf = plt.plot(xfn, yfn, label=fx_name)
plt.plot(xfp, yfp, color=yf[0].get_color())
plt.legend(loc='upper left')
plt.show()
Is there are short-cut?
Many thanks.
Solution
Include zero in the domain array, and suppress the divide by zero. This forces one element of the returned co-domain array as "inf", and "inf" is not plotted.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
with np.errstate(divide='ignore', invalid='ignore'):
return 1/x
fx_name = r'$f(x)=\frac{1}{x}$'
x=np.linspace(-10,10,101)
y=f(x)
plt.plot(x, y, label=fx_name)
plt.legend(loc='upper left')
plt.show()
I prefer this method since it avoids manual manipulation of the array, and can be easily reused for other functions which share the same domain (ex. y=1/(x+2)). Thank you all for contributions.
Actually you want to include x = 0 because this results in y = nan, forming a gap in the plot.
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return 1/x
fx_name = r'$f(x)=\frac{1}{x}$'
# using 101 steps results in in array including the value 0
x=np.linspace(-10,10,101)
# f(0) = nan -> a nan value creates a gap
y=f(x)
plt.plot(x, y, label=fx_name)
plt.legend(loc='upper left')
plt.show()
Not necessary easier as your workaround, but you could insert a 'nan' element at the index where the sign flips, for example:
idx = np.argmax(np.diff(np.sign(y)))+1
x = np.insert(x, idx, np.nan)
y = np.insert(y, idx, np.nan)
The 'nan' causes Matplotlib to interrupt the line.
based on Rutger Kassies ideas:
n_points = 100
x=np.setdiff1d(np.linspace(-10,10,n_points),[0]) #to remove the zero
y=f(x)
y[n_points//2-1:n_points//2+1] = np.nan
use your original plot an set the points around 0 to np.nan. that way too many points get set to None but it's symmetric.
you could also setup your linspace to includ 0 such that f(x) = np.nan: n_points = 101. (this answer and 2 comments stated that right before i did... please credit there).
Related
I need to build a graph from a group of files. My script below and output.
import sys
import matplotlib.pyplot as plt
import matplotlib.image as img
import pandas as pd
import numpy as np
import glob
df=ReadMultPRYFiles(f"/data/beegfs/projects/XOMG2201-FLD/databases/orient/RL53744.00/RL*RP_15*")
# Define variables
X = df['x num']
Y = df['y num']
z = df['value']
# Plot the x, y, and z coordinates as a scatter plot with color representing z
plt.scatter(X, Y, c=z, cmap='rainbow', s=20, marker = 's',zorder=10)
# Y ticks frequency
plt.yticks(np.arange(min(Y), max(Y), 10))
# Add labels to the x and y axes
plt.xlabel('REC_X')
plt.ylabel('REC_Y')
# display
plt.show()
All good but I would like to see on Y label only the values I actually have, from 15264 to 15808, without interpolation or values outside the range. The interval may vary, unfortunately.
to have yticks only for the existing y values you can change the following line
plt.yticks(np.arange(min(Y), max(Y), 10))
to the
plt.yticks(Y.sort_values().tolist())
Performance Improvements
The above answer seems a little bit inefficient. We only need unique values in the Y axis so the following piece of code could do the trick but in a more efficient way.
plt.yticks(np.sort(Y.unique()).tolist())
We are taking advantage of NumPy instead of pandas. and we perform the sorting/converting to list only on the unique values
plt.yticks(np.unique(Y))
As suggested by JohanC works well and quickly.
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.
Look at this pretty graph.
Is there a way, in matplotlib, to make parts of the red and green graph invisible (where f(x)=0)?
Not just those, but also the single line segment where the flat part connects to the sine curve.
Basically, is it possible to tell matplotlib to only plot graph on a certain interval and not draw the rest (or vice versa)?
You could try replacing your points of interest with np.nan as shown below:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# here is some example data because none was provided in the question;
# it is a quadratic from x=-5:5
x = np.arange(-5, 6)
s = pd.Series(x**2, index=x)
# replace all y values less than 4 with np.nan and store in a new Series object
s_mod = s.apply(lambda y: np.nan if y < 4 else y)
# plot the modified data with the original data
fig, ax = plt.subplots()
s.plot(marker='o', markersize=16, ax=ax, label='original')
s_mod.plot(marker='s', ax=ax, label='modified')
ax.legend()
fig # displays as follows
How to draw something like this?
There's kind of like a horizontal line until next data point show up, then a vertical line to adjust the location y. The usual plot function in matplotlib just plot a straight line between two data point, which doesn't satisfy what I need.
You may use one of the drawstyles "steps-pre", "steps-mid", "steps-post" to get a a step-like appearance of your curve.
plt.plot(x,y, drawstyle="steps-pre")
Full example:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed()
x = np.arange(12)
y = np.random.rand(12)
styles = ["default","steps-pre","steps-mid", "steps-post"]
fig, axes = plt.subplots(nrows=len(styles), figsize=(4,7))
for ax, style in zip(axes, styles):
ax.plot(x,y, drawstyle=style)
ax.set_title("drawstyle={}".format(style))
fig.tight_layout()
plt.show()
Just as #cricket_007 said in the comments -- make each y value repeat at the next x value. Below a way how to achieve this with numpy.
EDIT:
Thanks to the comment by #ImportanceOfBeingErnest I replaced the original code that extended the data with a much simpler solution.
from matplotlib import pyplot as plt
import numpy as np
#producing some sample data
x = np.linspace(0,1,20)
y = np.random.rand(x.shape[0])
#extending data to repeat each y value at the next x value
##x1 = np.zeros(2*x.shape[0]-1)
##x1[::2] = x
##x1[1::2] = x[1:]
x1 = np.repeat(x,2)[1:]
##y1 = np.zeros(2*y.shape[0]-1)
##y1[::2] = y
##y1[1::2] = y[:-1]
y1 = np.repeat(y,2)[:-1]
plt.plot(x1, y1)
plt.show()
The result looks like this:
I'm plotting a scatterplot using matplotlib in python. I want to color the points based on some function, like so:
import matplotlib.pyplot as plt
def color(x, y):
# based on some rules, return a color
if(condition):
return 'red'
else:
return 'blue'
plt.scatter(index, data) #c= something?
I'm aware of the matplotlib.from_levels_and_colors function, but the problem is that the mapping isn't based on levels of the values on the x or y axes. There's a third value associated with each data point that is calculated by the function, and that's what I want to color the dots based on.
Is there a way to do this?
Why don't you just make your c array an indicator function for a default colormap. For example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y = np.arange(100)
# Colors whether or not x+y is a multiple of 5
c = (x + y)%5 == 0
# Use 'jet' colormap for red/blue.
plt.scatter(x, y, c=c, cmap='jet')
Of course you could use a colormap with different extremes that will get mapped to 0 and 1.
If your desired result has more than 2 colors, then it's totally fine to pass as c an array with many different values (and it doesn't need to be normalized). See here for an example.
This is the way I ended up doing it. I created a list of colors based on the x and y values, and then passed that to the scatter function. Not as nice as wflynny's answer, but it does mean you can do as much computation as needed to come up with the array, rather than having to create a single function to do it.
import matplotlib.pyplot as plt
colors = calculate_colors(x, y)
plt.scatter(index, data, c=colors)