make matplotlib fill_between overwrite the subplot in front - python

I am trying to plot several lines which partially overlap and occlude each other. This is what I tried:
# Create the figure
fig = plt.figure(figsize=(7, 4))
ax = plt.subplot(111)
# remove grid
ax.set_xticks([])
ax.set_yticks([])
# define data
X = np.linspace(-2*np.pi, 2*np.pi, 400)
Y1 = np.cos(2*X)
Y2 = X**2/10-0.5
ax.plot(X, Y1, lw=1)
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1))
ax.plot(X, Y2)
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1))
plt.show()
which produces
but when the second fill_between goes down to -1, I would like it to also occlude the blue graph. Like so:
Any suggestions are appreciated.

My solution:
# Create the figure
fig = plt.figure(figsize=(7, 4))
ax = plt.subplot(111)
# remove grid
ax.set_xticks([])
ax.set_yticks([])
# define data
X = np.linspace(-2*np.pi, 2*np.pi, 400)
Y1 = np.cos(2*X)
Y2 = X**2/10-0.5
# NEW #########################
for i in range(Y1.size):
if Y1[i] < Y2[i] :
Y1[i] = Y2[i]
###############################
ax.plot(X, Y1, lw=1, color="blue")
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1))
ax.plot(X, Y2, color="orange")
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1))
plt.show()
Output:
Basically at any point Y1 is less than Y2 we set the point on Y1 equal to Y2.

I ended up going with #JohanC's suggestion, as it makes it easier to generalize to more graphs. So for example
ax.plot(X, Y1, lw=1)
ax.fill_between(X, Y1, -1, facecolor=(1,0,0,1), zorder=2)
ax.plot(X, Y2)
ax.fill_between(X, Y2, -1, facecolor=(1,1,1,1), zorder=3)
ax.plot(X, Y3, lw=1, zorder=1)
ax.fill_between(X, Y3, -1, facecolor=(0,0,1,1), zorder=1)
ax.plot(X, Y4, lw=1, zorder=0)
ax.fill_between(X, Y4, -1, facecolor=(0,1,0,0.5), zorder=0)
Can plot something like this:

Related

Colouring the area between two step lines with crossovers

I am trying to fill colours between two-step line plots. I have tried to do the same using fill_between function with step and interpolate parameters. However, I am not getting the output as expected. I am filling the region between two lines after comparing their values. Below is the code. Any help will be appreciated.
fig = plt.figure()
fig.tight_layout()
plt.subplot(2, 2, 1)
p1 = plt.step(df2['datetime'], df2['T1'], color='b', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',interpolate = True ,label ='A1')
p4 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',interpolate = True, label ='A2')
plt.ylabel("T1", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
plt.subplot(2, 2, 2)
p1 = plt.step(df2['datetime'], df2[‘T2’], color='k', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',label ='A1')
p4 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',label ='A2')
plt.ylabel("T2", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
I am also attaching my output for reference.
Left plot is with step = post and interpolate = True
Right plot is without interpolate.
As you can see filling in not working as expected near cross overs.
Apparently, fill_between between step plots and using a where parameter doesn't fill as expected.
A workaround is to mimic the step function via np.repeat:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(20)
y1 = np.random.rand(20)
y2 = np.random.rand(20)
xx = np.repeat(x, 2)[1:]
yy1 = np.repeat(y1, 2)[:-1]
yy2 = np.repeat(y2, 2)[:-1]
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(18, 4))
for ax in (ax1, ax2, ax3):
ax.step(x, y1, color='r', lw=3, where='post')
ax.step(x, y2, color='b', lw=3, where='post')
ax1.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2)
ax1.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2)
ax1.set_title('fill_between with step and where')
ax2.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2, interpolate=True)
ax2.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2, interpolate=True)
ax2.set_title('setting interpolate=True')
ax3.fill_between(xx, yy1, yy2, color='b', alpha=0.3, where=yy1 < yy2)
ax3.fill_between(xx, yy1, yy2, color='r', alpha=0.3, where=yy1 > yy2)
ax3.set_title('mimicking step')
plt.tight_layout()
plt.show()

Making the lines of the scatter plot smooth in MatPlotlib

I want to make the lines of the following graph smooth. I tried to search and it seems that we have to represent the x-axis in terms of a float or some type such as date time. Here since the x-axis are just labels, I could not figure out how I should change my code. Any help is appreciated.
import matplotlib.pyplot as plt
x1 = [">1", ">10",">20"]
y1 = [18,8,3]
y2 = [22,15,10]
y3=[32,17,11]
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.scatter(x1, y1, color='blue', label='Heuristic')
ax1.scatter(x1, y2, color='green', label='SAFE')
ax1.scatter(x1, y3, color='red', label='discovRE')
plt.plot(x1, y2, '.g:')
plt.plot(x1, y1, '.b:')
plt.plot(x1, y3, '.r:')
plt.ylabel('False Positives',fontsize=8)
plt.xlabel('Function instruction sizes',fontsize=8)
plt.legend()
plt.show()
Following is the graph that I get right now.
Maybe you can fit a curve to 'smooth' the curve
import matplotlib.pyplot as plt
x1 = [">1", ">10",">20"]
y1 = [18,8,3]
y2 = [22,15,10]
y3=[32,17,11]
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.scatter(x1, y1, color='blue', label='Heuristic')
ax1.scatter(x1, y2, color='green', label='SAFE')
ax1.scatter(x1, y3, color='red', label='discovRE')
buff_x = np.linspace(0,2,100)
def reg_func(y):
params = np.polyfit(range(len(y)),y,2)
return np.polyval(params,buff_x)
plt.plot(buff_x, reg_func(y2), 'g',linestyle='dotted')
plt.plot(buff_x, reg_func(y1), 'b',linestyle='dotted')
plt.plot(buff_x, reg_func(y3), 'r',linestyle='dotted')
plt.ylabel('False Positives',fontsize=8)
plt.xlabel('Function instruction sizes',fontsize=8)
plt.legend()
plt.show()
as you can see, I use a function reg_func to fit your data, and plot the predicted curves

How to set all the four axes with matplotlib

I want to draw a picture like this one, the top and right axes have different labels and ticks, anyone can help me?
To double both axes you have to use ax1.twinx().twiny().
Here an example:
# Create some mock data
x1 = np.arange(0, 10, 1)
y1 = [random.randint(1,5) for n in x1]
#print(x1,y1)
x2 = np.arange(0, 100, 10)
y2 = [random.randint(10,50) for n in x2]
#print(x2,y2)
fig, ax1 = plt.subplots()
ax1.set_xlabel('x1', color='red')
ax1.set_ylabel('y1', color='red')
ax1.plot(x1, y1, color='red')
ax1.tick_params(axis='both', labelcolor='red')
ax2 = ax1.twinx().twiny() #here is the trick!
ax2.set_xlabel('x2', color='blue')
ax2.set_ylabel('y2', color='blue')
ax2.plot(x2, y2, color='blue')
ax2.tick_params(axis='both', labelcolor='blue') #y2 does not get blue... can't yet figure out why
plt.show()
Here the result:
Since both datasets are completely independent, one would probably not use twin axes here. Instead, just use two different axes.
import numpy as np
import matplotlib.pyplot as plt
# Create some mock data
x1 = np.linspace(0,1,11)
y1 = np.random.rand(11)
x2 = np.linspace(1,0,101)
y2 = np.random.rand(101)*20+20
fig, ax1 = plt.subplots()
ax2 = fig.add_subplot(111, label="second axes")
ax2.set_facecolor("none")
ax1.set_xlabel('x1', color='red')
ax1.set_ylabel('y1', color='red')
ax1.plot(x1, y1, color='red')
ax1.tick_params(colors='red')
ax2.set_xlabel('x2', color='blue')
ax2.set_ylabel('y2', color='blue')
ax2.plot(x2, y2, color='blue')
ax2.xaxis.tick_top()
ax2.xaxis.set_label_position('top')
ax2.yaxis.tick_right()
ax2.yaxis.set_label_position('right')
ax2.tick_params(colors='blue')
for which in ["top", "right"]:
ax2.spines[which].set_color("blue")
ax1.spines[which].set_visible(False)
for which in ["bottom", "left"]:
ax1.spines[which].set_color("red")
ax2.spines[which].set_visible(False)
plt.show()
You should use twinx and twiny functions, take a look at this link

matplotlib with multiple scales showing only one plot

I'm using the function below to plot three lines with two different scales.
def plot2(x1, y1, x2, y2, x3, y3):
fig, ax1 = plt.subplots()
ax1.plot(x1, y1, 'b')
ax1.plot(x2, y2, 'g')
ax2 = ax1.twinx()
ax2.plot(x3, y3, 'r')
fig.tight_layout()
plt.show()
However, ax2 is shadowing ax1 in the plot.
I can't reproduce the problem. By extending the code to a minimal running example:
import matplotlib.pyplot as plt
def plot2(x1, y1, x2, y2, x3, y3):
fig, ax1 = plt.subplots()
ax1.plot(x1, y1, 'b')
ax1.plot(x2, y2, 'g')
ax2 = ax1.twinx()
ax2.plot(x3, y3, 'r')
fig.tight_layout()
plt.show()
x1 = [1,2,3,4,5]
x2 = [1.25,2.25,3.25,4.25,5.25]
x3 = [1.5, 2.5, 3.5, 4.5, 5.5]
y1 = [1,2,3,4,5]
y2 = [6,7,8,9,10]
y3 = [11,12,13,14,15]
plot2(x1,y1,x2,y2,x3,y3)
I get the following result:
png

Controlling the tracker when using twinx

The tracker in the lower-right corner (highlighted in red) reports y-values relative to the y-axis on the right.
How can I get the tracker to report y-values relative to the y-axis on the left instead?
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(6)
numdata = 100
t = np.linspace(0.05, 0.11, numdata)
y1 = np.cumsum(np.random.random(numdata) - 0.5) * 40000
y2 = np.cumsum(np.random.random(numdata) - 0.5) * 0.002
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax1.plot(t, y1, 'r-', label='y1')
ax2.plot(t, y2, 'g-', label='y2')
ax1.legend()
plt.show()
I know swapping y1 with y2 will make the tracker report y1-values,
but this also places the y1 tickmarks on the right-hand side, which is not what I want to happen.
ax1.plot(t, y2, 'g-', label='y2')
ax2.plot(t, y1, 'r-', label='y1')
Ah, found it: ax.yaxis.set_ticks_position("right").
Instead of trying to "control the tracker", you can swap the location of the y-axes.
ax1.yaxis.set_ticks_position("right")
ax2.yaxis.set_ticks_position("left")
ax1.plot(t, y2, 'g-', label='y1')
ax2.plot(t, y1, 'r-', label='y2')
AFAIK, the tracker always follows ax2 when using twinx.
Please note that if you create an ax3= ax1.twiny() axes after ax1 and ax2, the tracker goes to ax3 and you have again it reporting y1 values.
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(6)
numdata = 100
t = np.linspace(0.05, 0.11, numdata)
y1 = np.cumsum(np.random.random(numdata) - 0.5) * 40000
y2 = np.cumsum(np.random.random(numdata) - 0.5) * 0.002
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax1.plot(t, y1, 'r-', label='y1')
ax2.plot(t, y2, 'g-', label='y2')
ax1.legend()
ax3 = ax1.twiny()
ax3.set_xticks([])
plt.show()

Categories