BUG: Fix #13149 and ENH: 'copy' param in Index.astype()
State: closed

- [x] closes #13149
- [x] tests added / passed
- [x] passes `git diff upstream/master | flake8 --diff`
- [x] whatsnew entry

**Update**
1. Float64Index.astype(int) raises ValueError if a NaN is present. 
   Previously, it converted NaN's to the smallest negative integer.
2. TimedeltaIndex.astype(int) and DatetimeIndex.astype(int) return.
   Int64Index, which is consistent with behavior of other Indexes.
   Previously, they returned a numpy.array of ints.
3. Added:
   - bool parameter 'copy' to Index.astype()
   - shared doc string to .astype()
   - tests on .astype() (consolidated and added new)
   - bool parameter 'copy' to Categorical.astype()
4. Fixed core.common.is_timedelta64_ns_dtype().
5. Set a default NaT representation to a string type in a parameter of DatetimeIndex._format_native_types().
   Previously, it produced a unicode u'NaT' in Python2.


Labels:
name: Bug
name: Dtype Conversions
description: Unexpected or buggy dtype conversions

Comments:
tests pls.

Added tests. (I hope I'm doing this right.)

looks pretty good. a couple of syntax changes. ping when green.

ok this looks pretty good. can you move all of the test_astype test to `tests/tseries/tests_timeseries.py` and `tests/tseries/test_timedeltas.py` as appropriate & consolidate all of the tests into a new `TestAstype` sub-class as will be adding multiple tests..

AND can you expand them a bit to cover all of the astypes that are possible and assert that we raise for others.

So for example, need to assert that:

This works (actually, we really need a `copy=False` keyword in astype to control whether we return a copy, pls add this as well).

```
In [4]: pd.timedelta_range('1 day',periods=3).astype('m8[ns]')
Out[4]: TimedeltaIndex(['1 days', '2 days', '3 days'], dtype='timedelta64[ns]', freq='D')
```

This should work (and respect `copy` as above), its a missing case.

```
In [2]: pd.date_range('20130101',periods=3).astype('M8[ns]')
ValueError: Cannot cast DatetimeIndex to dtype datetime64[ns]
```

while you are at it, pls do the same for `PeriodIndex` (test-wise) (and make the slight code changes in `.astype` there as well (e.g. `dtype == np.object_` and such).

```
In [4]: pd.PeriodIndex([pd.Period('20130101',freq='D'),pd.Period('NaT',freq='D')])
Out[4]: PeriodIndex(['2013-01-01', 'NaT'], dtype='int64', freq='D')

In [5]: pd.PeriodIndex([pd.Period('20130101',freq='D'),pd.Period('NaT',freq='D')]).astype(int)
Out[5]: Int64Index([15706, -9223372036854775808], dtype='int64')

In [6]: pd.PeriodIndex([pd.Period('20130101',freq='D'),pd.Period('NaT',freq='D')]).astype(object)
Out[6]: Index([2013-01-01, NaT], dtype='object')
```

> AND can you expand them a bit to cover all of the astypes that are possible and assert that we raise for others.
> 
> So for example, need to assert that:

Some examples missing. Do you mean to expand tests for `TimedeltaIndex.astype()` with all possible timedelta64 subtypes (and test them with `is_timedelta64_ns_dtype`) or all possible arguments (ints, strings, etc)? Or also `DatetimeIndex.astype()`  and other Indexes?

> we really need a copy=False keyword in astype to control whether we return a copy, pls add this as well)

Should I add `copy` to the argument list of `TimedeltaIndex.astype()`? (And to other datetime like indexes too?) This would have an effect only if we return self/self.copy(), right?

> AND can you expand them a bit to cover all of the astypes that are possible and assert that we raise for others.
> 
> > So for example, need to assert that:
> > Some examples missing. Do you mean to expand tests for TimedeltaIndex.astype() with all possible timedelta64 subtypes (and test them with is_timedelta64_ns_dtype) or all possible arguments (ints, strings, etc)? Or also DatetimeIndex.astype() and other Indexes?

want to have the tests mimic the code (e.g. for DTI test object, to M8[ns] & M8[ns] w/tz, int) and then a bunch of disallowed ones. same for all indexes (these are almost all there, just a bit scattered)

> we really need a copy=False keyword in astype to control whether we return a copy, pls add this as well)
> 
> > Should I add copy to the argument list of TimedeltaIndex.astype()? (And to other datetime like indexes too?) This would have an effect only if we return self/self.copy(), right?

Yes, the others by definition copy. Its only necessary to control if `self.copy()` is called or just `self` when the same dtype is passed in. Mention this in the enhancements section (use this PR number as the issue)

@pijucha looks pretty good.

There are some existing `test_astype` tests that would like you to consolidate with the new ones 

```
[Sat May 21 15:29:01 ~/pandas]$ grin 'test_astype' pandas

pandas/tseries/tests/test_base.py:
   53 :     def test_astype_str(self):
pandas/tseries/tests/test_period.py:
 1630 :     def test_astype(self):
pandas/tseries/tests/test_timedeltas.py:
 1194 :     def test_astype(self):
pandas/tseries/tests/test_timeseries.py:
 1809 :     def test_astype_object(self):
 2566 :     def test_astype(self):
```

@pijucha looks really good!

some test moving around / slight refactoring.

ping when green.

@jreback It's green.

@pijucha thanks!

very nice PR. you really dove deep into some internals! keep em coming!

@jreback Thanks for the guidelines! They helped a lot.