Matplotlib: Argument dimensions are incompatible fill between curves - python

I have this plot.
fig,ax = plt.subplots(figsize=(7.5,7.5))
ax.plot(time, y)
ax.plot(time, y1, color='red')
ax.plot(time, y2, color='black')
I want to fill the area between the black and red curves. So I am doing:
y1=np.array(y1)
y2=np.array(y2)
ax.fill_between(time, y1, y2,where=y1>=y2,color='grey', alpha='0.5')
But it returns the following:
ValueError: Argument dimensions are incompatible

In your case, you do not need to put a where condition for what you want to do. fill_between function only requires to put the maximum array and minimum array for your proposal of equal length of OX array (time in your case).
The following is an example:
import numpy as np
import matplotlib.pyplot as plt
fig,ax = plt.subplots(figsize=(7.5,7.5))
time = np.linspace(0,1,100)
y = np.sin(time*10)
y1 = y - 0.5
y2 = y + 0.5
ax.plot(time, y)
ax.plot(time, y1, color='red')
ax.plot(time, y2, color='black')
ax.fill_between(time, y1, y2, color='grey', alpha='0.5')
plt.tight_layout()
plt.show()
That gives the following output:
To see how where works, change the line to this one in my example:
ax.fill_between(time, y1, y2, where=(time<0.5), color='grey', alpha='0.5')
Or this as another conditional example:
ax.fill_between(time, y1, y2, where=(y1<0.0), color='grey', alpha='0.5')
As you can see, what it makes is to create a boolean array and draws only on that points of OX axis where the condition is true.
You can make your boolean array by hand also (length of OX axis, of course).

It seems that the arguments color and alpha need to be passed as color =...,alpha=....
Correct:
ax.fill_between(x, y_min, y_max,color = color, alpha=0.1)
Wrong:
ax.fill_between(x, y_min, y_max,color, alpha=0.1)

Related

How to make dotted and bold lines in the same plot

I have a list and i want to plot the list in such a way that for certain range of x axis the lines are dotted while for other range it is solid.
e.g.:
y=[11,22,33,44,55,66,77,88,99,100]
x=[1,2,3,4,5,6,7,8,9,10]
i did this:
if i range(4,8):
plt.plot(x,y,marker='D')
else :
plt.plot(x,y,'--')
plt.show()
but this doesnot work.
can someone help?
Slice the data into 3 intervals
import matplotlib.pyplot as plt
import numpy as np
# Data for plotting
x = [1,2,3,4,5,6,7,8,9,10]
y = [11,22,33,44,55,66,77,88,99,100]
fig, ax = plt.subplots()
m, n = 4, 8
x1, x2, x3 = x[:m+1], x[m:n+1], x[n:]
y1, y2, y3 = y[:m+1], y[m:n+1], y[n:]
ax.plot(x1, y1, color='red', linestyle='solid', marker='D')
ax.plot(x2, y2, color='blue', linestyle='dashed')
ax.plot(x3, y3, color='red', linestyle='solid', marker='D')
plt.show()
Here is a solution with the same colours for the whole line:
import matplotlib.pyplot as plt
x = [1,2,3,4,5,6,7,8,9,10]
y = [11,22,33,44,55,66,77,88,99,100]
fig, ax = plt.subplots()
x1, y1 = x[:4], y[:4]
x2, y2 = x[3:8], y[3:8]
x3, y3 = x[7:], y[7:]
ax.plot(x1, y1, marker='D', color='b')
ax.plot(x2, y2, '--', color='b')
ax.plot(x3, y3, marker='D', color='b')
Change line characteristics based on the value of x:
import numpy as np
from matplotlib import pyplot as plt
Make arrays of the lists;
y = np.array([11,22,33,44,55,66,77,88,99,100])
x = np.array([1,2,3,4,5,6,7,8,9,10])
make a boolean array based on your condition(s);
dashed = np.logical_or(x<4,x>=8)
use the boolean array to filter the data when you plot.
plt.plot(x[~dashed],y[~dashed],color='blue',marker='D')
plt.plot(x[dashed],y[dashed],color='blue',ls='--')

Why there is a extra graph in a subplot which i did not mention anywhere

I have used the following code and in the last the figure of thee subplot why I am having an extra plot(red curve)
fig= plt.figure()
plt.rcParams.update({'font.size':24})
fig, (ax1,ax2,ax3)=plt.subplots(3,1,figsize=
(20,24),sharex='row',sharey='col')
p1=plt.plot(x_axis,Clean_Current_TEST,"g",x_axis,Clean_Current_Prediction1,"r--")
ax1.plot(x_axis,Clean_Current_TEST,"g",x_axis,Clean_Current_Prediction1,"r--", lw=3.0)
ax2.plot(x_axis,Clean_Current_TEST,"g",x_axis,Clean_Current_Prediction2,"r--", lw=3.0)
ax3.plot(x_axis,Clean_Current_TEST,"g",x_axis,Clean_Current_Prediction3,"b--", lw=3.0)#,x_axis,Clean_Current_Prediction3,"b--"
ax1.legend(p1[:2], ["Experimental Output","Predicted Output1"],loc='best');
ax2.legend(p1[:2], ["Experimental Output","Predicted Output2"],loc='best');
ax3.legend(p1[:2], ["Experimental Output","Predicted Output3"],loc='best');
ax1.title.set_text('Short Circuit Current of Clean Module vs
Time Graph')
fig.text(0.5, 0.01, 'Time(hr)', ha='left', va='center')
fig.text(0.01, 0.5, 'Clean Module Current(mA)', ha='center',
va='center', rotation='vertical')
[enter image description here][1]
plt.show()
plt.tight_layout(pad=1)
(The last figure of the subplot)
Here is a minimum, reproducible example that recreates the problem you are experiencing:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1,1,20)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.arctan(x)
fig, (ax1,ax2) = plt.subplots(2,1)
plt.plot(x, y3, "g", x, y1, "r--")
ax1.plot(x, y3, "g", x, y1, "r--", lw=3.0)
ax2.plot(x, y3, "g", x, y2, "b--", lw=3.0)
plt.show()
It seems that the plt.plot function actually plots its graph on the last axis. This is why you are seeing the "red" graph on the last axis, even though the line that plots on axis 2 explicitly calls for a "blue" line. In general, if you are plotting separate things on various axes, then you should not use the convenience methods in matplotlib.pyplot, and should instead use the methods on the Axis objects.
fig, (ax1,ax2) = plt.subplots(2,1)
ax1.plot(x, y3, 'g', x, y1, "r--", lw=3.0)
ax2.plot(x, y3, 'g', x, y2, "b--", lw=3.0)
plt.show()

How to plot arrays of different lengths

How do you plot arrays of different lengths but extend properly on the x-axis? The code below generates 2 data sets, the second one being shorter. I run an interpolation over each set resampling the data with multiple samples per data point. When I plot all of the data the data sets that are shorter don't extend to the end of the plot. I don't want subplots, I need to overlay the data onto each other.
#!/usr/bin/env python3
from scipy import interpolate
import matplotlib.pyplot as plt
import numpy as np
num_points = 100
# Generate an array of data, interpolate, re-sample and graph
x1 = np.arange(0, num_points)
y1 = np.cos(x1)
f1 = interpolate.interp1d(x1, y1, kind='cubic')
xnew1 = np.arange(0, num_points - 1, 0.2)
ynew1 = f1(xnew1)
plt.plot(x1, y1, color='g', label='input 1')
plt.plot(x1, y1, 'o', color='g')
plt.plot(xnew1, ynew1, color='m', label='interp 1')
plt.plot(xnew1, ynew1, '+', color='m')
# Generate ana array different size of data, interpolate, re-sample and graph
x2 = np.arange(0, num_points/2)
y2 = np.sin(x2)
f2 = interpolate.interp1d(x2, y2, kind='cubic')
xnew2 = np.arange(0, (num_points/2) - 1, 0.2)
ynew2 = f2(xnew2)
plt.plot(x2, y2, color='k', label='input 2')
plt.plot(x2, y2, 'o', color='k')
plt.plot(xnew2, ynew2, color='r', label='interp 2')
plt.plot(xnew2, ynew2, '+', color='r')
plt.legend(loc='upper left')
plt.show()
If I am understanding correctly, this can be done by using two different axes which share the same y-axis, as outlined in this matplotlib example.
In your case you can accomplish this by making the following modifications:
from scipy import interpolate
import matplotlib.pyplot as plt
import numpy as np
num_points = 100
# Generate an array of data, interpolate, re-sample and graph
x1 = np.arange(0, num_points)
y1 = np.cos(x1)
f1 = interpolate.interp1d(x1, y1, kind='cubic')
xnew1 = np.arange(0, num_points - 1, 0.2)
ynew1 = f1(xnew1)
fig, ax1 = plt.subplots() # Create the first axis
ax1.plot(x1, y1, color='g', label='input 1')
ax1.plot(x1, y1, 'o', color='g')
ax1.plot(xnew1, ynew1, color='m', label='interp 1')
ax1.plot(xnew1, ynew1, '+', color='m')
ax2 = ax1.twiny() # Create a twin which shares the y-axis
# Generate an array different size of data, interpolate, re-sample and graph
x2 = np.arange(0, num_points/2)
y2 = np.sin(x2)
f2 = interpolate.interp1d(x2, y2, kind='cubic')
xnew2 = np.arange(0, (num_points/2) - 1, 0.2)
ynew2 = f2(xnew2)
ax2.plot(x2, y2, color='k', label='input 2')
ax2.plot(x2, y2, 'o', color='k')
ax2.plot(xnew2, ynew2, color='r', label='interp 2')
ax2.plot(xnew2, ynew2, '+', color='r')
plt.figlegend(loc='upper left', bbox_to_anchor=(0.065, 0.3, 0.5, 0.5))
plt.show()
This will give you something that looks like
Edit
In order to properly display the legend you can construct one legend for all the subplots, as outlined in this demo. Note that using this method will require some manhandling of the bounding box for the legend, and there are much cleaner ways to do this than specifying a 4-tuple of floats as I have in the line
plt.figlegend(loc='upper left', bbox_to_anchor=(0.065, 0.3, 0.5, 0.5))

Use mark_inset with different range plot

Say I want to inset a plot to a figure, but the inset plot has different axis range than the one I am marking the inset to. For example:
fig, ax = plt.subplots()
axins = inset_axes(ax, 1,1 , loc=2, bbox_to_anchor=(0.35,0.85),bbox_transform=ax.figure.transFigure)
x = np.linspace(0, 3, 100)
y = x**2
ax.plot(x, y)
axins.plot(x, x**3)
x1, x2, y1, y2 = 2.,3, 6, 8 # specify the limits
axins.set_xlim(x1, x2) # apply the x-limits
axins.set_ylim(y1, y2) # apply the y-limits
plt.xticks(visible=False)
plt.yticks(visible=False)
mark_inset(ax, axins, loc1=4, loc2=1)#, fc="none")#, ec="0.5")
The result is an empty inset plot:
But this is obvious, since I set the limits of x and y to ranges where x**3 does not pass.
What I want to see is, for example, a plot of x**3 for 0 to 1 in the inset plot, while the mark_inset will still 'zoom' to the region boxed above, which is of different range.
How can I do this?
In that case you cannot use mark_inset directly, because that is exactly what this function does: synchronizing the marker with the axes limits of the inset.
Using a rectangle
Instead you may position some rectangle whereever you want it to be and use ConnectionPatches to draw some lines in between the inset and the rectangle.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axes_grid1.inset_locator as il
import matplotlib.patches as mpatches
fig, ax = plt.subplots()
axins = il.inset_axes(ax, 1,1 , loc=2, bbox_to_anchor=(0.35,0.85),bbox_transform=ax.figure.transFigure)
x = np.linspace(0, 3, 100)
y = x**2
ax.plot(x, y)
axins.plot(x, x**3)
x1, x2, y1, y2 = 2.,3, 6, 8 # specify the limits
rect = mpatches.Rectangle((x1,y1), width=x2-x1, height=y2-y1, facecolor="None", edgecolor="k", linewidth=0.8)
fig.canvas.draw()
p1 = mpatches.ConnectionPatch(xyA=(1,0), xyB=(x2,y1), coordsA="axes fraction", coordsB="data", axesA=axins, axesB=ax)
p2 = mpatches.ConnectionPatch(xyA=(1,1), xyB=(x2,y2), coordsA="axes fraction", coordsB="data", axesA=axins, axesB=ax)
ax.add_patch(rect)
ax.add_patch(p1)
ax.add_patch(p2)
plt.show()
Use dummy axes
You may also simply add an additional inset, just for the purpose of using mark_inset with it.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axes_grid1.inset_locator as il
fig, ax = plt.subplots()
axins_dummy = il.inset_axes(ax, 1,1 , loc=2, bbox_to_anchor=(0.35,0.85),bbox_transform=ax.figure.transFigure)
axins = il.inset_axes(ax, 1,1 , loc=2, bbox_to_anchor=(0.35,0.85),bbox_transform=ax.figure.transFigure)
x = np.linspace(0, 3, 100)
y = x**2
ax.plot(x, y)
axins.plot(x, x**3)
x1, x2, y1, y2 = 2.,3, 6, 8 # specify the limits
axins_dummy .set_xlim(x1, x2) # apply the x-limits
axins_dummy .set_ylim(y1, y2) # apply the y-limits
axins_dummy.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False )
il.mark_inset(ax,axins_dummy , loc1=4, loc2=1)#, fc="none")#, ec="0.5")
plt.show()
In both cases, the resulting plot would look like
Maybe it's worth noting that the resulting graph is of course incorrect. Any reader would assume that the inset shows part of the curve, which is not the case. Hence make sure not to use such graph in a publication or report.

Filling between two lines with Matplotlib with 2 restrictions

I would like to fill the area between the curve y1=x^3 and then line y2=3x-2.
Below is code I have that will do this, however, I want to place the restriction that y1 < y2 (which I have done with the where option of fill_between) and that x<1.
The problem that occurs with the code below is that the area between the curve is filled for x>1. I would like to plot these graphs on the range [-2.5,2.5]. How do I get matplotlib to stop filling between the curves for x>1?
My code:
import matplotlib.pyplot as plot
import numpy as np
x = np.linspace(-2.5, 2.5, 100)
y1 = np.array([i**3 for i in x])
y2 = np.array([3*i-2 for i in x])
fig = plot.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x, y1, label=r"$y=x^3$")
ax.plot(x, y2, label=r"$y=3x-2$")
ax.spines['left'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_color('none')
ax.spines['left'].set_smart_bounds(True)
ax.spines['bottom'].set_smart_bounds(True)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.fill_between(x, y1, y2, where=y2<y1, facecolor='green')
ax.legend()
plot.show()
I got it. The easiest fix is to define 3 new variables, u,v, and w, where u holds the x values for v and w, and v = x^3, w=3x-2.
u=x[x<1]
v=y1[y1<1]
w=y2[y2<1]
Then plot these values with fill_between:
ax.fill_between(u, v, w, where=w<v, facecolor='green')

Categories