Using Matplotlib to create a simple schedule - python-3.x

I am trying to create a simple schedule using Matplotlib. I have the following start. I want to remove the y-axis numbers and display "a=1", "a=2", "a=3" instead. Indeed, y1 and y2 defined in my function are sort of fake because the thickness of the box does not really have to be some number. As long as I have an equal thickness for each "a," it is all fine. I want to plot a box with a shade for a=1 in between 370 and 560, for a=2 in between 550 and 980 and so on. The x-axis shows the time in minutes. I checked horizontal bar plots, but they all start from 0 and I couldn't figure out a way to convert them into a scheduling type. Any suggestions?
import matplotlib.pyplot as plt
x = [(370, 560), (550,980), (380,440)]
def activity_filler(x,y1,y2):
# Shade the area between y1 and y2
plt.fill_between(x, y1, y2,
facecolor="grey", # The fill color
color='grey', # The outline color
alpha=0.4, hatch = 'X\/|-') # Transparency of the fill
activity_filler(x[0],[1],[2])
activity_filler(x[1],[2],[3])
activity_filler(x[2],[3],[4])
plt.show()

x = [(370, 560), (550,980), (380,440)]
fig, ax = plt.subplots()
for i,evt in enumerate(x):
ax.barh(i,width=evt[1]-evt[0],left=evt[0])
ax.set_yticks(range(len(x)))
ax.set_yticklabels([f'a={i+1}' for i in range(len(x))])

Related

How to plot fill_betweenx to fill the area between y1 and y2 with different scales using matplotlib.pyplot?

I am trying to fill the area between two vertical curves(RHOB and NPHI) using matplotlib.pyplot. Both RHOB and NPHI are having different scale of x-axis.
But when i try to plot i noticed that the fill_between is filling the area between RHOB and NPHI in the same scale.
#well_data is the data frame i am reading to get my data
#creating my subplot
fig, ax=plt.subplots(1,2,figsize=(8,6),sharey=True)
ax[0].get_xaxis().set_visible(False)
ax[0].invert_yaxis()
#subplot 1:
#ax01 to house the NPHI curve (NPHI curve are having values between 0-45)
ax01=ax[0].twiny()
ax01.set_xlim(-15,45)
ax01.invert_xaxis()
ax01.set_xlabel('NPHI',color='blue')
ax01.spines['top'].set_position(('outward',0))
ax01.tick_params(axis='x',colors='blue')
ax01.plot(well_data.NPHI,well_data.index,color='blue')
#ax02 to house the RHOB curve (RHOB curve having values between 1.95,2.95)
ax02=ax[0].twiny()
ax02.set_xlim(1.95,2.95)
ax02.set_xlabel('RHOB',color='red')
ax02.spines['top'].set_position(('outward',40))
ax02.tick_params(axis='x',colors='red')
ax02.plot(well_data.RHOB,well_data.index,color='red')
# ax03=ax[0].twiny()
# ax03.set_xlim(0,50)
# ax03.spines['top'].set_position(('outward',80))
# ax03.fill_betweenx(well_data.index,well_data.RHOB,well_data.NPHI,alpha=0.5)
plt.show()
ax03=ax[0].twiny()
ax03.set_xlim(0,50)
ax03.spines['top'].set_position(('outward',80))
ax03.fill_betweenx(well_data.index,well_data.RHOB,well_data.NPHI,alpha=0.5)
above is the code that i tried, but the end result is not what i expected.
it is filling area between RHOB and NPHI assuming RHOB and NPHI is in the same scale.
How can i fill the area between the blue and the red curve?
Since the data are on two different axes, but each artist needs to be on one axes alone, this is hard. What would need to be done here is to calculate all data in a single unit system. You might opt to transform both datasets to display-space first (meaning pixels), then plot those transformed data via fill_betweenx without transforming again (transform=None).
import numpy as np
import matplotlib.pyplot as plt
y = np.linspace(0, 22, 101)
x1 = np.sin(y)/2
x2 = np.cos(y/2)+20
fig, ax1 = plt.subplots()
ax2 = ax1.twiny()
ax1.tick_params(axis="x", colors="C0", labelcolor="C0")
ax2.tick_params(axis="x", colors="C1", labelcolor="C1")
ax1.set_xlim(-1,3)
ax2.set_xlim(15,22)
ax1.plot(x1,y, color="C0")
ax2.plot(x2,y, color="C1")
x1p, yp = ax1.transData.transform(np.c_[x1,y]).T
x2p, _ = ax2.transData.transform(np.c_[x2,y]).T
ax1.autoscale(False)
ax1.fill_betweenx(yp, x1p, x2p, color="C9", alpha=0.4, transform=None)
plt.show()
We might equally opt to transform the data from the second axes to the first. This has the advantage that it's not defined in pixel space and hence circumvents a problem that occurs when the figure size is changed after the figure is created.
x2p, _ = (ax2.transData + ax1.transData.inverted()).transform(np.c_[x2,y]).T
ax1.autoscale(False)
ax1.fill_betweenx(y, x1, x2p, color="grey", alpha=0.4)

Matplotlib: How to copy a contour plot to another figure?

I have a figure with many different plots (contour plots and lots of other stuff). I want to extract the contour plot to another single figure to see more details. But I fail how to do so.
Have a look on this code:
import numpy as np
from matplotlib import gridspec as gs, pyplot as plt
# Figure 1 with many different plots.
fig1 = plt.figure()
gridSpec = gs.GridSpec(2, 3)
for i in range(6):
fig1.add_subplot(gridSpec[i])
# Create contour plot
x = np.arange(-3.0, 3.0, 0.02)
y = np.arange(-2.0, 2.0, 0.01)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) ** 4
# Plot it to a particular axes.
ax1 = fig1.axes[2]
contour = ax1.contour(X, Y, Z)
# Try to copy the contour plot to another figure (with only 1 subplot).
fig2, ax2 = plt.subplots()
# How to copy the content of ax1 to ax2?
plt.show()
This will give me the following:
I want to create a second figure with only 1 subplot and its content should be the same as you can see in top right corner of the first figure with 6 subplots.
First thing I tried was
ax2.add_collection(contour.collections[1])
but I got the error message
RuntimeError: Can not put single artist in more than one figure
This is because the content is already plottet to figure 1, so it is not possible to plot it to figure 2 as well. So I tried to make a copy of the contour plot:
from copy import deepcopy
ax2.add_collection(deepcopy(contour.collections[1]))
But this will get me a new error that copiing is not possible ...
NotImplementedError: TransformNode instances can not be copied. Consider using frozen() instead.
So .. what can I do? Any ideas for that problem? :)
Thanks a lot!
(Python 3.7.4, Matplotlib 3.1.1)

Pass a list of values to a tick constructor in matplotlib [duplicate]

I hope one of you may be able to help. I have a plot with one y-axis value and one x-axis corresponding to these y values. I want to add a second y-axis on the right hand side of the plot. The values that will appear on the second y-axis are determined through the first y-axis values by some relation: for example, y2 might be y2 = y1**2 - 100. How do I make a second y-axis which has its values determined by the y1 values, so that the y2 values correctly align with their y1 values on the y-axis?
twin axis
Adding a second y axis can be done by creating a twin axes, ax2 = ax.twinx().
The scale of this axes can be set using its limits, ax2.set_ylim(y2min, y2max). The values of y2min, y2max can be calculated using some known relationship (e.g. implemented as a function) from the limits of the left axis.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
x = np.linspace(0,50,101)
y = np.cumsum(np.random.normal(size=len(x)))+20.
fig, ax = plt.subplots()
ax2 = ax.twinx()
ax.plot(x,y, color="#dd0011")
ax.set_ylabel("Temperature [Celsius]")
ax2.set_ylabel("Temperature [Fahrenheit]")
# set twin scale (convert degree celsius to fahrenheit)
T_f = lambda T_c: T_c*1.8 + 32.
# get left axis limits
ymin, ymax = ax.get_ylim()
# apply function and set transformed values to right axis limits
ax2.set_ylim((T_f(ymin),T_f(ymax)))
# set an invisible artist to twin axes
# to prevent falling back to initial values on rescale events
ax2.plot([],[])
plt.show()
secondary axis
From matplotlib 3.1 onwards one can use a secondary_yaxis. This takes care of synchronizing the limits automatically. As input one needs the conversion function and its inverse.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
x = np.linspace(0,50,101)
y = np.cumsum(np.random.normal(size=len(x)))+20.
# Convert celsius to Fahrenheit
T_f = lambda T_c: T_c*1.8 + 32.
# Convert Fahrenheit to Celsius
T_c = lambda T_f: (T_f - 32.)/1.8
fig, ax = plt.subplots()
ax2 = ax.secondary_yaxis("right", functions=(T_f, T_c))
ax.plot(x,y, color="#dd0011")
ax.set_ylabel("Temperature [Celsius]")
ax2.set_ylabel("Temperature [Fahrenheit]")
plt.show()
The output is the same as above, but as you can see one does not need to set any limits.

Setting ticks on matplotlib 3-D plots

I'm doing some cluster analysis and want to use matplotlib to visualise the results. For the most part, this is working out OK. However, I'm struggling with controlling tick placement on the axes. That is, the ticks on the y axis are overcrowded and I'd like to thin them out. I've tried supplying a range for the ticks using the numpy arrange function, but this isn't working.
I don't know if this is because I'm not familiar enough with matplotlib, or if it's an issue with 3-D plotting. In any event, I've tried all the solutions I can find on Stack and nothing seems to be working.
My code:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(data['col_1'], data['col_2'], data['col_3'], c = data.index, cmap = cm.winter, s=60)
ax.view_init(15, 240)
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
ax.set_zlabel('Z- Axis')
plt.title('Sample Plot')
plt.show()
My solution to this is to supply the ticks as follows:
ticks = np.arange(0.3, 0.7, 0.02)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(data['col_1'], data['col_2'], data['col_3'], c = data.index, cmap = cm.winter, s=60)
ax.view_init(15, 240)
ax.set_xticks(ticks)
ax.set_yticks(ticks)
ax.set_zticks(ticks)
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
ax.set_zlabel('Z- Axis')
plt.title('Sample Bad Plot')
plt.show()
However, this only produces the hot mess below. Any help to be had?
The problem is that your x-values lie approximately within the range 0.54-0.68, your y-values lie within the range 0.34-0.42 and your z-values lie within the range0.55-0.63. Now in your second code, you define ticks = np.arange(0.3, 0.7, 0.02) which creates ticks from 0.3 to 0.68 and then you assign these values to be displayed on x, y, z axis using ax.set_xticks(ticks) and so on. You get this mess because your supplied ticks values are outside the range of actual x, y, z data points. Since you are only interested in refining the y axis ticks, you can just do
ticks = np.arange(0.34, 0.44, 0.02)
and then just set the ticks for the y axis as
ax.set_yticks(ticks).
If you don't want to specify the numbers 0.34 and 0.44 manually, you can find the maximum and minimum y value and use something like ticks = np.arange(min_value, max_value, 0.02).
Since I do not have access to your original data data['col_1'] and so on, I can't play with your code but the above tips will surely help.

How to add another scale on the right part of y-axis in the same Python plot? [duplicate]

I hope one of you may be able to help. I have a plot with one y-axis value and one x-axis corresponding to these y values. I want to add a second y-axis on the right hand side of the plot. The values that will appear on the second y-axis are determined through the first y-axis values by some relation: for example, y2 might be y2 = y1**2 - 100. How do I make a second y-axis which has its values determined by the y1 values, so that the y2 values correctly align with their y1 values on the y-axis?
twin axis
Adding a second y axis can be done by creating a twin axes, ax2 = ax.twinx().
The scale of this axes can be set using its limits, ax2.set_ylim(y2min, y2max). The values of y2min, y2max can be calculated using some known relationship (e.g. implemented as a function) from the limits of the left axis.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
x = np.linspace(0,50,101)
y = np.cumsum(np.random.normal(size=len(x)))+20.
fig, ax = plt.subplots()
ax2 = ax.twinx()
ax.plot(x,y, color="#dd0011")
ax.set_ylabel("Temperature [Celsius]")
ax2.set_ylabel("Temperature [Fahrenheit]")
# set twin scale (convert degree celsius to fahrenheit)
T_f = lambda T_c: T_c*1.8 + 32.
# get left axis limits
ymin, ymax = ax.get_ylim()
# apply function and set transformed values to right axis limits
ax2.set_ylim((T_f(ymin),T_f(ymax)))
# set an invisible artist to twin axes
# to prevent falling back to initial values on rescale events
ax2.plot([],[])
plt.show()
secondary axis
From matplotlib 3.1 onwards one can use a secondary_yaxis. This takes care of synchronizing the limits automatically. As input one needs the conversion function and its inverse.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
x = np.linspace(0,50,101)
y = np.cumsum(np.random.normal(size=len(x)))+20.
# Convert celsius to Fahrenheit
T_f = lambda T_c: T_c*1.8 + 32.
# Convert Fahrenheit to Celsius
T_c = lambda T_f: (T_f - 32.)/1.8
fig, ax = plt.subplots()
ax2 = ax.secondary_yaxis("right", functions=(T_f, T_c))
ax.plot(x,y, color="#dd0011")
ax.set_ylabel("Temperature [Celsius]")
ax2.set_ylabel("Temperature [Fahrenheit]")
plt.show()
The output is the same as above, but as you can see one does not need to set any limits.

Resources