How can i set the location of minor ticks in matplotlib - python

I want to draw a grid on the x-axis in a matplotlib plot at the positions of the minor ticks but not at the positions of the major ticks. My mayor ticks are at the positions 0, 1, 2, 3, 4, 5 and have to remain there. I want the grid at 0.5, 1.5, 2.5, 3.5, 4.5.
....
from matplotlib.ticker import MultipleLocator
....
minorLocator = MultipleLocator(0.5)
ax.xaxis.set_minor_locator(minorLocator)
plt.grid(which='minor')
The code above does not work since it gives the locations at 0.5, 1.0, 1.5, ... How can I set the positions of the minor ticks manually?

You can use matplotlib.ticker.AutoMinorLocator. This will automatically place N-1 minor ticks at locations spaced equally between your major ticks.
For example, if you used AutoMinorLocator(5) then that will place 4 minor ticks equally spaced between each pair of major ticks. For your use case you want AutoMinorLocator(2) to just place one at the mid-point.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import AutoMinorLocator
N = 1000
x = np.linspace(0, 5, N)
y = x**2
fig, ax = plt.subplots()
ax.plot(x, y)
minor_locator = AutoMinorLocator(2)
ax.xaxis.set_minor_locator(minor_locator)
plt.grid(which='minor')
plt.show()
Using AutoMinorLocator has the advantage that should you need to scale your data up, for example so your major ticks are at [0, 10, 20, 30, 40, 50], then your minor ticks will scale up to positions [5, 15, 25, 35, 45].
If you really need hard-set locations, even after scaling/changing, then look up matplotlib.ticker.FixedLocator. With this you can pass a fixed list, for example FixedLocator([0.5, 1.5, 2.5, 3.5, 4.5]).

Related

Vertical and Horizontal figures on one plot

I would like to create sth like the following graph in matplotlib:
I have x = [0, 1, ..., 10], and for each x I have values from range [0, 60]. Lets say that the black line is the quantile of values for a given i from range x. For selected i I want to add horizontally histogram (with parameter density = True) like in the picture with the possibility to control the width of this histogram (in the picture it goes from 2 to 5 but I would like to set fixed width). How can I do that?
Yes, this is relatively straightforward with inset_axes:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.random.randn(100)
ax.plot(x)
ylim = ax.get_ylim()
histax = ax.inset_axes([0.3, 0, 0.2, 1], transform=ax.transAxes)
histax.hist(x, orientation='horizontal', alpha=0.5 )
histax.set_facecolor('none')
histax.set_ylim(ylim)
plt.show()
You will probably want to clean up the axes etc, but that is the general idea.

Matplotlib colorbar: some ticks appear without labels

I'm using plr.scatter and logariphmic scale, and i'm trying to add some specific tick values to the colorbar, but it seems to work really arbitrary. See the example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib
from matplotlib.ticker import LogFormatter
x, y = np.meshgrid(np.linspace(0, 1, 30), np.linspace(0, 1, 30))
z = x**2 + 15*y**3 + 1.5
plt.figure(figsize=(9, 4.5))
plt.scatter(x, y, c=z, cmap=cm.jet, norm=matplotlib.colors.LogNorm(), vmin=1, vmax=20)
formatter = LogFormatter(10, labelOnlyBase=False)
cbar = plt.colorbar(ticks=[1, 2, 5, 10, 15, 20], format=formatter)
This code produced all the required major ticks, plus some minor ticks, but only labeled 1 and 10, while I need all numbers to be seen in colorbar. At first I though it was due to the fact that 1 and 10 are integer powers of 10, and other number are not, but...
...if I change the log base to 2, we can see tick labels at 1 and 2, which are powers of 2, but we also see labels at 5, 10 and 20, which are not. 15 did not appear this time too, but if I try adding 17 it works (not shown on the picture, but it does)
formatter = LogFormatter(2, labelOnlyBase=False)
What is this sorcery and how do I make matplotlib add exactly the labels I want to the ticks? I can do it manually by using
cbar.ax.set_yticklabels(['1', '2', '5', '10', '15', '20'])
but it seems redundant. Is there a better way?
You can format any axis ticks with formatter. Below is the example .
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib
from matplotlib.colors import LogNorm
x, y = np.meshgrid(np.linspace(0, 1, 30), np.linspace(0, 1, 30))
z = x**2 + 15*y**3 + 1.5
f, ax = plt.subplots(figsize=(9, 4.5))
p = plt.scatter(x, y, c=z, cmap=cm.jet, norm=LogNorm(vmin=1, vmax=20) )
v1 = np.linspace(z.min(), z.max(), 8, endpoint=True)
cbar=plt.colorbar(ticks=v1)
cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in v1]) # add the labels
LogFormatter and its subclasses use the minor_thresholds parameter to decide when to hide non-decade tick labels to prevent overcrowding. By default this will hide nearly all non-decade labels, but you can increase it to allow more labels to appear.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LogFormatter
from matplotlib.colors import LogNorm
x, y = np.meshgrid(np.linspace(0, 1, 30), np.linspace(0, 1, 30))
z = x**2 + 15*y**3 + 1.5
plt.figure(figsize=(9, 4.5))
cnorm = LogNorm(vmin=1, vmax=20)
plt.scatter(x, y, c=z, cmap=cm.jet, norm=cnorm)
# define minor_thresholds to be >= the range of the color scale
decades = np.ceil(np.log10(cnorm.vmax / cnorm.vmin))
formatter = LogFormatter(10, minor_thresholds=(decades, decades))
cbar = plt.colorbar(ticks=[1, 2, 5, 10, 15, 20], format=formatter)

X and Y Ticks on a 4x4 multiplot using matplotlib in Python

I am using Python3.6.5 from an Anaconda install.
I have 16 data files containing two columns of data. I am trying to make a plot that shows all the data in one 4x4 plot. I have managed to get all the plots plotted on a large 4x4 plot, but can't adjust the X and Y ticks. The X values range from 0 to 2000 and the Y values range from 0 to 4.5.
This is my current script:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import math
ph_values = [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5]
all_xs = []
all_ys = []
for ph in ph_values:
xs = []
ys = []
with open('rmsd_ph' + str(ph) + '.dat', "r") as f:
for line in f:
if line[0] != "#":
x,y = line.split()
xs.append(float(x))
ys.append(float(y))
all_xs.append(xs)
all_ys.append(ys)
fig, axes = plt.subplots(nrows=math.ceil(len(ph_values)/4), ncols=4, figsize=(6,6))
axes = axes.flatten()
for index,ph in enumerate(ph_values):
axes[index].plot(np.asarray(all_xs[index]),np.asarray(all_ys[index]))
plt.xticks(np.arange(0, 2000, step=500))
plt.tight_layout()
plt.savefig('test.pdf')
plt.show()
Currently the script outputs something that looks like this.
As you can see the last plot has the X-axis adjusted. I have not tried to adjust the Y-axis yet because I have not not been successful with the y axis.
Overall, I would like 4 ticks on both the y and x axis.
This is what I found that answered the problem I was having.
fig, axes = plt.subplots(nrows=math.ceil(len(ph_values)/4), ncols=4, figsize=(9,9))
axes = axes.flatten()
for index,ph in enumerate(ph_values):
axes[index].scatter(np.asarray(all_xs[index]),np.asarray(all_ys[index]), s=1)
plt.sca(axes[index]) <------------------ Fixed Problem
plt.xticks([0, 500, 1000, 1500, 2000]) <- Fixed Problem
plt.yticks([0, 1, 2, 3, 4, 5]) <---------- Fixed Problem
plt.title('pH:' + str(ph))
if (index % 4 == 0):
plt.ylabel('RMSD [$\\rm{\\AA}$]')
if (index >= 12):
plt.xlabel('Steps')
plt.tight_layout()
plt.savefig(output)
plt.show()
Here is an image of the result.
If I understand right that you'd like to have all x-axes comparable the same and so the y-axes, too, I'd recommend to try shared axes:
fig, axes = plt.subplots(4, 4, sharex=True, sharey=True, figsize=(6,6))

Multiple imshow-subplots, each with colorbar

I want to have a figure consisting of, let's say, four subplots. Two of them are usual line-plots, two of them imshow-images.
I can format the imshow-images to proper plots itself, because every single one of them needs its own colorbar, a modified axis and the other axis removed.
This, however, seems to be absolutely useless for the subplotting. Can anyone help me with that?
I use this for displaying the data of the "regular" plots above as a colormap (by scaling the input-array i to [ i, i, i, i, i, i ] for 2D and calling imshow() with it).
The following code first displays what I need as a subplot and the second one shows all I can do, which is not sufficient.
#!/usr/bin/env python
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
s = { 't':1, 'x':[1,2,3,4,5,6,7,8], 'D':[0.3,0.5,0.2,0.3,0.5,0.5,0.3,0.4] }
width = 40
# how I do it in just one plot
tot = []
for i in range(width):
tot.append(s['D'])
plt.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1))
plt.colorbar()
plt.axes().axes.get_xaxis().set_visible(False)
plt.yticks([0, 2, 4, 6], [s['x'][0], s['x'][2], s['x'][4], s['x'][6]])
plt.show()
f = plt.figure(figsize=(20,20))
plt.subplot(211)
plt.plot(s['x'], s['D'])
plt.ylim([0, 1])
#colorplot
sp = f.add_subplot(212)
#reshape (just necessary to see something)
tot = []
for i in range(width):
tot.append(s['D'])
sp.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1))
#what I can't do now but needs to be done:
#sp.colorbar()
#sp.axes().axes.get_xaxis().set_visible(False)
#sp.yticks([0, 200, 400, 600, 800, 1000], [s['x'][0], s['x'][200], s['x'][400], s['x'][600], s['x'][800], s['x'][1000]])
plt.show()
You can make use of matplotlibs object oriented interface rather than the state-machine interace in order to get better control over each axes. Also, to get control over the height/width of the colorbar you can make use of the AxesGrid toolkit of matplotlib.
For example:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colors import LogNorm
from matplotlib.ticker import MultipleLocator
s = {'t': 1,
'x': [1, 2, 3, 4, 5, 6, 7, 8],
'T': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],
'D': [0.3, 0.5, 0.2, 0.3, 0.5, 0.5, 0.3, 0.4]}
width = 40
tot = np.repeat(s['D'],width).reshape(len(s['D']), width)
tot2 = np.repeat(s['T'],width).reshape(len(s['D']), width)
fig, (ax1, ax2, ax3, ax4) = plt.subplots(1,4)
fig.suptitle('Title of figure', fontsize=20)
# Line plots
ax1.set_title('Title of ax1')
ax1.plot(s['x'], s['T'])
ax1.set_ylim(0,1)
ax2.set_title('Title of ax2')
ax2.plot(s['x'], s['D'])
# Set locations of ticks on y-axis (at every multiple of 0.25)
ax2.yaxis.set_major_locator(MultipleLocator(0.25))
# Set locations of ticks on x-axis (at every multiple of 2)
ax2.xaxis.set_major_locator(MultipleLocator(2))
ax2.set_ylim(0,1)
ax3.set_title('Title of ax3')
# Display image, `aspect='auto'` makes it fill the whole `axes` (ax3)
im3 = ax3.imshow(tot, norm=LogNorm(vmin=0.001, vmax=1), aspect='auto')
# Create divider for existing axes instance
divider3 = make_axes_locatable(ax3)
# Append axes to the right of ax3, with 20% width of ax3
cax3 = divider3.append_axes("right", size="20%", pad=0.05)
# Create colorbar in the appended axes
# Tick locations can be set with the kwarg `ticks`
# and the format of the ticklabels with kwarg `format`
cbar3 = plt.colorbar(im3, cax=cax3, ticks=MultipleLocator(0.2), format="%.2f")
# Remove xticks from ax3
ax3.xaxis.set_visible(False)
# Manually set ticklocations
ax3.set_yticks([0.0, 2.5, 3.14, 4.0, 5.2, 7.0])
ax4.set_title('Title of ax4')
im4 = ax4.imshow(tot2, norm=LogNorm(vmin=0.001, vmax=1), aspect='auto')
divider4 = make_axes_locatable(ax4)
cax4 = divider4.append_axes("right", size="20%", pad=0.05)
cbar4 = plt.colorbar(im4, cax=cax4)
ax4.xaxis.set_visible(False)
# Manually set ticklabels (not ticklocations, they remain unchanged)
ax4.set_yticklabels([0, 50, 30, 'foo', 'bar', 'baz'])
plt.tight_layout()
# Make space for title
plt.subplots_adjust(top=0.85)
plt.show()
You can change the locations and labels of the ticks on either axis with the set_ticks and set_ticklabels methods as in the example above.
As for what the make_axes_locatable function does, from the matplotlib site about the AxesGrid toolkit:
The axes_divider module provides a helper function
make_axes_locatable, which can be useful. It takes a existing axes
instance and create a divider for it.
ax = subplot(1,1,1)
divider = make_axes_locatable(ax)
make_axes_locatable returns an instance of the AxesLocator class,
derived from the Locator. It provides append_axes method that creates
a new axes on the given side of (“top”, “right”, “bottom” and “left”)
of the original axes.

How to plot pseudo-3d bar chart in matplotlib?

I'd like to prepare some statistics for my boss. The flat style of matplotlib bar chart would make them look cheap for those used to Excel charts, although for clarity, using styles like this probably should be avoided.
I'm not that far away, but I don't get how to give the right thickness of the bars:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
row = [0, 0, 0, 22, 0, 0, 4, 16, 2, 0, 4, 4, 12, 26]
length = len(row)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.arange(length)
y = np.zeros(14)
z = np.array(row)
width = 0.8
ax.bar3d(x, y, [0]*length, 0.5, 0.001, z)
ax.set_xticks(x + width/2)
ax.set_xticklabels(titles[2:], rotation=90)
ax.set_yticks(y)
ax.set_zlabel('count')
plt.show()
Result:
The thickness of the bars are set by the dx, dy arguments in ax.bar3d for which you have the values 0.5, 0.001. The issue, as I'm sure you noticed is that changing dy will change the length of the bar (in your case the untitled axis), but matplotlib helpfully rescales the y axis so the data fills it. This makes it look strange (I am assuming this is the problem, sorry if it isn't).
To remedy this you could set the y limits using ax.set_ylim(0, 0.002) (basically your y values go from 0->0.001). If you change either dy or the value of y given to bar3d which is currently 0, then you will need to update the limits accordingly.
Example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
row = [0, 0, 0, 22, 0, 0, 4, 16, 2, 0, 4, 4, 12, 26]
length = len(row)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.bar3d(range(length), [0]*length, [0]*length, 0.5, 0.001, row)
ax.set_ylim(-0.005, 0.005)
plt.show()

Categories