BUG: Fixes #36918 boxplots with matplotlib
State: closed

Fixes #36918

I believe that the @TomAugspurger in #35393 found out that, when `sharex` or `sharey` are enabled, matplotlib counts the number of ticks of an axis in a subplot as the total number of ticks of the shared axis in all subplots. Since matplotlib became stricter with `set_xticks`, the proposed workaround was replicating the labels n times where n is "total number of ticks"/"number of ticks of this subplot"="number of subplots".

However, that fix was implemented only for vertical boxplots and not for horizontal boxplots, hence why the issue.
Also, that fix causes the labels of the shared axis to be drawn n times (where n is the number of subplots) in some cases, see below (matplotlib 3.3.2, code adapted from the one posted by the author of issue 36918)

```
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

df1 = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))

f, axs = plt.subplots(nrows=2, ncols=1, figsize=(10,7), sharex=True)
df1.boxplot(ax=axs[0], vert=True)
df2.boxplot(ax=axs[1], vert=True)

plt.show()
```

![before-3 3 2](https://user-images.githubusercontent.com/36233478/95907150-69242300-0d9b-11eb-9ec8-652744d9ad0e.png)

In this fix I used `set_major_formatter` with a `FixedFormatter` which fixes the issue and does not overdraw the labels:

![after-3 3 2](https://user-images.githubusercontent.com/36233478/95907331-a7214700-0d9b-11eb-960c-a566e90cc040.png)


Labels:
name: Stale

Comments:
Sorry I've now added a test and a note in the 1.2.0 rst
Thanks for the feedback about the tests, I looked better into the issue of the duplicated and empty ticks and checked better if it was really related to the shared axis or not.

Turns out that it was not.

The problem is that when `matplotlib`'s `boxplot` is called multiple times on the same axis (or on different shared axes) with `manage_ticks=True` (which is the default), it will append a new list of locations to the previous one of the `FixedLocator` without removing duplicates. See https://github.com/matplotlib/matplotlib/blob/c8203fb8ed1a24fb999942340e55ea4a7a9fe6e8/lib/matplotlib/axes/_axes.py#L4066

This is really why there are double ticks when `boxplot` is called twice (on two shared axes or on the same axis). Therefore the previous workaround would overdraw the labels both in the case of using `boxplot` on two shared axes and in the case of using `boxplot` on the same axis twice, because, for what concernes the ticklabels, the two situation are the same.
I leave a sample code to check that behaviour:

```
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.random((100, 5)), columns=["A", "B", "C", "D", "E"])

fig, ax = plt.subplots(1, 1)
df.boxplot(ax=ax, vert=True)
df.boxplot(ax=ax, vert=True)
plt.show()
```

So now I think that the duplicated ticks are not a feature of the shared axis as I initially thought, but rather a "feature" of `matplotlib`'s `boxplot` which does not handle well multiple calls on the same axis and which also incidentally is problematic on shared axes. I think that I will rise an issue in the matplotlib repo because, for other plots such as scatter, this problem of double ticks does not arise:

```
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1)
_ = ax.scatter(['a'], [1])
_ = ax.scatter(['a'], [2])
fig.canvas.draw()

print(ax.xaxis.get_ticklabels())
[Text(0, 0, 'a')]


import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1)
_ = ax.boxplot([1,2,3])
_ = ax.boxplot([5,6,7])
fig.canvas.draw()

print(ax.xaxis.get_ticklabels())
[Text(1, 0, '1'), Text(1, 0, '1')]
```

The problem can be solved by preventing this redundant ticks to show up when multiple call of `boxplot` are made.
This can be done by setting a new `FixedLocator` along with the `FixedFormatter` so that there are no duplicated ticks.
This in turn solves the two things you mentioned:
 - The problem with the horizontal boxplots
 - The overdrawing of the labels

However I have got some questions:
 - If the user specifies `manage_ticks=False`, should pandas add the labels anyways or not?
 - Is the user supposed to run `boxplot` multiple times on the same axis? If yes, then the proposed workaround cannot be accepted because something like the code reported below wouldn't work

```
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.random((100, 5)), columns=["A", "B", "C", "D", "E"])

fig, ax = plt.subplots(1, 1)
df.boxplot(ax=ax)
df.boxplot(ax=ax, positions=[10, 11, 12, 13, 14])
plt.show()
-> Only the second boxplot is labels, the first one has no labels
```

There could be other solutions (such as removing the duplicates in the FixedLocator)

I do not know if a more complex solution is acceptable if it can work in a wider range of cases,
or if this simple solution is preferred

Sorry for the wall of text and for changing the reviewed code

(TODO: still to update the whatsnew and test)
This pull request is stale because it has been open for thirty days with no activity. Please update or respond to this comment if you're still interested in working on this.
@francibm97 if you can merge master and update we can get this reviewed and in
closing as stale. if you want to continue, pls ping and can re-open.