|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from typing import ( |
4 | | - TYPE_CHECKING, |
5 | | - Any, |
| 3 | +from collections.abc import ( |
6 | 4 | Callable, |
7 | 5 | Collection, |
| 6 | + Generator, |
8 | 7 | Hashable, |
9 | 8 | Iterable, |
10 | | - Iterator, |
11 | | - List, |
12 | | - Literal, |
13 | | - Mapping, |
14 | 9 | Sequence, |
| 10 | +) |
| 11 | +from functools import wraps |
| 12 | +from sys import getsizeof |
| 13 | +from typing import ( |
| 14 | + TYPE_CHECKING, |
| 15 | + Any, |
| 16 | + Literal, |
15 | 17 | cast, |
16 | | - overload, |
17 | 18 | ) |
| 19 | +import warnings |
18 | 20 |
|
19 | 21 | import numpy as np |
20 | 22 |
|
21 | | -from pandas._libs import lib |
22 | | -from pandas._libs.hashtable import duplicated_int64 |
| 23 | +from pandas._config import get_option |
| 24 | + |
| 25 | +from pandas._libs import ( |
| 26 | + algos as libalgos, |
| 27 | + index as libindex, |
| 28 | + lib, |
| 29 | +) |
| 30 | +from pandas._libs.hashtable import duplicated |
23 | 31 | from pandas._typing import ( |
24 | | - ArrayLike, |
| 32 | + AnyAll, |
| 33 | + AnyArrayLike, |
25 | 34 | Axis, |
| 35 | + DropKeep, |
26 | 36 | DtypeObj, |
27 | 37 | F, |
| 38 | + IgnoreRaise, |
| 39 | + IndexLabel, |
| 40 | + IndexT, |
| 41 | + Scalar, |
| 42 | + Self, |
28 | 43 | Shape, |
29 | 44 | npt, |
30 | 45 | ) |
31 | | -from pandas.errors import InvalidIndexError |
| 46 | +from pandas.compat.numpy import function as nv |
| 47 | +from pandas.errors import ( |
| 48 | + InvalidIndexError, |
| 49 | + PerformanceWarning, |
| 50 | + UnsortedIndexError, |
| 51 | +) |
32 | 52 | from pandas.util._decorators import ( |
| 53 | + Appender, |
33 | 54 | cache_readonly, |
34 | 55 | doc, |
| 56 | + set_module, |
35 | 57 | ) |
36 | 58 | from pandas.util._exceptions import find_stack_level |
37 | 59 |
|
38 | 60 | from pandas.core.dtypes.cast import coerce_indexer_dtype |
39 | 61 | from pandas.core.dtypes.common import ( |
40 | 62 | ensure_int64, |
41 | 63 | ensure_platform_int, |
42 | | - is_categorical_dtype, |
43 | | - is_extension_array_dtype, |
| 64 | + is_hashable, |
| 65 | + is_integer, |
| 66 | + is_iterator, |
44 | 67 | is_list_like, |
45 | 68 | is_object_dtype, |
| 69 | + is_scalar, |
| 70 | + is_string_dtype, |
46 | 71 | pandas_dtype, |
47 | 72 | ) |
48 | | -from pandas.core.dtypes.dtypes import ExtensionDtype |
49 | | -from pandas.core.dtypes.missing import array_equivalent, isna |
| 73 | +from pandas.core.dtypes.dtypes import ( |
| 74 | + CategoricalDtype, |
| 75 | + ExtensionDtype, |
| 76 | +) |
| 77 | +from pandas.core.dtypes.generic import ( |
| 78 | + ABCDataFrame, |
| 79 | + ABCSeries, |
| 80 | +) |
| 81 | +from pandas.core.dtypes.inference import is_array_like |
| 82 | +from pandas.core.dtypes.missing import ( |
| 83 | + array_equivalent, |
| 84 | + isna, |
| 85 | +) |
50 | 86 |
|
51 | 87 | import pandas.core.algorithms as algos |
52 | | -from pandas.core.arrays.categorical import Categorical |
| 88 | +from pandas.core.array_algos.putmask import validate_putmask |
| 89 | +from pandas.core.arrays import ( |
| 90 | + Categorical, |
| 91 | + ExtensionArray, |
| 92 | +) |
| 93 | +from pandas.core.arrays.categorical import ( |
| 94 | + factorize_from_iterables, |
| 95 | + recode_for_categories, |
| 96 | +) |
| 97 | +import pandas.core.common as com |
| 98 | +from pandas.core.construction import sanitize_array |
| 99 | +import pandas.core.indexes.base as ibase |
53 | 100 | from pandas.core.indexes.base import ( |
54 | 101 | Index, |
55 | 102 | _index_shared_docs, |
56 | 103 | ensure_index, |
57 | 104 | get_unanimous_names, |
58 | 105 | ) |
59 | 106 | from pandas.core.indexes.frozen import FrozenList |
| 107 | +from pandas.core.ops.invalid import make_invalid_op |
| 108 | +from pandas.core.sorting import ( |
| 109 | + get_group_index, |
| 110 | + lexsort_indexer, |
| 111 | +) |
| 112 | + |
| 113 | +from pandas.io.formats.printing import pprint_thing |
60 | 114 |
|
61 | 115 | if TYPE_CHECKING: |
62 | | - from pandas import DataFrame |
| 116 | + from pandas import ( |
| 117 | + CategoricalIndex, |
| 118 | + DataFrame, |
| 119 | + Series, |
| 120 | + ) |
63 | 121 |
|
64 | 122 | _index_doc_kwargs = dict(ibase._index_doc_kwargs) |
65 | 123 | _index_doc_kwargs.update( |
@@ -468,54 +526,44 @@ def from_tuples( |
468 | 526 | ) -> MultiIndex: |
469 | 527 | """ |
470 | 528 | Convert list of tuples to MultiIndex. |
471 | | -
|
472 | | - Parameters |
473 | | - ---------- |
474 | | - tuples : list / sequence of tuple-likes |
475 | | - Each tuple is the index of one row/column. |
476 | | - sortorder : int or None |
477 | | - Level of sortedness (must be lexicographically sorted by that level). |
478 | | - names : list / sequence of str, optional |
479 | | - Names for the levels in the index. |
480 | | -
|
481 | | - Returns |
482 | | - ------- |
483 | | - MultiIndex |
484 | 529 | """ |
485 | 530 | if not is_list_like(tuples): |
486 | 531 | raise TypeError("Input must be a list / sequence of tuple-likes.") |
487 | | - |
488 | | - # Handle empty tuples case first |
489 | | - if isinstance(tuples, (list, tuple)) and len(tuples) == 0: |
490 | | - if names is None: |
491 | | - raise TypeError("Cannot infer number of levels from empty list") |
492 | | - names_seq = cast(Sequence[Hashable], names) |
493 | | - arrays: List[ArrayLike] = [[]] * len(names_seq) |
494 | | - return cls.from_arrays(arrays, sortorder=sortorder, names=names) |
495 | | - |
496 | | - # Convert iterator to list |
| 532 | + |
497 | 533 | if is_iterator(tuples): |
498 | 534 | tuples = list(tuples) |
499 | | - |
| 535 | + |
500 | 536 | tuples = cast(Collection[tuple[Hashable, ...]], tuples) |
501 | 537 |
|
502 | | - # Handle numpy array or Index |
503 | | - if isinstance(tuples, (np.ndarray, Index)): |
| 538 | + # handling the empty tuple cases |
| 539 | + if len(tuples) and all(isinstance(e, tuple) and not e for e in tuples): |
| 540 | + codes = [np.zeros(len(tuples))] |
| 541 | + levels = [Index(com.asarray_tuplesafe(tuples, dtype=np.dtype("object")))] |
| 542 | + return cls( |
| 543 | + levels=levels, |
| 544 | + codes=codes, |
| 545 | + sortorder=sortorder, |
| 546 | + names=names, |
| 547 | + verify_integrity=False, |
| 548 | + ) |
| 549 | + |
| 550 | + arrays: list[Sequence[Hashable]] |
| 551 | + if len(tuples) == 0: |
| 552 | + if names is None: |
| 553 | + raise TypeError("Cannot infer number of levels from empty list") |
| 554 | + # error: Argument 1 to "len" has incompatible type "Hashable"; |
| 555 | + # expected "Sized" |
| 556 | + arrays = [[]] * len(names) # type: ignore[arg-type] |
| 557 | + elif isinstance(tuples, (np.ndarray, Index)): |
504 | 558 | if isinstance(tuples, Index): |
505 | 559 | tuples = np.asarray(tuples._values) |
506 | | - arrays = list(lib.tuples_to_object_array(tuples).T) |
507 | | - return cls.from_arrays(arrays, sortorder=sortorder, names=names) |
508 | 560 |
|
509 | | - # Convert to list and normalize |
510 | | - tuples_list = [t if isinstance(t, tuple) else (t,) for t in tuples] |
511 | | - if not tuples_list: |
512 | | - arrays = [] |
| 561 | + arrays = list(lib.tuples_to_object_array(tuples).T) |
| 562 | + elif isinstance(tuples, list): |
| 563 | + arrays = list(lib.to_object_array_tuples(tuples).T) |
513 | 564 | else: |
514 | | - max_length = max(len(t) for t in tuples_list) |
515 | | - result_tuples = [ |
516 | | - t + (np.nan,) * (max_length - len(t)) for t in tuples_list |
517 | | - ] |
518 | | - arrays = list(lib.to_object_array_tuples(result_tuples).T) |
| 565 | + arrs = zip(*tuples) |
| 566 | + arrays = cast(list[Sequence[Hashable]], arrs) |
519 | 567 |
|
520 | 568 | return cls.from_arrays(arrays, sortorder=sortorder, names=names) |
521 | 569 |
|
|
0 commit comments