0

Here is an example plot along with a dataset.

fig, ax = plt.subplots(figsize = (5,5))
x_data = np.arange(0,5,1)
y_data = [10,20,4,1,28]
x_labels = ['Val1', 'Val2', 'A Lengthy', "Unexpected", 'Val5']
ax.bar(x_data, y_data, tick_label = x_labels)

enter image description here

I want to move the xticklabel Unexpected a little to the right. So I thought of using this

for val in ax.get_xticklabels():
    if val.get_text() == "Unexpected":
        x,y = val.get_position()
        print(f'Old Position [{x},{y}]')
        val.set_y(-0.03)
        val.set_x(x + 0.25)
        x,y = val.get_position()
        print(f'New Position [{x},{y}]')

Here is the outcome

enter image description here

The label moves downwards but not towards the right.

I want to know why is set_x not working as expected. Is there anything overriding it? I got a solution online that uses transforms. Is that overriding the set_x command?

Dhruv
  • 117
  • 11

2 Answers2

0

if your question is on why set_x is not working, you can refer to this post and response from explorerDude on how to use it.

However, if you are trying to get the readability better, a simple solution would be to use rotation of the axis by some degrees.

But, if you require only the Unexpected label to be lower, the below code using set_transform should do the trick... Use dx and dy to move the label by as many pixels as you need.

Code

import matplotlib.transforms as trns

fig, ax = plt.subplots(figsize = (5,5))
x_data = np.arange(0,5,1)
y_data = [10,20,4,1,28]
x_labels = ['Val1', 'Val2', 'A Lengthy', "Unexpected", 'Val5']
ax.bar(x_data, y_data, tick_label = x_labels)

dx = 10/72.; dy = -10/72. 
offset = trns.ScaledTranslation(dx, dy, fig.dpi_scale_trans)

for val in ax.get_xticklabels():
    if val.get_text() == "Unexpected":
        x,y = val.get_position()
        val.set_transform(val.get_transform() + offset)
        x,y = val.get_position()

Output

enter image description here

Redox
  • 9,321
  • 5
  • 9
  • 26
  • Thanks for the link to why `set_x` didn't work. I explored a little more and will add a more detailed answer to why `set_x` didn't work – Dhruv Jun 19 '22 at 19:22
  • Also for transform, we could use the answer [here](https://stackoverflow.com/questions/48326151/moving-matplotlib-xticklabels-by-pixel-value) as it only uses `x` and `y` values and not that `fig.dpi_scale_trans` – Dhruv Jun 19 '22 at 19:37
  • Nice, just to clarify I think that the question is asking exactly what you are answering in your first sentence. The question states quite clearly that the solution to the problem is in the other linked post and just asks why is `set_x` overriden. – My Work Jun 19 '22 at 19:59
0

As in the link provided by @Redox, the reason is that the call to set_x is overriden when the rendering is done.
It can be seen by making some changes to the custom set_x made by @explorerDude as shown in the link.

import types

SHIFT = 0.1 # Data coordinates
def func(self, x):
    print("set_x called with x =", x)
    mlt.text.Text.set_x(self, x + SHIFT)

for label in ax.xaxis.get_majorticklabels():
    if label.get_text() == "Unexpected":
        label.set_x = types.MethodType( lambda self,x: func(self, x), label)
        print("mycall")

It's output

mycall
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3
set_x called with x = 3

As seen set_x is called afterwards internally. It's argument is 3 and is calculated internally. So no matter what we write to set_x, it's overriden.

However as seen in func, matplotlib.text.Text.set_x is called with x+SHIFT which changes the position in every call and the final result is as below.

enter image description here

However overriding internal functions seems dangerous and instead using transform as in @Redox's answer or here is better

Dhruv
  • 117
  • 11