COMPAT: map infers all-nan / empty correctly
State: closed

xref #18482 

Labels:
name: Dtype Conversions
description: Unexpected or buggy dtype conversions
name: Compat
description: pandas objects compatability with Numpy or Python functions

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@38f41e6`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/18491/graphs/tree.svg?token=eZ4WkYLtcO&src=pr&height=150&width=650)](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master   #18491   +/-   ##
=========================================
  Coverage          ?   91.32%           
=========================================
  Files             ?      163           
  Lines             ?    49791           
  Branches          ?        0           
=========================================
  Hits              ?    45473           
  Misses            ?     4318           
  Partials          ?        0
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.12% <100%> (?)` | |
| #single | `40.71% <0%> (?)` | |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9iYXNlLnB5) | `96.42% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=footer). Last update [38f41e6...9447d2a](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=h1) Report
> Merging [#18491](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/38f41e64f4b8a0479f8835022af5e7343ccf8498?src=pr&el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/18491/graphs/tree.svg?token=eZ4WkYLtcO&width=650&src=pr&height=150)](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #18491      +/-   ##
==========================================
+ Coverage    91.3%   91.32%   +0.02%     
==========================================
  Files         163      163              
  Lines       49781    49791      +10     
==========================================
+ Hits        45451    45473      +22     
+ Misses       4330     4318      -12
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.12% <100%> (+0.02%)` | :arrow_up: |
| #single | `40.71% <0%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9iYXNlLnB5) | `96.42% <100%> (+0.01%)` | :arrow_up: |
| [pandas/plotting/\_converter.py](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=tree#diff-cGFuZGFzL3Bsb3R0aW5nL19jb252ZXJ0ZXIucHk=) | `65.25% <0%> (+1.81%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=footer). Last update [38f41e6...9447d2a](https://codecov.io/gh/pandas-dev/pandas/pull/18491?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

I am not sure I find this special casing a good idea. I would rather keep `.map` 'dummy' and simply create a new index from the newly created values, without trying to infer anything more than `pd.Index(..)` already does. 
Also if we do this, we should do it consistently and always try to use the original dtype (eg also in Series.map)

```
In [12]: pd.date_range("2012-01-01", periods=3).map({})
Out[12]: DatetimeIndex(['NaT', 'NaT', 'NaT'], dtype='datetime64[ns]', freq=None)

In [13]: pd.Series(pd.date_range("2012-01-01", periods=3)).map({})
Out[13]: 
0   NaN
1   NaN
2   NaN
dtype: float64
```

Do you have a specific use case for this? (for wanting to be smart?) 
I think the user can always change the dtype after doing the map if it wants something specific or to preserve the index.

(also please try to keep PRs with substantial changes open for a bit longer)
@jorisvandenbossche this is not special casing at all, rather the reverse. We DO try to infer based on the return values. However nan by-definition a case that needs handling. It defaults to all float which is kind of arbitrary. Having this retain the character of the original is pretty reasonable here.

[13] is a bug I think.
Unless I'm misunderstanding, it doesn't look like this fully hits all of the different types of index.  Each index that can hold NA should return the same index class with all NA values, right?

On master:
```python
In [2]: pd.__version__
Out[2]: '0.22.0.dev0+241.gf745e52'

In [3]: pd.date_range('20170101', periods=4).map({})
Out[3]: DatetimeIndex(['NaT', 'NaT', 'NaT', 'NaT'], dtype='datetime64[ns]', freq=None)
```

`IntervalIndex`, `CategoricalIndex`, and `Index` with `object` dtype get coerced to `Float64Index`:

```python
In [4]: pd.interval_range(0, 5).map({})
Out[4]: Float64Index([nan, nan, nan, nan, nan], dtype='float64')

In [5]: pd.CategoricalIndex(list('abca')).map({})
Out[5]: Float64Index([nan, nan, nan, nan], dtype='float64')

In [6]: pd.Index(list('abca')).map({})
Out[6]: Float64Index([nan, nan, nan, nan], dtype='float64')
```

`PeriodIndex` and `TimedeltaIndex` get coerced `DatetimeIndex`:
```python
In [7]: pd.period_range('2017Q1', periods=4, freq='Q').map({})
Out[7]: DatetimeIndex(['NaT', 'NaT', 'NaT', 'NaT'], dtype='datetime64[ns]', freq=None)

In [8]: pd.timedelta_range(1, periods=4).map({})
Out[8]: DatetimeIndex(['NaT', 'NaT', 'NaT', 'NaT'], dtype='datetime64[ns]', freq=None)
```