Subclassed reshape
State: closed

 - [x] closes #15563
 - [x] tests added / passed
 - [x] passes ``git diff upstream/master | flake8 --diff``
 - [x] whatsnew entry

Reshape operations in pandas/core/reshape.py modified to use `obj._constructor`, `obj._constructor_sliced`, and `obj._constructor_expanddim` for Series and DataFrame. This preserves a subclass family on `stack`, `unstack`, and `pivot` operations.

Labels:
name: Compat
description: pandas objects compatability with Numpy or Python functions

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


```diff
@@            Coverage Diff             @@
##           master   #15564      +/-   ##
==========================================
- Coverage   91.06%   91.04%   -0.02%     
==========================================
  Files         137      136       -1     
  Lines       49307    49171     -136     
==========================================
- Hits        44899    44769     -130     
+ Misses       4408     4402       -6
```


| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/15564?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/reshape.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvcmVzaGFwZS5weQ==) | `99.08% <92.85%> (-0.2%)` | :x: |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :x: |
| [pandas/io/json/json.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2lvL2pzb24vanNvbi5weQ==) | `89.22% <0%> (-1.05%)` | :x: |
| [pandas/tools/merge.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL3Rvb2xzL21lcmdlLnB5) | `91.78% <0%> (-0.35%)` | :x: |
| [pandas/indexes/frozen.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2luZGV4ZXMvZnJvemVuLnB5) | `93.24% <0%> (-0.31%)` | :x: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.83% <0%> (-0.1%)` | :x: |
| [pandas/core/config_init.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvY29uZmlnX2luaXQucHk=) | `95.27% <0%> (-0.08%)` | :x: |
| [pandas/indexes/base.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2luZGV4ZXMvYmFzZS5weQ==) | `96.17% <0%> (-0.07%)` | :x: |
| [pandas/io/json/normalize.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL2lvL2pzb24vbm9ybWFsaXplLnB5) | `97.87% <0%> (-0.05%)` | :x: |
| [pandas/tools/pivot.py](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...bfd146da6519154fd2ff9811ab65b47d333a59ee?src=pr&el=tree#diff-cGFuZGFzL3Rvb2xzL3Bpdm90LnB5) | `95.03% <0%> (-0.04%)` | :x: |
| ... and [11 more](https://codecov.io/gh/pandas-dev/pandas/pull/15564?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/15564?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/15564?src=pr&el=footer). Last update [e097bf5...dacdf40](https://codecov.io/gh/pandas-dev/pandas/compare/e097bf596509779294b2ebf320a4b271deaec6ec...dacdf4040ca94ee42fcbf2b537d2703275306557?src=pr&el=footer&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
can you also have a look over: http://pandas-docs.github.io/pandas-docs-travis/internals.html#subclassing-pandas-data-structures

and see if anything needs amendment
thanks @delgadom 

reshape could certainly use some tlc!
Thanks @jreback - I'm working on your suggestions.

Keeping this clean is a bit tough, especially because unstacking is sometimes handled by `_unstack_multiple` and sometimes by `_Unstacker`. I'd modify `_unstack_multiple` and `_Unstacker`, except `_Unstacker` accepts a numpy array and returns a `DataFrame`, so there's no way to access the `_constructor*` properties from within this class.

To me this class seems really nasty and should be handled by functions rather than a class (would you ever use `_Unstacker` except to get an unstacked object?) but originally was tentative in making changes. Do you think this should be re-written? At the very least it seems as though `_Unstacker` should be able to handle `DataFrame` arguments.

Happy to take a stab if you agree.
@delgadom the reshaping code is certainly in need of refactoring to make things easier :>

certainly take a stab at it. Yes the ``_Unstacker`` class is merely a helper class, so you can change things if you need (not public).

You *might* want to do a refactor *before* this PR to make it easier to test / debug. Though if its relatively simple go ahead here.

Yeah I think I'll refactor and then create a new PR. Thanks for the help!
ok!