DEPR: Deprecate from_items
State: closed

- [ ] closes #17320
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Labels:
name: Deprecate
description: Functionality to remove in pandas

Comments:
@reidy-p can you look at the original issue; do we have a replacement for ``.from_items(...., orient=....)``; I guess this is the same issue as ``.from_dict(...., orient=...)``

suppose we could just add an optional ``orient=`` kwarg to ``DataFrame()`` to alleviate these cases.

@jorisvandenbossche @TomAugspurger 

since we are doing pretty much *everything* with the main constructors and not the ``from_*`` constructors (ship has sailed as far as that). Then for consistency this makes sense.
-1 on adding even more to the main DataFrame constructor. The ship has not fully sailed since `from_dict` and `from_items` are effectively giving functionality not available in the `DataFrame(..)`. 

If there is no actual alternative for `.from_items(.., orient=..)`, then for me that sounds as a reason to not deprecate this one. 

Although I suppose you can do it with `from_dict(dict(..), orient=..)` ?
cc @wesm as you recently posted an issue related to `from_items`
Yeah, you can use ``from_dict(.., orient=)`` to achieve the same results as ``from_items(.., orient=)`` or you could just use ``DataFrame()``:
```
In [1]: DataFrame.from_items([('A', [1, 2]), ('B', [3, 4])], columns=['col1', 'col2'], orient='index')
Out[1]:    
    col1  col2
A     1     2
B     3     4

In [2]: DataFrame([[1, 2], [3, 4]], columns=['col1', 'col2'], index=['A', 'B'])
Out[2]:
    col1  col2
A     1     2
B     3     4

In [3]: df = DataFrame.from_dict(dict([('A', [1, 2]), ('B', [3, 4])]), orient='index')
In [4]: df.columns = ['col1', 'col2'] # from_dict has no columns argument
Out[4]: df
    col1  col2
A     1     2
B     3     4
```

This PR also seems to be failing a lot of tests because when I replaced ``from_items`` with ``DataFrame(dict())`` in some tests (e.g., ``tests/io/test_stata.py``) the order of the columns changes. But the tests all pass on my local branch for some reason. I will investigate it a bit more.
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=h1) Report
> Merging [#18529](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/2a0e54bc841f27164b116135ebda4b74bae2fc4a?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `83.33%`.

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

```diff
@@            Coverage Diff             @@
##           master   #18529      +/-   ##
==========================================
- Coverage   91.35%   91.33%   -0.02%     
==========================================
  Files         164      164              
  Lines       49802    49804       +2     
==========================================
- Hits        45496    45489       -7     
- Misses       4306     4315       +9
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.13% <83.33%> (ø)` | :arrow_up: |
| #single | `40.81% <16.66%> (-0.07%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.81% <100%> (-0.1%)` | :arrow_down: |
| [pandas/io/stata.py](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=tree#diff-cGFuZGFzL2lvL3N0YXRhLnB5) | `93.71% <80%> (ø)` | :arrow_up: |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18529?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/18529?src=pr&el=footer). Last update [2a0e54b...80dcec6](https://codecov.io/gh/pandas-dev/pandas/pull/18529?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/18529?src=pr&el=h1) Report
> Merging [#18529](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/238499ab0a48a0ad4a2011e2ce1c6a02c86124eb?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master   #18529      +/-   ##
==========================================
- Coverage   91.62%   91.61%   -0.01%     
==========================================
  Files         150      150              
  Lines       48724    48725       +1     
==========================================
- Hits        44642    44640       -2     
- Misses       4082     4085       +3
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.98% <100%> (-0.01%)` | :arrow_down: |
| #single | `41.74% <0%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/18529/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.42% <100%> (-0.16%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18529?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/18529?src=pr&el=footer). Last update [238499a...1838f65](https://codecov.io/gh/pandas-dev/pandas/pull/18529?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

if we can deprecate ``.from_items`` and recommend ``.from_dict`` would be great. You might want to recomment ``DataFrame.from_dict(OrderedDict(...))`` for preserving order (and do this in tests).
thanks!
at least 1 deprecation warnings are still showing, can you convert (and see if any more)

```
pandas/tests/reshape/test_reshape.py::TestGetDummies::()::test_get_dummies_dont_sparsify_all_columns[True]
  C:\projects\pandas\pandas\tests\reshape\test_reshape.py:460: FutureWarning: from_items is deprecated. Please use DataFrame.from_dict(dict()) instead. DataFrame.from_dict(OrderedDict()) may be used to preserve the key order.
    df = DataFrame.from_items([('GDP', [1, 2]), ('Nation', ['AB', 'CD'])])
```
We have an example in the documentation about `from_items`: http://pandas-docs.github.io/pandas-docs-travis/dsintro.html#alternate-constructors 
The first one is easy to change to `from_dict` (or even just DataFrame()), but for the second with `orient='index'` I don't see a simple alternative:

```
In [55]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
   ....:                         orient='index', columns=['one', 'two', 'three'])
   ....: 
Out[55]: 
   one  two  three
A    1    2      3
B    4    5      6
```

Also, the deprecation warning in that case is not really correct, as simply changing to `from_dict(dict(..))` does not work:

```
In [50]: pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]), orient='index', columns=['one', 'two', 'three'])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-50-e431c313820a> in <module>()
----> 1 pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]), orient='index', columns=['one', 'two', 'three'])

TypeError: from_dict() got an unexpected keyword argument 'columns'
```

Should we add a `columns` keyword for this to `from_dict` ?

@jorisvandenbossche yes, these are good points. As I show above, it is possible to recreate the functionality of ``from_items(..., orient='index', columns=['one', 'two', 'three'])`` without ``from_items`` but it's not obvious how to do this from the deprecation warning. So I would be in favour of adding a ``columns`` keyword to ``from_dict`` if possible. ``from_dict`` only has three parameters at present anyway.
I think it would be rather easy to add a `columns` keyword to `from_dict` (it is basically passing this through, which it is already doing but with a hardcoded value of None). 
That would make this usecase of `from_items` easier to replace, so I think worth it. @jreback thoughts?

@reidy-p would you like to do a PR for this?

that sounds reasonable to me, adding a kwarg to ``.from_dict()``
@jorisvandenbossche yes I’ll try and do a PR
I haven't seen any mention that the `from_dict(OrderedDict(items)...` doesn't work in cases where there would be duplicates in the index. Here is a test that would fail:
```
import pandas
from collections import OrderedDict
def test_from_dict_replacing_from_items_with_duplicates():
    rows = [(1, (2,)), (1, (2,))]
    df1 = pandas.DataFrame.from_items(rows, columns=('a', ), orient='index')
    df2 = pandas.DataFrame.from_dict(OrderedDict(rows), columns=('a', ), orient='index')
    pandas.testing.assert_frame_equal(df1, df2)
```