Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/common.py: 30%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

209 statements  

1""" 

2Misc tools for implementing data structures 

3 

4Note: pandas.core.common is *not* part of the public API. 

5""" 

6 

7from __future__ import annotations 

8 

9import builtins 

10from collections import ( 

11 abc, 

12 defaultdict, 

13) 

14from collections.abc import ( 

15 Callable, 

16 Collection, 

17 Generator, 

18 Hashable, 

19 Iterable, 

20 Sequence, 

21) 

22import contextlib 

23from functools import partial 

24import inspect 

25import sys 

26from typing import ( 

27 TYPE_CHECKING, 

28 Any, 

29 Concatenate, 

30 TypeVar, 

31 cast, 

32 overload, 

33) 

34 

35import numpy as np 

36 

37from pandas._libs import lib 

38 

39from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike 

40from pandas.core.dtypes.common import ( 

41 is_bool_dtype, 

42 is_integer, 

43) 

44from pandas.core.dtypes.generic import ( 

45 ABCExtensionArray, 

46 ABCIndex, 

47 ABCMultiIndex, 

48 ABCNumpyExtensionArray, 

49 ABCSeries, 

50) 

51from pandas.core.dtypes.inference import iterable_not_string 

52 

53from pandas.core.col import Expression 

54 

55if TYPE_CHECKING: 

56 from pandas._typing import ( 

57 AnyArrayLike, 

58 ArrayLike, 

59 NpDtype, 

60 P, 

61 RandomState, 

62 T, 

63 ) 

64 

65 from pandas import Index 

66 

67 

68def flatten(line): 

69 """ 

70 Flatten an arbitrarily nested sequence. 

71 

72 Parameters 

73 ---------- 

74 line : sequence 

75 The non string sequence to flatten 

76 

77 Notes 

78 ----- 

79 This doesn't consider strings sequences. 

80 

81 Returns 

82 ------- 

83 flattened : generator 

84 """ 

85 for element in line: 

86 if iterable_not_string(element): 

87 yield from flatten(element) 

88 else: 

89 yield element 

90 

91 

92def consensus_name_attr(objs): 

93 name = objs[0].name 

94 for obj in objs[1:]: 

95 try: 

96 if obj.name != name: 

97 name = None 

98 break 

99 except ValueError: 

100 name = None 

101 break 

102 return name 

103 

104 

105def is_bool_indexer(key: Any) -> bool: 

106 """ 

107 Check whether `key` is a valid boolean indexer. 

108 

109 Parameters 

110 ---------- 

111 key : Any 

112 Only list-likes may be considered boolean indexers. 

113 All other types are not considered a boolean indexer. 

114 For array-like input, boolean ndarrays or ExtensionArrays 

115 with ``_is_boolean`` set are considered boolean indexers. 

116 

117 Returns 

118 ------- 

119 bool 

120 Whether `key` is a valid boolean indexer. 

121 

122 Raises 

123 ------ 

124 ValueError 

125 When the array is an object-dtype ndarray or ExtensionArray 

126 and contains missing values. 

127 

128 See Also 

129 -------- 

130 check_array_indexer : Check that `key` is a valid array to index, 

131 and convert to an ndarray. 

132 """ 

133 if isinstance( 

134 key, 

135 (ABCSeries, np.ndarray, ABCIndex, ABCExtensionArray, ABCNumpyExtensionArray), 

136 ) and not isinstance(key, ABCMultiIndex): 

137 if key.dtype == np.object_: 

138 key_array = np.asarray(key) 

139 

140 if not lib.is_bool_array(key_array): 

141 na_msg = "Cannot mask with non-boolean array containing NA / NaN values" 

142 if lib.is_bool_array(key_array, skipna=True): 

143 # Don't raise on e.g. ["A", "B", np.nan], see 

144 # test_loc_getitem_list_of_labels_categoricalindex_with_na 

145 raise ValueError(na_msg) 

146 return False 

147 return True 

148 elif is_bool_dtype(key.dtype): 

149 return True 

150 elif isinstance(key, list): 

151 # check if np.array(key).dtype would be bool 

152 if len(key) > 0: 

153 if type(key) is not list: 

154 # GH#42461 cython will raise TypeError if we pass a subclass 

155 key = list(key) 

156 return lib.is_bool_list(key) 

157 

158 return False 

159 

160 

161def cast_scalar_indexer(val): 

162 """ 

163 Disallow indexing with a float key, even if that key is a round number. 

164 

165 Parameters 

166 ---------- 

167 val : scalar 

168 

169 Returns 

170 ------- 

171 outval : scalar 

172 """ 

173 # assumes lib.is_scalar(val) 

174 if lib.is_float(val) and val.is_integer(): 

175 raise IndexError( 

176 # GH#34193 

177 "Indexing with a float is no longer supported. Manually convert " 

178 "to an integer key instead." 

179 ) 

180 return val 

181 

182 

183def not_none(*args): 

184 """ 

185 Returns a generator consisting of the arguments that are not None. 

186 """ 

187 return (arg for arg in args if arg is not None) 

188 

189 

190def any_none(*args) -> bool: 

191 """ 

192 Returns a boolean indicating if any argument is None. 

193 """ 

194 return any(arg is None for arg in args) 

195 

196 

197def all_none(*args) -> bool: 

198 """ 

199 Returns a boolean indicating if all arguments are None. 

200 """ 

201 return all(arg is None for arg in args) 

202 

203 

204def any_not_none(*args) -> bool: 

205 """ 

206 Returns a boolean indicating if any argument is not None. 

207 """ 

208 return any(arg is not None for arg in args) 

209 

210 

211def all_not_none(*args) -> bool: 

212 """ 

213 Returns a boolean indicating if all arguments are not None. 

214 """ 

215 return all(arg is not None for arg in args) 

216 

217 

218def count_not_none(*args) -> int: 

219 """ 

220 Returns the count of arguments that are not None. 

221 """ 

222 return sum(x is not None for x in args) 

223 

224 

225@overload 

226def asarray_tuplesafe( 

227 values: ArrayLike | list | tuple | zip, dtype: NpDtype | None = ... 

228) -> np.ndarray: 

229 # ExtensionArray can only be returned when values is an Index, all other iterables 

230 # will return np.ndarray. Unfortunately "all other" cannot be encoded in a type 

231 # signature, so instead we special-case some common types. 

232 ... 

233 

234 

235@overload 

236def asarray_tuplesafe(values: Iterable, dtype: NpDtype | None = ...) -> ArrayLike: ... 

237 

238 

239def asarray_tuplesafe(values: Iterable, dtype: NpDtype | None = None) -> ArrayLike: 

240 if not (isinstance(values, (list, tuple)) or hasattr(values, "__array__")): 

241 values = list(values) 

242 elif isinstance(values, ABCIndex): 

243 return values._values 

244 elif isinstance(values, ABCSeries): 

245 return values._values 

246 

247 if isinstance(values, list) and dtype in [np.object_, object]: 

248 return construct_1d_object_array_from_listlike(values) 

249 

250 try: 

251 result = np.asarray(values, dtype=dtype) 

252 except ValueError: 

253 # Using try/except since it's more performant than checking is_list_like 

254 # over each element 

255 # error: Argument 1 to "construct_1d_object_array_from_listlike" 

256 # has incompatible type "Iterable[Any]"; expected "Sized" 

257 return construct_1d_object_array_from_listlike(values) # type: ignore[arg-type] 

258 

259 if issubclass(result.dtype.type, str): 

260 result = np.asarray(values, dtype=object) 

261 

262 if result.ndim == 2: 

263 # Avoid building an array of arrays: 

264 values = [tuple(x) for x in values] 

265 result = construct_1d_object_array_from_listlike(values) 

266 

267 return result 

268 

269 

270def index_labels_to_array( 

271 labels: np.ndarray | Iterable, dtype: NpDtype | None = None 

272) -> np.ndarray: 

273 """ 

274 Transform label or iterable of labels to array, for use in Index. 

275 

276 Parameters 

277 ---------- 

278 dtype : dtype 

279 If specified, use as dtype of the resulting array, otherwise infer. 

280 

281 Returns 

282 ------- 

283 array 

284 """ 

285 if isinstance(labels, (str, tuple)): 

286 labels = [labels] 

287 

288 if not isinstance(labels, (list, np.ndarray)): 

289 try: 

290 labels = list(labels) 

291 except TypeError: # non-iterable 

292 labels = [labels] 

293 

294 rlabels = asarray_tuplesafe(labels, dtype=dtype) 

295 

296 return rlabels 

297 

298 

299def maybe_make_list(obj): 

300 if obj is not None and not isinstance(obj, (tuple, list)): 

301 return [obj] 

302 return obj 

303 

304 

305def maybe_iterable_to_list(obj: Iterable[T] | T) -> Collection[T] | T: 

306 """ 

307 If obj is Iterable but not list-like, consume into list. 

308 """ 

309 if isinstance(obj, abc.Iterable) and not isinstance(obj, abc.Sized): 

310 return list(obj) 

311 obj = cast(Collection, obj) 

312 return obj 

313 

314 

315def is_null_slice(obj) -> bool: 

316 """ 

317 We have a null slice. 

318 """ 

319 return ( 

320 isinstance(obj, slice) 

321 and obj.start is None 

322 and obj.stop is None 

323 and obj.step is None 

324 ) 

325 

326 

327def is_empty_slice(obj) -> bool: 

328 """ 

329 We have an empty slice, e.g. no values are selected. 

330 """ 

331 return ( 

332 isinstance(obj, slice) 

333 and obj.start is not None 

334 and obj.stop is not None 

335 and obj.start == obj.stop 

336 ) 

337 

338 

339def is_true_slices(line: abc.Iterable) -> abc.Generator[bool, None, None]: 

340 """ 

341 Find non-trivial slices in "line": yields a bool. 

342 """ 

343 for k in line: 

344 yield isinstance(k, slice) and not is_null_slice(k) 

345 

346 

347# TODO: used only once in indexing; belongs elsewhere? 

348def is_full_slice(obj, line: int) -> bool: 

349 """ 

350 We have a full length slice. 

351 """ 

352 return ( 

353 isinstance(obj, slice) 

354 and obj.start == 0 

355 and obj.stop == line 

356 and obj.step is None 

357 ) 

358 

359 

360def get_callable_name(obj): 

361 # typical case has name 

362 if hasattr(obj, "__name__"): 

363 return obj.__name__ 

364 # some objects don't; could recurse 

365 if isinstance(obj, partial): 

366 return get_callable_name(obj.func) 

367 # fall back to class name 

368 if callable(obj): 

369 return type(obj).__name__ 

370 # everything failed (probably because the argument 

371 # wasn't actually callable); we return None 

372 # instead of the empty string in this case to allow 

373 # distinguishing between no name and a name of '' 

374 return None 

375 

376 

377def apply_if_callable(maybe_callable, obj, **kwargs): 

378 """ 

379 Evaluate possibly callable input using obj and kwargs if it is callable, 

380 otherwise return as it is. 

381 

382 Parameters 

383 ---------- 

384 maybe_callable : possibly a callable 

385 obj : NDFrame 

386 **kwargs 

387 """ 

388 if isinstance(maybe_callable, Expression): 

389 return maybe_callable._eval_expression(obj, **kwargs) 

390 elif callable(maybe_callable): 

391 return maybe_callable(obj, **kwargs) 

392 

393 return maybe_callable 

394 

395 

396def standardize_mapping(into): 

397 """ 

398 Helper function to standardize a supplied mapping. 

399 

400 Parameters 

401 ---------- 

402 into : instance or subclass of collections.abc.Mapping 

403 Must be a class, an initialized collections.defaultdict, 

404 or an instance of a collections.abc.Mapping subclass. 

405 

406 Returns 

407 ------- 

408 mapping : a collections.abc.Mapping subclass or other constructor 

409 a callable object that can accept an iterator to create 

410 the desired Mapping. 

411 

412 See Also 

413 -------- 

414 DataFrame.to_dict 

415 Series.to_dict 

416 """ 

417 if not inspect.isclass(into): 

418 if isinstance(into, defaultdict): 

419 return partial(defaultdict, into.default_factory) 

420 into = type(into) 

421 if not issubclass(into, abc.Mapping): 

422 raise TypeError(f"unsupported type: {into}") 

423 if into == defaultdict: 

424 raise TypeError("to_dict() only accepts initialized defaultdicts") 

425 return into 

426 

427 

428@overload 

429def random_state(state: np.random.Generator) -> np.random.Generator: ... 

430 

431 

432@overload 

433def random_state( 

434 state: int | np.ndarray | np.random.BitGenerator | np.random.RandomState | None, 

435) -> np.random.RandomState: ... 

436 

437 

438def random_state(state: RandomState | None = None): 

439 """ 

440 Helper function for processing random_state arguments. 

441 

442 Parameters 

443 ---------- 

444 state : int, array-like, BitGenerator, Generator, np.random.RandomState, None. 

445 If receives an int, array-like, or BitGenerator, passes to 

446 np.random.RandomState() as seed. 

447 If receives an np.random RandomState or Generator, just returns that unchanged. 

448 If receives `None`, returns np.random. 

449 If receives anything else, raises an informative ValueError. 

450 

451 Default None. 

452 

453 Returns 

454 ------- 

455 np.random.RandomState or np.random.Generator. If state is None, returns np.random 

456 

457 """ 

458 if is_integer(state) or isinstance(state, (np.ndarray, np.random.BitGenerator)): 

459 return np.random.RandomState(state) 

460 elif isinstance(state, np.random.RandomState): 

461 return state 

462 elif isinstance(state, np.random.Generator): 

463 return state 

464 elif state is None: 

465 return np.random 

466 else: 

467 raise ValueError( 

468 "random_state must be an integer, array-like, a BitGenerator, Generator, " 

469 "a numpy RandomState, or None" 

470 ) 

471 

472 

473_T = TypeVar("_T") # Secondary TypeVar for use in pipe's type hints 

474 

475 

476@overload 

477def pipe( 

478 obj: _T, 

479 func: Callable[Concatenate[_T, P], T], 

480 *args: P.args, 

481 **kwargs: P.kwargs, 

482) -> T: ... 

483 

484 

485@overload 

486def pipe( 

487 obj: Any, 

488 func: tuple[Callable[..., T], str], 

489 *args: Any, 

490 **kwargs: Any, 

491) -> T: ... 

492 

493 

494def pipe( 

495 obj: _T, 

496 func: Callable[Concatenate[_T, P], T] | tuple[Callable[..., T], str], 

497 *args: Any, 

498 **kwargs: Any, 

499) -> T: 

500 """ 

501 Apply a function ``func`` to object ``obj`` either by passing obj as the 

502 first argument to the function or, in the case that the func is a tuple, 

503 interpret the first element of the tuple as a function and pass the obj to 

504 that function as a keyword argument whose key is the value of the second 

505 element of the tuple. 

506 

507 Parameters 

508 ---------- 

509 func : callable or tuple of (callable, str) 

510 Function to apply to this object or, alternatively, a 

511 ``(callable, data_keyword)`` tuple where ``data_keyword`` is a 

512 string indicating the keyword of ``callable`` that expects the 

513 object. 

514 *args : iterable, optional 

515 Positional arguments passed into ``func``. 

516 **kwargs : dict, optional 

517 A dictionary of keyword arguments passed into ``func``. 

518 

519 Returns 

520 ------- 

521 object : the return type of ``func``. 

522 """ 

523 if isinstance(func, tuple): 

524 # Assigning to func_ so pyright understands that it's a callable 

525 func_, target = func 

526 if target in kwargs: 

527 msg = f"{target} is both the pipe target and a keyword argument" 

528 raise ValueError(msg) 

529 kwargs[target] = obj 

530 return func_(*args, **kwargs) 

531 else: 

532 return func(obj, *args, **kwargs) 

533 

534 

535def get_rename_function(mapper): 

536 """ 

537 Returns a function that will map names/labels, dependent if mapper 

538 is a dict, Series or just a function. 

539 """ 

540 

541 def f(x): 

542 if x in mapper: 

543 return mapper[x] 

544 else: 

545 return x 

546 

547 return f if isinstance(mapper, (abc.Mapping, ABCSeries)) else mapper 

548 

549 

550def convert_to_list_like( 

551 values: Hashable | Iterable | AnyArrayLike, 

552) -> list | AnyArrayLike: 

553 """ 

554 Convert list-like or scalar input to list-like. List, numpy and pandas array-like 

555 inputs are returned unmodified whereas others are converted to list. 

556 """ 

557 if isinstance(values, (list, np.ndarray, ABCIndex, ABCSeries, ABCExtensionArray)): 

558 return values 

559 elif isinstance(values, abc.Iterable) and not isinstance(values, str): 

560 return list(values) 

561 

562 return [values] 

563 

564 

565@contextlib.contextmanager 

566def temp_setattr(obj, attr: str, value, condition: bool = True) -> Generator[None]: 

567 """ 

568 Temporarily set attribute on an object. 

569 

570 Parameters 

571 ---------- 

572 obj : object 

573 Object whose attribute will be modified. 

574 attr : str 

575 Attribute to modify. 

576 value : Any 

577 Value to temporarily set attribute to. 

578 condition : bool, default True 

579 Whether to set the attribute. Provided in order to not have to 

580 conditionally use this context manager. 

581 

582 Yields 

583 ------ 

584 object : obj with modified attribute. 

585 """ 

586 if condition: 

587 old_value = getattr(obj, attr) 

588 setattr(obj, attr, value) 

589 try: 

590 yield obj 

591 finally: 

592 if condition: 

593 setattr(obj, attr, old_value) 

594 

595 

596def require_length_match(data, index: Index) -> None: 

597 """ 

598 Check the length of data matches the length of the index. 

599 """ 

600 if len(data) != len(index): 

601 raise ValueError( 

602 "Length of values " 

603 f"({len(data)}) " 

604 "does not match length of index " 

605 f"({len(index)})" 

606 ) 

607 

608 

609_cython_table = { 

610 builtins.sum: "sum", 

611 builtins.max: "max", 

612 builtins.min: "min", 

613 np.all: "all", 

614 np.any: "any", 

615 np.sum: "sum", 

616 np.nansum: "sum", 

617 np.mean: "mean", 

618 np.nanmean: "mean", 

619 np.prod: "prod", 

620 np.nanprod: "prod", 

621 np.std: "std", 

622 np.nanstd: "std", 

623 np.var: "var", 

624 np.nanvar: "var", 

625 np.median: "median", 

626 np.nanmedian: "median", 

627 np.max: "max", 

628 np.nanmax: "max", 

629 np.min: "min", 

630 np.nanmin: "min", 

631 np.cumprod: "cumprod", 

632 np.nancumprod: "cumprod", 

633 np.cumsum: "cumsum", 

634 np.nancumsum: "cumsum", 

635} 

636 

637 

638def get_cython_func(arg: Callable) -> str | None: 

639 """ 

640 if we define an internal function for this argument, return it 

641 """ 

642 return _cython_table.get(arg) 

643 

644 

645def fill_missing_names(names: Sequence[Hashable | None]) -> list[Hashable]: 

646 """ 

647 If a name is missing then replace it by level_n, where n is the count 

648 

649 Parameters 

650 ---------- 

651 names : list-like 

652 list of column names or None values. 

653 

654 Returns 

655 ------- 

656 list 

657 list of column names with the None values replaced. 

658 """ 

659 return [f"level_{i}" if name is None else name for i, name in enumerate(names)] 

660 

661 

662def is_local_in_caller_frame(obj): 

663 """ 

664 Helper function used in detecting chained assignment. 

665 

666 If the pandas object (DataFrame/Series) is a local variable 

667 in the caller's frame, it should not be a case of chained 

668 assignment or method call. 

669 

670 For example: 

671 

672 def test(): 

673 df = pd.DataFrame(...) 

674 df["a"] = 1 # not chained assignment 

675 

676 Inside ``df.__setitem__``, we call this function to check whether `df` 

677 (`self`) is a local variable in `test` frame (the frame calling setitem). If 

678 so, we know it is not a case of chained assignment (even when the refcount 

679 of `df` is below the threshold due to optimization of local variables). 

680 """ 

681 frame = sys._getframe(2) 

682 for v in frame.f_locals.values(): 

683 if v is obj: 

684 return True 

685 return False