Repository: pandas-dev/pandas

Issue:
BUG: plotting with DatetimeIndex containing NaT
xref #8914

```
In [1]: %matplotlib
Using matplotlib backend: Qt4Agg

In [2]: df = pd.DataFrame({'date': pd.date_range('2016-01-01', periods=5), 'vals
': range(5)})

In [3]: df.loc[2, 'date'] = np.nan

In [4]: s = df.set_index('date')['vals']

In [5]: s
Out[5]:
date
2016-01-01    0
2016-01-02    1
NaT           2
2016-01-04    3
2016-01-05    4
Name: vals, dtype: int64

In [6]: ax = s.plot()

In [11]: ax.get_lines()[0].get_data()
Out[11]:
(array([datetime.datetime(2016, 1, 1, 0, 0),
        datetime.datetime(2016, 1, 2, 0, 0),
        datetime.datetime(2016, 1, 4, 0, 0),
        datetime.datetime(2016, 1, 5, 0, 0), NaT], dtype=object),
 array([0, 1, 3, 4, 2], dtype=int64))

In [44]: ax.get_lines()[0].get_xydata()
Out[44]:
array([[  7.35964000e+05,   0.00000000e+00],
       [  7.35965000e+05,   1.00000000e+00],
       [  7.35967000e+05,   3.00000000e+00],
       [  7.35968000e+05,   4.00000000e+00],
       [  6.12411009e+05,   2.00000000e+00]])

```

So this gives you a plot with one of the values in the year 1677, September 22, so the minimum possible Timestamp.

This is of course not correct, but there are two things:
- NaT gets converted to `Timestamp.min` 
- the order of the values is changed, as the NaT is put at the end

There is a related issue about `plt.plot(pd.NaT)` erroring (https://github.com/pydata/pandas/issues/9253), but in this case it is pandas code that gives wrong results, so we have more control over this. I find it also strange that this does not error as in the pd.NaT case, but converts the NaT to Timestamp.min (but didn't look into detail)

BUG: plotting with DatetimeIndex containing NaT
xref #8914

```
In [1]: %matplotlib
Using matplotlib backend: Qt4Agg

In [2]: df = pd.DataFrame({'date': pd.date_range('2016-01-01', periods=5), 'vals
': range(5)})

In [3]: df.loc[2, 'date'] = np.nan

In [4]: s = df.set_index('date')['vals']

In [5]: s
Out[5]:
date
2016-01-01    0
2016-01-02    1
NaT           2
2016-01-04    3
2016-01-05    4
Name: vals, dtype: int64

In [6]: ax = s.plot()

In [11]: ax.get_lines()[0].get_data()
Out[11]:
(array([datetime.datetime(2016, 1, 1, 0, 0),
        datetime.datetime(2016, 1, 2, 0, 0),
        datetime.datetime(2016, 1, 4, 0, 0),
        datetime.datetime(2016, 1, 5, 0, 0), NaT], dtype=object),
 array([0, 1, 3, 4, 2], dtype=int64))

In [44]: ax.get_lines()[0].get_xydata()
Out[44]:
array([[  7.35964000e+05,   0.00000000e+00],
       [  7.35965000e+05,   1.00000000e+00],
       [  7.35967000e+05,   3.00000000e+00],
       [  7.35968000e+05,   4.00000000e+00],
       [  6.12411009e+05,   2.00000000e+00]])

```

So this gives you a plot with one of the values in the year 1677, September 22, so the minimum possible Timestamp.

This is of course not correct, but there are two things:
- NaT gets converted to `Timestamp.min` 
- the order of the values is changed, as the NaT is put at the end

There is a related issue about `plt.plot(pd.NaT)` erroring (https://github.com/pydata/pandas/issues/9253), but in this case it is pandas code that gives wrong results, so we have more control over this. I find it also strange that this does not error as in the pd.NaT case, but converts the NaT to Timestamp.min (but didn't look into detail)



Discussion:

Resolving patch:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -11,6 +11,7 @@
 
 from pandas.util._decorators import cache_readonly
 from pandas.core.base import PandasObject
+from pandas.core.dtypes.missing import notnull
 from pandas.core.dtypes.common import (
     is_list_like,
     is_integer,
@@ -538,6 +539,7 @@ def _get_xticks(self, convert_period=False):
                 """
                 x = index._mpl_repr()
             elif is_datetype:
+                self.data = self.data[notnull(self.data.index)]
                 self.data = self.data.sort_index()
                 x = self.data.index._mpl_repr()
             else:


Test patch:
diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py
--- a/pandas/tests/plotting/test_datetimelike.py
+++ b/pandas/tests/plotting/test_datetimelike.py
@@ -6,7 +6,7 @@
 from pandas.compat import lrange, zip
 
 import numpy as np
-from pandas import Index, Series, DataFrame
+from pandas import Index, Series, DataFrame, NaT
 from pandas.compat import is_platform_mac
 from pandas.core.indexes.datetimes import date_range, bdate_range
 from pandas.core.indexes.timedeltas import timedelta_range
@@ -811,6 +811,20 @@ def test_mixed_freq_shared_ax(self):
         # assert (ax1.lines[0].get_xydata()[0, 0] ==
         #         ax2.lines[0].get_xydata()[0, 0])
 
+    def test_nat_handling(self):
+
+        fig = self.plt.gcf()
+        # self.plt.clf()
+        ax = fig.add_subplot(111)
+
+        dti = DatetimeIndex(['2015-01-01', NaT, '2015-01-03'])
+        s = Series(range(len(dti)), dti)
+        s.plot(ax=ax)
+        xdata = ax.get_lines()[0].get_xdata()
+        # plot x data is bounded by index values
+        assert s.index.min() <= Series(xdata).min()
+        assert Series(xdata).max() <= s.index.max()
+
     @slow
     def test_to_weekly_resampling(self):
         idxh = date_range('1/1/1999', periods=52, freq='W')