Skip to content

Commit bc3957c

Browse files
committed
MAINT: refactor from_items() using from_dict(). Fixes pandas-dev#21850
This removes the deprecation warnings introduced in pandas-dev#18262, by reimplementing DataFrame.from_items() in the recommended way using DataFrame.from_dict() and collections.OrderedDict. This eliminates the maintenance burden of separate code for from_items(), while allowing existing uses to keep working. A small cleanup can be done once pandas-dev#8425 is fixed.
1 parent dfd58e8 commit bc3957c

File tree

3 files changed

+29
-98
lines changed

3 files changed

+29
-98
lines changed

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ Removal of prior version deprecations/changes
391391

392392
- The ``LongPanel`` and ``WidePanel`` classes have been removed (:issue:`10892`)
393393
- Several private functions were removed from the (non-public) module ``pandas.core.common`` (:issue:`22001`)
394+
- :meth: `~pandas.DataFrame.from_items` has been refactored and the deprecation warning from v0.23.0 has been removed (i.e. it is no longer deprecated)
394395
-
395396
-
396397

pandas/core/frame.py

+11-50
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
ensure_int64,
5555
ensure_platform_int,
5656
is_list_like,
57-
is_nested_list_like,
5857
is_iterator,
5958
is_sequence,
6059
is_named_tuple)
@@ -1445,17 +1444,13 @@ def to_records(self, index=True, convert_datetime64=None):
14451444
def from_items(cls, items, columns=None, orient='columns'):
14461445
"""Construct a dataframe from a list of tuples
14471446
1448-
.. deprecated:: 0.23.0
1449-
`from_items` is deprecated and will be removed in a future version.
1450-
Use :meth:`DataFrame.from_dict(dict(items)) <DataFrame.from_dict>`
1451-
instead.
1452-
:meth:`DataFrame.from_dict(OrderedDict(items)) <DataFrame.from_dict>`
1453-
may be used to preserve the key order.
1454-
14551447
Convert (key, value) pairs to DataFrame. The keys will be the axis
14561448
index (usually the columns, but depends on the specified
14571449
orientation). The values should be arrays or Series.
14581450
1451+
`from_items(items)` is equivalent to
1452+
:meth:`DataFrame.from_dict(OrderedDict(items)) <DataFrame.from_dict>`.
1453+
14591454
Parameters
14601455
----------
14611456
items : sequence of (key, value) pairs
@@ -1473,57 +1468,23 @@ def from_items(cls, items, columns=None, orient='columns'):
14731468
frame : DataFrame
14741469
"""
14751470

1476-
warnings.warn("from_items is deprecated. Please use "
1477-
"DataFrame.from_dict(dict(items), ...) instead. "
1478-
"DataFrame.from_dict(OrderedDict(items)) may be used to "
1479-
"preserve the key order.",
1480-
FutureWarning, stacklevel=2)
1481-
1482-
keys, values = lzip(*items)
1471+
odict = collections.OrderedDict(items)
14831472

14841473
if orient == 'columns':
14851474
if columns is not None:
1486-
columns = ensure_index(columns)
1487-
1488-
idict = dict(items)
1489-
if len(idict) < len(items):
1490-
if not columns.equals(ensure_index(keys)):
1491-
raise ValueError('With non-unique item names, passed '
1492-
'columns must be identical')
1493-
arrays = values
1494-
else:
1495-
arrays = [idict[k] for k in columns if k in idict]
1475+
return cls.from_dict(odict).reindex(columns=columns)
14961476
else:
1497-
columns = ensure_index(keys)
1498-
arrays = values
1499-
1500-
# GH 17312
1501-
# Provide more informative error msg when scalar values passed
1502-
try:
1503-
return cls._from_arrays(arrays, columns, None)
1504-
1505-
except ValueError:
1506-
if not is_nested_list_like(values):
1507-
raise ValueError('The value in each (key, value) pair '
1508-
'must be an array, Series, or dict')
1477+
return cls.from_dict(odict, orient)
15091478

15101479
elif orient == 'index':
15111480
if columns is None:
1481+
# we can produce a DataFrame even in this case,
1482+
# but raise for consistency with previous versions
15121483
raise TypeError("Must pass columns with orient='index'")
15131484

1514-
keys = ensure_index(keys)
1515-
1516-
# GH 17312
1517-
# Provide more informative error msg when scalar values passed
1518-
try:
1519-
arr = np.array(values, dtype=object).T
1520-
data = [lib.maybe_convert_objects(v) for v in arr]
1521-
return cls._from_arrays(data, columns, keys)
1522-
1523-
except TypeError:
1524-
if not is_nested_list_like(values):
1525-
raise ValueError('The value in each (key, value) pair '
1526-
'must be an array, Series, or dict')
1485+
# reindex will not be needed once GH 8425 is fixed
1486+
idx = odict.keys()
1487+
return cls.from_dict(odict, orient, columns=columns).reindex(idx)
15271488

15281489
else: # pragma: no cover
15291490
raise ValueError("'orient' must be either 'columns' or 'index'")

pandas/tests/frame/test_constructors.py

+17-48
Original file line numberDiff line numberDiff line change
@@ -1283,87 +1283,56 @@ def test_constructor_manager_resize(self):
12831283

12841284
def test_constructor_from_items(self):
12851285
items = [(c, self.frame[c]) for c in self.frame.columns]
1286-
with tm.assert_produces_warning(FutureWarning,
1287-
check_stacklevel=False):
1288-
recons = DataFrame.from_items(items)
1286+
recons = DataFrame.from_items(items)
12891287
tm.assert_frame_equal(recons, self.frame)
12901288

12911289
# pass some columns
1292-
with tm.assert_produces_warning(FutureWarning,
1293-
check_stacklevel=False):
1294-
recons = DataFrame.from_items(items, columns=['C', 'B', 'A'])
1290+
recons = DataFrame.from_items(items, columns=['C', 'B', 'A'])
12951291
tm.assert_frame_equal(recons, self.frame.loc[:, ['C', 'B', 'A']])
12961292

12971293
# orient='index'
12981294

12991295
row_items = [(idx, self.mixed_frame.xs(idx))
13001296
for idx in self.mixed_frame.index]
1301-
with tm.assert_produces_warning(FutureWarning,
1302-
check_stacklevel=False):
1303-
recons = DataFrame.from_items(row_items,
1304-
columns=self.mixed_frame.columns,
1305-
orient='index')
1297+
recons = DataFrame.from_items(row_items,
1298+
columns=self.mixed_frame.columns,
1299+
orient='index')
13061300
tm.assert_frame_equal(recons, self.mixed_frame)
13071301
assert recons['A'].dtype == np.float64
13081302

13091303
with tm.assert_raises_regex(TypeError,
13101304
"Must pass columns with "
13111305
"orient='index'"):
1312-
with tm.assert_produces_warning(FutureWarning,
1313-
check_stacklevel=False):
1314-
DataFrame.from_items(row_items, orient='index')
1306+
DataFrame.from_items(row_items, orient='index')
13151307

13161308
# orient='index', but thar be tuples
13171309
arr = construct_1d_object_array_from_listlike(
13181310
[('bar', 'baz')] * len(self.mixed_frame))
13191311
self.mixed_frame['foo'] = arr
13201312
row_items = [(idx, list(self.mixed_frame.xs(idx)))
13211313
for idx in self.mixed_frame.index]
1322-
with tm.assert_produces_warning(FutureWarning,
1323-
check_stacklevel=False):
1324-
recons = DataFrame.from_items(row_items,
1325-
columns=self.mixed_frame.columns,
1326-
orient='index')
1314+
recons = DataFrame.from_items(row_items,
1315+
columns=self.mixed_frame.columns,
1316+
orient='index')
13271317
tm.assert_frame_equal(recons, self.mixed_frame)
13281318
assert isinstance(recons['foo'][0], tuple)
13291319

1330-
with tm.assert_produces_warning(FutureWarning,
1331-
check_stacklevel=False):
1332-
rs = DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
1333-
orient='index',
1334-
columns=['one', 'two', 'three'])
1320+
rs = DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
1321+
orient='index',
1322+
columns=['one', 'two', 'three'])
13351323
xp = DataFrame([[1, 2, 3], [4, 5, 6]], index=['A', 'B'],
13361324
columns=['one', 'two', 'three'])
13371325
tm.assert_frame_equal(rs, xp)
13381326

13391327
def test_constructor_from_items_scalars(self):
13401328
# GH 17312
13411329
with tm.assert_raises_regex(ValueError,
1342-
r'The value in each \(key, value\) '
1343-
'pair must be an array, Series, or dict'):
1344-
with tm.assert_produces_warning(FutureWarning,
1345-
check_stacklevel=False):
1346-
DataFrame.from_items([('A', 1), ('B', 4)])
1347-
1348-
with tm.assert_raises_regex(ValueError,
1349-
r'The value in each \(key, value\) '
1350-
'pair must be an array, Series, or dict'):
1351-
with tm.assert_produces_warning(FutureWarning,
1352-
check_stacklevel=False):
1353-
DataFrame.from_items([('A', 1), ('B', 2)], columns=['col1'],
1354-
orient='index')
1355-
1356-
def test_from_items_deprecation(self):
1357-
# GH 17320
1358-
with tm.assert_produces_warning(FutureWarning,
1359-
check_stacklevel=False):
1360-
DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])])
1330+
r'If using all scalar values, '
1331+
'you must pass an index'):
1332+
DataFrame.from_items([('A', 1), ('B', 4)])
13611333

1362-
with tm.assert_produces_warning(FutureWarning,
1363-
check_stacklevel=False):
1364-
DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
1365-
columns=['col1', 'col2', 'col3'],
1366-
orient='index')
1334+
DataFrame.from_items([('A', 1), ('B', 2)], columns=['col1'],
1335+
orient='index')
13671336

13681337
def test_constructor_mix_series_nonseries(self):
13691338
df = DataFrame({'A': self.frame['A'],

0 commit comments

Comments
 (0)