Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleanest way to set xtickslabel in specific position

I want to plot a histogram with binned data.

data

## x_da: 1,2,3,4,5
x_da = np.arange(1,1+5*1,1)
## bin setting 
bin_range = ["< 1 ","1 - 2","2 - 3","3 - 4","> 4"]
## Counts base on the bin(Already st)
y_da = np.array([178,2301,2880,1686,1715])      

Plot

fig = plt.figure(figsize= (5,3))
ax = plt.subplot(111)  
plt.bar(x_da,y_da,width=1,edgecolor='none',align='center', \
        fill=True,facecolor = "green",zorder = 1 ,alpha=0.5) 
ax.get_yaxis().set_tick_params(which='both', direction='out')
ax.get_xaxis().set_tick_params(which='both', direction='out')    

## Set the "range" mapping to its bar
ax.set_xticks(np.arange(1,1+5*1,1))
ax.set_xticklabels(bin_range,fontsize = 14)    

Figure

http://7xrn7f.com1.z0.glb.clouddn.com/16-3-9/18987922.jpg

My aim

  1. adjust xtickslines' positions which represent the extension of bar vertical edges like follows:

http://7xrn7f.com1.z0.glb.clouddn.com/16-3-9/5475187.jpg

  1. the xticklabels still locate in the same position(midpoint of each bar)

My attempt

My method was to creat a xticks contain those position above, and set the midpoint ones invisble.
With code like this:

ax.set_xticks(np.arange(0.5,1+10*0.5,1))
for xtick in ax.xaxis.get_ticklines()[1::2]:
    xtick.set_visible(False)     
ax.set_xticklabels(bin_range,fontsize = 14)

http://7xrn7f.com1.z0.glb.clouddn.com/16-3-9/78024677.jpg

The "invisible" approach has solved the issue in an easy way, but the xticklabels' position has changed too.

I don't want to add some " "(empty string) to the bin_range in order to jump one xtickline. Can someone offer some advice to solve this problem in the cleanest way?

like image 438
Han Zhengzu Avatar asked Mar 09 '16 15:03

Han Zhengzu


1 Answers

The easiest solution that I have found is to use the major xticks to denote the tick marks where the bars begin/end but to simply leave their labels blank. Then you can set the minor tick positions to be at the middle of the bars and set their labels to be the labels that you have created. This way you are managing two completely different set of ticks (major and minor) and can adjust their display independently.

Here is the code that I was able to come up with.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FixedLocator, FixedFormatter

barwidth = 1

x_da = np.arange(0,  5 * barwidth, barwidth)
y_da = np.array([178,2301,2880,1686,1715])

bin_labels = ["< 1 ","1 - 2","2 - 3","3 - 4","> 4"]

fig = plt.figure(figsize=(5,3))
ax = plt.subplot(111)
plt.bar(x_da,y_da,width=barwidth, edgecolor='none', align='center',
        fill=True, facecolor="green", zorder=1, alpha=0.5)

ax.get_yaxis().set_tick_params(which='both', direction='out')
ax.get_xaxis().set_tick_params(which='both', direction='out')

# Creat major ticks at the bars - 0.5 * barwidth plus one at the end
major_locations = np.append(x_da, x_da[-1] + barwidth) - (0.5 * barwidth)
ax.set_xticks(major_locations)

# Don't display *any* major ticks labels
ax.set_xticklabels('')

# Create minor ticks and labels
# Display the labels at MINOR ticks spaced between the bars
ax.xaxis.set_minor_locator(FixedLocator(x_da))
ax.xaxis.set_minor_formatter(FixedFormatter(bin_labels))

# Now actually hide the minor ticks but leave the labels
ax.tick_params(axis='x', which='minor', length=0, labelsize=14)

enter image description here

like image 195
Suever Avatar answered Nov 15 '22 05:11

Suever