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