Home > front end >  How to modify xtick label of plt in Matplotlib
How to modify xtick label of plt in Matplotlib

Time:12-17

The objective is to modify the xticklabel upon plotting pcolormesh and scatter.

However, I am having difficulties accessing the existing xtick labels.

Simply

ax = plt.axes()
labels_x = [item.get_text() for item in ax.get_xticklabels()]

which produced:

['', '', '', '', '', '']

or

fig.canvas.draw()
xticks = ax.get_xticklabels()

which produced:

['', '', '', '', '', '']

does not return the corresponding label.

May I know how to properly access axis tick labels for a plt cases.

For readability, I split the code into two section.

  1. The first section to generate the data used for plotting
  2. Second section deal the plotting

Section 1: Generate data used for plotting

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import math

np.random.seed(0)
increment=120
max_val=172800

aran=np.arange(0,max_val,increment).astype(int)
arr=np.concatenate((aran.reshape(-1,1), np.random.random((aran.shape[0],4))), axis=1)
df=pd.DataFrame(arr,columns=[('lapse',''),('a','i'),('a','j'),('b','k'),('c','')])

ridx=df.index[df[('lapse','')] == 3600].tolist()[0] 1 # minus 1 so to allow 3600 start at new row


df[('event','')]=0
df.loc[[1,2,3,10,20,30],[('event','')]]=1

arr=df[[('a','i'),('event','')]].to_numpy()
col_len=ridx
v=arr[:,0].view()

nrow_size=math.ceil(v.shape[0]/col_len)
X=np.pad(arr[:,0].astype(float), (0, nrow_size*col_len - arr[:,0].size),
       mode='constant', constant_values=np.nan).reshape(nrow_size,col_len)

mask_append_val=0  # This value must equal to 1 for masking
arrshape=np.pad(arr[:,1].astype(float), (0, nrow_size*col_len - arr[:,1].size),
       mode='constant', constant_values=mask_append_val).reshape(nrow_size,col_len)

Section 2 Plotting

fig = plt.figure(figsize=(8,6))
plt.pcolormesh(X,cmap="plasma")

x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)

plt.scatter(ys[:,u[:,1]].ravel() .5,xs[u[:,0]].ravel() 0.5,marker='*', color='r', s=55)

plt.gca().invert_yaxis()

xlabels_to_use_this=df.loc[:30,[('lapse','')]].values.tolist()

# ax = plt.axes()
# labels_x = [item.get_text() for item in ax.get_xticklabels()]
# labels_y = [item.get_text() for item in ax.get_yticklabels()]

plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title("Plot 2D array")
plt.colorbar()
plt.tight_layout()
plt.show()

Expected output

b

CodePudding user response:

I end up using Seaborn to address this issue.

Specifically, the following lines able to easily tweak the xticklabel

fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]

i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]

The full code for plotting is as below

import seaborn as sns
fig, ax = plt.subplots()

sns.heatmap(X,ax=ax)
x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)
g=sns.scatterplot(ys[:,u[:,1]].ravel() .5,xs[u[:,0]].ravel() 0.5,marker='*', color='r', s=55)


fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]

i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]

ax.set_xticklabels(newlabel)

ax.set_xticklabels(ax.get_xticklabels(),rotation = 90)

for ind, label in enumerate(g.get_xticklabels()):
    if ind % 2 == 0:  # every 10th label is kept
        label.set_visible(True)
    else:
        label.set_visible(False)


for ind, label in enumerate(g.get_yticklabels()):
    if ind % 4 == 0:  # every 10th label is kept
        label.set_visible(True)
    else:
        label.set_visible(False)
      
plt.xlabel('Elapsed (s)')
plt.ylabel('Hour (h)')
plt.title("Rastar Plot")
plt.tight_layout()
plt.show()

enter image description here

CodePudding user response:

This is how the plot could be generated using matplotlib's pcolormesh and scatter:

import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import pandas as pd
import numpy as np

np.random.seed(0)
increment = 120
max_val = 172800
aran = np.arange(0, max_val, increment).astype(int)
arr_df = np.concatenate((aran.reshape(-1, 1), np.random.random((aran.shape[0], 4))), axis=1)
df = pd.DataFrame(arr_df, columns=[('lapse', ''), ('a', 'i'), ('a', 'j'), ('b', 'k'), ('c', '')])
df[('event', '')] = 0
df.loc[[1, 2, 3, 10, 20, 30], [('event', '')]] = 1

col_len_lapse = 3600
col_len = df[df[('lapse', '')] == col_len_lapse].index[0]
nrow_size = int(np.ceil(v.shape[0] / col_len))

a_i_values = df[('a', 'i')].values
a_i_values_meshed = np.pad(a_i_values.astype(float), (0, nrow_size * col_len - len(a_i_values)),
                           mode='constant', constant_values=np.nan).reshape(nrow_size, col_len)

fig, ax = plt.subplots(figsize=(8, 6))
# the x_values indicate the mesh borders, subtract one half so the ticks can be at the centers
x_values = df[('lapse', '')][:col_len   1].values - increment / 2
# divide lapses for y by col_len_lapse to get hours
y_values = df[('lapse', '')][::col_len].values / col_len_lapse - 0.5
y_values = np.append(y_values, 2 * y_values[-1] - y_values[-2])  # add the bottommost border (linear extension)

mesh = ax.pcolormesh(x_values, y_values, a_i_values_meshed, cmap="plasma")

event_lapses = df[('lapse', '')][df[('event', '')] == 1]
ax.scatter(event_lapses % col_len_lapse,
           np.floor(event_lapses / col_len_lapse),
           marker='*', color='red', edgecolor='white', s=55)

ax.xaxis.set_major_locator(MultipleLocator(increment * 5))
ax.yaxis.set_major_locator(MultipleLocator(5))
ax.invert_yaxis()
ax.set_xlabel('X-axis (s)')
ax.set_ylabel('Y-axis (hours)')
ax.set_title("Plot 2D array")
plt.colorbar(mesh)

plt.tight_layout()  # fit the labels nicely into the plot
plt.show()

pcolormesh with seconds in the x-direction and hours in the y-direction

With Seaborn things can be simplified, adding new columns for hours and seconds, and using pandas' pivot (which automatically fills unavailable data with NaNs). Adding xtick_labels=5 sets the labels every 5 positions. (The star for lapse=3600 is at 1 hour, 0 seconds).

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

# df created as before

df['hours'] = (df[('lapse', '')].astype(int) // 3600)
df['seconds'] = (df[('lapse', '')].astype(int) % 3600)

df_heatmap = df.pivot(index='hours', columns='seconds', values=('a', 'i'))
df_heatmap_markers = df.pivot(index='hours', columns='seconds', values=('event', '')).replace(
    {0: '', 1: '★', np.nan: ''})

fig, ax = plt.subplots(figsize=(8, 6))

sns.heatmap(df_heatmap, xticklabels=5, yticklabels=5,
            annot=df_heatmap_markers, fmt='s', annot_kws={'color': 'lime'}, ax=ax)
ax.tick_params(rotation=0)

plt.tight_layout()
plt.show()

sns.heatmap from pivoted dataframe

PS: Instead of a 'seconds' column, a 'minutes' column also might be interesting.

CodePudding user response:

The docs mention the following:

Notes The tick label strings are not populated until a draw method has been called.

So, place ax.get_xticklabels() after plt.show(). Does that work? (also see 32700935)

  • Related