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

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

404 statements  

1""" 

2Functions for preparing various inputs passed to the DataFrame or Series 

3constructors before passing them to a BlockManager. 

4""" 

5 

6from __future__ import annotations 

7 

8from collections import abc 

9from typing import ( 

10 TYPE_CHECKING, 

11 Any, 

12) 

13 

14import numpy as np 

15from numpy import ma 

16 

17from pandas._config import using_string_dtype 

18 

19from pandas._libs import lib 

20 

21from pandas.core.dtypes.astype import astype_is_view 

22from pandas.core.dtypes.cast import ( 

23 construct_1d_arraylike_from_scalar, 

24 dict_compat, 

25 maybe_cast_to_datetime, 

26 maybe_convert_platform, 

27) 

28from pandas.core.dtypes.common import ( 

29 is_1d_only_ea_dtype, 

30 is_integer_dtype, 

31 is_list_like, 

32 is_named_tuple, 

33 is_object_dtype, 

34 is_scalar, 

35) 

36from pandas.core.dtypes.dtypes import ( 

37 BaseMaskedDtype, 

38 ExtensionDtype, 

39) 

40from pandas.core.dtypes.generic import ( 

41 ABCDataFrame, 

42 ABCSeries, 

43) 

44from pandas.core.dtypes.missing import isna 

45 

46from pandas.core import ( 

47 algorithms, 

48 common as com, 

49) 

50from pandas.core.arrays import ExtensionArray 

51from pandas.core.arrays.string_ import StringDtype 

52from pandas.core.construction import ( 

53 array as pd_array, 

54 extract_array, 

55 range_to_ndarray, 

56 sanitize_array, 

57) 

58from pandas.core.indexes.api import ( 

59 DatetimeIndex, 

60 Index, 

61 MultiIndex, 

62 TimedeltaIndex, 

63 default_index, 

64 ensure_index, 

65 get_objs_combined_axis, 

66 maybe_sequence_to_range, 

67 union_indexes, 

68) 

69from pandas.core.internals.blocks import ( 

70 BlockPlacement, 

71 ensure_block_shape, 

72 new_block, 

73 new_block_2d, 

74) 

75from pandas.core.internals.managers import ( 

76 create_block_manager_from_blocks, 

77 create_block_manager_from_column_arrays, 

78) 

79 

80if TYPE_CHECKING: 

81 from collections.abc import ( 

82 Hashable, 

83 Sequence, 

84 ) 

85 

86 from pandas._typing import ( 

87 ArrayLike, 

88 DtypeObj, 

89 Manager, 

90 npt, 

91 ) 

92# --------------------------------------------------------------------- 

93# BlockManager Interface 

94 

95 

96def arrays_to_mgr( 

97 arrays, 

98 columns: Index, 

99 index, 

100 *, 

101 dtype: DtypeObj | None = None, 

102 verify_integrity: bool = True, 

103 consolidate: bool = True, 

104) -> Manager: 

105 """ 

106 Segregate Series based on type and coerce into matrices. 

107 

108 Needs to handle a lot of exceptional cases. 

109 """ 

110 if verify_integrity: 

111 # figure out the index, if necessary 

112 if index is None: 

113 index = _extract_index(arrays) 

114 else: 

115 index = ensure_index(index) 

116 

117 # don't force copy because getting jammed in an ndarray anyway 

118 arrays, refs = _homogenize(arrays, index, dtype) 

119 # _homogenize ensures 

120 # - all(len(x) == len(index) for x in arrays) 

121 # - all(x.ndim == 1 for x in arrays) 

122 # - all(isinstance(x, (np.ndarray, ExtensionArray)) for x in arrays) 

123 # - all(type(x) is not NumpyExtensionArray for x in arrays) 

124 

125 else: 

126 index = ensure_index(index) 

127 arrays = [extract_array(x, extract_numpy=True) for x in arrays] 

128 # with _from_arrays, the passed arrays should never be Series objects 

129 refs = [None] * len(arrays) 

130 

131 # Reached via DataFrame._from_arrays; we do minimal validation here 

132 for arr in arrays: 

133 if ( 

134 not isinstance(arr, (np.ndarray, ExtensionArray)) 

135 or arr.ndim != 1 

136 or len(arr) != len(index) 

137 ): 

138 raise ValueError( 

139 "Arrays must be 1-dimensional np.ndarray or ExtensionArray " 

140 "with length matching len(index)" 

141 ) 

142 

143 columns = ensure_index(columns) 

144 if len(columns) != len(arrays): 

145 raise ValueError("len(arrays) must match len(columns)") 

146 

147 # from BlockManager perspective 

148 axes = [columns, index] 

149 

150 return create_block_manager_from_column_arrays( 

151 arrays, axes, consolidate=consolidate, refs=refs 

152 ) 

153 

154 

155def rec_array_to_mgr( 

156 data: np.rec.recarray | np.ndarray, 

157 index, 

158 columns, 

159 dtype: DtypeObj | None, 

160 copy: bool, 

161) -> Manager: 

162 """ 

163 Extract from a masked rec array and create the manager. 

164 """ 

165 # essentially process a record array then fill it 

166 fdata = ma.getdata(data) 

167 if index is None: 

168 index = default_index(len(fdata)) 

169 else: 

170 index = ensure_index(index) 

171 

172 if columns is not None: 

173 columns = ensure_index(columns) 

174 arrays, arr_columns = to_arrays(fdata, columns) 

175 

176 # create the manager 

177 

178 arrays, arr_columns = reorder_arrays(arrays, arr_columns, columns, len(index)) 

179 if columns is None: 

180 columns = arr_columns 

181 

182 mgr = arrays_to_mgr(arrays, columns, index, dtype=dtype) 

183 

184 if copy: 

185 mgr = mgr.copy(deep=True) 

186 return mgr 

187 

188 

189# --------------------------------------------------------------------- 

190# DataFrame Constructor Interface 

191 

192 

193def ndarray_to_mgr( 

194 values, index, columns, dtype: DtypeObj | None, copy: bool 

195) -> Manager: 

196 # used in DataFrame.__init__ 

197 # input must be an ndarray, list, Series, Index, ExtensionArray 

198 infer_object = not isinstance(values, (ABCSeries, Index, ExtensionArray)) 

199 

200 if isinstance(values, ABCSeries): 

201 if columns is None: 

202 if values.name is not None: 

203 columns = Index([values.name]) 

204 if index is None: 

205 index = values.index 

206 else: 

207 values = values.reindex(index) 

208 

209 # zero len case (GH #2234) 

210 if not len(values) and columns is not None and len(columns): 

211 values = np.empty((0, 1), dtype=object) 

212 

213 vdtype = getattr(values, "dtype", None) 

214 refs = None 

215 if is_1d_only_ea_dtype(vdtype) or is_1d_only_ea_dtype(dtype): 

216 # GH#19157 

217 

218 if isinstance(values, (np.ndarray, ExtensionArray)) and values.ndim > 1: 

219 # GH#12513 an EA dtype passed with a 2D array, split into 

220 # multiple EAs that view the values 

221 # error: No overload variant of "__getitem__" of "ExtensionArray" 

222 # matches argument type "Tuple[slice, int]" 

223 values = [ 

224 values[:, n] # type: ignore[call-overload] 

225 for n in range(values.shape[1]) 

226 ] 

227 else: 

228 values = [values] 

229 

230 # Handle copy semantics: already copy 1d-only EA. Other arrays will 

231 # be copied when consolidating the blocks 

232 if copy: 

233 values = [ 

234 (x.copy(deep=True) if isinstance(x, Index) else x.copy()) 

235 if isinstance(x, (ExtensionArray, Index, ABCSeries)) 

236 and is_1d_only_ea_dtype(x.dtype) 

237 else x 

238 for x in values 

239 ] 

240 

241 if columns is None: 

242 columns = Index(range(len(values))) 

243 else: 

244 columns = ensure_index(columns) 

245 

246 return arrays_to_mgr(values, columns, index, dtype=dtype, consolidate=copy) 

247 

248 if isinstance(values, (ABCSeries, Index)): 

249 if not copy and (dtype is None or astype_is_view(values.dtype, dtype)): 

250 refs = values._references 

251 

252 if isinstance(vdtype, ExtensionDtype): 

253 # i.e. Datetime64TZ, PeriodDtype; cases with is_1d_only_ea_dtype(vdtype) 

254 # are already caught above 

255 values = extract_array(values, extract_numpy=True) 

256 if copy: 

257 values = values.copy() 

258 if values.ndim == 1: 

259 values = values.reshape(-1, 1) 

260 

261 elif isinstance(values, (ABCSeries, Index)): 

262 if copy: 

263 values = values._values.copy() 

264 else: 

265 values = values._values 

266 

267 values = _ensure_2d(values) 

268 

269 elif isinstance(values, (np.ndarray, ExtensionArray)): 

270 # drop subclass info 

271 if copy and (dtype is None or astype_is_view(values.dtype, dtype)): 

272 # only force a copy now if copy=True was requested 

273 # and a subsequent `astype` will not already result in a copy 

274 values = np.array(values, copy=True, order="F") 

275 else: 

276 values = np.asarray(values) 

277 values = _ensure_2d(values) 

278 

279 else: 

280 # by definition an array here 

281 # the dtypes will be coerced to a single dtype 

282 values = _prep_ndarraylike(values, copy=copy) 

283 

284 if dtype is not None and values.dtype != dtype: 

285 # GH#40110 see similar check inside sanitize_array 

286 values = sanitize_array( 

287 values, 

288 None, 

289 dtype=dtype, 

290 copy=copy, 

291 allow_2d=True, 

292 ) 

293 

294 # _prep_ndarraylike ensures that values.ndim == 2 at this point 

295 index, columns = _get_axes( 

296 values.shape[0], values.shape[1], index=index, columns=columns 

297 ) 

298 

299 _check_values_indices_shape_match(values, index, columns) 

300 

301 values = values.T 

302 

303 # if we don't have a dtype specified, then try to convert objects 

304 # on the entire block; this is to convert if we have datetimelike's 

305 # embedded in an object type 

306 if dtype is None and infer_object and is_object_dtype(values.dtype): 

307 obj_columns = list(values) 

308 maybe_datetime = [ 

309 lib.maybe_convert_objects( 

310 x, 

311 # Here we do not convert numeric dtypes, as if we wanted that, 

312 # numpy would have done it for us. 

313 convert_numeric=False, 

314 convert_non_numeric=True, 

315 convert_to_nullable_dtype=False, 

316 dtype_if_all_nat=np.dtype("M8[s]"), 

317 ) 

318 for x in obj_columns 

319 ] 

320 # don't convert (and copy) the objects if no type inference occurs 

321 if any(x is not y for x, y in zip(obj_columns, maybe_datetime, strict=True)): 

322 block_values = [ 

323 new_block_2d(ensure_block_shape(dval, 2), placement=BlockPlacement(n)) 

324 for n, dval in enumerate(maybe_datetime) 

325 ] 

326 else: 

327 bp = BlockPlacement(slice(len(columns))) 

328 nb = new_block_2d(values, placement=bp, refs=refs) 

329 block_values = [nb] 

330 elif dtype is None and values.dtype.kind == "U" and using_string_dtype(): 

331 dtype = StringDtype(na_value=np.nan) 

332 

333 obj_columns = list(values) 

334 block_values = [ 

335 new_block( 

336 dtype.construct_array_type()._from_sequence(data, dtype=dtype), 

337 BlockPlacement(slice(i, i + 1)), 

338 ndim=2, 

339 ) 

340 for i, data in enumerate(obj_columns) 

341 ] 

342 

343 else: 

344 bp = BlockPlacement(slice(len(columns))) 

345 nb = new_block_2d(values, placement=bp, refs=refs) 

346 block_values = [nb] 

347 

348 if len(columns) == 0: 

349 # TODO: check len(values) == 0? 

350 block_values = [] 

351 

352 return create_block_manager_from_blocks( 

353 block_values, [columns, index], verify_integrity=False 

354 ) 

355 

356 

357def _check_values_indices_shape_match( 

358 values: np.ndarray, index: Index, columns: Index 

359) -> None: 

360 """ 

361 Check that the shape implied by our axes matches the actual shape of the 

362 data. 

363 """ 

364 if values.shape[1] != len(columns) or values.shape[0] != len(index): 

365 # Could let this raise in Block constructor, but we get a more 

366 # helpful exception message this way. 

367 if values.shape[0] == 0 < len(index): 

368 raise ValueError("Empty data passed with indices specified.") 

369 

370 passed = values.shape 

371 implied = (len(index), len(columns)) 

372 raise ValueError(f"Shape of passed values is {passed}, indices imply {implied}") 

373 

374 

375def dict_to_mgr( 

376 data: dict, 

377 index, 

378 columns, 

379 *, 

380 dtype: DtypeObj | None = None, 

381 copy: bool = True, 

382) -> Manager: 

383 """ 

384 Segregate Series based on type and coerce into matrices. 

385 Needs to handle a lot of exceptional cases. 

386 

387 Used in DataFrame.__init__ 

388 """ 

389 arrays: Sequence[Any] 

390 

391 if columns is not None: 

392 columns = ensure_index(columns) 

393 if dtype is not None and not isinstance(dtype, np.dtype): 

394 # e.g. test_dataframe_from_dict_of_series 

395 arrays = [dtype.na_value] * len(columns) 

396 else: 

397 arrays = [np.nan] * len(columns) 

398 midxs = set() 

399 data_keys = ensure_index(data.keys()) # type: ignore[arg-type] 

400 data_values = list(data.values()) 

401 

402 for i, column in enumerate(columns): 

403 try: 

404 idx = data_keys.get_loc(column) 

405 except KeyError: 

406 midxs.add(i) 

407 continue 

408 array = data_values[idx] 

409 arrays[i] = array 

410 if is_scalar(array) and isna(array): 

411 midxs.add(i) 

412 

413 if index is None: 

414 # GH10856 

415 # raise ValueError if only scalars in dict 

416 if midxs: 

417 index = _extract_index( 

418 [array for i, array in enumerate(arrays) if i not in midxs] 

419 ) 

420 else: 

421 index = _extract_index(arrays) 

422 else: 

423 index = ensure_index(index) 

424 

425 # no obvious "empty" int column 

426 if midxs and not is_integer_dtype(dtype): 

427 # GH#1783 

428 for i in midxs: 

429 arr = construct_1d_arraylike_from_scalar( 

430 arrays[i], 

431 len(index), 

432 dtype if dtype is not None else np.dtype("object"), 

433 ) 

434 arrays[i] = arr 

435 

436 else: 

437 keys = maybe_sequence_to_range(list(data.keys())) 

438 columns = Index(keys) if keys else default_index(0) 

439 arrays = [com.maybe_iterable_to_list(data[k]) for k in keys] 

440 

441 if copy: 

442 # We only need to copy arrays that will not get consolidated, i.e. 

443 # only EA arrays 

444 arrays = [ 

445 ( 

446 x.copy() 

447 if isinstance(x, ExtensionArray) 

448 else ( 

449 x.copy(deep=True) 

450 if ( 

451 isinstance(x, Index) 

452 or (isinstance(x, ABCSeries) and is_1d_only_ea_dtype(x.dtype)) 

453 ) 

454 else x 

455 ) 

456 ) 

457 for x in arrays 

458 ] 

459 

460 return arrays_to_mgr(arrays, columns, index, dtype=dtype, consolidate=copy) 

461 

462 

463def nested_data_to_arrays( 

464 data: Sequence, 

465 columns: Index | None, 

466 index: Index | None, 

467 dtype: DtypeObj | None, 

468) -> tuple[list[ArrayLike], Index, Index]: 

469 """ 

470 Convert a single sequence of arrays to multiple arrays. 

471 """ 

472 # By the time we get here we have already checked treat_as_nested(data) 

473 

474 if is_named_tuple(data[0]) and columns is None: 

475 columns = ensure_index(data[0]._fields) 

476 

477 arrays, columns = to_arrays(data, columns, dtype=dtype) 

478 columns = ensure_index(columns) 

479 

480 if index is None: 

481 if isinstance(data[0], ABCSeries): 

482 index = _get_names_from_index(data) 

483 else: 

484 index = default_index(len(data)) 

485 

486 return arrays, columns, index 

487 

488 

489def treat_as_nested(data) -> bool: 

490 """ 

491 Check if we should use nested_data_to_arrays. 

492 """ 

493 return ( 

494 len(data) > 0 

495 and is_list_like(data[0]) 

496 and getattr(data[0], "ndim", 1) == 1 

497 and not (isinstance(data, ExtensionArray) and data.ndim == 2) 

498 ) 

499 

500 

501# --------------------------------------------------------------------- 

502 

503 

504def _prep_ndarraylike(values, copy: bool = True) -> np.ndarray: 

505 # values is specifically _not_ ndarray, EA, Index, or Series 

506 # We only get here with `not treat_as_nested(values)` 

507 

508 if len(values) == 0: 

509 # TODO: check for length-zero range, in which case return int64 dtype? 

510 # TODO: reuse anything in try_cast? 

511 return np.empty((0, 0), dtype=object) 

512 elif isinstance(values, range): 

513 arr = range_to_ndarray(values) 

514 return arr[..., np.newaxis] 

515 

516 def convert(v): 

517 if not is_list_like(v) or isinstance(v, ABCDataFrame): 

518 return v 

519 

520 v = extract_array(v, extract_numpy=True) 

521 res = maybe_convert_platform(v) 

522 # We don't do maybe_infer_objects here bc we will end up doing 

523 # it column-by-column in ndarray_to_mgr 

524 return res 

525 

526 # we could have a 1-dim or 2-dim list here 

527 # this is equiv of np.asarray, but does object conversion 

528 # and platform dtype preservation 

529 # does not convert e.g. [1, "a", True] to ["1", "a", "True"] like 

530 # np.asarray would 

531 if is_list_like(values[0]): 

532 values = np.array([convert(v) for v in values]) 

533 elif isinstance(values[0], np.ndarray) and values[0].ndim == 0: 

534 # GH#21861 see test_constructor_list_of_lists 

535 values = np.array([convert(v) for v in values]) 

536 else: 

537 values = convert(values) 

538 

539 return _ensure_2d(values) 

540 

541 

542def _ensure_2d(values: np.ndarray) -> np.ndarray: 

543 """ 

544 Reshape 1D values, raise on anything else other than 2D. 

545 """ 

546 if values.ndim == 1: 

547 values = values.reshape((values.shape[0], 1)) 

548 elif values.ndim != 2: 

549 raise ValueError(f"Must pass 2-d input. shape={values.shape}") 

550 return values 

551 

552 

553def _homogenize( 

554 data, index: Index, dtype: DtypeObj | None 

555) -> tuple[list[ArrayLike], list[Any]]: 

556 oindex = None 

557 homogenized = [] 

558 # if the original array-like in `data` is a Series, keep track of this Series' refs 

559 refs: list[Any] = [] 

560 

561 for val in data: 

562 if isinstance(val, (ABCSeries, Index)): 

563 if dtype is not None: 

564 val = val.astype(dtype) 

565 if isinstance(val, ABCSeries) and val.index is not index: 

566 # Forces alignment. No need to copy data since we 

567 # are putting it into an ndarray later 

568 val = val.reindex(index) 

569 refs.append(val._references) 

570 val = val._values 

571 else: 

572 if isinstance(val, dict): 

573 # GH#41785 this _should_ be equivalent to (but faster than) 

574 # val = Series(val, index=index)._values 

575 if oindex is None: 

576 oindex = index.astype("O") 

577 

578 if isinstance(index, (DatetimeIndex, TimedeltaIndex)): 

579 # see test_constructor_dict_datetime64_index 

580 val = dict_compat(val) 

581 else: 

582 # see test_constructor_subclass_dict 

583 val = dict(val) 

584 

585 if not isinstance(index, MultiIndex) and index.hasnans: 

586 # GH#63889 Check if dict has missing value keys that need special 

587 # handling (i.e. None/np.nan/pd.NA might no longer be matched 

588 # when using fast_multiget with processed object index values) 

589 from pandas import Series 

590 

591 val = Series(val).reindex(index)._values 

592 else: 

593 # Fast path: use lib.fast_multiget for dicts without missing keys 

594 val = lib.fast_multiget(val, oindex._values, default=np.nan) 

595 

596 val = sanitize_array(val, index, dtype=dtype, copy=False) 

597 com.require_length_match(val, index) 

598 refs.append(None) 

599 

600 homogenized.append(val) 

601 

602 return homogenized, refs 

603 

604 

605def _extract_index(data) -> Index: 

606 """ 

607 Try to infer an Index from the passed data, raise ValueError on failure. 

608 """ 

609 index: Index 

610 if len(data) == 0: 

611 return default_index(0) 

612 

613 raw_lengths = set() 

614 indexes: list[list[Hashable] | Index] = [] 

615 

616 have_raw_arrays = False 

617 have_series = False 

618 have_dicts = False 

619 

620 for val in data: 

621 if isinstance(val, ABCSeries): 

622 have_series = True 

623 indexes.append(val.index) 

624 elif isinstance(val, dict): 

625 have_dicts = True 

626 indexes.append(list(val.keys())) 

627 elif is_list_like(val) and getattr(val, "ndim", 1) == 1: 

628 have_raw_arrays = True 

629 raw_lengths.add(len(val)) 

630 elif isinstance(val, np.ndarray) and val.ndim > 1: 

631 raise ValueError("Per-column arrays must each be 1-dimensional") 

632 

633 if not indexes and not raw_lengths: 

634 raise ValueError("If using all scalar values, you must pass an index") 

635 

636 if have_series: 

637 index = union_indexes(indexes) 

638 elif have_dicts: 

639 index = union_indexes(indexes, sort=False) 

640 

641 if have_raw_arrays: 

642 if len(raw_lengths) > 1: 

643 raise ValueError("All arrays must be of the same length") 

644 

645 if have_dicts: 

646 raise ValueError( 

647 "Mixing dicts with non-Series may lead to ambiguous ordering." 

648 ) 

649 raw_length = raw_lengths.pop() 

650 if have_series: 

651 if raw_length != len(index): 

652 msg = ( 

653 f"array length {raw_length} does not match index " 

654 f"length {len(index)}" 

655 ) 

656 raise ValueError(msg) 

657 else: 

658 index = default_index(raw_length) 

659 

660 return ensure_index(index) 

661 

662 

663def reorder_arrays( 

664 arrays: list[ArrayLike], arr_columns: Index, columns: Index | None, length: int 

665) -> tuple[list[ArrayLike], Index]: 

666 """ 

667 Preemptively (cheaply) reindex arrays with new columns. 

668 """ 

669 # reorder according to the columns 

670 if columns is not None: 

671 if not columns.equals(arr_columns): 

672 # if they are equal, there is nothing to do 

673 new_arrays: list[ArrayLike] = [] 

674 indexer = arr_columns.get_indexer(columns) 

675 for i, k in enumerate(indexer): 

676 if k == -1: 

677 # by convention default is all-NaN object dtype 

678 arr = np.empty(length, dtype=object) 

679 arr.fill(np.nan) 

680 else: 

681 arr = arrays[k] 

682 new_arrays.append(arr) 

683 

684 arrays = new_arrays 

685 arr_columns = columns 

686 

687 return arrays, arr_columns 

688 

689 

690def _get_names_from_index(data) -> Index: 

691 has_some_name = any(getattr(s, "name", None) is not None for s in data) 

692 if not has_some_name: 

693 return default_index(len(data)) 

694 

695 index: list[Hashable] = list(range(len(data))) 

696 count = 0 

697 for i, s in enumerate(data): 

698 n = getattr(s, "name", None) 

699 if n is not None: 

700 index[i] = n 

701 else: 

702 index[i] = f"Unnamed {count}" 

703 count += 1 

704 

705 return Index(index) 

706 

707 

708def _get_axes( 

709 N: int, K: int, index: Index | None, columns: Index | None 

710) -> tuple[Index, Index]: 

711 # helper to create the axes as indexes 

712 # return axes or defaults 

713 

714 if index is None: 

715 index = default_index(N) 

716 else: 

717 index = ensure_index(index) 

718 

719 if columns is None: 

720 columns = default_index(K) 

721 else: 

722 columns = ensure_index(columns) 

723 return index, columns 

724 

725 

726def dataclasses_to_dicts(data): 

727 """ 

728 Converts a list of dataclass instances to a list of dictionaries. 

729 

730 Parameters 

731 ---------- 

732 data : List[Type[dataclass]] 

733 

734 Returns 

735 -------- 

736 list_dict : List[dict] 

737 

738 Examples 

739 -------- 

740 >>> from dataclasses import dataclass 

741 >>> @dataclass 

742 ... class Point: 

743 ... x: int 

744 ... y: int 

745 

746 >>> dataclasses_to_dicts([Point(1, 2), Point(2, 3)]) 

747 [{'x': 1, 'y': 2}, {'x': 2, 'y': 3}] 

748 

749 """ 

750 from dataclasses import asdict 

751 

752 return list(map(asdict, data)) 

753 

754 

755# --------------------------------------------------------------------- 

756# Conversion of Inputs to Arrays 

757 

758 

759def to_arrays( 

760 data, columns: Index | None, dtype: DtypeObj | None = None 

761) -> tuple[list[ArrayLike], Index]: 

762 """ 

763 Return list of arrays, columns. 

764 

765 Returns 

766 ------- 

767 list[ArrayLike] 

768 These will become columns in a DataFrame. 

769 Index 

770 This will become frame.columns. 

771 

772 Notes 

773 ----- 

774 Ensures that len(result_arrays) == len(result_index). 

775 """ 

776 

777 if not len(data): 

778 if isinstance(data, np.ndarray): 

779 if data.dtype.names is not None: 

780 # i.e. numpy structured array 

781 columns = ensure_index(data.dtype.names) 

782 arrays = [data[name] for name in columns] 

783 

784 if len(data) == 0: 

785 # GH#42456 the indexing above results in list of 2D ndarrays 

786 # TODO: is that an issue with numpy? 

787 for i, arr in enumerate(arrays): 

788 if arr.ndim == 2: 

789 arrays[i] = arr[:, 0] 

790 

791 return arrays, columns 

792 return [], ensure_index([]) 

793 

794 elif isinstance(data, np.ndarray) and data.dtype.names is not None: 

795 # e.g. recarray 

796 if columns is None: 

797 columns = Index(data.dtype.names) 

798 arrays = [data[k] for k in columns] 

799 return arrays, columns 

800 

801 if isinstance(data[0], (list, tuple)): 

802 arr = _list_to_arrays(data) 

803 elif isinstance(data[0], abc.Mapping): 

804 arr, columns = _list_of_dict_to_arrays(data, columns) 

805 elif isinstance(data[0], ABCSeries): 

806 arr, columns = _list_of_series_to_arrays(data, columns) 

807 else: 

808 # last ditch effort 

809 data = [tuple(x) for x in data] 

810 arr = _list_to_arrays(data) 

811 

812 content, columns = _finalize_columns_and_data(arr, columns, dtype) 

813 return content, columns 

814 

815 

816def _list_to_arrays(data: list[tuple | list]) -> np.ndarray: 

817 # Returned np.ndarray has ndim = 2 

818 # Note: we already check len(data) > 0 before getting hre 

819 if isinstance(data[0], tuple): 

820 content = lib.to_object_array_tuples(data) 

821 else: 

822 # list of lists 

823 content = lib.to_object_array(data) 

824 return content 

825 

826 

827def _list_of_series_to_arrays( 

828 data: list, 

829 columns: Index | None, 

830) -> tuple[np.ndarray, Index]: 

831 # returned np.ndarray has ndim == 2 

832 

833 if columns is None: 

834 # We know pass_data is non-empty because data[0] is a Series 

835 pass_data = [x for x in data if isinstance(x, (ABCSeries, ABCDataFrame))] 

836 columns = get_objs_combined_axis(pass_data, sort=False) 

837 

838 indexer_cache: dict[int, np.ndarray] = {} 

839 

840 aligned_values = [] 

841 for s in data: 

842 index = getattr(s, "index", None) 

843 if index is None: 

844 index = default_index(len(s)) 

845 

846 if id(index) in indexer_cache: 

847 indexer = indexer_cache[id(index)] 

848 else: 

849 indexer = indexer_cache[id(index)] = index.get_indexer(columns) 

850 

851 values = extract_array(s, extract_numpy=True) 

852 aligned_values.append(algorithms.take_nd(values, indexer)) 

853 

854 content = np.vstack(aligned_values) 

855 return content, columns 

856 

857 

858def _list_of_dict_to_arrays( 

859 data: list[dict], 

860 columns: Index | None, 

861) -> tuple[np.ndarray, Index]: 

862 """ 

863 Convert list of dicts to numpy arrays 

864 

865 if `columns` is not passed, column names are inferred from the records 

866 - for OrderedDict and dicts, the column names match 

867 the key insertion-order from the first record to the last. 

868 - For other kinds of dict-likes, the keys are lexically sorted. 

869 

870 Parameters 

871 ---------- 

872 data : iterable 

873 collection of records (OrderedDict, dict) 

874 columns: iterables or None 

875 

876 Returns 

877 ------- 

878 content : np.ndarray[object, ndim=2] 

879 columns : Index 

880 """ 

881 # assure that they are of the base dict class and not of derived 

882 # classes 

883 data = [d if type(d) is dict else dict(d) for d in data] 

884 

885 if columns is None: 

886 gen = (list(x.keys()) for x in data) 

887 sort = not any(isinstance(d, dict) for d in data) 

888 pre_cols = lib.fast_unique_multiple_list_gen(gen, sort=sort) 

889 columns = ensure_index(pre_cols) 

890 

891 # use pre_cols to preserve exact values that were present as dict keys 

892 # (e.g. otherwise missing values might be coerced to the canonical repr) 

893 content = lib.dicts_to_array(data, pre_cols) 

894 else: 

895 content = lib.dicts_to_array(data, list(columns)) 

896 

897 return content, columns 

898 

899 

900def _finalize_columns_and_data( 

901 content: np.ndarray, # ndim == 2 

902 columns: Index | None, 

903 dtype: DtypeObj | None, 

904) -> tuple[list[ArrayLike], Index]: 

905 """ 

906 Ensure we have valid columns, cast object dtypes if possible. 

907 """ 

908 contents = list(content.T) 

909 

910 try: 

911 columns = _validate_or_indexify_columns(contents, columns) 

912 except AssertionError as err: 

913 # GH#26429 do not raise user-facing AssertionError 

914 raise ValueError(err) from err 

915 

916 if contents and contents[0].dtype == np.object_: 

917 contents = convert_object_array(contents, dtype=dtype) 

918 

919 return contents, columns 

920 

921 

922def _validate_or_indexify_columns( 

923 content: list[np.ndarray], columns: Index | None 

924) -> Index: 

925 """ 

926 If columns is None, make numbers as column names; Otherwise, validate that 

927 columns have valid length. 

928 

929 Parameters 

930 ---------- 

931 content : list of np.ndarrays 

932 columns : Index or None 

933 

934 Returns 

935 ------- 

936 Index 

937 If columns is None, assign positional column index value as columns. 

938 

939 Raises 

940 ------ 

941 1. AssertionError when content is not composed of list of lists, and if 

942 length of columns is not equal to length of content. 

943 2. ValueError when content is list of lists, but length of each sub-list 

944 is not equal 

945 3. ValueError when content is list of lists, but length of sub-list is 

946 not equal to length of content 

947 """ 

948 if columns is None: 

949 columns = default_index(len(content)) 

950 else: 

951 # Add mask for data which is composed of list of lists 

952 is_mi_list = isinstance(columns, list) and all( 

953 isinstance(col, list) for col in columns 

954 ) 

955 

956 if not is_mi_list and len(columns) != len(content): # pragma: no cover 

957 # caller's responsibility to check for this... 

958 raise AssertionError( 

959 f"{len(columns)} columns passed, passed data had {len(content)} columns" 

960 ) 

961 if is_mi_list: 

962 # check if nested list column, length of each sub-list should be equal 

963 if len({len(col) for col in columns}) > 1: 

964 raise ValueError( 

965 "Length of columns passed for MultiIndex columns is different" 

966 ) 

967 

968 # if columns is not empty and length of sublist is not equal to content 

969 if columns and len(columns[0]) != len(content): 

970 raise ValueError( 

971 f"{len(columns[0])} columns passed, passed data had " 

972 f"{len(content)} columns" 

973 ) 

974 return columns 

975 

976 

977def convert_object_array( 

978 content: list[npt.NDArray[np.object_]], 

979 dtype: DtypeObj | None, 

980 dtype_backend: str = "numpy", 

981 coerce_float: bool = False, 

982) -> list[ArrayLike]: 

983 """ 

984 Internal function to convert object array. 

985 

986 Parameters 

987 ---------- 

988 content: List[np.ndarray] 

989 dtype: np.dtype or ExtensionDtype 

990 dtype_backend: Controls if nullable/pyarrow dtypes are returned. 

991 coerce_float: Cast floats that are integers to int. 

992 

993 Returns 

994 ------- 

995 List[ArrayLike] 

996 """ 

997 # provide soft conversion of object dtypes 

998 

999 def convert(arr): 

1000 if dtype != np.dtype("O"): 

1001 # e.g. if dtype is UInt32 then we want to cast Nones to NA instead of 

1002 # NaN in maybe_convert_objects. 

1003 to_nullable = dtype_backend != "numpy" or isinstance(dtype, BaseMaskedDtype) 

1004 arr = lib.maybe_convert_objects( 

1005 arr, 

1006 try_float=coerce_float, 

1007 convert_to_nullable_dtype=to_nullable, 

1008 ) 

1009 # Notes on cases that get here 2023-02-15 

1010 # 1) we DO get here when arr is all Timestamps and dtype=None 

1011 # 2) disabling this doesn't break the world, so this must be 

1012 # getting caught at a higher level 

1013 # 3) passing convert_non_numeric to maybe_convert_objects get this right 

1014 # 4) convert_non_numeric? 

1015 

1016 if dtype is None: 

1017 if arr.dtype == np.dtype("O"): 

1018 # i.e. maybe_convert_objects didn't convert 

1019 convert_to_nullable_dtype = dtype_backend != "numpy" 

1020 arr = lib.maybe_convert_objects( 

1021 arr, 

1022 # Here we do not convert numeric dtypes, as if we wanted that, 

1023 # numpy would have done it for us. 

1024 convert_numeric=False, 

1025 convert_non_numeric=True, 

1026 convert_to_nullable_dtype=convert_to_nullable_dtype, 

1027 dtype_if_all_nat=np.dtype("M8[s]"), 

1028 ) 

1029 if convert_to_nullable_dtype and arr.dtype == np.dtype("O"): 

1030 new_dtype = StringDtype() 

1031 arr_cls = new_dtype.construct_array_type() 

1032 arr = arr_cls._from_sequence(arr, dtype=new_dtype) 

1033 elif dtype_backend != "numpy" and isinstance(arr, np.ndarray): 

1034 if arr.dtype.kind in "iufb": 

1035 arr = pd_array(arr, copy=False) 

1036 

1037 elif isinstance(dtype, ExtensionDtype): 

1038 # TODO: test(s) that get here 

1039 # TODO: try to de-duplicate this convert function with 

1040 # core.construction functions 

1041 cls = dtype.construct_array_type() 

1042 arr = cls._from_sequence(arr, dtype=dtype, copy=False) 

1043 elif dtype.kind in "mM": 

1044 # This restriction is harmless bc these are the only cases 

1045 # where maybe_cast_to_datetime is not a no-op. 

1046 # Here we know: 

1047 # 1) dtype.kind in "mM" and 

1048 # 2) arr is either object or numeric dtype 

1049 arr = maybe_cast_to_datetime(arr, dtype) 

1050 

1051 return arr 

1052 

1053 arrays = [convert(arr) for arr in content] 

1054 

1055 return arrays