Setting ticks on matplotlib 3-D plots - python-3.x

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.

Related

using % range in plot labels

I want to change the range in terms of %. In the attached figure along x-axis, I want to label it as -1%, -0.05%, 0, 0.05% and 1% along x-axis. Is there any way to do that directly in python using range function?
ax.set_xlim(-0.012, 0.012, 0.2)
You can modify the tick labels as shown below
fig, ax = plt.subplots()
x = np.linspace(-0.01, 0.01, 10)
ax.plot(x, -x/10, '-bo')
ax.set_xlim(-0.012, 0.012, 0.2)
labels = ['{:.2f}%'.format(item*100) for item in ax.get_xticks()]
ax.set_xticklabels(labels)

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)

Using Matplotlib to create a simple schedule

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

How to change the location of the symbols/text within a legend box?

I have a subplot with a single legend entry. I am placing the legend at the bottom of the figure and using mode='expand'; however, the single legend entry is placed to the very left of the legend box. To my understanding, changing kwargs such as bbox_to_anchor changes the legend box parameters but not the parameters of the symbols/text within. Below is an example to reproduce my issue.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 21)
y = np.exp(x)
z = x **2
fig, axes = plt.subplots(nrows=1, ncols=2)
axes[0].plot(x, y, color='r', label='exponential')
axes[1].plot(x, z, color='b')
# handles, labels = axes[0].get_legend_handles_labels()
plt.subplots_adjust(bottom=0.125)
fig.legend(mode='expand', loc='lower center')
plt.show()
plt.close(fig)
This code produces . How can I change the position of the symbol and text such that they are centered in the legend box?
PS: I am aware that exponential is a bad label for this subplot since it only describes the first subfigure. But, this is just for examples-sake so that I can apply it to my actual use-case.
The legend entries are placed using a HPacker object. This does not allow to be centered. The behaviour is rather that those HPackers are "justified" (similar to the "justify" option in common word processing software).
A workaround would be to create three (or any odd number of) legend entries, such that the desired entry is in the middle. This would be accomplished via the ncol argument and the use of "dummy" entries (which might be transparent and have no associated label).
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 21)
y = np.exp(x)
z = x **2
fig, axes = plt.subplots(nrows=1, ncols=2)
fig.subplots_adjust(bottom=0.125)
l1, = axes[0].plot(x, y, color='r', label='exponential')
axes[1].plot(x, z, color='b')
dummy = plt.Line2D([],[], alpha=0)
fig.legend(handles=[dummy, l1, dummy],
mode='expand', loc='lower center', ncol=3)
plt.show()

Resources