Skip to content

Commit 8836589

Browse files
committed
BUG: fix concat to work with more iterables (GH8645)
1 parent ff6b98a commit 8836589

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

doc/source/whatsnew/v0.15.1.txt

+22
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@ API changes
7575

7676
gr.apply(sum)
7777

78+
- ``concat`` permits a wider variety of iterables of pandas objects to be
79+
passed as the first parameter (:issue:`8645`):
80+
81+
.. ipython:: python
82+
83+
from collections import deque
84+
df1 = pd.DataFrame([1, 2, 3])
85+
df2 = pd.DataFrame([4, 5, 6])
86+
87+
previous behavior:
88+
89+
.. code-block:: python
90+
91+
In [7]: pd.concat(deque((df1, df2)))
92+
TypeError: first argument must be a list-like of pandas objects, you passed an object of type "deque"
93+
94+
current behavior:
95+
96+
.. ipython:: python
97+
98+
pd.concat(deque((df1, df2)))
99+
78100
.. _whatsnew_0151.enhancements:
79101

80102
Enhancements

pandas/tools/merge.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
675675
676676
Parameters
677677
----------
678-
objs : list or dict of Series, DataFrame, or Panel objects
678+
objs : a sequence or mapping of Series, DataFrame, or Panel objects
679679
If a dict is passed, the sorted keys will be used as the `keys`
680680
argument, unless it is passed, in which case the values will be
681681
selected (see below). Any None objects will be dropped silently unless
@@ -731,8 +731,8 @@ class _Concatenator(object):
731731
def __init__(self, objs, axis=0, join='outer', join_axes=None,
732732
keys=None, levels=None, names=None,
733733
ignore_index=False, verify_integrity=False, copy=True):
734-
if not isinstance(objs, (list,tuple,types.GeneratorType,dict,TextFileReader)):
735-
raise TypeError('first argument must be a list-like of pandas '
734+
if isinstance(objs, (NDFrame, compat.string_types)):
735+
raise TypeError('first argument must be an iterable of pandas '
736736
'objects, you passed an object of type '
737737
'"{0}"'.format(type(objs).__name__))
738738

pandas/tools/tests/test_merge.py

+27
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,33 @@ def test_concat_series_axis1_same_names_ignore_index(self):
22032203
result = concat([s1, s2], axis=1, ignore_index=True)
22042204
self.assertTrue(np.array_equal(result.columns, [0, 1]))
22052205

2206+
def test_concat_iterables(self):
2207+
from collections import deque, Iterable
2208+
2209+
# GH8645 check concat works with tuples, list, generators, and weird
2210+
# stuff like deque and custom iterables
2211+
df1 = DataFrame([1, 2, 3])
2212+
df2 = DataFrame([4, 5, 6])
2213+
expected = DataFrame([1, 2, 3, 4, 5, 6])
2214+
assert_frame_equal(pd.concat((df1, df2), ignore_index=True), expected)
2215+
assert_frame_equal(pd.concat([df1, df2], ignore_index=True), expected)
2216+
assert_frame_equal(pd.concat((df for df in (df1, df2)), ignore_index=True), expected)
2217+
assert_frame_equal(pd.concat(deque((df1, df2)), ignore_index=True), expected)
2218+
class CustomIterator1(object):
2219+
def __len__(self):
2220+
return 2
2221+
def __getitem__(self, index):
2222+
try:
2223+
return {0: df1, 1: df2}[index]
2224+
except KeyError:
2225+
raise IndexError
2226+
assert_frame_equal(pd.concat(CustomIterator1(), ignore_index=True), expected)
2227+
class CustomIterator2(Iterable):
2228+
def __iter__(self):
2229+
yield df1
2230+
yield df2
2231+
assert_frame_equal(pd.concat(CustomIterator2(), ignore_index=True), expected)
2232+
22062233
def test_concat_invalid(self):
22072234

22082235
# trying to concat a ndframe with a non-ndframe

0 commit comments

Comments
 (0)