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 Any,
13 cast,
14 overload,
15)
16
17import numpy as np
18from numpy import ma
19
20from pandas._config import using_string_dtype
21
22from pandas._libs import lib
23from pandas._libs.tslibs import (
24 get_supported_dtype,
25 is_supported_dtype,
26)
27from pandas.util._decorators import set_module
28
29from pandas.core.dtypes.base import ExtensionDtype
30from pandas.core.dtypes.cast import (
31 construct_1d_arraylike_from_scalar,
32 construct_1d_object_array_from_listlike,
33 maybe_cast_to_datetime,
34 maybe_cast_to_integer_array,
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 (
44 BaseMaskedDtype,
45 NumpyEADtype,
46)
47from pandas.core.dtypes.generic import (
48 ABCDataFrame,
49 ABCExtensionArray,
50 ABCIndex,
51 ABCSeries,
52)
53from pandas.core.dtypes.missing import (
54 isna,
55 na_value_for_dtype,
56)
57
58import pandas.core.common as com
59
60if TYPE_CHECKING:
61 from collections.abc import Sequence
62
63 from pandas._typing import (
64 AnyArrayLike,
65 ArrayLike,
66 Dtype,
67 DtypeObj,
68 T,
69 )
70
71 from pandas import (
72 Index,
73 Series,
74 )
75 from pandas.core.arrays import (
76 DatetimeArray,
77 ExtensionArray,
78 TimedeltaArray,
79 )
80
81
82@set_module("pandas")
83def array(
84 data: Sequence[object] | AnyArrayLike,
85 dtype: Dtype | None = None,
86 copy: bool = True,
87) -> ExtensionArray:
88 """
89 Create an array.
90
91 This method constructs an array using pandas extension types when possible.
92 If `dtype` is specified, it determines the type of array returned. Otherwise,
93 pandas attempts to infer the appropriate dtype based on `data`.
94
95 Parameters
96 ----------
97 data : Sequence of objects
98 The scalars inside `data` should be instances of the
99 scalar type for `dtype`. It's expected that `data`
100 represents a 1-dimensional array of data.
101
102 When `data` is an Index or Series, the underlying array
103 will be extracted from `data`.
104
105 dtype : str, np.dtype, or ExtensionDtype, optional
106 The dtype to use for the array. This may be a NumPy
107 dtype or an extension type registered with pandas using
108 :meth:`pandas.api.extensions.register_extension_dtype`.
109
110 If not specified, there are two possibilities:
111
112 1. When `data` is a :class:`Series`, :class:`Index`, or
113 :class:`ExtensionArray`, the `dtype` will be taken
114 from the data.
115 2. Otherwise, pandas will attempt to infer the `dtype`
116 from the data.
117
118 Note that when `data` is a NumPy array, ``data.dtype`` is
119 *not* used for inferring the array type. This is because
120 NumPy cannot represent all the types of data that can be
121 held in extension arrays.
122
123 Currently, pandas will infer an extension dtype for sequences of
124
125 ============================== =======================================
126 Scalar Type Array Type
127 ============================== =======================================
128 :class:`pandas.Interval` :class:`pandas.arrays.IntervalArray`
129 :class:`pandas.Period` :class:`pandas.arrays.PeriodArray`
130 :class:`datetime.datetime` :class:`pandas.arrays.DatetimeArray`
131 :class:`datetime.timedelta` :class:`pandas.arrays.TimedeltaArray`
132 :class:`int` :class:`pandas.arrays.IntegerArray`
133 :class:`float` :class:`pandas.arrays.FloatingArray`
134 :class:`str` :class:`pandas.arrays.StringArray` or
135 :class:`pandas.arrays.ArrowStringArray`
136 :class:`bool` :class:`pandas.arrays.BooleanArray`
137 ============================== =======================================
138
139 The ExtensionArray created when the scalar type is :class:`str` is determined by
140 ``pd.options.mode.string_storage`` if the dtype is not explicitly given.
141
142 For all other cases, NumPy's usual inference rules will be used.
143 copy : bool, default True
144 Whether to copy the data, even if not necessary. Depending
145 on the type of `data`, creating the new array may require
146 copying data, even if ``copy=False``.
147
148 Returns
149 -------
150 ExtensionArray
151 The newly created array.
152
153 Raises
154 ------
155 ValueError
156 When `data` is not 1-dimensional.
157
158 See Also
159 --------
160 numpy.array : Construct a NumPy array.
161 Series : Construct a pandas Series.
162 Index : Construct a pandas Index.
163 arrays.NumpyExtensionArray : ExtensionArray wrapping a NumPy array.
164 Series.array : Extract the array stored within a Series.
165
166 Notes
167 -----
168 Omitting the `dtype` argument means pandas will attempt to infer the
169 best array type from the values in the data. As new array types are
170 added by pandas and 3rd party libraries, the "best" array type may
171 change. We recommend specifying `dtype` to ensure that
172
173 1. the correct array type for the data is returned
174 2. the returned array type doesn't change as new extension types
175 are added by pandas and third-party libraries
176
177 Additionally, if the underlying memory representation of the returned
178 array matters, we recommend specifying the `dtype` as a concrete object
179 rather than a string alias or allowing it to be inferred. For example,
180 a future version of pandas or a 3rd-party library may include a
181 dedicated ExtensionArray for string data. In this event, the following
182 would no longer return a :class:`arrays.NumpyExtensionArray` backed by a
183 NumPy array.
184
185 >>> pd.array(["a", "b"], dtype=str)
186 <ArrowStringArray>
187 ['a', 'b']
188 Length: 2, dtype: str
189
190 This would instead return the new ExtensionArray dedicated for string
191 data. If you really need the new array to be backed by a NumPy array,
192 specify that in the dtype.
193
194 >>> pd.array(["a", "b"], dtype=np.dtype("<U1"))
195 <NumpyExtensionArray>
196 ['a', 'b']
197 Length: 2, dtype: str32
198
199 pandas converts entries of a multidimensional sequence (excluding NumPy arrays)
200 to its string representation if the ``dtype`` is a string data type.
201
202 >>> pd.array([[1], [2], [3]], dtype="str")
203 <ArrowStringArray>
204 ['[1]', '[2]', '[3]']
205 Length: 3, dtype: str
206
207 Finally, Pandas has arrays that mostly overlap with NumPy
208
209 * :class:`arrays.DatetimeArray`
210 * :class:`arrays.TimedeltaArray`
211
212 When data with a ``datetime64[ns]`` or ``timedelta64[ns]`` dtype is
213 passed, pandas will always return a ``DatetimeArray`` or ``TimedeltaArray``
214 rather than a ``NumpyExtensionArray``. This is for symmetry with the case of
215 timezone-aware data, which NumPy does not natively support.
216
217 >>> pd.array(["2015", "2016"], dtype="datetime64[ns]")
218 <DatetimeArray>
219 ['2015-01-01 00:00:00', '2016-01-01 00:00:00']
220 Length: 2, dtype: datetime64[ns]
221
222 >>> pd.array(["1h", "2h"], dtype="timedelta64[ns]")
223 <TimedeltaArray>
224 ['0 days 01:00:00', '0 days 02:00:00']
225 Length: 2, dtype: timedelta64[ns]
226
227 Examples
228 --------
229 If a dtype is not specified, pandas will infer the best dtype from the values.
230 See the description of `dtype` for the types pandas infers for.
231
232 >>> pd.array([1, 2])
233 <IntegerArray>
234 [1, 2]
235 Length: 2, dtype: Int64
236
237 >>> pd.array([1, 2, np.nan])
238 <IntegerArray>
239 [1, 2, <NA>]
240 Length: 3, dtype: Int64
241
242 >>> pd.array([1.1, 2.2])
243 <FloatingArray>
244 [1.1, 2.2]
245 Length: 2, dtype: Float64
246
247 >>> pd.array(["a", None, "c"])
248 <ArrowStringArray>
249 ['a', <NA>, 'c']
250 Length: 3, dtype: string
251
252 >>> with pd.option_context("string_storage", "python"):
253 ... arr = pd.array(["a", None, "c"])
254 >>> arr
255 <StringArray>
256 ['a', <NA>, 'c']
257 Length: 3, dtype: string
258
259 >>> pd.array([pd.Period("2000", freq="D"), pd.Period("2000", freq="D")])
260 <PeriodArray>
261 ['2000-01-01', '2000-01-01']
262 Length: 2, dtype: period[D]
263
264 You can use the string alias for `dtype`
265
266 >>> pd.array(["a", "b", "a"], dtype="category")
267 ['a', 'b', 'a']
268 Categories (2, str): ['a', 'b']
269
270 Or specify the actual dtype
271
272 >>> pd.array(
273 ... ["a", "b", "a"], dtype=pd.CategoricalDtype(["a", "b", "c"], ordered=True)
274 ... )
275 ['a', 'b', 'a']
276 Categories (3, str): ['a' < 'b' < 'c']
277
278 If pandas does not infer a dedicated extension type a
279 :class:`arrays.NumpyExtensionArray` is returned.
280
281 >>> pd.array([1 + 1j, 3 + 2j])
282 <NumpyExtensionArray>
283 [(1+1j), (3+2j)]
284 Length: 2, dtype: complex128
285
286 As mentioned in the "Notes" section, new extension types may be added
287 in the future (by pandas or 3rd party libraries), causing the return
288 value to no longer be a :class:`arrays.NumpyExtensionArray`. Specify the
289 `dtype` as a NumPy dtype if you need to ensure there's no future change in
290 behavior.
291
292 >>> pd.array([1, 2], dtype=np.dtype("int32"))
293 <NumpyExtensionArray>
294 [1, 2]
295 Length: 2, dtype: int32
296
297 `data` must be 1-dimensional. A ValueError is raised when the input
298 has the wrong dimensionality.
299
300 >>> pd.array(1)
301 Traceback (most recent call last):
302 ...
303 ValueError: Cannot pass scalar '1' to 'pandas.array'.
304 """
305 from pandas.core.arrays import (
306 BooleanArray,
307 DatetimeArray,
308 ExtensionArray,
309 FloatingArray,
310 IntegerArray,
311 NumpyExtensionArray,
312 TimedeltaArray,
313 )
314 from pandas.core.arrays.string_ import StringDtype
315
316 if lib.is_scalar(data):
317 msg = f"Cannot pass scalar '{data}' to 'pandas.array'."
318 raise ValueError(msg)
319 elif isinstance(data, ABCDataFrame):
320 raise TypeError("Cannot pass DataFrame to 'pandas.array'")
321
322 if dtype is None and isinstance(data, (ABCSeries, ABCIndex, ExtensionArray)):
323 # Note: we exclude np.ndarray here, will do type inference on it
324 dtype = data.dtype
325
326 data = extract_array(data, extract_numpy=True)
327
328 # Handle numpy masked arrays: convert masked values to NA
329 # GH#63879
330 if isinstance(data, ma.MaskedArray):
331 if data.dtype.names is not None:
332 raise ValueError(
333 "Cannot construct an array from an ndarray with compound dtype. "
334 "Use DataFrame instead."
335 )
336 mask = ma.getmask(data)
337 if mask is ma.nomask:
338 # Avoid materializing an all-False mask.
339 data = np.asarray(data)
340 else:
341 try:
342 masked_cls = BaseMaskedDtype.from_numpy_dtype(
343 data.dtype
344 ).construct_array_type()
345 except (KeyError, NotImplementedError):
346 # No nullable masked dtype for this kind.
347 if data.dtype.char in "SU":
348 data = lib.ensure_string_array(np.asarray(data.data))
349 data[mask] = np.nan
350 else:
351 # datetime64/timedelta64 use NaT; float16/complex/object NaN.
352 # Argument to "filled" of "MaskedArray" has incompatible type...;
353 # expected "complex | str | bytes | generic[Any] | None"
354 data = data.filled(
355 na_value_for_dtype(data.dtype, compat=False) # type: ignore[arg-type]
356 )
357 else:
358 # Argument 2 to "BaseMaskedArray" has incompatible type...;
359 # expected "ndarray[tuple[Any, ...], dtype[numpy.bool[builtins.bool]]]"
360 data = masked_cls(np.asarray(data.data), mask, copy=copy) # type: ignore[arg-type]
361
362 # this returns None for not-found dtypes.
363 if dtype is not None:
364 dtype = pandas_dtype(dtype)
365
366 if isinstance(data, ExtensionArray) and (dtype is None or data.dtype == dtype):
367 # e.g. TimedeltaArray[s], avoid casting to NumpyExtensionArray
368 if copy:
369 return data.copy()
370 return data
371
372 if isinstance(dtype, ExtensionDtype):
373 cls = dtype.construct_array_type()
374 return cls._from_sequence(data, dtype=dtype, copy=copy)
375
376 if dtype is None:
377 was_ndarray = isinstance(data, np.ndarray)
378 # error: Item "Sequence[object]" of "Sequence[object] | ExtensionArray |
379 # ndarray[Any, Any]" has no attribute "dtype"
380 if not was_ndarray or data.dtype == object: # type: ignore[union-attr]
381 result = lib.maybe_convert_objects(
382 ensure_object(data),
383 convert_non_numeric=True,
384 convert_to_nullable_dtype=True,
385 dtype_if_all_nat=np.dtype("M8[s]"),
386 )
387 result = ensure_wrapped_if_datetimelike(result)
388 if isinstance(result, np.ndarray):
389 if len(result) == 0 and not was_ndarray:
390 # e.g. empty list
391 return FloatingArray._from_sequence(data, dtype="Float64")
392 return NumpyExtensionArray._from_sequence(
393 data, dtype=result.dtype, copy=copy
394 )
395 # We know result is an ExtensionArray here, but mypy doesn't
396 assert isinstance(result, ExtensionArray)
397 if result is data and copy:
398 return result.copy()
399 return result
400
401 data = cast("np.ndarray", data)
402 result = ensure_wrapped_if_datetimelike(data)
403 if result is not data:
404 result = cast("DatetimeArray | TimedeltaArray", result)
405 if copy and result.dtype == data.dtype:
406 return result.copy()
407 return result
408
409 if data.dtype.kind in "SU":
410 # StringArray/ArrowStringArray depending on pd.options.mode.string_storage
411 dtype = StringDtype()
412 cls = dtype.construct_array_type()
413 return cls._from_sequence(data, dtype=dtype, copy=copy)
414
415 elif data.dtype.kind in "iu":
416 dtype = IntegerArray._dtype_cls._get_dtype_mapping()[data.dtype]
417 return IntegerArray._from_sequence(data, dtype=dtype, copy=copy)
418 elif data.dtype.kind == "f":
419 # GH#44715 Exclude np.float16 bc FloatingArray does not support it;
420 # we will fall back to NumpyExtensionArray.
421 if data.dtype == np.float16:
422 return NumpyExtensionArray._from_sequence(
423 data, dtype=data.dtype, copy=copy
424 )
425 dtype = FloatingArray._dtype_cls._get_dtype_mapping()[data.dtype]
426 return FloatingArray._from_sequence(data, dtype=dtype, copy=copy)
427
428 elif data.dtype.kind == "b":
429 return BooleanArray._from_sequence(data, dtype="boolean", copy=copy)
430 else:
431 # e.g. complex
432 return NumpyExtensionArray._from_sequence(data, dtype=data.dtype, copy=copy)
433
434 # Pandas overrides NumPy for
435 # 1. datetime64[ns,us,ms,s]
436 # 2. timedelta64[ns,us,ms,s]
437 # so that a DatetimeArray is returned.
438 if lib.is_np_dtype(dtype, "M") and is_supported_dtype(dtype):
439 return DatetimeArray._from_sequence(data, dtype=dtype, copy=copy)
440 if lib.is_np_dtype(dtype, "m") and is_supported_dtype(dtype):
441 return TimedeltaArray._from_sequence(data, dtype=dtype, copy=copy)
442
443 elif lib.is_np_dtype(dtype, "mM"):
444 raise ValueError(
445 # GH#53817
446 r"datetime64 and timedelta64 dtype resolutions other than "
447 r"'s', 'ms', 'us', and 'ns' are no longer supported."
448 )
449
450 return NumpyExtensionArray._from_sequence(data, dtype=dtype, copy=copy)
451
452
453_typs = frozenset(
454 {
455 "index",
456 "rangeindex",
457 "multiindex",
458 "datetimeindex",
459 "timedeltaindex",
460 "periodindex",
461 "categoricalindex",
462 "intervalindex",
463 "series",
464 }
465)
466
467
468@overload
469def extract_array(
470 obj: Series | Index, extract_numpy: bool = ..., extract_range: bool = ...
471) -> ArrayLike: ...
472
473
474@overload
475def extract_array(
476 obj: T, extract_numpy: bool = ..., extract_range: bool = ...
477) -> T | ArrayLike: ...
478
479
480def extract_array(
481 obj: T, extract_numpy: bool = False, extract_range: bool = False
482) -> T | ArrayLike:
483 """
484 Extract the ndarray or ExtensionArray from a Series or Index.
485
486 For all other types, `obj` is just returned as is.
487
488 Parameters
489 ----------
490 obj : object
491 For Series / Index, the underlying ExtensionArray is unboxed.
492
493 extract_numpy : bool, default False
494 Whether to extract the ndarray from a NumpyExtensionArray.
495
496 extract_range : bool, default False
497 If we have a RangeIndex, return range._values if True
498 (which is a materialized integer ndarray), otherwise return unchanged.
499
500 Returns
501 -------
502 arr : object
503
504 Examples
505 --------
506 >>> extract_array(pd.Series(["a", "b", "c"], dtype="category"))
507 ['a', 'b', 'c']
508 Categories (3, str): ['a', 'b', 'c']
509
510 Other objects like lists, arrays, and DataFrames are just passed through.
511
512 >>> extract_array([1, 2, 3])
513 [1, 2, 3]
514
515 For an ndarray-backed Series / Index the ndarray is returned.
516
517 >>> extract_array(pd.Series([1, 2, 3]))
518 array([1, 2, 3])
519
520 To extract all the way down to the ndarray, pass ``extract_numpy=True``.
521
522 >>> extract_array(pd.Series([1, 2, 3]), extract_numpy=True)
523 array([1, 2, 3])
524 """
525 typ = getattr(obj, "_typ", None)
526 if typ in _typs:
527 # i.e. isinstance(obj, (ABCIndex, ABCSeries))
528 if typ == "rangeindex":
529 if extract_range:
530 # error: "T" has no attribute "_values"
531 return obj._values # type: ignore[attr-defined]
532 return obj
533
534 # error: "T" has no attribute "_values"
535 return obj._values # type: ignore[attr-defined]
536
537 elif extract_numpy and typ == "npy_extension":
538 # i.e. isinstance(obj, ABCNumpyExtensionArray)
539 # error: "T" has no attribute "to_numpy"
540 return obj.to_numpy() # type: ignore[attr-defined]
541
542 return obj
543
544
545def ensure_wrapped_if_datetimelike(arr):
546 """
547 Wrap datetime64 and timedelta64 ndarrays in DatetimeArray/TimedeltaArray.
548 """
549 if isinstance(arr, np.ndarray):
550 if arr.dtype.kind == "M":
551 from pandas.core.arrays import DatetimeArray
552
553 dtype = get_supported_dtype(arr.dtype)
554 return DatetimeArray._from_sequence(arr, dtype=dtype)
555
556 elif arr.dtype.kind == "m":
557 from pandas.core.arrays import TimedeltaArray
558
559 dtype = get_supported_dtype(arr.dtype)
560 return TimedeltaArray._from_sequence(arr, dtype=dtype)
561
562 return arr
563
564
565def sanitize_masked_array(data: ma.MaskedArray) -> np.ndarray:
566 """
567 Convert numpy MaskedArray to a plain ndarray with the masked entries filled.
568 """
569 mask = ma.getmaskarray(data)
570 if mask.any():
571 dtype, fill_value = maybe_promote(data.dtype, np.nan)
572 data = ma.asarray(data.astype(dtype, copy=True))
573 data.soften_mask() # set hardmask False if it was True
574 data[mask] = fill_value
575 else:
576 data = data.copy()
577 return np.asarray(data)
578
579
580def sanitize_array(
581 data,
582 index: Index | None,
583 dtype: DtypeObj | None = None,
584 copy: bool = False,
585 *,
586 allow_2d: bool = False,
587) -> ArrayLike:
588 """
589 Sanitize input data to an ndarray or ExtensionArray, copy if specified,
590 coerce to the dtype if specified.
591
592 Parameters
593 ----------
594 data : Any
595 index : Index or None, default None
596 dtype : np.dtype, ExtensionDtype, or None, default None
597 copy : bool, default False
598 allow_2d : bool, default False
599 If False, raise if we have a 2D Arraylike.
600
601 Returns
602 -------
603 np.ndarray or ExtensionArray
604 """
605 original_dtype = dtype
606 if isinstance(data, ma.MaskedArray):
607 data = sanitize_masked_array(data)
608
609 if isinstance(dtype, NumpyEADtype):
610 # Avoid ending up with a NumpyExtensionArray
611 dtype = dtype.numpy_dtype
612
613 infer_object = not isinstance(data, (ABCIndex, ABCSeries))
614
615 # extract ndarray or ExtensionArray, ensure we have no NumpyExtensionArray
616 data = extract_array(data, extract_numpy=True, extract_range=True)
617
618 if isinstance(data, np.ndarray) and data.ndim == 0:
619 if dtype is None:
620 dtype = data.dtype
621 data = lib.item_from_zerodim(data)
622 elif isinstance(data, range):
623 # GH#16804
624 data = range_to_ndarray(data)
625 copy = False
626
627 if not is_list_like(data):
628 if index is None:
629 raise ValueError("index must be specified when data is not list-like")
630 if isinstance(data, str) and using_string_dtype() and original_dtype is None:
631 from pandas.core.arrays.string_ import StringDtype
632
633 dtype = StringDtype(na_value=np.nan)
634 data = construct_1d_arraylike_from_scalar(data, len(index), dtype)
635
636 return data
637
638 elif isinstance(data, ABCExtensionArray):
639 # it is already ensured above this is not a NumpyExtensionArray
640 # Until GH#49309 is fixed this check needs to come before the
641 # ExtensionDtype check
642 if dtype is not None:
643 subarr = data.astype(dtype, copy=copy)
644 elif copy:
645 subarr = data.copy()
646 else:
647 subarr = data
648
649 elif isinstance(dtype, ExtensionDtype):
650 # create an extension array from its dtype
651 _sanitize_non_ordered(data)
652 cls = dtype.construct_array_type()
653 if not hasattr(data, "__array__"):
654 data = list(data)
655 subarr = cls._from_sequence(data, dtype=dtype, copy=copy)
656
657 # GH#846
658 elif isinstance(data, np.ndarray):
659 if isinstance(data, np.matrix):
660 data = data.A
661
662 if dtype is None:
663 subarr = data
664 if data.dtype == object and infer_object:
665 subarr = lib.maybe_convert_objects(
666 data,
667 # Here we do not convert numeric dtypes, as if we wanted that,
668 # numpy would have done it for us.
669 convert_numeric=False,
670 convert_non_numeric=True,
671 convert_to_nullable_dtype=False,
672 dtype_if_all_nat=np.dtype("M8[s]"),
673 )
674 elif data.dtype.kind == "U" and using_string_dtype():
675 from pandas.core.arrays.string_ import StringDtype
676
677 dtype = StringDtype(na_value=np.nan)
678 subarr = dtype.construct_array_type()._from_sequence(data, dtype=dtype)
679
680 if (
681 subarr is data
682 or (subarr.dtype == "str" and subarr.dtype.storage == "python") # type: ignore[union-attr]
683 ) and copy:
684 subarr = subarr.copy()
685
686 else:
687 # we will try to copy by-definition here
688 subarr = _try_cast(data, dtype, copy)
689
690 elif hasattr(data, "__array__"):
691 # e.g. dask array GH#38645
692 if not copy:
693 data = np.asarray(data)
694 else:
695 data = np.array(data, copy=copy)
696 return sanitize_array(
697 data,
698 index=index,
699 dtype=dtype,
700 copy=False,
701 allow_2d=allow_2d,
702 )
703
704 else:
705 _sanitize_non_ordered(data)
706 # materialize e.g. generators, convert e.g. tuples, abc.ValueView
707 data = list(data)
708
709 if len(data) == 0 and dtype is None:
710 # We default to float64, matching numpy
711 subarr = np.array([], dtype=np.float64)
712
713 elif dtype is not None:
714 subarr = _try_cast(data, dtype, copy)
715
716 else:
717 subarr = construct_1d_object_array_from_listlike(data)
718 subarr = lib.maybe_convert_objects(
719 subarr,
720 convert_non_numeric=True,
721 dtype_if_all_nat=np.dtype("M8[s]"),
722 )
723
724 subarr = _sanitize_ndim(subarr, data, dtype, index, allow_2d=allow_2d)
725
726 if isinstance(subarr, np.ndarray):
727 # at this point we should have dtype be None or subarr.dtype == dtype
728 dtype = cast("np.dtype", dtype)
729 subarr = _sanitize_str_dtypes(subarr, data, dtype, copy)
730
731 return subarr
732
733
734def range_to_ndarray(rng: range) -> np.ndarray:
735 """
736 Cast a range object to ndarray.
737 """
738 # GH#30171 perf avoid realizing range as a list in np.array
739 try:
740 arr = np.arange(rng.start, rng.stop, rng.step, dtype="int64")
741 except OverflowError:
742 # GH#30173 handling for ranges that overflow int64
743 if (rng.start >= 0 and rng.step > 0) or (rng.step < 0 <= rng.stop):
744 try:
745 arr = np.arange(rng.start, rng.stop, rng.step, dtype="uint64")
746 except OverflowError:
747 arr = construct_1d_object_array_from_listlike(list(rng))
748 else:
749 arr = construct_1d_object_array_from_listlike(list(rng))
750 return arr
751
752
753def _sanitize_non_ordered(data) -> None:
754 """
755 Raise only for unordered sets, e.g., not for dict_keys
756 """
757 if isinstance(data, (set, frozenset)):
758 raise TypeError(f"'{type(data).__name__}' type is unordered")
759
760
761def _sanitize_ndim(
762 result: ArrayLike,
763 data,
764 dtype: DtypeObj | None,
765 index: Index | None,
766 *,
767 allow_2d: bool = False,
768) -> ArrayLike:
769 """
770 Ensure we have a 1-dimensional result array.
771 """
772 if getattr(result, "ndim", 0) == 0:
773 raise ValueError("result should be arraylike with ndim > 0")
774
775 if result.ndim == 1:
776 # the result that we want
777 result = _maybe_repeat(result, index)
778
779 elif result.ndim > 1:
780 if isinstance(data, np.ndarray):
781 if allow_2d:
782 return result
783 raise ValueError(
784 f"Data must be 1-dimensional, got ndarray of shape {data.shape} instead"
785 )
786 elif allow_2d and isinstance(result, ABCExtensionArray):
787 # e.g. test_2d_ea_with_dtype_cast
788 return result
789 if is_object_dtype(dtype) and isinstance(dtype, ExtensionDtype):
790 # i.e. NumpyEADtype("O")
791
792 result = com.asarray_tuplesafe(data, dtype=np.dtype("object"))
793 cls = dtype.construct_array_type()
794 result = cls._from_sequence(result, dtype=dtype)
795 else:
796 # error: Argument "dtype" to "asarray_tuplesafe" has incompatible type
797 # "Union[dtype[Any], ExtensionDtype, None]"; expected "Union[str,
798 # dtype[Any], None]"
799 result = com.asarray_tuplesafe(data, dtype=dtype) # type: ignore[arg-type]
800 return result
801
802
803def _sanitize_str_dtypes(
804 result: np.ndarray, data, dtype: np.dtype | None, copy: bool
805) -> np.ndarray:
806 """
807 Ensure we have a dtype that is supported by pandas.
808 """
809
810 # This is to prevent mixed-type Series getting all casted to
811 # NumPy string type, e.g. NaN --> '-1#IND'.
812 if issubclass(result.dtype.type, str):
813 # GH#16605
814 # If not empty convert the data to dtype
815 # GH#19853: If data is a scalar, result has already the result
816 if not lib.is_scalar(data):
817 if not np.all(isna(data)):
818 data = np.asarray(data, dtype=dtype)
819 if not copy:
820 result = np.asarray(data, dtype=object)
821 else:
822 result = np.array(data, dtype=object, copy=copy)
823 return result
824
825
826def _maybe_repeat(arr: ArrayLike, index: Index | None) -> ArrayLike:
827 """
828 If we have a length-1 array and an index describing how long we expect
829 the result to be, repeat the array.
830 """
831 if index is not None:
832 if 1 == len(arr) != len(index):
833 arr = arr.repeat(len(index))
834 return arr
835
836
837def _try_cast(
838 arr: list[Any] | np.ndarray,
839 dtype: np.dtype,
840 copy: bool,
841) -> ArrayLike:
842 """
843 Convert input to numpy ndarray and optionally cast to a given dtype.
844
845 Parameters
846 ----------
847 arr : ndarray or list
848 Excludes: ExtensionArray, Series, Index.
849 dtype : np.dtype
850 copy : bool
851 If False, don't copy the data if not needed.
852
853 Returns
854 -------
855 np.ndarray or ExtensionArray
856 """
857 is_ndarray = isinstance(arr, np.ndarray)
858
859 if dtype == object:
860 if not is_ndarray:
861 subarr = construct_1d_object_array_from_listlike(arr)
862 return subarr
863 return ensure_wrapped_if_datetimelike(arr).astype(dtype, copy=copy)
864
865 elif dtype.kind == "U":
866 # TODO: test cases with arr.dtype.kind in "mM"
867 if is_ndarray:
868 arr = cast("np.ndarray", arr)
869 shape = arr.shape
870 if arr.ndim > 1:
871 arr = arr.ravel()
872 else:
873 shape = (len(arr),)
874 return lib.ensure_string_array(arr, convert_na_value=False, copy=copy).reshape(
875 shape
876 )
877
878 elif dtype.kind in "mM":
879 if is_ndarray:
880 arr = cast("np.ndarray", arr)
881 if arr.ndim == 2 and arr.shape[1] == 1:
882 # GH#60081: DataFrame Constructor converts 1D data to array of
883 # shape (N, 1), but maybe_cast_to_datetime assumes 1D input
884 return maybe_cast_to_datetime(arr[:, 0], dtype).reshape(arr.shape)
885 return maybe_cast_to_datetime(arr, dtype)
886
887 # GH#15832: Check if we are requesting a numeric dtype and
888 # that we can convert the data to the requested dtype.
889 elif dtype.kind in "iu":
890 # this will raise if we have e.g. floats
891
892 subarr = maybe_cast_to_integer_array(arr, dtype)
893 elif not copy:
894 subarr = np.asarray(arr, dtype=dtype)
895 else:
896 subarr = np.array(arr, dtype=dtype, copy=copy)
897
898 return subarr