Skip to content

Commit e561986

Browse files
holymonsonvictor
authored and
victor
committed
allow using Iterable in Series and DataFrame constructor (pandas-dev#21987)
1 parent e6c23cb commit e561986

File tree

5 files changed

+44
-24
lines changed

5 files changed

+44
-24
lines changed

doc/source/whatsnew/v0.24.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ Other Enhancements
179179
- :class:`IntervalIndex` has gained the :meth:`~IntervalIndex.set_closed` method to change the existing ``closed`` value (:issue:`21670`)
180180
- :func:`~DataFrame.to_csv` and :func:`~DataFrame.to_json` now support ``compression='infer'`` to infer compression based on filename (:issue:`15008`)
181181
- :func:`to_timedelta` now supports iso-formated timedelta strings (:issue:`21877`)
182-
-
182+
- :class:`Series` and :class:`DataFrame` now support :class:`Iterable` in constructor (:issue:`2193`)
183183

184184
.. _whatsnew_0240.api_breaking:
185185

pandas/core/frame.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import collections
1717
import itertools
1818
import sys
19-
import types
2019
import warnings
2120
from textwrap import dedent
2221

@@ -75,7 +74,8 @@
7574
from pandas.core.arrays import Categorical, ExtensionArray
7675
import pandas.core.algorithms as algorithms
7776
from pandas.compat import (range, map, zip, lrange, lmap, lzip, StringIO, u,
78-
OrderedDict, raise_with_traceback)
77+
OrderedDict, raise_with_traceback,
78+
string_and_binary_types)
7979
from pandas import compat
8080
from pandas.compat import PY36
8181
from pandas.compat.numpy import function as nv
@@ -267,7 +267,7 @@ class DataFrame(NDFrame):
267267
268268
Parameters
269269
----------
270-
data : numpy ndarray (structured or homogeneous), dict, or DataFrame
270+
data : ndarray (structured or homogeneous), Iterable, dict, or DataFrame
271271
Dict can contain Series, arrays, constants, or list-like objects
272272
273273
.. versionchanged :: 0.23.0
@@ -391,8 +391,11 @@ def __init__(self, data=None, index=None, columns=None, dtype=None,
391391
else:
392392
mgr = self._init_ndarray(data, index, columns, dtype=dtype,
393393
copy=copy)
394-
elif isinstance(data, (list, types.GeneratorType)):
395-
if isinstance(data, types.GeneratorType):
394+
395+
# For data is list-like, or Iterable (will consume into list)
396+
elif (isinstance(data, collections.Iterable)
397+
and not isinstance(data, string_and_binary_types)):
398+
if not isinstance(data, collections.Sequence):
396399
data = list(data)
397400
if len(data) > 0:
398401
if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1:
@@ -417,8 +420,6 @@ def __init__(self, data=None, index=None, columns=None, dtype=None,
417420
copy=copy)
418421
else:
419422
mgr = self._init_dict({}, index, columns, dtype=dtype)
420-
elif isinstance(data, collections.Iterator):
421-
raise TypeError("data argument can't be an iterator")
422423
else:
423424
try:
424425
arr = np.array(data, dtype=dtype, copy=copy)

pandas/core/series.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# pylint: disable=E1101,E1103
77
# pylint: disable=W0703,W0622,W0613,W0201
88

9-
import types
9+
import collections
1010
import warnings
1111
from textwrap import dedent
1212

@@ -144,7 +144,7 @@ class Series(base.IndexOpsMixin, generic.NDFrame):
144144
145145
Parameters
146146
----------
147-
data : array-like, dict, or scalar value
147+
data : array-like, Iterable, dict, or scalar value
148148
Contains data stored in Series
149149
150150
.. versionchanged :: 0.23.0
@@ -238,12 +238,13 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
238238

239239
elif is_extension_array_dtype(data):
240240
pass
241-
elif (isinstance(data, types.GeneratorType) or
242-
(compat.PY3 and isinstance(data, map))):
243-
data = list(data)
244241
elif isinstance(data, (set, frozenset)):
245242
raise TypeError("{0!r} type is unordered"
246243
"".format(data.__class__.__name__))
244+
# If data is Iterable but not list-like, consume into list.
245+
elif (isinstance(data, collections.Iterable)
246+
and not isinstance(data, collections.Sized)):
247+
data = list(data)
247248
else:
248249

249250
# handle sparse passed here (and force conversion)

pandas/tests/frame/test_constructors.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -865,12 +865,6 @@ def test_constructor_more(self):
865865
dm = DataFrame(index=np.arange(10))
866866
assert dm.values.shape == (10, 0)
867867

868-
# corner, silly
869-
# TODO: Fix this Exception to be better...
870-
with tm.assert_raises_regex(ValueError, 'constructor not '
871-
'properly called'):
872-
DataFrame((1, 2, 3))
873-
874868
# can't cast
875869
mat = np.array(['foo', 'bar'], dtype=object).reshape(2, 1)
876870
with tm.assert_raises_regex(ValueError, 'cast'):
@@ -953,6 +947,17 @@ def __len__(self, n):
953947
array.array('i', range(10))])
954948
tm.assert_frame_equal(result, expected, check_dtype=False)
955949

950+
def test_constructor_iterable(self):
951+
# GH 21987
952+
class Iter():
953+
def __iter__(self):
954+
for i in range(10):
955+
yield [1, 2, 3]
956+
957+
expected = DataFrame([[1, 2, 3]] * 10)
958+
result = DataFrame(Iter())
959+
tm.assert_frame_equal(result, expected)
960+
956961
def test_constructor_iterator(self):
957962

958963
expected = DataFrame([list(range(10)), list(range(10))])
@@ -1374,10 +1379,6 @@ def test_constructor_miscast_na_int_dtype(self):
13741379
expected = DataFrame([[np.nan, 1], [1, 0]])
13751380
tm.assert_frame_equal(df, expected)
13761381

1377-
def test_constructor_iterator_failure(self):
1378-
with tm.assert_raises_regex(TypeError, 'iterator'):
1379-
DataFrame(iter([1, 2, 3]))
1380-
13811382
def test_constructor_column_duplicates(self):
13821383
# it works! #2079
13831384
df = DataFrame([[8, 5]], columns=['a', 'a'])

pandas/tests/series/test_constructors.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,29 @@ def test_constructor_series(self):
156156

157157
assert_series_equal(s2, s1.sort_index())
158158

159-
def test_constructor_iterator(self):
159+
def test_constructor_iterable(self):
160+
# GH 21987
161+
class Iter():
162+
def __iter__(self):
163+
for i in range(10):
164+
yield i
160165

166+
expected = Series(list(range(10)), dtype='int64')
167+
result = Series(Iter(), dtype='int64')
168+
assert_series_equal(result, expected)
169+
170+
def test_constructor_sequence(self):
171+
# GH 21987
161172
expected = Series(list(range(10)), dtype='int64')
162173
result = Series(range(10), dtype='int64')
163174
assert_series_equal(result, expected)
164175

176+
def test_constructor_single_str(self):
177+
# GH 21987
178+
expected = Series(['abc'])
179+
result = Series('abc')
180+
assert_series_equal(result, expected)
181+
165182
def test_constructor_list_like(self):
166183

167184
# make sure that we are coercing different

0 commit comments

Comments
 (0)