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

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

260 statements  

1""" 

2Constructor functions intended to be shared by pd.array, Series.__init__, 

3and Index.__new__. 

4 

5These should not depend on core.internals. 

6""" 

7 

8from __future__ import annotations 

9 

10from typing import ( 

11 TYPE_CHECKING, 

12 cast, 

13 overload, 

14) 

15 

16import numpy as np 

17from numpy import ma 

18 

19from pandas._config import using_string_dtype 

20 

21from pandas._libs import lib 

22from pandas._libs.tslibs import ( 

23 get_supported_dtype, 

24 is_supported_dtype, 

25) 

26from pandas.util._decorators import set_module 

27 

28from pandas.core.dtypes.base import ExtensionDtype 

29from pandas.core.dtypes.cast import ( 

30 construct_1d_arraylike_from_scalar, 

31 construct_1d_object_array_from_listlike, 

32 maybe_cast_to_datetime, 

33 maybe_cast_to_integer_array, 

34 maybe_convert_platform, 

35 maybe_promote, 

36) 

37from pandas.core.dtypes.common import ( 

38 ensure_object, 

39 is_list_like, 

40 is_object_dtype, 

41 pandas_dtype, 

42) 

43from pandas.core.dtypes.dtypes import NumpyEADtype 

44from pandas.core.dtypes.generic import ( 

45 ABCDataFrame, 

46 ABCExtensionArray, 

47 ABCIndex, 

48 ABCSeries, 

49) 

50from pandas.core.dtypes.missing import isna 

51 

52import pandas.core.common as com 

53 

54if TYPE_CHECKING: 

55 from collections.abc import Sequence 

56 

57 from pandas._typing import ( 

58 AnyArrayLike, 

59 ArrayLike, 

60 Dtype, 

61 DtypeObj, 

62 T, 

63 ) 

64 

65 from pandas import ( 

66 Index, 

67 Series, 

68 ) 

69 from pandas.core.arrays import ( 

70 DatetimeArray, 

71 ExtensionArray, 

72 TimedeltaArray, 

73 ) 

74 

75 

76@set_module("pandas") 

77def array( 

78 data: Sequence[object] | AnyArrayLike, 

79 dtype: Dtype | None = None, 

80 copy: bool = True, 

81) -> ExtensionArray: 

82 """ 

83 Create an array. 

84 

85 This method constructs an array using pandas extension types when possible. 

86 If `dtype` is specified, it determines the type of array returned. Otherwise, 

87 pandas attempts to infer the appropriate dtype based on `data`. 

88 

89 Parameters 

90 ---------- 

91 data : Sequence of objects 

92 The scalars inside `data` should be instances of the 

93 scalar type for `dtype`. It's expected that `data` 

94 represents a 1-dimensional array of data. 

95 

96 When `data` is an Index or Series, the underlying array 

97 will be extracted from `data`. 

98 

99 dtype : str, np.dtype, or ExtensionDtype, optional 

100 The dtype to use for the array. This may be a NumPy 

101 dtype or an extension type registered with pandas using 

102 :meth:`pandas.api.extensions.register_extension_dtype`. 

103 

104 If not specified, there are two possibilities: 

105 

106 1. When `data` is a :class:`Series`, :class:`Index`, or 

107 :class:`ExtensionArray`, the `dtype` will be taken 

108 from the data. 

109 2. Otherwise, pandas will attempt to infer the `dtype` 

110 from the data. 

111 

112 Note that when `data` is a NumPy array, ``data.dtype`` is 

113 *not* used for inferring the array type. This is because 

114 NumPy cannot represent all the types of data that can be 

115 held in extension arrays. 

116 

117 Currently, pandas will infer an extension dtype for sequences of 

118 

119 ============================== ======================================= 

120 Scalar Type Array Type 

121 ============================== ======================================= 

122 :class:`pandas.Interval` :class:`pandas.arrays.IntervalArray` 

123 :class:`pandas.Period` :class:`pandas.arrays.PeriodArray` 

124 :class:`datetime.datetime` :class:`pandas.arrays.DatetimeArray` 

125 :class:`datetime.timedelta` :class:`pandas.arrays.TimedeltaArray` 

126 :class:`int` :class:`pandas.arrays.IntegerArray` 

127 :class:`float` :class:`pandas.arrays.FloatingArray` 

128 :class:`str` :class:`pandas.arrays.StringArray` or 

129 :class:`pandas.arrays.ArrowStringArray` 

130 :class:`bool` :class:`pandas.arrays.BooleanArray` 

131 ============================== ======================================= 

132 

133 The ExtensionArray created when the scalar type is :class:`str` is determined by 

134 ``pd.options.mode.string_storage`` if the dtype is not explicitly given. 

135 

136 For all other cases, NumPy's usual inference rules will be used. 

137 copy : bool, default True 

138 Whether to copy the data, even if not necessary. Depending 

139 on the type of `data`, creating the new array may require 

140 copying data, even if ``copy=False``. 

141 

142 Returns 

143 ------- 

144 ExtensionArray 

145 The newly created array. 

146 

147 Raises 

148 ------ 

149 ValueError 

150 When `data` is not 1-dimensional. 

151 

152 See Also 

153 -------- 

154 numpy.array : Construct a NumPy array. 

155 Series : Construct a pandas Series. 

156 Index : Construct a pandas Index. 

157 arrays.NumpyExtensionArray : ExtensionArray wrapping a NumPy array. 

158 Series.array : Extract the array stored within a Series. 

159 

160 Notes 

161 ----- 

162 Omitting the `dtype` argument means pandas will attempt to infer the 

163 best array type from the values in the data. As new array types are 

164 added by pandas and 3rd party libraries, the "best" array type may 

165 change. We recommend specifying `dtype` to ensure that 

166 

167 1. the correct array type for the data is returned 

168 2. the returned array type doesn't change as new extension types 

169 are added by pandas and third-party libraries 

170 

171 Additionally, if the underlying memory representation of the returned 

172 array matters, we recommend specifying the `dtype` as a concrete object 

173 rather than a string alias or allowing it to be inferred. For example, 

174 a future version of pandas or a 3rd-party library may include a 

175 dedicated ExtensionArray for string data. In this event, the following 

176 would no longer return a :class:`arrays.NumpyExtensionArray` backed by a 

177 NumPy array. 

178 

179 >>> pd.array(["a", "b"], dtype=str) 

180 <ArrowStringArray> 

181 ['a', 'b'] 

182 Length: 2, dtype: str 

183 

184 This would instead return the new ExtensionArray dedicated for string 

185 data. If you really need the new array to be backed by a NumPy array, 

186 specify that in the dtype. 

187 

188 >>> pd.array(["a", "b"], dtype=np.dtype("<U1")) 

189 <NumpyExtensionArray> 

190 ['a', 'b'] 

191 Length: 2, dtype: str32 

192 

193 Finally, Pandas has arrays that mostly overlap with NumPy 

194 

195 * :class:`arrays.DatetimeArray` 

196 * :class:`arrays.TimedeltaArray` 

197 

198 When data with a ``datetime64[ns]`` or ``timedelta64[ns]`` dtype is 

199 passed, pandas will always return a ``DatetimeArray`` or ``TimedeltaArray`` 

200 rather than a ``NumpyExtensionArray``. This is for symmetry with the case of 

201 timezone-aware data, which NumPy does not natively support. 

202 

203 >>> pd.array(["2015", "2016"], dtype="datetime64[ns]") 

204 <DatetimeArray> 

205 ['2015-01-01 00:00:00', '2016-01-01 00:00:00'] 

206 Length: 2, dtype: datetime64[ns] 

207 

208 >>> pd.array(["1h", "2h"], dtype="timedelta64[ns]") 

209 <TimedeltaArray> 

210 ['0 days 01:00:00', '0 days 02:00:00'] 

211 Length: 2, dtype: timedelta64[ns] 

212 

213 Examples 

214 -------- 

215 If a dtype is not specified, pandas will infer the best dtype from the values. 

216 See the description of `dtype` for the types pandas infers for. 

217 

218 >>> pd.array([1, 2]) 

219 <IntegerArray> 

220 [1, 2] 

221 Length: 2, dtype: Int64 

222 

223 >>> pd.array([1, 2, np.nan]) 

224 <IntegerArray> 

225 [1, 2, <NA>] 

226 Length: 3, dtype: Int64 

227 

228 >>> pd.array([1.1, 2.2]) 

229 <FloatingArray> 

230 [1.1, 2.2] 

231 Length: 2, dtype: Float64 

232 

233 >>> pd.array(["a", None, "c"]) 

234 <ArrowStringArray> 

235 ['a', <NA>, 'c'] 

236 Length: 3, dtype: string 

237 

238 >>> with pd.option_context("string_storage", "python"): 

239 ... arr = pd.array(["a", None, "c"]) 

240 >>> arr 

241 <StringArray> 

242 ['a', <NA>, 'c'] 

243 Length: 3, dtype: string 

244 

245 >>> pd.array([pd.Period("2000", freq="D"), pd.Period("2000", freq="D")]) 

246 <PeriodArray> 

247 ['2000-01-01', '2000-01-01'] 

248 Length: 2, dtype: period[D] 

249 

250 You can use the string alias for `dtype` 

251 

252 >>> pd.array(["a", "b", "a"], dtype="category") 

253 ['a', 'b', 'a'] 

254 Categories (2, str): ['a', 'b'] 

255 

256 Or specify the actual dtype 

257 

258 >>> pd.array( 

259 ... ["a", "b", "a"], dtype=pd.CategoricalDtype(["a", "b", "c"], ordered=True) 

260 ... ) 

261 ['a', 'b', 'a'] 

262 Categories (3, str): ['a' < 'b' < 'c'] 

263 

264 If pandas does not infer a dedicated extension type a 

265 :class:`arrays.NumpyExtensionArray` is returned. 

266 

267 >>> pd.array([1 + 1j, 3 + 2j]) 

268 <NumpyExtensionArray> 

269 [(1+1j), (3+2j)] 

270 Length: 2, dtype: complex128 

271 

272 As mentioned in the "Notes" section, new extension types may be added 

273 in the future (by pandas or 3rd party libraries), causing the return 

274 value to no longer be a :class:`arrays.NumpyExtensionArray`. Specify the 

275 `dtype` as a NumPy dtype if you need to ensure there's no future change in 

276 behavior. 

277 

278 >>> pd.array([1, 2], dtype=np.dtype("int32")) 

279 <NumpyExtensionArray> 

280 [1, 2] 

281 Length: 2, dtype: int32 

282 

283 `data` must be 1-dimensional. A ValueError is raised when the input 

284 has the wrong dimensionality. 

285 

286 >>> pd.array(1) 

287 Traceback (most recent call last): 

288 ... 

289 ValueError: Cannot pass scalar '1' to 'pandas.array'. 

290 """ 

291 from pandas.core.arrays import ( 

292 BooleanArray, 

293 DatetimeArray, 

294 ExtensionArray, 

295 FloatingArray, 

296 IntegerArray, 

297 NumpyExtensionArray, 

298 TimedeltaArray, 

299 ) 

300 from pandas.core.arrays.string_ import StringDtype 

301 

302 if lib.is_scalar(data): 

303 msg = f"Cannot pass scalar '{data}' to 'pandas.array'." 

304 raise ValueError(msg) 

305 elif isinstance(data, ABCDataFrame): 

306 raise TypeError("Cannot pass DataFrame to 'pandas.array'") 

307 

308 if dtype is None and isinstance(data, (ABCSeries, ABCIndex, ExtensionArray)): 

309 # Note: we exclude np.ndarray here, will do type inference on it 

310 dtype = data.dtype 

311 

312 data = extract_array(data, extract_numpy=True) 

313 

314 # this returns None for not-found dtypes. 

315 if dtype is not None: 

316 dtype = pandas_dtype(dtype) 

317 

318 if isinstance(data, ExtensionArray) and (dtype is None or data.dtype == dtype): 

319 # e.g. TimedeltaArray[s], avoid casting to NumpyExtensionArray 

320 if copy: 

321 return data.copy() 

322 return data 

323 

324 if isinstance(dtype, ExtensionDtype): 

325 cls = dtype.construct_array_type() 

326 return cls._from_sequence(data, dtype=dtype, copy=copy) 

327 

328 if dtype is None: 

329 was_ndarray = isinstance(data, np.ndarray) 

330 # error: Item "Sequence[object]" of "Sequence[object] | ExtensionArray | 

331 # ndarray[Any, Any]" has no attribute "dtype" 

332 if not was_ndarray or data.dtype == object: # type: ignore[union-attr] 

333 result = lib.maybe_convert_objects( 

334 ensure_object(data), 

335 convert_non_numeric=True, 

336 convert_to_nullable_dtype=True, 

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

338 ) 

339 result = ensure_wrapped_if_datetimelike(result) 

340 if isinstance(result, np.ndarray): 

341 if len(result) == 0 and not was_ndarray: 

342 # e.g. empty list 

343 return FloatingArray._from_sequence(data, dtype="Float64") 

344 return NumpyExtensionArray._from_sequence( 

345 data, dtype=result.dtype, copy=copy 

346 ) 

347 if result is data and copy: 

348 return result.copy() 

349 return result 

350 

351 data = cast(np.ndarray, data) 

352 result = ensure_wrapped_if_datetimelike(data) 

353 if result is not data: 

354 result = cast("DatetimeArray | TimedeltaArray", result) 

355 if copy and result.dtype == data.dtype: 

356 return result.copy() 

357 return result 

358 

359 if data.dtype.kind in "SU": 

360 # StringArray/ArrowStringArray depending on pd.options.mode.string_storage 

361 dtype = StringDtype() 

362 cls = dtype.construct_array_type() 

363 return cls._from_sequence(data, dtype=dtype, copy=copy) 

364 

365 elif data.dtype.kind in "iu": 

366 dtype = IntegerArray._dtype_cls._get_dtype_mapping()[data.dtype] 

367 return IntegerArray._from_sequence(data, dtype=dtype, copy=copy) 

368 elif data.dtype.kind == "f": 

369 # GH#44715 Exclude np.float16 bc FloatingArray does not support it; 

370 # we will fall back to NumpyExtensionArray. 

371 if data.dtype == np.float16: 

372 return NumpyExtensionArray._from_sequence( 

373 data, dtype=data.dtype, copy=copy 

374 ) 

375 dtype = FloatingArray._dtype_cls._get_dtype_mapping()[data.dtype] 

376 return FloatingArray._from_sequence(data, dtype=dtype, copy=copy) 

377 

378 elif data.dtype.kind == "b": 

379 return BooleanArray._from_sequence(data, dtype="boolean", copy=copy) 

380 else: 

381 # e.g. complex 

382 return NumpyExtensionArray._from_sequence(data, dtype=data.dtype, copy=copy) 

383 

384 # Pandas overrides NumPy for 

385 # 1. datetime64[ns,us,ms,s] 

386 # 2. timedelta64[ns,us,ms,s] 

387 # so that a DatetimeArray is returned. 

388 if lib.is_np_dtype(dtype, "M") and is_supported_dtype(dtype): 

389 return DatetimeArray._from_sequence(data, dtype=dtype, copy=copy) 

390 if lib.is_np_dtype(dtype, "m") and is_supported_dtype(dtype): 

391 return TimedeltaArray._from_sequence(data, dtype=dtype, copy=copy) 

392 

393 elif lib.is_np_dtype(dtype, "mM"): 

394 raise ValueError( 

395 # GH#53817 

396 r"datetime64 and timedelta64 dtype resolutions other than " 

397 r"'s', 'ms', 'us', and 'ns' are no longer supported." 

398 ) 

399 

400 return NumpyExtensionArray._from_sequence(data, dtype=dtype, copy=copy) 

401 

402 

403_typs = frozenset( 

404 { 

405 "index", 

406 "rangeindex", 

407 "multiindex", 

408 "datetimeindex", 

409 "timedeltaindex", 

410 "periodindex", 

411 "categoricalindex", 

412 "intervalindex", 

413 "series", 

414 } 

415) 

416 

417 

418@overload 

419def extract_array( 

420 obj: Series | Index, extract_numpy: bool = ..., extract_range: bool = ... 

421) -> ArrayLike: ... 

422 

423 

424@overload 

425def extract_array( 

426 obj: T, extract_numpy: bool = ..., extract_range: bool = ... 

427) -> T | ArrayLike: ... 

428 

429 

430def extract_array( 

431 obj: T, extract_numpy: bool = False, extract_range: bool = False 

432) -> T | ArrayLike: 

433 """ 

434 Extract the ndarray or ExtensionArray from a Series or Index. 

435 

436 For all other types, `obj` is just returned as is. 

437 

438 Parameters 

439 ---------- 

440 obj : object 

441 For Series / Index, the underlying ExtensionArray is unboxed. 

442 

443 extract_numpy : bool, default False 

444 Whether to extract the ndarray from a NumpyExtensionArray. 

445 

446 extract_range : bool, default False 

447 If we have a RangeIndex, return range._values if True 

448 (which is a materialized integer ndarray), otherwise return unchanged. 

449 

450 Returns 

451 ------- 

452 arr : object 

453 

454 Examples 

455 -------- 

456 >>> extract_array(pd.Series(["a", "b", "c"], dtype="category")) 

457 ['a', 'b', 'c'] 

458 Categories (3, str): ['a', 'b', 'c'] 

459 

460 Other objects like lists, arrays, and DataFrames are just passed through. 

461 

462 >>> extract_array([1, 2, 3]) 

463 [1, 2, 3] 

464 

465 For an ndarray-backed Series / Index the ndarray is returned. 

466 

467 >>> extract_array(pd.Series([1, 2, 3])) 

468 array([1, 2, 3]) 

469 

470 To extract all the way down to the ndarray, pass ``extract_numpy=True``. 

471 

472 >>> extract_array(pd.Series([1, 2, 3]), extract_numpy=True) 

473 array([1, 2, 3]) 

474 """ 

475 typ = getattr(obj, "_typ", None) 

476 if typ in _typs: 

477 # i.e. isinstance(obj, (ABCIndex, ABCSeries)) 

478 if typ == "rangeindex": 

479 if extract_range: 

480 # error: "T" has no attribute "_values" 

481 return obj._values # type: ignore[attr-defined] 

482 return obj 

483 

484 # error: "T" has no attribute "_values" 

485 return obj._values # type: ignore[attr-defined] 

486 

487 elif extract_numpy and typ == "npy_extension": 

488 # i.e. isinstance(obj, ABCNumpyExtensionArray) 

489 # error: "T" has no attribute "to_numpy" 

490 return obj.to_numpy() # type: ignore[attr-defined] 

491 

492 return obj 

493 

494 

495def ensure_wrapped_if_datetimelike(arr): 

496 """ 

497 Wrap datetime64 and timedelta64 ndarrays in DatetimeArray/TimedeltaArray. 

498 """ 

499 if isinstance(arr, np.ndarray): 

500 if arr.dtype.kind == "M": 

501 from pandas.core.arrays import DatetimeArray 

502 

503 dtype = get_supported_dtype(arr.dtype) 

504 return DatetimeArray._from_sequence(arr, dtype=dtype) 

505 

506 elif arr.dtype.kind == "m": 

507 from pandas.core.arrays import TimedeltaArray 

508 

509 dtype = get_supported_dtype(arr.dtype) 

510 return TimedeltaArray._from_sequence(arr, dtype=dtype) 

511 

512 return arr 

513 

514 

515def sanitize_masked_array(data: ma.MaskedArray) -> np.ndarray: 

516 """ 

517 Convert numpy MaskedArray to ensure mask is softened. 

518 """ 

519 mask = ma.getmaskarray(data) 

520 if mask.any(): 

521 dtype, fill_value = maybe_promote(data.dtype, np.nan) 

522 dtype = cast(np.dtype, dtype) 

523 data = ma.asarray(data.astype(dtype, copy=True)) 

524 data.soften_mask() # set hardmask False if it was True 

525 data[mask] = fill_value 

526 else: 

527 data = data.copy() 

528 return data 

529 

530 

531def sanitize_array( 

532 data, 

533 index: Index | None, 

534 dtype: DtypeObj | None = None, 

535 copy: bool = False, 

536 *, 

537 allow_2d: bool = False, 

538) -> ArrayLike: 

539 """ 

540 Sanitize input data to an ndarray or ExtensionArray, copy if specified, 

541 coerce to the dtype if specified. 

542 

543 Parameters 

544 ---------- 

545 data : Any 

546 index : Index or None, default None 

547 dtype : np.dtype, ExtensionDtype, or None, default None 

548 copy : bool, default False 

549 allow_2d : bool, default False 

550 If False, raise if we have a 2D Arraylike. 

551 

552 Returns 

553 ------- 

554 np.ndarray or ExtensionArray 

555 """ 

556 original_dtype = dtype 

557 if isinstance(data, ma.MaskedArray): 

558 data = sanitize_masked_array(data) 

559 

560 if isinstance(dtype, NumpyEADtype): 

561 # Avoid ending up with a NumpyExtensionArray 

562 dtype = dtype.numpy_dtype 

563 

564 infer_object = not isinstance(data, (ABCIndex, ABCSeries)) 

565 

566 # extract ndarray or ExtensionArray, ensure we have no NumpyExtensionArray 

567 data = extract_array(data, extract_numpy=True, extract_range=True) 

568 

569 if isinstance(data, np.ndarray) and data.ndim == 0: 

570 if dtype is None: 

571 dtype = data.dtype 

572 data = lib.item_from_zerodim(data) 

573 elif isinstance(data, range): 

574 # GH#16804 

575 data = range_to_ndarray(data) 

576 copy = False 

577 

578 if not is_list_like(data): 

579 if index is None: 

580 raise ValueError("index must be specified when data is not list-like") 

581 if isinstance(data, str) and using_string_dtype() and original_dtype is None: 

582 from pandas.core.arrays.string_ import StringDtype 

583 

584 dtype = StringDtype(na_value=np.nan) 

585 data = construct_1d_arraylike_from_scalar(data, len(index), dtype) 

586 

587 return data 

588 

589 elif isinstance(data, ABCExtensionArray): 

590 # it is already ensured above this is not a NumpyExtensionArray 

591 # Until GH#49309 is fixed this check needs to come before the 

592 # ExtensionDtype check 

593 if dtype is not None: 

594 subarr = data.astype(dtype, copy=copy) 

595 elif copy: 

596 subarr = data.copy() 

597 else: 

598 subarr = data 

599 

600 elif isinstance(dtype, ExtensionDtype): 

601 # create an extension array from its dtype 

602 _sanitize_non_ordered(data) 

603 cls = dtype.construct_array_type() 

604 if not hasattr(data, "__array__"): 

605 data = list(data) 

606 subarr = cls._from_sequence(data, dtype=dtype, copy=copy) 

607 

608 # GH#846 

609 elif isinstance(data, np.ndarray): 

610 if isinstance(data, np.matrix): 

611 data = data.A 

612 

613 if dtype is None: 

614 subarr = data 

615 if data.dtype == object and infer_object: 

616 subarr = lib.maybe_convert_objects( 

617 data, 

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

619 # numpy would have done it for us. 

620 convert_numeric=False, 

621 convert_non_numeric=True, 

622 convert_to_nullable_dtype=False, 

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

624 ) 

625 elif data.dtype.kind == "U" and using_string_dtype(): 

626 from pandas.core.arrays.string_ import StringDtype 

627 

628 dtype = StringDtype(na_value=np.nan) 

629 subarr = dtype.construct_array_type()._from_sequence(data, dtype=dtype) 

630 

631 if ( 

632 subarr is data 

633 or (subarr.dtype == "str" and subarr.dtype.storage == "python") # type: ignore[union-attr] 

634 ) and copy: 

635 subarr = subarr.copy() 

636 

637 else: 

638 # we will try to copy by-definition here 

639 subarr = _try_cast(data, dtype, copy) 

640 

641 elif hasattr(data, "__array__"): 

642 # e.g. dask array GH#38645 

643 if not copy: 

644 data = np.asarray(data) 

645 else: 

646 data = np.array(data, copy=copy) 

647 return sanitize_array( 

648 data, 

649 index=index, 

650 dtype=dtype, 

651 copy=False, 

652 allow_2d=allow_2d, 

653 ) 

654 

655 else: 

656 _sanitize_non_ordered(data) 

657 # materialize e.g. generators, convert e.g. tuples, abc.ValueView 

658 data = list(data) 

659 

660 if len(data) == 0 and dtype is None: 

661 # We default to float64, matching numpy 

662 subarr = np.array([], dtype=np.float64) 

663 

664 elif dtype is not None: 

665 subarr = _try_cast(data, dtype, copy) 

666 

667 else: 

668 subarr = maybe_convert_platform(data) 

669 if subarr.dtype == object: 

670 subarr = cast(np.ndarray, subarr) 

671 subarr = lib.maybe_convert_objects( 

672 subarr, 

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

674 # numpy would have done it for us. 

675 convert_numeric=False, 

676 convert_non_numeric=True, 

677 convert_to_nullable_dtype=False, 

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

679 ) 

680 

681 subarr = _sanitize_ndim(subarr, data, dtype, index, allow_2d=allow_2d) 

682 

683 if isinstance(subarr, np.ndarray): 

684 # at this point we should have dtype be None or subarr.dtype == dtype 

685 dtype = cast(np.dtype, dtype) 

686 subarr = _sanitize_str_dtypes(subarr, data, dtype, copy) 

687 

688 return subarr 

689 

690 

691def range_to_ndarray(rng: range) -> np.ndarray: 

692 """ 

693 Cast a range object to ndarray. 

694 """ 

695 # GH#30171 perf avoid realizing range as a list in np.array 

696 try: 

697 arr = np.arange(rng.start, rng.stop, rng.step, dtype="int64") 

698 except OverflowError: 

699 # GH#30173 handling for ranges that overflow int64 

700 if (rng.start >= 0 and rng.step > 0) or (rng.step < 0 <= rng.stop): 

701 try: 

702 arr = np.arange(rng.start, rng.stop, rng.step, dtype="uint64") 

703 except OverflowError: 

704 arr = construct_1d_object_array_from_listlike(list(rng)) 

705 else: 

706 arr = construct_1d_object_array_from_listlike(list(rng)) 

707 return arr 

708 

709 

710def _sanitize_non_ordered(data) -> None: 

711 """ 

712 Raise only for unordered sets, e.g., not for dict_keys 

713 """ 

714 if isinstance(data, (set, frozenset)): 

715 raise TypeError(f"'{type(data).__name__}' type is unordered") 

716 

717 

718def _sanitize_ndim( 

719 result: ArrayLike, 

720 data, 

721 dtype: DtypeObj | None, 

722 index: Index | None, 

723 *, 

724 allow_2d: bool = False, 

725) -> ArrayLike: 

726 """ 

727 Ensure we have a 1-dimensional result array. 

728 """ 

729 if getattr(result, "ndim", 0) == 0: 

730 raise ValueError("result should be arraylike with ndim > 0") 

731 

732 if result.ndim == 1: 

733 # the result that we want 

734 result = _maybe_repeat(result, index) 

735 

736 elif result.ndim > 1: 

737 if isinstance(data, np.ndarray): 

738 if allow_2d: 

739 return result 

740 raise ValueError( 

741 f"Data must be 1-dimensional, got ndarray of shape {data.shape} instead" 

742 ) 

743 if is_object_dtype(dtype) and isinstance(dtype, ExtensionDtype): 

744 # i.e. NumpyEADtype("O") 

745 

746 result = com.asarray_tuplesafe(data, dtype=np.dtype("object")) 

747 cls = dtype.construct_array_type() 

748 result = cls._from_sequence(result, dtype=dtype) 

749 else: 

750 # error: Argument "dtype" to "asarray_tuplesafe" has incompatible type 

751 # "Union[dtype[Any], ExtensionDtype, None]"; expected "Union[str, 

752 # dtype[Any], None]" 

753 result = com.asarray_tuplesafe(data, dtype=dtype) # type: ignore[arg-type] 

754 return result 

755 

756 

757def _sanitize_str_dtypes( 

758 result: np.ndarray, data, dtype: np.dtype | None, copy: bool 

759) -> np.ndarray: 

760 """ 

761 Ensure we have a dtype that is supported by pandas. 

762 """ 

763 

764 # This is to prevent mixed-type Series getting all casted to 

765 # NumPy string type, e.g. NaN --> '-1#IND'. 

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

767 # GH#16605 

768 # If not empty convert the data to dtype 

769 # GH#19853: If data is a scalar, result has already the result 

770 if not lib.is_scalar(data): 

771 if not np.all(isna(data)): 

772 data = np.asarray(data, dtype=dtype) 

773 if not copy: 

774 result = np.asarray(data, dtype=object) 

775 else: 

776 result = np.array(data, dtype=object, copy=copy) 

777 return result 

778 

779 

780def _maybe_repeat(arr: ArrayLike, index: Index | None) -> ArrayLike: 

781 """ 

782 If we have a length-1 array and an index describing how long we expect 

783 the result to be, repeat the array. 

784 """ 

785 if index is not None: 

786 if 1 == len(arr) != len(index): 

787 arr = arr.repeat(len(index)) 

788 return arr 

789 

790 

791def _try_cast( 

792 arr: list | np.ndarray, 

793 dtype: np.dtype, 

794 copy: bool, 

795) -> ArrayLike: 

796 """ 

797 Convert input to numpy ndarray and optionally cast to a given dtype. 

798 

799 Parameters 

800 ---------- 

801 arr : ndarray or list 

802 Excludes: ExtensionArray, Series, Index. 

803 dtype : np.dtype 

804 copy : bool 

805 If False, don't copy the data if not needed. 

806 

807 Returns 

808 ------- 

809 np.ndarray or ExtensionArray 

810 """ 

811 is_ndarray = isinstance(arr, np.ndarray) 

812 

813 if dtype == object: 

814 if not is_ndarray: 

815 subarr = construct_1d_object_array_from_listlike(arr) 

816 return subarr 

817 return ensure_wrapped_if_datetimelike(arr).astype(dtype, copy=copy) 

818 

819 elif dtype.kind == "U": 

820 # TODO: test cases with arr.dtype.kind in "mM" 

821 if is_ndarray: 

822 arr = cast(np.ndarray, arr) 

823 shape = arr.shape 

824 if arr.ndim > 1: 

825 arr = arr.ravel() 

826 else: 

827 shape = (len(arr),) 

828 return lib.ensure_string_array(arr, convert_na_value=False, copy=copy).reshape( 

829 shape 

830 ) 

831 

832 elif dtype.kind in "mM": 

833 if is_ndarray: 

834 arr = cast(np.ndarray, arr) 

835 if arr.ndim == 2 and arr.shape[1] == 1: 

836 # GH#60081: DataFrame Constructor converts 1D data to array of 

837 # shape (N, 1), but maybe_cast_to_datetime assumes 1D input 

838 return maybe_cast_to_datetime(arr[:, 0], dtype).reshape(arr.shape) 

839 return maybe_cast_to_datetime(arr, dtype) 

840 

841 # GH#15832: Check if we are requesting a numeric dtype and 

842 # that we can convert the data to the requested dtype. 

843 elif dtype.kind in "iu": 

844 # this will raise if we have e.g. floats 

845 

846 subarr = maybe_cast_to_integer_array(arr, dtype) 

847 elif not copy: 

848 subarr = np.asarray(arr, dtype=dtype) 

849 else: 

850 subarr = np.array(arr, dtype=dtype, copy=copy) 

851 

852 return subarr