ENH: is_homogeneous
Split https://github.com/pandas-dev/pandas/pull/22325

@jorisvandenbossche suggested moving this off of the BlockManager.

Right now, I've made this public. Do we want that? If so I'll add to api.rst, release note, etc. Otherwise, I'll make it private.
Hello @TomAugspurger! Thanks for submitting the PR.

 - There are no PEP8 issues in the file [`pandas/core/base.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/core/base.py) !

 - There are no PEP8 issues in the file [`pandas/core/frame.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/core/frame.py) !

 - There are no PEP8 issues in the file [`pandas/core/indexes/multi.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/core/indexes/multi.py) !

 - There are no PEP8 issues in the file [`pandas/tests/frame/test_dtypes.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/tests/frame/test_dtypes.py) !

 - There are no PEP8 issues in the file [`pandas/tests/indexing/test_multiindex.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/tests/indexing/test_multiindex.py) !

 - There are no PEP8 issues in the file [`pandas/tests/series/test_dtypes.py`](https://github.com/TomAugspurger/pandas/blob/90c76cb55f1c8aef8ccae7b4cc55d384a4cf1512/pandas/tests/series/test_dtypes.py) !

 I would maybe rather go with private for now. Or do think of good practical use cases outside of our internals? My main use-case would be in when writing a custom scikit-learn estimator. "Can I safely convert this DataFrame to an ndarray without converting to an ndarray of objects". Even then, it's not quite perfect as there are things like converting ints to floats, which may be OK there.

Regardless, let's keep it private for now, and open it up if there's demand.. # [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/22780?src=pr&el=h1) Report
> Merging [#22780](https://codecov.io/gh/pandas-dev/pandas/pull/22780?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/1c113db60b68c5a262d64e92dc9de72bfe59aed5?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master   #22780      +/-   ##
==========================================
+ Coverage   92.17%   92.18%   +<.01%     
==========================================
  Files         169      169              
  Lines       50778    50804      +26     
==========================================
+ Hits        46807    46833      +26     
  Misses       3971     3971
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.59% <100%> (ø)` | :arrow_up: |
| #single | `42.33% <37.5%> (ø)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/22780?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/multi.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9tdWx0aS5weQ==) | `95.45% <100%> (ø)` | :arrow_up: |
| [pandas/core/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvYmFzZS5weQ==) | `97.61% <100%> (+0.01%)` | :arrow_up: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.2% <100%> (ø)` | :arrow_up: |
| [pandas/core/dtypes/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2Jhc2UucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `86.03% <0%> (ø)` | :arrow_up: |
| [pandas/core/arrays/categorical.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvYXJyYXlzL2NhdGVnb3JpY2FsLnB5) | `95.75% <0%> (+0.01%)` | :arrow_up: |
| [pandas/core/dtypes/dtypes.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2R0eXBlcy5weQ==) | `96.11% <0%> (+0.03%)` | :arrow_up: |
| [pandas/core/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvY29tbW9uLnB5) | `97.44% <0%> (+0.05%)` | :arrow_up: |
| [pandas/core/dtypes/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/22780/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2NvbW1vbi5weQ==) | `95.02% <0%> (+0.08%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/22780?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/22780?src=pr&el=footer). Last update [1c113db...332dbca](https://codecov.io/gh/pandas-dev/pandas/pull/22780?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
 Although some dtypes will still convert to object dtype of course (but that's maybe not your direct concern). Isn't it rather something like "all numeric dtypes" that you need there? (as indeed mixed int and float might not be a problem) Yes, most likely.

For reference though, this is directly useful within pandas for determining whether we can safely do a cross-section on a DataFrame containing extension dtypes. If they're all the same type we want to take a special path. A PR implementing that is incoming, but this felt separate enough to be standalone. > For reference though, this is directly useful within pandas for determining whether we can safely do a cross-section on a DataFrame containing extension dtypes

Yes, I know, but that seems more internal usage (therefore my question). 

But as you said, can always make it public later if there is demand / use case. his should be internal only Does this look OK? I'd like to get back to SparseArray today, which will depend on this and #22785. maybe name _is_homogeneous_type to be consistent?

otherwise lgtm Unlike `is_*_dtype`, I don't think there's any ambiguity here as to whether this is an array or dtype that's being checked, so I prefer the shorter name if that's OK. no it IS different, this is like is_mixed_type (and not dype things)

pls change this Ahh, I see what you think it should be consistent with. Will fix that in #22785. If we want to add a suffix, I personally find 'dtype' more logical as 'type', since it is the dtype that is checked to be homogenous