Repository: pandas-dev/pandas

Issue:
qcut raising ValueError if NaT present
#### Code Sample, a copy-pastable example if possible

```python
from io import StringIO
import pandas as pd

csv = 'Index,Date\n1,2013-01-01 23:00:00\n2,\n3,2013-01-01 23:00:01'
df = pd.read_csv(StringIO(csv), index_col=0, parse_dates=[1])
pd.qcut(df["Date"], 2)
```
#### Problem description
`qcut` raises a `ValueError`:

```
Traceback (most recent call last):
  File "mve.py", line 26, in <module>
    pd.qcut(df["Date"], 2)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/reshape/tile.py", line 208, in qcut
    dtype=dtype, duplicates=duplicates)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/reshape/tile.py", line 251, in _bins_to_cuts
    dtype=dtype)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/reshape/tile.py", line 344, in _format_labels
    labels = IntervalIndex.from_breaks(breaks, closed=closed)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/indexes/interval.py", line 370, in from_breaks
    name=name, copy=copy)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/indexes/interval.py", line 411, in from_arrays
    copy=copy, verify_integrity=True)
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/indexes/interval.py", line 225, in _simple_new
    result._validate()
  File "/tmp/test/env/lib/python3.5/site-packages/pandas/core/indexes/interval.py", line 265, in _validate
    raise ValueError('missing values must be missing in the same '
ValueError: missing values must be missing in the same location both left and right sides
```

#### Expected Output

`qcut` returning something like

```
Index
1    (2013-01-01 22:59:59.999999999, 2013-01-01 23:00:01.0
2    NaT
3    (2013-01-01 22:59:59.999999999, 2013-01-01 23:00:01.0
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Linux
OS-release: 4.13.0-32-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: de_DE.UTF-8
LOCALE: de_DE.UTF-8

pandas: 0.22.0
pytest: None
pip: 9.0.1
setuptools: 38.5.1
Cython: None
numpy: 1.14.0
scipy: None
pyarrow: None
xarray: None
IPython: None
sphinx: None
patsy: None
dateutil: 2.6.1
pytz: 2018.3
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: None
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: None
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None


</details>



Discussion:
1) Could you rewrite your example to not use `read_csv` (i.e. just construct the `DataFrame` from scratch).

2) Yeah...that does look weird indeed.  Even the error message is confusing.  PR to patch is welcome!

Resolving patch:
diff --git a/pandas/core/reshape/tile.py b/pandas/core/reshape/tile.py
--- a/pandas/core/reshape/tile.py
+++ b/pandas/core/reshape/tile.py
@@ -279,18 +279,22 @@ def _trim_zeros(x):
 def _coerce_to_type(x):
     """
     if the passed data is of datetime/timedelta type,
-    this method converts it to integer so that cut method can
+    this method converts it to numeric so that cut method can
     handle it
     """
     dtype = None
 
     if is_timedelta64_dtype(x):
-        x = to_timedelta(x).view(np.int64)
+        x = to_timedelta(x)
         dtype = np.timedelta64
     elif is_datetime64_dtype(x):
-        x = to_datetime(x).view(np.int64)
+        x = to_datetime(x)
         dtype = np.datetime64
 
+    if dtype is not None:
+        # GH 19768: force NaT to NaN during integer conversion
+        x = np.where(x.notna(), x.view(np.int64), np.nan)
+
     return x, dtype
 
 


Test patch:
diff --git a/pandas/tests/reshape/test_tile.py b/pandas/tests/reshape/test_tile.py
--- a/pandas/tests/reshape/test_tile.py
+++ b/pandas/tests/reshape/test_tile.py
@@ -6,7 +6,8 @@
 
 from pandas import (Series, isna, to_datetime, DatetimeIndex,
                     Timestamp, Interval, IntervalIndex, Categorical,
-                    cut, qcut, date_range)
+                    cut, qcut, date_range, NaT, TimedeltaIndex)
+from pandas.tseries.offsets import Nano, Day
 import pandas.util.testing as tm
 from pandas.api.types import CategoricalDtype as CDT
 
@@ -250,6 +251,18 @@ def test_qcut_nas(self):
         result = qcut(arr, 4)
         assert isna(result[:20]).all()
 
+    @pytest.mark.parametrize('s', [
+        Series(DatetimeIndex(['20180101', NaT, '20180103'])),
+        Series(TimedeltaIndex(['0 days', NaT, '2 days']))],
+        ids=lambda x: str(x.dtype))
+    def test_qcut_nat(self, s):
+        # GH 19768
+        intervals = IntervalIndex.from_tuples(
+            [(s[0] - Nano(), s[2] - Day()), np.nan, (s[2] - Day(), s[2])])
+        expected = Series(Categorical(intervals, ordered=True))
+        result = qcut(s, 2)
+        tm.assert_series_equal(result, expected)
+
     def test_qcut_index(self):
         result = qcut([0, 2], 2)
         intervals = [Interval(-0.001, 1), Interval(1, 2)]