(WIP) ENH: Add PeriodBlock
State: closed

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

This PR is **NOT** ready for review, but to show basic direction. Especially, supporting period arithmetic is quite unreliable because current `_TimeOp` class is basically for `np.ndarray` operations. I think refactoring `_TimeOp` is needed prior to this PR.

Would like to split the PR to 3 parts:
- 1. Add `PeriodDtype` definition, let `PeriodIndex` to have `PeriodDtype` like `period[M]`.  (#13941)
- 2. Refactor `_TimeOp` splitting to `_DatetimeOp` and `_TimedeltaOp`, merging `DatetimeIndex` and `TimedeltaIndex` arithmetics.
  - `_DatetimeOp` is for lhs or rhs is `datetime64` or `datetimetz` dtypes.
  - `_TimedeltaOp` is for other `timedelta` related types.
- 3. Add `PeriodBlock`, adding `_PeriodOp` for arithmetic (this PR).


Labels:
name: Enhancement
name: Performance
description: Memory or execution speed performance
name: Period
description: Period data type

Comments:
## [Current coverage](https://codecov.io/gh/pandas-dev/pandas/pull/13755?src=pr) is 85.51% (diff: 84.37%)
> Merging [#13755](https://codecov.io/gh/pandas-dev/pandas/pull/13755?src=pr) into [master](https://codecov.io/gh/pandas-dev/pandas/branch/master?src=pr) will decrease coverage by **0.03%**

```diff
@@             master     #13755   diff @@
==========================================
  Files           145        145          
  Lines         51404      51655   +251   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits          43975      44172   +197   
- Misses         7429       7483    +54   
  Partials          0          0          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [8e13da2...80e9caf](https://codecov.io/gh/pandas-dev/pandas/compare/8e13da24818dffaec88321a75a1e728b50a642f2...80e9caf944636dec65b0b3b802a23ef117f5f135?src=pr)
@sinhrks looks pretty good. certainly can merge mostly as is and just make a to-do on other issues. Looks like pretty much works and doesn't change the api much.

Just on a small whatsnew section to we can keep updating.

frrom #13345 

```
In [31]: dates = pd.date_range('1/1/2011', periods=500000, freq='H')
    ...: values = np.random.random(size=len(dates))
    ...: df = pd.DataFrame({"Date":dates, "Values":values})
    ...: 

In [32]: df
Out[32]: 
                      Date    Values
0      2011-01-01 00:00:00  0.787923
1      2011-01-01 01:00:00  0.730256
2      2011-01-01 02:00:00  0.032293
3      2011-01-01 03:00:00  0.411157
4      2011-01-01 04:00:00  0.295745
5      2011-01-01 05:00:00  0.815852
...                    ...       ...
499994 2068-01-15 02:00:00  0.368692
499995 2068-01-15 03:00:00  0.676244
499996 2068-01-15 04:00:00  0.123631
499997 2068-01-15 05:00:00  0.640574
499998 2068-01-15 06:00:00  0.709926
499999 2068-01-15 07:00:00  0.665387

[500000 rows x 2 columns]

In [33]: df.dtypes
Out[33]: 
Date      datetime64[ns]
Values           float64
dtype: object

In [34]: df['period'] = df.Date.dt.to_period(freq="1M")

In [35]: df.head()
IndexError: too many indices for array
```

This should return a Series

```
In [61]: x
Out[61]: 
0        2011-01
1        2011-01
2        2011-01
3        2011-01
4        2011-01
5        2011-01
           ...  
499994   2068-01
499995   2068-01
499996   2068-01
499997   2068-01
499998   2068-01
499999   2068-01
Name: Date, dtype: period[M]

In [62]: x.dt.to_timestamp()
Out[62]: 
DatetimeIndex(['2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01', '2011-01-01',
               ...
               '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01', '2068-01-01'],
              dtype='datetime64[ns]', name=u'Date', length=500000, freq=None)
```

This is correct (the reverse)

```
In [64]: s2 = Series(x.dt.to_timestamp())

In [65]: s2.dt.to_period('M')
Out[65]: 
0        2011-01
1        2011-01
2        2011-01
3        2011-01
4        2011-01
5        2011-01
           ...  
499994   2068-01
499995   2068-01
499996   2068-01
499997   2068-01
499998   2068-01
499999   2068-01
Name: Date, dtype: period[M]
```

something going on here (prob to/from object conversion)

```
In [71]: s2
Out[71]: 
0        2011-01-01
1        2011-01-01
2        2011-01-01
3        2011-01-01
4        2011-01-01
5        2011-01-01
            ...    
499994   2068-01-01
499995   2068-01-01
499996   2068-01-01
499997   2068-01-01
499998   2068-01-01
499999   2068-01-01
Name: Date, dtype: datetime64[ns]

In [72]: x
Out[72]: 
0        2011-01
1        2011-01
2        2011-01
3        2011-01
4        2011-01
5        2011-01
           ...  
499994   2068-01
499995   2068-01
499996   2068-01
499997   2068-01
499998   2068-01
499999   2068-01
Name: Date, dtype: period[M]
```

```
In [69]: %timeit s2[s2>'2015']
100 loops, best of 3: 2.66 ms per loop

In [70]: %timeit x[x>'2015']
1 loop, best of 3: 637 ms per loop
```

```
        2000795 function calls (2000789 primitive calls) in 1.233 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   500000    0.617    0.000    0.845    0.000 {built-in method _from_ordinal}
   500000    0.208    0.000    1.053    0.000 period.py:409(<lambda>)
        1    0.151    0.151    1.204    1.204 {pandas.lib.map_infer}
   500003    0.139    0.000    0.228    0.000 frequencies.py:388(to_offset)
   500152    0.088    0.000    0.089    0.000 {isinstance}
```

@sinhrks @jreback What is the status of this one? Still try to include it in 0.19.0 or push to 0.20?

ideally I'd like this to go in. I think most of the prep work has been done. 

@sinhrks Is this ready for review? (no docs yet, and some todo's listed in the code) Just to prioritize my time :-)
Not yet. I'll make a notice once ready, hopefully this weekend... :)
let's close for now. If you want to rebase and fixup lmk. Would like to do this (*maybe* in concert with a ``IntervalBlock``).