Skip to content

Commit 65fbc91

Browse files
committed
Make independent test cases for issue 18434
1 parent aaee541 commit 65fbc91

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

doc/source/whatsnew/v0.22.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ Indexing
140140
- Bug in :func:`Series.truncate` which raises ``TypeError`` with a monotonic ``PeriodIndex`` (:issue:`17717`)
141141
- Bug in :func:`DataFrame.groupby` where tuples were interpreted as lists of keys rather than as keys (:issue:`17979`, :issue:`18249`)
142142
- Bug in :func:`MultiIndex.remove_unused_levels`` which would fill nan values (:issue:`18417`)
143+
- Bug in :func:`MultiIndex.from_tuples`` which would fail to take zipped tuples in python3 (:issue:`18434`)
143144
- Bug in :class:`IntervalIndex` where empty and purely NA data was constructed inconsistently depending on the construction method (:issue:`18421`)
144145
-
145146

pandas/core/indexes/multi.py

+15
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,11 @@ def from_arrays(cls, arrays, sortorder=None, names=None):
11621162
MultiIndex.from_product : Make a MultiIndex from cartesian product
11631163
of iterables
11641164
"""
1165+
if not is_list_like(arrays):
1166+
raise TypeError("Input must be a list / sequence of array-likes.")
1167+
elif is_iterator(arrays):
1168+
arrays = list(arrays)
1169+
11651170
# Check if lengths of all arrays are equal or not,
11661171
# raise ValueError, if not
11671172
for i in range(1, len(arrays)):
@@ -1206,6 +1211,11 @@ def from_tuples(cls, tuples, sortorder=None, names=None):
12061211
MultiIndex.from_product : Make a MultiIndex from cartesian product
12071212
of iterables
12081213
"""
1214+
if not is_list_like(tuples):
1215+
raise TypeError('Input must be a list / sequence of tuple-likes.')
1216+
elif is_iterator(tuples):
1217+
tuples = list(tuples)
1218+
12091219
if len(tuples) == 0:
12101220
if names is None:
12111221
msg = 'Cannot infer number of levels from empty list'
@@ -1260,6 +1270,11 @@ def from_product(cls, iterables, sortorder=None, names=None):
12601270
from pandas.core.categorical import _factorize_from_iterables
12611271
from pandas.core.reshape.util import cartesian_product
12621272

1273+
if not is_list_like(iterables):
1274+
raise TypeError("Input must be a list / sequence of iterables.")
1275+
elif is_iterator(iterables):
1276+
iterables = list(iterables)
1277+
12631278
labels, levels = _factorize_from_iterables(iterables)
12641279
labels = cartesian_product(labels)
12651280
return MultiIndex(levels, labels, sortorder=sortorder, names=names)

pandas/tests/indexes/test_multi.py

+60-2
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ def test_from_arrays(self):
672672
for lev, lab in zip(self.index.levels, self.index.labels):
673673
arrays.append(np.asarray(lev).take(lab))
674674

675+
# list of arrays as input
675676
result = MultiIndex.from_arrays(arrays)
676677
assert list(result) == list(self.index)
677678

@@ -681,6 +682,21 @@ def test_from_arrays(self):
681682
assert result.levels[0].equals(Index([Timestamp('20130101')]))
682683
assert result.levels[1].equals(Index(['a', 'b']))
683684

685+
def test_from_arrays_iterator(self):
686+
# GH 18434
687+
arrays = []
688+
for lev, lab in zip(self.index.levels, self.index.labels):
689+
arrays.append(np.asarray(lev).take(lab))
690+
691+
# iterator as input
692+
result = MultiIndex.from_arrays(iter(arrays))
693+
assert list(result) == list(self.index)
694+
695+
# invalid iterator input
696+
with tm.assert_raises_regex(
697+
TypeError, "Input must be a list / sequence of array-likes."):
698+
MultiIndex.from_arrays(0)
699+
684700
def test_from_arrays_index_series_datetimetz(self):
685701
idx1 = pd.date_range('2015-01-01 10:00', freq='D', periods=3,
686702
tz='US/Eastern')
@@ -827,6 +843,26 @@ def test_from_product(self):
827843
tm.assert_index_equal(result, expected)
828844
assert result.names == names
829845

846+
def test_from_product_iterator(self):
847+
# GH 18434
848+
first = ['foo', 'bar', 'buz']
849+
second = ['a', 'b', 'c']
850+
names = ['first', 'second']
851+
tuples = [('foo', 'a'), ('foo', 'b'), ('foo', 'c'), ('bar', 'a'),
852+
('bar', 'b'), ('bar', 'c'), ('buz', 'a'), ('buz', 'b'),
853+
('buz', 'c')]
854+
expected = MultiIndex.from_tuples(tuples, names=names)
855+
856+
# iterator as input
857+
result = MultiIndex.from_product(iter([first, second]), names=names)
858+
assert result.equals(expected)
859+
assert result.names == names
860+
861+
# Invalid non-iterable input
862+
with tm.assert_raises_regex(
863+
TypeError, "Input must be a list / sequence of iterables."):
864+
MultiIndex.from_product(0)
865+
830866
def test_from_product_empty(self):
831867
# 0 levels
832868
with tm.assert_raises_regex(
@@ -1725,8 +1761,30 @@ def test_from_tuples(self):
17251761
'from empty list',
17261762
MultiIndex.from_tuples, [])
17271763

1728-
idx = MultiIndex.from_tuples(((1, 2), (3, 4)), names=['a', 'b'])
1729-
assert len(idx) == 2
1764+
expected = MultiIndex(levels=[[1, 3], [2, 4]],
1765+
labels=[[0, 1], [0, 1]],
1766+
names=['a', 'b'])
1767+
1768+
# input tuples
1769+
result = MultiIndex.from_tuples(((1, 2), (3, 4)), names=['a', 'b'])
1770+
assert expected.names == result.names
1771+
assert result.equals(expected)
1772+
1773+
def test_from_tuples_iterator(self):
1774+
# GH 18434
1775+
# input iterator for tuples
1776+
expected = MultiIndex(levels=[[1, 3], [2, 4]],
1777+
labels=[[0, 1], [0, 1]],
1778+
names=['a', 'b'])
1779+
1780+
result = MultiIndex.from_tuples(zip([1, 3], [2, 4]), names=['a', 'b'])
1781+
assert expected.names == result.names
1782+
assert result.equals(expected)
1783+
1784+
# input non-iterables
1785+
with tm.assert_raises_regex(
1786+
TypeError, 'Input must be a list / sequence of tuple-likes.'):
1787+
MultiIndex.from_tuples(0)
17301788

17311789
def test_from_tuples_empty(self):
17321790
# GH 16777

0 commit comments

Comments
 (0)