Repository: pandas-dev/pandas

Issue:
Plotting methods (line, area, ..) don't handle string x values
From https://github.com/pandas-dev/pandas/pull/22307#pullrequestreview-145585750: when specifying the x labels in one of our plotting calls where this consists of strings, nothing is shown on the x labels:

```
In [6]: df = pd.DataFrame({'sales': [3, 2, 3],
   ...:                    'visits': [20, 42, 28],
   ...:                    'day': ['Monday', 'Tuesday', 'Wednesday']})

In [7]: ax = df.plot.area(x='day')
```

![figure_1-1](https://user-images.githubusercontent.com/1020496/44081733-b7cb56ba-9faf-11e8-89f0-e38544760df2.png)

The same is true for line plot, for bar plot it does work.

Related to https://github.com/pandas-dev/pandas/issues/18687 (but not exactly the same), and potentially https://github.com/pandas-dev/pandas/pull/18726. I would have expected that there is already an issue for this, but didn't directly find it.


Discussion:
I think I have found the place this is happening, or not happening.   Bar and barh both work with the labels as above, but lineplot and areaplot (subclass of lineplot), both do not.  For bar and barh the xtick labeling happens in `_post_plot_logic` which has the following (this is not in lineplot _post_plot_logic).

```python
        ...
        if self.use_index:
            str_index = [pprint_thing(key) for key in data.index]
        else:
            str_index = [pprint_thing(key) for key in range(data.shape[0])]
        name = self._get_index_name()

        self._decorate_ticks(ax, name, str_index)

    def _decorate_ticks(self, ax, name, ticklabels):
        ax.set_xticks(self.tick_pos)
        ax.set_xticklabels(ticklabels)
        if name is not None and self.use_index:
            ax.set_xlabel(name)
```
I have it changed and working locally in the lineplot class when using the `df.plot.area`  and `df.plot.line` with this toy example as above.  Wanted to pass that information to smarter minds for review before proceeding with a PR.
This seems to be partially fixed in 0.24.1 where the code from above gives 

![image](https://user-images.githubusercontent.com/23121882/52385596-9c6cc580-2a82-11e9-9256-7fc0a658ae7e.png)


However, this just brings to light the real problem, namely that the ticks are essentially only at the correct position by coincidence. E.g. if you let the axes autoscale, 

```
df = pd.DataFrame({'sales': [3, 2, 3],
                   'visits': [20, 42, 28],
                   'day': ['Monday', 'Tuesday', 'Wednesday']})

ax = df.plot.area(x='day')
ax.set_xlim(None, None)
ax.autoscale()
plt.show()
```

the ticks are completely off.

![image](https://user-images.githubusercontent.com/23121882/52385655-ddfd7080-2a82-11e9-9b00-17cc2a56f9c3.png)


In total this is the same issue as **plotting with index of objects should use `FixedLocator`** #7612 which is already 5 years old and still unsolved. 

>  this is the same issue as **plotting with index of objects should use `FixedLocator`** #7612

Ah, thanks for the link, that is probably the issue I was looking for.

> which is already 5 years old and still unsolved.

Contributions are very welcome!

This issue still persists! In my case, I'm seeing some `xticks` but not all of them for `line` and `area` plots. In case of `bar` and `barh`, things work correctly.
![Untitled](https://user-images.githubusercontent.com/40484319/55533681-bc5de580-56cf-11e9-8bde-9c669be57779.png)

I'm going to take a crack at this. If anyone else is trying as well let me know!
Hey everyone I want to contribute my solution to this issue. Can I??
> Hey everyone I want to contribute my solution to this issue. Can I??

Sure, post how are you going to solve this issue.

Resolving patch:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -401,6 +401,7 @@ def _add_table(self):
 
     def _post_plot_logic_common(self, ax, data):
         """Common post process for each axes"""
+        from matplotlib.ticker import FixedLocator, FixedFormatter
 
         def get_label(i):
             try:
@@ -410,8 +411,12 @@ def get_label(i):
 
         if self.orientation == 'vertical' or self.orientation is None:
             if self._need_to_set_index:
-                xticklabels = [get_label(x) for x in ax.get_xticks()]
+                xticks = ax.get_xticks()
+                xticklabels = [get_label(x) for x in xticks]
                 ax.set_xticklabels(xticklabels)
+                ax.xaxis.set_major_locator(FixedLocator(xticks))
+                ax.xaxis.set_major_formatter(FixedFormatter(xticklabels))
+
             self._apply_axis_properties(ax.xaxis, rot=self.rot,
                                         fontsize=self.fontsize)
             self._apply_axis_properties(ax.yaxis, fontsize=self.fontsize)
@@ -422,8 +427,11 @@ def get_label(i):
 
         elif self.orientation == 'horizontal':
             if self._need_to_set_index:
-                yticklabels = [get_label(y) for y in ax.get_yticks()]
+                yticks = ax.get_yticks()
+                yticklabels = [get_label(y) for y in yticks]
                 ax.set_yticklabels(yticklabels)
+                ax.xaxis.set_major_locator(FixedLocator(yticks))
+                ax.xaxis.set_major_formatter(FixedFormatter(yticklabels))
             self._apply_axis_properties(ax.yaxis, rot=self.rot,
                                         fontsize=self.fontsize)
             self._apply_axis_properties(ax.xaxis, fontsize=self.fontsize)


Test patch:
diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py
--- a/pandas/tests/plotting/test_frame.py
+++ b/pandas/tests/plotting/test_frame.py
@@ -2995,6 +2995,40 @@ def test_secondary_axis_font_size(self, method):
         self._check_ticks_props(axes=ax.right_ax,
                                 ylabelsize=fontsize)
 
+    @pytest.mark.slow
+    def test_x_string_values_ticks(self):
+        # Test if string plot index have a fixed xtick position
+        # GH: 7612, GH: 22334
+        df = pd.DataFrame({'sales': [3, 2, 3],
+                           'visits': [20, 42, 28],
+                           'day': ['Monday', 'Tuesday', 'Wednesday']})
+        ax = df.plot.area(x='day')
+        ax.set_xlim(-1, 3)
+        xticklabels = [t.get_text() for t in ax.get_xticklabels()]
+        labels_position = dict(zip(xticklabels, ax.get_xticks()))
+        # Testing if the label stayed at the right position
+        assert labels_position['Monday'] == 0.0
+        assert labels_position['Tuesday'] == 1.0
+        assert labels_position['Wednesday'] == 2.0
+
+    @pytest.mark.slow
+    def test_x_multiindex_values_ticks(self):
+        # Test if multiindex plot index have a fixed xtick position
+        # GH: 15912
+        index = pd.MultiIndex.from_product([[2012, 2013], [1, 2]])
+        df = pd.DataFrame(np.random.randn(4, 2),
+                          columns=['A', 'B'],
+                          index=index)
+        ax = df.plot()
+        ax.set_xlim(-1, 4)
+        xticklabels = [t.get_text() for t in ax.get_xticklabels()]
+        labels_position = dict(zip(xticklabels, ax.get_xticks()))
+        # Testing if the label stayed at the right position
+        assert labels_position['(2012, 1)'] == 0.0
+        assert labels_position['(2012, 2)'] == 1.0
+        assert labels_position['(2013, 1)'] == 2.0
+        assert labels_position['(2013, 2)'] == 3.0
+
 
 def _generate_4_axes_via_gridspec():
     import matplotlib.pyplot as plt