diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 531d8d7a3917e..46e6486d37c20 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -7,6 +7,7 @@ ensure_index_from_sequences, InvalidIndexError) # noqa from pandas.core.indexes.category import CategoricalIndex # noqa +from pandas.core.indexes.frozen import FrozenList from pandas.core.indexes.multi import MultiIndex # noqa from pandas.core.indexes.interval import IntervalIndex # noqa from pandas.core.indexes.numeric import (NumericIndex, Float64Index, # noqa @@ -95,9 +96,7 @@ def _get_combined_index(indexes, intersect=False, sort=False): elif len(indexes) == 1: index = indexes[0] elif intersect: - index = indexes[0] - for other in indexes[1:]: - index = index.intersection(other) + index = _intersect_indexes(indexes) else: index = _union_indexes(indexes, sort=sort) index = ensure_index(index) @@ -110,6 +109,17 @@ def _get_combined_index(indexes, intersect=False, sort=False): return index +def _intersect_indexes(indexes): + """Return the intersection of indexes + + Preserves the order of the first index. + """ + index = indexes[0] + for other in indexes[1:]: + index = index.intersection(other) + return index + + def _union_indexes(indexes, sort=True): """Return the union of indexes @@ -125,15 +135,6 @@ def _union_indexes(indexes, sort=True): ------- Index """ - if len(indexes) == 0: - raise AssertionError('Must have at least 1 Index to union') - if len(indexes) == 1: - result = indexes[0] - if isinstance(result, list): - result = Index(sorted(result)) - return result - - indexes, kind = _sanitize_and_check(indexes) def _unique_indices(inds): """Convert indexes to lists and concatenate them, removing duplicates @@ -153,35 +154,46 @@ def conv(i): i = i.tolist() return i - return Index( - lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) + inds = [conv(i) for i in inds] + ind_values = lib.fast_unique_multiple_list(inds, sort=sort) + return Index(ind_values) - if kind == 'special': + if len(indexes) == 0: + raise AssertionError('Must have at least 1 Index to union') + if len(indexes) == 1: result = indexes[0] + if isinstance(result, list): + result = Index(sorted(result)) + return result + + indexes, kind = _sanitize_and_check(indexes) + if kind == 'special': + + result = indexes[0] if hasattr(result, 'union_many'): return result.union_many(indexes[1:]) else: for other in indexes[1:]: result = result.union(other) return result - elif kind == 'array': - index = indexes[0] - for other in indexes[1:]: - if not index.equals(other): - if sort is None: - # TODO: remove once pd.concat sort default changes - warnings.warn(_sort_msg, FutureWarning, stacklevel=8) - sort = True + elif kind == 'array': - return _unique_indices(indexes) + if _all_indexes_same(indexes): + index = indexes[0] + name = _get_consensus_names(indexes)[0] + if name != index.name: + index = index._shallow_copy(name=name) + return index + else: + if sort is None: + # TODO: remove once pd.concat sort default changes + warnings.warn(_sort_msg, FutureWarning, stacklevel=8) + sort = True + return _unique_indices(indexes) - name = _get_consensus_names(indexes)[0] - if name != index.name: - index = index._shallow_copy(name=name) - return index - else: # kind='list' + else: # kind == 'list' return _unique_indices(indexes) @@ -205,18 +217,18 @@ def _sanitize_and_check(indexes): sanitized_indexes: list of Index or array-like objects type: {'list', 'array', 'special'} """ - kinds = list({type(index) for index in indexes}) + kinds = {type(index) for index in indexes} if list in kinds: - if len(kinds) > 1: - indexes = [Index(com.try_sort(x)) - if not isinstance(x, Index) else - x for x in indexes] - kinds.remove(list) - else: + if len(kinds) == 1: return indexes, 'list' + else: + indexes = [Index(com.try_sort(i)) + if not isinstance(i, Index) else i + for i in indexes] + kinds.remove(list) - if len(kinds) > 1 or Index not in kinds: + if any(kind != Index for kind in kinds): return indexes, 'special' else: return indexes, 'array' @@ -237,14 +249,11 @@ def _get_consensus_names(indexes): list A list representing the consensus 'names' found """ - - # find the non-none names, need to tupleify to make - # the set hashable, then reverse on return - consensus_names = {tuple(i.names) for i in indexes - if com._any_not_none(*i.names)} - if len(consensus_names) == 1: - return list(list(consensus_names)[0]) - return [None] * indexes[0].nlevels + non_empty = {i.names for i in indexes if com._any_not_none(*i.names)} + if len(non_empty) == 1: + return non_empty.pop() + else: + return FrozenList([None] * indexes[0].nlevels) def _all_indexes_same(indexes): @@ -260,7 +269,5 @@ def _all_indexes_same(indexes): True if all indexes contain the same elements, False otherwise """ first = indexes[0] - for index in indexes[1:]: - if not first.equals(index): - return False - return True + others = indexes[1:] + return all(first.equals(other) for other in others)