change the position (move) of tick labels when plotting with matplotlib - python

i am plotting with matplotlib. the code is the following (zvals has the values)
cmap = mpl.colors.ListedColormap(['darkblue', 'blue', 'lightblue','lightgreen','yellow','gold','orange','darkorange','orangered','red'])
bounds=[0, 10,20,30,40,50,60,70,80,100,200,1000]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
img2 = plt.imshow(zvals,interpolation='nearest',
cmap = cmap,
norm=norm,
origin='lower')
xlocations = na.array(range(30)) + 0.5
xticks(xlocations, [str(x+1) for x in arange(30)], rotation=0, size=5)
gca().xaxis.set_ticks_position('none')
gca().yaxis.set_ticks_position('none')
grid(True)
this results in the following picture:
http://imageshack.us/a/img145/7325/histogrammoverview.png
i would like to move the labels of the xticks (1,2,3,..) to the left a bit, so they are underneath the corresponding color boxes. correspondingly i would also like to move the labels of the yticks (user1 and user2) down a bit so they are displayed correctly. how can this be done?
EDIT: as a matter of fact i could change the following line
xlocations = na.array(range(30)) + 0.5
to
xlocations = na.array(range(30))
then the resulting pictures is like this:
http://imageshack.us/a/img338/7325/histogrammoverview.png
please see that the grid is going "through" the colored boxes, which is not what i want. i'd like the grid to edge the colored boxes as in the above picture. in this version though the labels (1,2,3,...) are placed correctly underneath the boxes. how can i have correctly places labels (underneath the colored boxes) and a grid which is around the colored boxes and not through the middle of the colored boxes.
SOLUTION
this solution works (as suggested by the answer):
periods = 30
xlocations = na.array(range(periods))
xminorlocations = na.array(range(periods))+0.5
xticks(xlocations, [str(x+1) for x in arange(periods)], rotation=0, size=5)
plt.set_xticks(xminorlocations, minor=True)
grid(True, which='minor', linestyle='-')
result: hxxp://imageshack.us/a/img9/7325/histogrammoverview.png

I think that you can manage that by
Setting the major tick locations to the middle of each square.
Setting the minor ticks to the edges of each square.
Setting the grid to show only in the minor ticks.
The grid can be showed only in the minor ticks using
plt.grid(True, which='minor')
I would set the line style to '-' too.

Related

Matplotlib: Fit plot with labels into subplot area

I want to make a plot with a grid of thumbnails on the left and a line plot on the right. Here is a minimal example
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
for i in range(n_grid):
for j in range(n_grid):
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(i,j))
plt.imshow(np.random.random((16,16)))
ax.set_axis_off()
### Line plot
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(0,n_grid), rowspan=n_grid-1, colspan=n_grid)
plt.plot(np.cumsum(np.random.random(100)), label='Random Sum')
plt.xlim([0, 100])
plt.ylim(0,50)
plt.xlabel('Number', fontsize=12)
plt.ylabel('Sum', fontsize=12)
plt.figtext(0.5, 0.01, f'Unique identifier', ha='center', va='baseline')
#plt.tight_layout()
plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_1.png', dpi=96)
The problem is that the yticklabels and ylabel stick over the center into the area of the thumbnails. The lineplot on the right is too wide.
One common solution found on the internet is using automatic resizing with tight_layout(), so I change the last three lines to
plt.tight_layout()
#plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_2.png', dpi=96)
This does not rescale the lineplot, but instead makes the wspace and hspace attributes so big I get way too much whitespace between the thumbnails.
I am looking for a solution to either
Set wspace and hspace of only the right subplot, not all of them together, or
resize the lineplot to fit into the designated area, without the labels sticking out
It would seem that this is an easy problem, but despite searching for about 2 hours and digging around in the object properties with iPython I found nothing suitable. All solutions seem to change the size and padding of the subplots, not fitting a plot into the area defined with subplot2grid. The only other solution I can think of is a hack that calculates a modified aspect from the value ranges to make the lineplot always a given percentage thinner.
You can play around with subfigures. For example, if you do:
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
# add 2 subfigures
subfigs = fig.subfigures(1, 2, wspace=0)
# add thumbnail grid into left subfig
gsLeft = subfigs[0].add_gridspec(n_grid, n_grid)
axLeft = []
for i in range(n_grid):
for j in range(n_grid):
axLeft.append(subfigs[0].add_subplot(gsLeft[i, j]))
axLeft[-1].imshow(np.random.random((16,16)))
axLeft[-1].set_axis_off()
### Line plot
gsRight = subfigs[1].add_gridspec(3, 1)
axRight = subfigs[1].add_subplot(gsRight[:2, 0])
axRight.plot(np.cumsum(np.random.random(100)), label='Random Sum')
axRight.set_xlim([0, 100])
axRight.set_ylim(0,50)
axRight.set_xlabel('Number', fontsize=12)
axRight.set_ylabel('Sum', fontsize=12)
# adjust subfigures here (play around with these to get the desired effect)
subfigs[0].subplots_adjust(wspace=0.03, hspace=0.03, bottom=0.05, top=0.95, left=0.05, right=0.95)
subfigs[1].subplots_adjust(left=0.01)
# add title (here I've had to add it to the left figure, so it's not centred,
# in my test adding it to the figure itself meant it was not visible, although
# the example in the Matplotlib docs suggests it should work!)
# fig.suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
subfigs[0].suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
fig.savefig("plot_1.png", dpi=150)
This gives:
but you can play around with the values to adjust it as you like.

Removing ticks when using grid with imshow matplotlib

In this question, they answer how to correctly use grid with imshow with matplotlib. I am trying to do the same as they do, but I want to remove all ticks (x and y). When I try to do it, it also eliminates the grid and I just the image displayed without grid and ticks. My code is:
fig, ax = plt.subplots()
data = np.random.rand(20,20)
ax.imshow(data)
ax.set_xticks(np.arange(20))
ax.set_xticklabels(np.arange(20))
ax.set_xticks(np.arange(20)+0.5, minor=True)
ax.grid(which='minor',color='w',axis='x',linewidth=6)
ax.axes.xaxis.set_visible(False)
ax.axes.yaxis.set_visible(False)
plt.show()
Does anyone how to remove the ticks while keeping the grid (along the x axis in my case)?
Removing the axes (via set_visible(False)) will also remove the grid.
However, there's a workaround setting both spines and tick marks/labels to be invisible individually:
fig, ax = plt.subplots()
data = np.random.rand(20,20)
ax.imshow(data)
ax.set_xticks(np.arange(20))
ax.set_xticklabels(np.arange(20))
ax.set_xticks(np.arange(20)+0.5, minor=True)
ax.grid(which='minor',color='w',axis='x',linewidth=6)
# set axis spines (the box around the plot) to be invisible
plt.setp(ax.spines.values(), alpha = 0)
# set both tick marks and tick labels to size 0
ax.tick_params(which = 'both', size = 0, labelsize = 0)
plt.show()
Gives you output as:
Note, you might need to adjust xlim/ylim and grid parameters to fit your needs.
This is not perfect, but you can just set the tick label as an empty list.
ax.axes.get_xaxis().set_ticks([])
ax.axes.get_yaxis().set_ticks([])
Only the minor xticks, used in the grid, remain.

Matplotlib - How to draw a line from the top of any bar to the y - axis?

I created a cumulative histogram. Now I want to draw a line from top of any bin to the y-axis in that histogram and show the value of it like this:
Can you show me the way to do?
Below is my code to draw that histogram:
plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False
plt.figure(figsize=[8, 6])
plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black'
,cumulative=-1, density=True
,histtype='barstacked')
plt.xlabel('Number of Days')
plt.ylabel('Density')
Thank you so much!
Oneliner:
plt.axhline(y, color='k', linestyle='dashed', linewidth=1)
Use this to add a horizontal line to your histogram.
Place your mean or value of y in place of y in the above code snippet.
Simply drawing a horizontal line rises two problems:
The line will be drawn on top of the bars, from the left to the right. To have it behind the bars, use zorder=0.
The line will still be visible at the far left, as there are no bars there. Changing the x-axis to a "tight" layout with plt.autoscale(enable=True, axis='x', tight=True) solves that.
To add a new tick at the specific y-position, you can take the list of existing ticks, create a list including the new tick and set those as the new ticks.
To change the color of the newly added tick, you first find its index in the list, and then change the color of the tick with that index.
One problem with this approach, is that the new tick might overlap with an existing tick. This could be solved by looping through the list and if an existing tick is nearer than some epsilon to the new tick, remove the existing tick. This is not yet implemented in the code example.
Alternatively, the tick value could be displayed to the left of the axis, on top of the horizontal line. Of course, that would lead to a problem in case there wouldn't be enough place for the text.
You might want to round the value of the special tick to the nearest hundredths to prevent that the other ticks also get displayed with more digits.
I created an example with simulated data:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({"days": np.random.normal(25, 10, 10000)})
plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False
plt.figure(figsize=[8, 6])
bin_heights, _, _ = plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black',
cumulative=-1, density=True,
histtype='barstacked')
plt.autoscale(enable=True, axis='both', tight=True) # use axis='x' to only set the x axis tight
special_y = bin_heights[15]
# draw a horizontal line, use zorder=0 so it is drawn behind the bars
plt.axhline(special_y, 0, 1, color='red', linestyle='dashed', linewidth=1, zorder=0)
plt.yticks(list(plt.yticks()[0]) + [special_y]) # add a tick in y for special_y
# find the index of special_y in the new ticks (ticks are sorted automatically)
index_special_y = list(plt.yticks()[0]).index(special_y)
plt.gca().get_yticklabels()[index_special_y].set_color('red') # change the color of the special tick
plt.xlabel('Number of Days')
plt.ylabel('Density')
plt.show()

Matplotlib Tick lines and labels of different color

I am trying to plot a 2d image using a specific colormap in matplotlib and I need to change the color of the ticks to white. But when I do so, the labels or tick numbers change of color too and they become invisible on a white background.
How can I change only the color of the tick lines and not the color of their label or associated number???
Example
import matplotlib.pyplot as plt
import numpy as np
fig=plt.figure()
ax=fig.add_subplot(111)
pc=ax.pcolormesh(xf,yf,F, vmin=F.min(), vmax=F.max(), cmap=colormap)
fig.colorbar(pc,orientation='horizontal',label=r'Intensity',format='%1.1f', ticks=np.arange(-0.1,0.6,0.1), pad=0.1)
ax.set_xlabel("RA offset [arcsec]")
ax.set_ylabel("DEC offset [arcsec]")
ax.set_xlim(3.0,-3.0)
ax.set_ylim(-3.0,3.0)
########## ticks
### x axis
major_ticks = np.arange(-2.0, 3.0, 2.0)
minor_ticks = np.arange(-3.0, 4.0, 1.0)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
after making your Axes and setting the appropriate tick locations, you can access the ticks with ax.get_xticklines() and ax.get_yticklines(). If you iterate over all the xticks or yticks, you can change their colours using set_color()
For minor ticks, you can use ax.xaxis.get_minorticklines(). Note that you could also use ax.xaxis.get_majorticklines() in place of ax.get_xticklines() if you prefer.
for tick in ax.get_xticklines():
tick.set_color('red')
for minortick in ax.xaxis.get_minorticklines():
minortick.set_color('r')
for tick in ax.get_yticklines():
tick.set_color('green')
Although the question is answered, I think that it is simpler to use
ax.tick_params(axis='y', which='both', color='w')
to change the colour of the ticks of the y axis without changing the labels colour (the argument which chooses major, minor or both).
To change the colour of the ticks and labels the command would be
ax.tick_params(axis='y', which='both', colors='w')
By simpler, I mean that it uses fewer lines to achieve the same result.

Adding a colorbar and a line to multiple imshow() plots

I have this source code:
idx=0
b=plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B=np.zeros((2*len(self.Chan),len(b[0])))
B[idx,:]=20*log10(b[0])
c=plt.psd(dOD_filt[:,idx],Fs=self.fs,NFFT=512)
C=np.zeros((2*len(self.Chan),len(b[0])))
C[idx,:]=20*log10(c[0])
for idx in range(2*len(self.Chan)):
b=plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B[idx,:]=20*log10(b[0])
c=plt.psd(dOD_filt[:,idx],Fs=self.fs,NFFT=512)
C[idx,:]=20*log10(c[0])
## Calculate the color scaling for the imshow()
aux1 = max(max(B[i,:]) for i in range(size(B,0)))
aux2 = min(min(B[i,:]) for i in range(size(B,0)))
bux1 = max(max(C[i,:]) for i in range(size(C,0)))
bux2 = min(min(C[i,:]) for i in range(size(C,0)))
scale1 = 0.75*max(aux1,bux1)
scale2 = 0.75*min(aux2,bux2)
fig, axes = plt.subplots(nrows=2, ncols=1,figsize=(7,7))#,sharey='True')
fig.subplots_adjust(wspace=0.24, hspace=0.35)
ii=find(c[1]>frange)[0]
## Making the plots
cax=axes[0].imshow(B, origin = 'lower',vmin=scale2,vmax=scale1)
axes[0].set_ylim((0,2*len(self.Chan)))
axes[0].set_xlabel(' Frequency (Hz) ')
axes[0].set_ylabel(' Channel Number ')
axes[0].set_title('Pre-Filtered')
cax2=axes[1].imshow(C, origin = 'lower',vmin=scale2,vmax=scale1)
axes[1].set_ylim(0,2*len(self.Chan))
axes[1].set_xlabel(' Frequency (Hz) ')
axes[1].set_ylabel(' Channel Number ')
axes[1].set_title('Post-Filtered')
axes[0].annotate('690nm', xy=((ii+1)/2, len(self.Chan)/2-1),
xycoords='data', va='center', ha='right')
axes[0].annotate('830nm', xy=((ii+1)/2, len(self.Chan)*3/2-1 ),
xycoords='data', va='center', ha='right')
axes[1].annotate('690nm', xy=((ii+1)/2, len(self.Chan)/2-1),
xycoords='data', va='center', ha='right')
axes[1].annotate('830nm', xy=((ii+1)/2, len(self.Chan)*3/2-1 ),
xycoords='data', va='center', ha='right')
axes[0].axis('tight')
axes[1].axis('tight')
## Set up the xlim to aprox frange Hz
axes[0].set_xlim(left=0,right=ii)
axes[1].set_xlim(left=0,right=ii)
## Make the xlabels become the actual frequency number
tickslabel=np.zeros((ii))
ticks = r_[0:ii:5]
tickslabel = linspace(0.,2.,size(ticks))
axes[0].set_xticks(ticks)
axes[0].set_xticklabels(tickslabel)
axes[1].set_xticks(ticks)
axes[1].set_xticklabels(tickslabel)
## Draw a line to separate the two different wave lengths, and name each region
l1 = Line2D([0,ii],[28,10],ls=':',color='black')
axes[0].add_line(l1)
axes[1].add_line(l1)
This code generates this figure:
The fixed code to make the xticks looks properly are already inside the code, and the new plot is also shown.
How can I add a single colorbar (and give it a title) to both this subplots? (they are at same scale)
This colorbar should occupy the whole left side of the figure.
Inside the code there`s a place I try to draw a line in both figures (at the same place), but none of those are shown. Why is that?
If you need any more information about my code (like the size of the data entered, just ask).
Your ticks variable appears to be all zeros:
ticks=np.zeros((ii))
but it should enumerate X locations (in axis coordinates) where you'd like the tick marks to go. When you call set_xticklabels, the list gives the text to show for each tick.
Here's a simple example showing how xlim, set_xticks, and set_xticklabels interact:
from pylab import *
x = arange(128*128).reshape((128,128))
matshow(x)
xlim(right=64)
# xticks: where the xticks should go (indexes into x's columns)
xticks = r_[0:64:25]
gca().set_xticks(xticks)
# labels: text to show for each element of xticks
# here, we apply a multiplier just to show how the
# labels can differ from the xticks.
labels = ['%.1f' % (x,) for x in xticks * pi / 2]
gca().set_xticklabels(labels)
show()
As already mentioned, you need to have the xticks not be zeros. You could use something like
xticks = linspace(0.,2.,5)
which will give you 5 points between 0.0 and 2.0. To get a color bar use
fig.colorbar()
which is demonstrated at http://matplotlib.sourceforge.net/examples/pylab_examples/colorbar_tick_labelling_demo.html
Since the data has the same scale, adding a colorbar for either set of data should do the trick for you, but you might have to adjust its placement.

Categories

Resources