ax.locator_params(nbins=k) does not work in matplotlib - python

I have this simple piece of code where I try to plot simple graph while limiting number of x ticks. There are hundreds of items in iters variable and if they get plotted it would just create one fat black line.
However, ax.locator_params does not work and the number of ticks aren't reduced.
I have tried setting it on plt object, but no help.
I also tried specifying x and y axes in locator_params, but no help as well.
Finally, I have tried moving ax.locator_params before and after ax.plot, but nothing seemed to help. I am completely out of ideas.
fig, ax = plt.subplots(1, 1, figsize=(20,10))
ax.locator_params(tight=True, nbins=4)
ax.plot(iters, vals)
plt.xticks(rotation=30)
plt.show()

locator_params() with nbins= is only supported for numerical axes where the tick positions are set via MaxNLocator.
To get the same effect with text ticks, the current ticks can be stored in a list (get_xticks) and then be replaced by a subset. Note that changes to ticks (and to limits) should be called after the main plot functions.
xticks = ax.get_xticks()
ax.set_xticks(xticks[::len(xticks) // 4]) # set new tick positions
ax.tick_params(axis='x', rotation=30) # set tick rotation
ax.margins(x=0) # set tight margins

Related

Problem updating imshow in loop and colormap after loop

I have two imshow() problems that I suspect are closely related.
First, I can't figure out how to use set_data() to update an image I've created with imshow().
Second, I can't figure out why the colorbar I add to the imshow() plot after I'm done updating the plot doesn't match the colorbar I add to an imshow() plot of the same data that I create from scratch after I'm done taking data. The colorbar of the second plot appears to be correct.
Background.
I'm collecting measurement data in two nested loops, with each loop controlling one of the measurement conditions. I'm using pyplot.imshow() to plot my results, and I'm updating the imshow() plot every time I take data in the inner loop.
What I have works in terms of updating the imshow() plot but it seems to be getting increasingly slower as I add more loop iterations, so it's not scaling well. (The program I've included in with this post creates a plot that is eight rows high and six columns wide. A "real" plot might be 10x or 20x this size, in both dimensions.)
I think what I want to do is use the image's set_data() method but I can't figure out how. What I've tried either throws an error or doesn't appear to have any effect on the imshow() plot.
Once I'm done with the "take the data" loops, I add a colorbar to the imshow() plot I've been updating. However, the colorbar scale is obviously bogus.
In contrast, if I take create an entirely new imshow() plot, using the data I took in the loops, and then add a colorbar, the colorbar appears to be correct.
My hunch is the problem is with the vmin and vmax values associated with the imshow() plot I'm updating in the loops but I can't figure out how to fix it.
I've already looked at several related StackOverflow posts. For example:
update a figure made with imshow(), contour() and quiver()
Update matplotlib image in a function
How to update matplotlib's imshow() window interactively?
These have helped, in that they've pointed me to set_data() and given me solutions to some other
problems I had, but I still have the two problems I mentioned at the start.
Here's a simplified version of my code. Note that there are repeated zero values on the X and Y axes. This is on purpose.
I'm running Python 3.5.1, matplotlib 1.5.1, and numpy 1.10.4. (Yes, some of these are quite old. Corporate IT reasons.)
import numpy as np
import matplotlib.pyplot as plt
import random
import time
import warnings
warnings.filterwarnings("ignore", ".*GUI is implemented.*") # Filter out bogus matplotlib warning.
# Create the simulated data for plotting
v_max = 120
v_step_size = 40
h_max = 50
h_step_size = 25
scale = 8
v_points = np.arange(-1*abs(v_max), 0, abs(v_step_size))
v_points = np.append(v_points, [-0.0])
reversed_v_points = -1 * v_points[::-1] # Not just reverse order, but reversed sign
v_points = np.append(v_points, reversed_v_points)
h_points = np.arange(-1*abs(h_max), 0, abs(h_step_size))
h_points = np.append(h_points, [-0.0])
reversed_h_points = -1 * h_points[::-1] # Not just reverse order, but reversed sign
h_points = np.append(h_points, reversed_h_points)
h = 0 # Initialize
v = 0 # Initialize
plt.ion() # Turn on interactive mode.
fig, ax = plt.subplots() # So I have access to the figure and the axes of the plot.
# Initialize the data_points
data_points = np.zeros((v_points.size, h_points.size))
im = ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
ax.set_title('Dummy title for initial plot')
# Set up the X-axis ticks and label them
ax.set_xticks(np.arange(len(h_points)))
ax.set_xticklabels(h_points)
ax.set_xlabel('Horizontal axis measurement values')
# Set up the Y-axis ticks and label them
ax.set_yticks(np.arange(len(v_points)))
ax.set_yticklabels(v_points)
ax.set_ylabel('Vertical axis measurement values')
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.show()
for v, v_value in enumerate(v_points):
for h, h_value in enumerate(h_points):
# Measurement goes here.
time.sleep(0.1) # Simulate the measurement delay.
measured_value = scale * random.uniform(0.0, 1.0) # Create simulated data
data_points[v][h] = measured_value # Update data_points with the simulated data
# Update the heat map with the latest point.
# - I *think* I want to use im.set_data() here, not ax.imshow(), but how?
ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.draw()
# Create a colorbar
# - Except the colorbar here is wrong. It goes from -0.10 to +0.10 instead
# of matching the colorbar in the second imshow() plot, which goes from
# 0.0 to "scale". Why?
cbar = ax.figure.colorbar(im, ax=ax)
cbar.ax.set_ylabel('Default heatmap colorbar label')
plt.pause(0.0001) # In interactive mode, need a small delay to get the colorbar to appear
plt.show()
fig2, ax2 = plt.subplots() # So I have access to the figure and the axes of the plot.
im = ax2.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
ax2.set_title('Dummy title for plot with pseudo-data')
# Set up the X-axis ticks and label them
ax2.set_xticks(np.arange(len(h_points)))
ax2.set_xticklabels(h_points)
ax2.set_xlabel('Horizontal axis measurement values')
# Set up the Y-axis ticks and label them
ax2.set_yticks(np.arange(len(v_points)))
ax2.set_yticklabels(v_points)
ax2.set_ylabel('Vertical axis measurement values')
# Create a colorbar
cbar = ax2.figure.colorbar(im, ax=ax2)
cbar.ax.set_ylabel('Default heatmap colorbar label')
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.show()
dummy = input("In interactive mode, press the Enter key when you're done with the plots.")
OK, I Googled some more. More importantly, I Googled smarter and figured out my own answer.
To update my plot inside my nested loops, I was using the command:
ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
What I tried using to update my plot more efficiently was:
im.set_data(data_points)
What I should have used was:
im.set_data(data_points)
im.autoscale()
This updates the pixel scaling, which fixed both my "plot doesn't update" problem and my "colorbar has the wrong scale" problem.

matplotlib logarithmic y axis format [duplicate]

I am plotting some values in a semilogy plot. I need to use the
ax.set_yscale('log')
rather than
ax.semilogy(...
because i have some arrays with negative values (so I can set the scale to symlog).
My code
ax = plt.subplot(132)
ax.set_yscale('log')
ax.plot(np.arange(0,4),array,'-o')
ax.errorbar(np.arange(0,4),array,yerr=[yerr_neg,yerr_pos])
ax.set_yticks(np.exp(np.arange(-0.4,0.5,0.1)))
ax.set_yticklabels(np.arange(-0.4,0.5,0.1))
however the automatically placed ticks and labels are still there:
how do I properly get rid of the original ticks/labels? I get still the same error by eliminating either plot line (meaning deleting the ax.plot line or the ax.errorbar line)
The labels shown are minor ticklabels. You may set the minor ticks to an empty list to get rid of them.
ax.set_yticks([], minor=True)
or just set the minor ticks off,
ax.minorticks_off()

Matplotlib dual x-axis logarithmic ticks

I've set the upper x-axis manually (the conversion is 1.218x the values on the lower x-axis) and I'd like the upper minor logarithmic ticks to move up the scale by 1.218x too. Any suggestions?
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
X = np.linspace(0,10000000, 1000000)
ax1.semilogx(X, zero, label='$\mathbb{P} (X=0)$')
ax1.semilogx(X, one, label='$\mathbb{P} (X=1)$')
ax1.semilogx(X, two+more, label='$\mathbb{P} (X\geq2)$')
ax1.set_xlabel(r"Particle Concentration m$^{-3}$")
ax1.set_ylabel(r"Probability of occurrence")
ax1.legend(loc=6)
ax1.grid()
ax2.semilogx(X, one, label='one', alpha = 0)
ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks([12.18323586744639, 121.8323586744639, 1218.323586744639, 12183.23586744639, 121832.3586744639, 1218323.586744639])
ax2.set_xticklabels(['10$^1$','10$^2$','10$^3$','10$^4$','10$^5$','10$^6$'])
ax2.set_xlabel(r"Disdrometer Particle Count (min$^{-1}$)")
plt.show()
EDIT: Rewriting after your comment below.
There will be a way to move the minor ticks but actually I think your approach here is misguided. Looking close you aren't using ax2 to plot anything: you just want it as an alternative scale. You are messing with the ticks and ticklabels when as a way of faking changing the limits. It would be much easier to just change that limits (so that matplotlib can handle the ticks etc automatically).
Replace your code above with
ax2.set_xscale("log", nonposx='clip')
ax2.set_xlim(np.array(ax1.get_xlim())/1.218)

Python Matplotlib: Dual y-axis with same tick spacing and different scale [duplicate]

I created a matplotlib plot that has 2 y-axes. The y-axes have different scales, but I want the ticks and grid to be aligned. I am pulling the data from excel files, so there is no way to know the max limits beforehand. I have tried the following code.
# creates double-y axis
ax2 = ax1.twinx()
locs = ax1.yaxis.get_ticklocs()
ax2.set_yticks(locs)
The problem now is that the ticks on ax2 do not have labels anymore. Can anyone give me a good way to align ticks with different scales?
Aligning the tick locations of two different scales would mean to give up on the nice automatic tick locator and set the ticks to the same positions on the secondary axes as on the original one.
The idea is to establish a relation between the two axes scales using a function and set the ticks of the second axes at the positions of those of the first.
import matplotlib.pyplot as plt
import matplotlib.ticker
fig, ax = plt.subplots()
# creates double-y axis
ax2 = ax.twinx()
ax.plot(range(5), [1,2,3,4,5])
ax2.plot(range(6), [13,17,14,13,16,12])
ax.grid()
l = ax.get_ylim()
l2 = ax2.get_ylim()
f = lambda x : l2[0]+(x-l[0])/(l[1]-l[0])*(l2[1]-l2[0])
ticks = f(ax.get_yticks())
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks))
plt.show()
Note that this is a solution for the general case and it might result in totally unreadable labels depeding on the use case. If you happen to have more a priori information on the axes range, better solutions may be possible.
Also see this question for a case where automatic tick locations of the first axes is sacrificed for an easier setting of the secondary axes tick locations.
To anyone who's wondering (and for my future reference), the lambda function f in ImportanceofBeingErnest's answer maps the input left tick to a corresponding right tick through:
RHS tick = Bottom RHS tick + (% of LHS range traversed * RHS range)
Refer to this question on tick formatting to truncate decimal places:
from matplotlib.ticker import FormatStrFormatter
ax2.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) # ax2 is the RHS y-axis

customize ticks label when sharex is on in matplotlib

I have two stacked subplots which share the x axis, for both subplots visibility of ticks is set to false because I don't want to see tick labels. after having plotted both subplots, I would like to put some extra ticks on x-asis, only for second subplot, but they don't have to became the main ticks.
I mean, doing this:
#xticks = list of x points
#xlabs = list of labels
#secondplot.set_xticks(xticks)
#secondplot.set_xticklabels(xlabs)
will change the first sublplot grid according to these new ticks as if they became the new major ticks. is there a way to label just some x-axis point in second subplot without affecting the whole plots area? thank you
I know im late to the party but I faced a similar problem and want to share my solution, in case anyone else needs help.
You can use matplotlib.axes.Axes.tick_params to control the style of both major and minor ticks of the axes. Setting the tick lengths of the first subplot to 0 should do the trick:
ax.tick_params(axis="x", which="both", length=0.)
axis ("x", "y" or "both") selects the axes, on which the setting has an effect, which ("major", "minor" or "both") chooses the tick type.
Of course you can then also set major and minor ticks with ax.set_xticks(ticks, minor=False). A full example:
import matplotlib.pyplot as plt
fig, axarr = plt.subplots(2, 1, sharex="col")
axarr[0].plot(range(11))
axarr[1].plot(range(11)[::-1])
axarr[0].tick_params(axis="x", which="both", length=0.)
axarr[1].set_xticks(range(0, 11, 3))
axarr[1].set_xticks(range(0, 11), minor=True)
plt.show()
which yields: https://i.stack.imgur.com/oc7y0.png
This works for removing the tick labels from a single axis when using sharex, but I don't see a solution to also remove the ticks..
import matplotlib.pylab as pl
pl.figure()
ax1=pl.subplot(211)
ax1.plot([0,10],[0,10])
ax2=pl.subplot(212, sharex=ax1)
ax2.plot([0,10],[10,0])
pl.setp(ax1.get_xticklabels(), visible=False)

Categories