1

I'm trying to left align the y ticks on a barh plot following the ad hoc matplotlib documentaion. However, I get 2 sets of xticks labelling: a character type one (wanted) and a numerical one (unwanted). How can I remove the numerical ticks?

Here is the script:

import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt#Read Data from schedule.csv
import pdb
import mpl_toolkits.axisartist as axisartist

plot_bool = "yes"

df = pd.read_csv("so.csv")
c_dict={
    'invitro_exp2_exp1_exp':          '#b9f1ac', 
    'invitro_exp2_exp1_writing':      '#2faa12',
    'invitro_exp2_exp3_exp':      '#d0daff', 
    'invitro_exp2_exp3_writing':  '#1030fa',
    'clinical':                     '#ffa4a4', 
    'writing_clinical':             '#ff0000',
    'clinical_GPU':                 '#d0d0d0', 
    'writing_clinical_GPU':         '#7a7a7a',
    }


df.start=pd.to_datetime(df.start, format='%d-%m-%y')
df.end=pd.to_datetime(df.end, format='%d-%m-%y')#Add Duration
df['duration']=df.end-df.start
df.duration=df.duration.apply(lambda x: x.days+1)#sort in ascending order of start date
df=df.sort_values(by='start', ascending=True)#project level variables
p_start=df.start.min()
p_end=df.end.max()
p_duration=(p_end-p_start).days+1#Add relative date
df['rel_start']=df.start.apply(lambda x: (x-p_start).days)#Create custom x-ticks and x-tick labels
x_ticks=[i for i in range(p_duration+1)]
x_labels_full=[(p_start+dt.timedelta(days=i)).strftime('%d-%m-%Y') 
          for i in x_ticks]######  PLOTTING GANTT CHART ######
x_labels=[(p_start+dt.timedelta(days=i)).strftime('%b-%Y') 
          for i in x_ticks]######  PLOTTING GANTT CHART ######
idxlist = []
chosen_dates = [
    "01-02-2023",
    "01-05-2023",
    "01-08-2023",
    "01-11-2023",
    "01-02-2024",
    "01-05-2024",
    "01-08-2024",
    "01-11-2024",
    "01-01-2025",
    ]
for count, date in enumerate(x_labels_full):
  if date in chosen_dates:
    idxlist.append(count)

x_ticks_display = list( x_ticks[i] for i in idxlist )
x_labels_display = list( x_labels[i] for i in idxlist )

fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111, axes_class=axisartist.Axes)
for i in range(df.shape[0]):
    color=c_dict[df.Department[i]]
    #plt.barh(y=df.Task[i], 
    ax.barh(y=df.Task[i], 
        left=df.rel_start[i], 
        height=0.3,
        width=df.duration[i], 
        alpha=1, 
        color=color,
)
ax.axis["left"].major_ticklabels.set_ha("left")
ax.tick_params(axis='y', labelsize=7)
plt.gca().invert_yaxis()
ax.set_xticklabels(x_labels_display, fontsize=7)
ax.set_xticks(idxlist, x_labels_display, fontsize=4)
ax.grid( alpha = 0.4)
plt.tight_layout()
plt.savefig("figures/milestones.png", dpi = 200)
if plot_bool == "yes":
  plt.show()
if plot_bool == "no":
  plt.show(block=False)
plt.close()

Here is the so.csv

,Task,start,end,Department
0,exp1 vs exp2: Adjusting setup,01-02-23,01-04-23,invitro_exp2_exp1_exp
1,exp1 vs exp2: Modify code for exp2,01-02-23,01-05-23,invitro_exp2_exp1_exp
1,exp1 vs exp2: Acquiring data,01-04-23,01-07-23,invitro_exp2_exp1_exp
1,exp1 vs exp2: Analyzing data,01-06-23,01-09-23,invitro_exp2_exp1_exp
4,exp1 vs exp2: Writing manuscript,01-08-23,01-11-23,invitro_exp2_exp1_writing
1,exp2 vs exp3: Modify code for exp3,01-05-23,01-08-23,invitro_exp2_exp3_exp
1,exp2 vs exp3: Acquiring data,01-08-23,01-11-23,invitro_exp2_exp3_exp
1,exp2 vs exp3: Analyzing data,01-10-23,01-01-24,invitro_exp2_exp3_exp
4,exp2 vs exp3: Writing manuscript,01-11-23,01-02-24,invitro_exp2_exp3_writing
2,Clinical exp4: Writing ethic protocol,28-07-23,01-11-23,clinical
3,Clinical exp4: Recruit 40 patients,01-11-23,01-04-24,clinical
4,Clinical exp4: Analyze clinical data,01-03-24,01-06-24,clinical
4,Clinical exp4: Writing manuscript,01-05-24,01-08-24,writing_clinical
2,Clinical exp4: Writing code,01-12-23,01-03-24,clinical_GPU
2,Clinical exp4: Writing ethic protocol 2,01-02-24,01-07-24,clinical_GPU
4,Clinical exp4: Recruit 20 patients,28-06-24,01-11-24,clinical_GPU
4,Clinical exp4: Analyze data,01-08-24,01-11-24,clinical_GPU
4,Clinical exp4: Writing manuscript 2,01-10-24,01-01-25,writing_clinical_GPU

Edit 2

I edited the code by adding for tick in ax.get_yticklabels(): tick.set_ha("left") and removing the axisartist dependencies. Much better: I could left align the yticks but now yticks are inside the plot

enter image description here

The code is as follows:

import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt#Read Data from schedule.csv
import pdb

plot_bool = "yes"

df = pd.read_csv("so.csv")
c_dict={
    'invitro_exp2_exp1_exp':          '#b9f1ac', 
    'invitro_exp2_exp1_writing':      '#2faa12',
    'invitro_exp2_exp3_exp':      '#d0daff', 
    'invitro_exp2_exp3_writing':  '#1030fa',
    'clinical':                     '#ffa4a4', 
    'writing_clinical':             '#ff0000',
    'clinical_GPU':                 '#d0d0d0', 
    'writing_clinical_GPU':         '#7a7a7a',
    }


df.start=pd.to_datetime(df.start, format='%d-%m-%y')
df.end=pd.to_datetime(df.end, format='%d-%m-%y')#Add Duration
df['duration']=df.end-df.start
df.duration=df.duration.apply(lambda x: x.days+1)#sort in ascending order of start date
df=df.sort_values(by='start', ascending=True)#project level variables
p_start=df.start.min()
p_end=df.end.max()
p_duration=(p_end-p_start).days+1#Add relative date
df['rel_start']=df.start.apply(lambda x: (x-p_start).days)#Create custom x-ticks and x-tick labels
x_ticks=[i for i in range(p_duration+1)]
x_labels_full=[(p_start+dt.timedelta(days=i)).strftime('%d-%m-%Y') 
          for i in x_ticks]######  PLOTTING GANTT CHART ######
x_labels=[(p_start+dt.timedelta(days=i)).strftime('%b-%Y') 
          for i in x_ticks]######  PLOTTING GANTT CHART ######
idxlist = []
chosen_dates = [
    "01-02-2023",
    "01-05-2023",
    "01-08-2023",
    "01-11-2023",
    "01-02-2024",
    "01-05-2024",
    "01-08-2024",
    "01-11-2024",
    "01-01-2025",
    ]
for count, date in enumerate(x_labels_full):
  if date in chosen_dates:
    idxlist.append(count)

x_ticks_display = list( x_ticks[i] for i in idxlist )
x_labels_display = list( x_labels[i] for i in idxlist )

fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111)
for i in range(df.shape[0]):
    color=c_dict[df.Department[i]]
    #plt.barh(y=df.Task[i], 
    ax.barh(y=df.Task[i], 
        left=df.rel_start[i], 
        height=0.3,
        width=df.duration[i], 
        alpha=1, 
        color=color,
)
ax.tick_params(axis='y', labelsize=6, direction='out')
ax.invert_yaxis()
ax.set_xticks(idxlist, x_labels_display, fontsize = 6)
for tick in ax.get_yticklabels(): tick.set_ha("left")
ax.grid( alpha = 0.4)
plt.tight_layout()
plt.savefig("figures/milestones.png", dpi = 200)
if plot_bool == "yes":
  plt.show()
if plot_bool == "no":
  plt.show(block=False)
plt.close()


ecjb
  • 5,169
  • 12
  • 43
  • 79
  • 1
    You seem to have two sets of axes on top of each other. Did you try `ax.invert_yaxis()` instead of `plt.gca().invert_yaxis()` (or at least a test without that call)? Also you are calling both `ax.set_xticks()` (with labels) and `ax.set_xticklabels()`. Better leave out `ax.set_xticklabels()` (or call it after `ax.set_xticks()`). – JohanC Jan 10 '22 at 16:32
  • Many thanks for your comment @JohanC. To your points: 1. I would be glad no to use axisartist.Axes but this was the one mentioned in the code in the documentation. I just posted a question exactly for this problem there: https://stackoverflow.com/questions/70653824/attributeerror-axessubplot-object-has-no-property-axes-class-when-trying-to?noredirect=1#comment124901762_70653824 How could I adapt the code in order not use it?. – ecjb Jan 10 '22 at 16:44
  • 2. using `ax.invert_yaxis()` instead of `plt.gca().invert_yaxis()` worked grea! thanks. 3. it seems that `ax.set_xticks(idxlist, x_labels_display, fontsize = 3)` does not have any effect on the font size. What shall I do to decrease the size of xticks fonts? – ecjb Jan 10 '22 at 16:46
  • Many thanks for your comment @JohanC. Unfortunately `ax.set_xticklabels(x_labels_display, fontsize = 3)` after `ax.set_xticks(idxlist)` did not have any effect in the fontsize – ecjb Jan 10 '22 at 16:58
  • `ax.axis["bottom"].major_ticklabels.set_fontsize(4)` seems to work for these axis artists – JohanC Jan 10 '22 at 17:10
  • Many thanks @JohanC! Lots of improvement: Thanks to you I could remove the axisartist dependency, left align the yticks and set the font of both xticks and yticks. The only remaining problem is that the yticks are inside the plots. I added a screenshot of the output as well as the latest code in the edited question – ecjb Jan 10 '22 at 17:19
  • I tried to run your code. On my system it doesn't create the dummy numeric ticks. To change the direction of the ticks: `ax.tick_params(axis='y', ...., direction='out')`. Or `ax.tick_params(axis='y', ...., length=0)` to remove the ticks completely. – JohanC Jan 10 '22 at 17:21
  • @JohanC. Indeed the dummy numeric ticks are now gone. I also changed the line in `ax.tick_params(axis='y', labelsize=6, direction='out')` But the yticks are still inside the plot. It seems I just can't load a screenshot. Tried many times but it takes very long – ecjb Jan 10 '22 at 17:25
  • @JohanC. I could finally load the screenshot and I edited the code again. Do you have the same output on your system (with the yticks inside the plots)? – ecjb Jan 10 '22 at 17:29
  • See https://stackoverflow.com/questions/15882249/matplotlib-aligning-y-ticks-to-the-left You need to calculate `pad` (or experiment a bit to find a good value). – JohanC Jan 10 '22 at 17:39
  • @JohanC. Yes! I just found it as well! Many thanks!! – ecjb Jan 10 '22 at 17:41

0 Answers0