Skip to content

Commit 062cd20

Browse files
committed
BUG: Exception when frame constructed from dict of iterators
1 parent 5a286e4 commit 062cd20

File tree

5 files changed

+40
-6
lines changed

5 files changed

+40
-6
lines changed

doc/source/whatsnew/v0.25.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ Reshaping
459459
- Bug in :func:`pivot_table` where columns with ``NaN`` values are dropped even if ``dropna`` argument is ``False``, when the ``aggfunc`` argument contains a ``list`` (:issue:`22159`)
460460
- Bug in :func:`concat` where the resulting ``freq`` of two :class:`DatetimeIndex` with the same ``freq`` would be dropped (:issue:`3232`).
461461
- Bug in :func:`merge` where merging with equivalent Categorical dtypes was raising an error (:issue:`22501`)
462+
- bug in :class:`DataFrame` instantiating with a dict of iterators or generators (e.g. ``pd.DataFrame({'A': reversed(range(3))})``) raised an error (:issue:`26349`).
462463
- bug in :class:`DataFrame` instantiating with a ``range`` (e.g. ``pd.DataFrame(range(3))``) raised an error (:issue:`26342`).
463464
- Bug in :class:`DataFrame` constructor when passing non-empty tuples would cause a segmentation fault (:issue:`25691`)
464465
- Bug in :func:`Series.apply` failed when the series is a timezone aware :class:`DatetimeIndex` (:issue:`25959`)

pandas/core/common.py

+9
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,15 @@ def maybe_make_list(obj):
289289
return obj
290290

291291

292+
def maybe_itarable_to_list(obj: Any) -> Any:
293+
"""
294+
If obj is Iterable but not list-like, consume into list.
295+
"""
296+
if isinstance(obj, abc.Iterable) and not isinstance(obj, abc.Sized):
297+
return list(obj)
298+
return obj
299+
300+
292301
def is_null_slice(obj):
293302
"""
294303
We have a null slice.

pandas/core/internals/construction.py

+2
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ def init_dict(data, index, columns, dtype=None):
195195
arrays.loc[missing] = [val] * missing.sum()
196196

197197
else:
198+
data = OrderedDict((col_name, com.maybe_itarable_to_list(col))
199+
for col_name, col in data.items())
198200
keys = com.dict_keys_to_ordered_list(data)
199201
columns = data_names = Index(keys)
200202
# GH#24096 need copy to be deep for datetime64tz case

pandas/core/series.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,14 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
220220
elif isinstance(data, (set, frozenset)):
221221
raise TypeError("{0!r} type is unordered"
222222
"".format(data.__class__.__name__))
223-
# If data is Iterable but not list-like, consume into list.
224223
elif (isinstance(data, abc.Iterable) and
225224
not isinstance(data, abc.Sized)):
226-
data = list(data)
227-
else:
228-
225+
data = com.maybe_itarable_to_list(data)
226+
elif isinstance(data, ABCSparseArray):
229227
# handle sparse passed here (and force conversion)
230-
if isinstance(data, ABCSparseArray):
231-
data = data.to_dense()
228+
data = data.to_dense()
229+
else:
230+
pass
232231

233232
if index is None:
234233
if not is_list_like(data):

pandas/tests/frame/test_constructors.py

+23
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,29 @@ def test_constructor_dict_of_tuples(self):
533533
expected = DataFrame({k: list(v) for k, v in data.items()})
534534
tm.assert_frame_equal(result, expected, check_dtype=False)
535535

536+
def test_constructor_dict_of_ranges(self):
537+
data = {'a': range(3), 'b': range(3, 6)}
538+
539+
result = DataFrame(data)
540+
expected = DataFrame({'a': [0, 1, 2], 'b': [3, 4, 5]})
541+
tm.assert_frame_equal(result, expected)
542+
543+
def test_constructor_dict_of_iterators(self):
544+
# GH 26349
545+
data = {'a': iter(range(3)), 'b': reversed(range(3))}
546+
547+
result = DataFrame(data)
548+
expected = DataFrame({'a': [0, 1, 2], 'b': [2, 1, 0]})
549+
tm.assert_frame_equal(result, expected)
550+
551+
def test_constructor_dict_of_generators(self):
552+
# GH 26349
553+
data = {'a': (i for i in (range(3))),
554+
'b': (i for i in reversed(range(3)))}
555+
result = DataFrame(data)
556+
expected = DataFrame({'a': [0, 1, 2], 'b': [2, 1, 0]})
557+
tm.assert_frame_equal(result, expected)
558+
536559
def test_constructor_dict_multiindex(self):
537560
def check(result, expected):
538561
return tm.assert_frame_equal(result, expected, check_dtype=True,

0 commit comments

Comments
 (0)