From e9c709007f83dea2ede6268631ad450805442d17 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 3 Dec 2016 14:09:12 -0600 Subject: [PATCH 1/3] use zip_longest instead of zip --- pandas/compat/__init__.py | 4 ++++ pandas/indexes/multi.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 532f960468204..c9937b34f7c6e 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -259,6 +259,8 @@ def set_function_name(f, name, cls): return f ResourceWarning = ResourceWarning + + zip_longest = itertools.zip_longest else: string_types = basestring, @@ -309,6 +311,8 @@ def set_function_name(f, name, cls): class ResourceWarning(Warning): pass + zip_longest = itertools.izip_longest + string_and_binary_types = string_types + (binary_type,) diff --git a/pandas/indexes/multi.py b/pandas/indexes/multi.py index 45b6cad89d020..fbf529a574184 100644 --- a/pandas/indexes/multi.py +++ b/pandas/indexes/multi.py @@ -10,7 +10,7 @@ import pandas.index as _index from pandas.lib import Timestamp -from pandas.compat import range, zip, lrange, lzip, map +from pandas.compat import range, zip, lrange, lzip, map, zip_longest from pandas.compat.numpy import function as nv from pandas import compat @@ -980,7 +980,7 @@ def from_tuples(cls, tuples, sortorder=None, names=None): elif isinstance(tuples, list): arrays = list(lib.to_object_array_tuples(tuples).T) else: - arrays = lzip(*tuples) + arrays = zip_longest(*tuples) return MultiIndex.from_arrays(arrays, sortorder=sortorder, names=names) From 44c8ec31616503d2de53efd1eb481bead46e5688 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Tue, 6 Dec 2016 01:43:08 -0600 Subject: [PATCH 2/3] Add commonly used abc classes for Python 2/3. --- pandas/compat/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index c9937b34f7c6e..1d1358b846125 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -316,6 +316,25 @@ class ResourceWarning(Warning): pass string_and_binary_types = string_types + (binary_type,) +class ABC(object): + if PY3: + from collections import abc + Container = abc.Container + Iterable = abc.Iterable + Iterator = abc.Iterator + Sequence = abc.Sequence + MutableSequence = abc.MutableSequence + Mapping = abc.Mapping + else: + import collections + Container = collections.Container + Iterable = collections.Iterable + Iterator = collections.Iterator + Sequence = collections.Sequence + MutableSequence = collections.MutableSequence + Mapping = collections.Mapping + + try: # callable reintroduced in later versions of Python callable = callable From e530ca8dc1821931f66246708a5770086ccf899c Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Tue, 6 Dec 2016 01:44:10 -0600 Subject: [PATCH 3/3] First attempt at re-organzing from_tuples and from_arrays Clean up logic and generalize. --- pandas/indexes/multi.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/pandas/indexes/multi.py b/pandas/indexes/multi.py index fbf529a574184..14b3094b7bdaa 100644 --- a/pandas/indexes/multi.py +++ b/pandas/indexes/multi.py @@ -10,7 +10,7 @@ import pandas.index as _index from pandas.lib import Timestamp -from pandas.compat import range, zip, lrange, lzip, map, zip_longest +from pandas.compat import range, zip, lrange, lzip, map, zip_longest, ABC from pandas.compat.numpy import function as nv from pandas import compat @@ -916,19 +916,24 @@ def from_arrays(cls, arrays, sortorder=None, names=None): See Also -------- - MultiIndex.from_tuples : Convert list of tuples to MultiIndex + MultiIndex.from_tuples : Convert sequence of tuples to MultiIndex MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ + try: + arrays = list(arrays) + except: + raise ValueError('arrays must be iterable/sequence') + if len(arrays) == 1: name = None if names is None else names[0] return Index(arrays[0], name=name) # Check if lengths of all arrays are equal or not, # raise ValueError, if not - for i in range(1, len(arrays)): - if len(arrays[i]) != len(arrays[i - 1]): - raise ValueError('all arrays must be same length') + _len_first = len(arrays[0]) + if not all(len(x) == _len_first for x in arrays): + raise ValueError('all arrays must be same length') from pandas.core.categorical import _factorize_from_iterables @@ -942,7 +947,7 @@ def from_arrays(cls, arrays, sortorder=None, names=None): @classmethod def from_tuples(cls, tuples, sortorder=None, names=None): """ - Convert list of tuples to MultiIndex + Convert sequence of tuples to MultiIndex Parameters ---------- @@ -964,24 +969,18 @@ def from_tuples(cls, tuples, sortorder=None, names=None): See Also -------- - MultiIndex.from_arrays : Convert list of arrays to MultiIndex + MultiIndex.from_arrays : Convert sequence of arrays to MultiIndex MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ - if len(tuples) == 0: - # I think this is right? Not quite sure... - raise TypeError('Cannot infer number of levels from empty list') - - if isinstance(tuples, (np.ndarray, Index)): - if isinstance(tuples, Index): - tuples = tuples._values + if isinstance(tuples, Index): + tuples = tuples._values - arrays = list(lib.tuples_to_object_array(tuples).T) - elif isinstance(tuples, list): - arrays = list(lib.to_object_array_tuples(tuples).T) + if isinstance(tuples, np.ndarray): + arrays = lib.tuples_to_object_array(tuples).T else: arrays = zip_longest(*tuples) - + return MultiIndex.from_arrays(arrays, sortorder=sortorder, names=names) @classmethod