Skip to content

Commit 0bcd77e

Browse files
Xbarjreback
authored andcommitted
BUG: in Python3 MultiIndex.from_tuples cannot take "zipped" tuples (#18440)
1 parent be66ef8 commit 0bcd77e

File tree

3 files changed

+75
-5
lines changed

3 files changed

+75
-5
lines changed

doc/source/whatsnew/v0.22.0.txt

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

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

+59-5
Original file line numberDiff line numberDiff line change
@@ -672,15 +672,31 @@ 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-
result = MultiIndex.from_arrays(arrays)
676-
assert list(result) == list(self.index)
675+
# list of arrays as input
676+
result = MultiIndex.from_arrays(arrays, names=self.index.names)
677+
tm.assert_index_equal(result, self.index)
677678

678679
# infer correctly
679680
result = MultiIndex.from_arrays([[pd.NaT, Timestamp('20130101')],
680681
['a', 'b']])
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), names=self.index.names)
693+
tm.assert_index_equal(result, 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')
@@ -825,7 +841,25 @@ def test_from_product(self):
825841
expected = MultiIndex.from_tuples(tuples, names=names)
826842

827843
tm.assert_index_equal(result, expected)
828-
assert result.names == names
844+
845+
def test_from_product_iterator(self):
846+
# GH 18434
847+
first = ['foo', 'bar', 'buz']
848+
second = ['a', 'b', 'c']
849+
names = ['first', 'second']
850+
tuples = [('foo', 'a'), ('foo', 'b'), ('foo', 'c'), ('bar', 'a'),
851+
('bar', 'b'), ('bar', 'c'), ('buz', 'a'), ('buz', 'b'),
852+
('buz', 'c')]
853+
expected = MultiIndex.from_tuples(tuples, names=names)
854+
855+
# iterator as input
856+
result = MultiIndex.from_product(iter([first, second]), names=names)
857+
tm.assert_index_equal(result, expected)
858+
859+
# Invalid non-iterable input
860+
with tm.assert_raises_regex(
861+
TypeError, "Input must be a list / sequence of iterables."):
862+
MultiIndex.from_product(0)
829863

830864
def test_from_product_empty(self):
831865
# 0 levels
@@ -1725,8 +1759,28 @@ def test_from_tuples(self):
17251759
'from empty list',
17261760
MultiIndex.from_tuples, [])
17271761

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

17311785
def test_from_tuples_empty(self):
17321786
# GH 16777

0 commit comments

Comments
 (0)