|
4 | 4 |
|
5 | 5 | import numpy as np
|
6 | 6 |
|
7 |
| -from pandas._libs import tslib, tslibs |
8 |
| - |
9 | 7 | from pandas.core.dtypes.common import (
|
10 |
| - DT64NS_DTYPE, |
11 |
| - TD64NS_DTYPE, |
12 | 8 | is_bool_dtype,
|
13 | 9 | is_categorical_dtype,
|
14 | 10 | is_datetime64_dtype,
|
|
19 | 15 | is_sparse,
|
20 | 16 | is_timedelta64_dtype,
|
21 | 17 | )
|
22 |
| -from pandas.core.dtypes.generic import ( |
23 |
| - ABCCategoricalIndex, |
24 |
| - ABCDatetimeArray, |
25 |
| - ABCIndexClass, |
26 |
| - ABCRangeIndex, |
27 |
| - ABCSeries, |
28 |
| -) |
| 18 | +from pandas.core.dtypes.generic import ABCCategoricalIndex, ABCRangeIndex, ABCSeries |
29 | 19 |
|
30 | 20 |
|
31 | 21 | def get_dtype_kinds(l):
|
@@ -390,70 +380,39 @@ def concat_datetime(to_concat, axis=0, typs=None):
|
390 | 380 | if typs is None:
|
391 | 381 | typs = get_dtype_kinds(to_concat)
|
392 | 382 |
|
393 |
| - # multiple types, need to coerce to object |
394 |
| - if len(typs) != 1: |
395 |
| - return _concatenate_2d( |
396 |
| - [_convert_datetimelike_to_object(x) for x in to_concat], axis=axis |
397 |
| - ) |
398 |
| - |
399 |
| - # must be single dtype |
400 |
| - if any(typ.startswith("datetime") for typ in typs): |
401 |
| - |
402 |
| - if "datetime" in typs: |
403 |
| - to_concat = [x.astype(np.int64, copy=False) for x in to_concat] |
404 |
| - return _concatenate_2d(to_concat, axis=axis).view(DT64NS_DTYPE) |
405 |
| - else: |
406 |
| - # when to_concat has different tz, len(typs) > 1. |
407 |
| - # thus no need to care |
408 |
| - return _concat_datetimetz(to_concat) |
409 |
| - |
410 |
| - elif "timedelta" in typs: |
411 |
| - return _concatenate_2d([x.view(np.int64) for x in to_concat], axis=axis).view( |
412 |
| - TD64NS_DTYPE |
413 |
| - ) |
414 |
| - |
415 |
| - elif any(typ.startswith("period") for typ in typs): |
416 |
| - assert len(typs) == 1 |
417 |
| - cls = to_concat[0] |
418 |
| - new_values = cls._concat_same_type(to_concat) |
419 |
| - return new_values |
420 |
| - |
| 383 | + to_concat = [_wrap_datetimelike(x) for x in to_concat] |
| 384 | + single_dtype = len({x.dtype for x in to_concat}) == 1 |
421 | 385 |
|
422 |
| -def _convert_datetimelike_to_object(x): |
423 |
| - # coerce datetimelike array to object dtype |
| 386 | + # multiple types, need to coerce to object |
| 387 | + if not single_dtype: |
| 388 | + # wrap_datetimelike ensures that astype(object) wraps in Timestamp/Timedelta |
| 389 | + return _concatenate_2d([x.astype(object) for x in to_concat], axis=axis) |
424 | 390 |
|
425 |
| - # if dtype is of datetimetz or timezone |
426 |
| - if x.dtype.kind == DT64NS_DTYPE.kind: |
427 |
| - if getattr(x, "tz", None) is not None: |
428 |
| - x = np.asarray(x.astype(object)) |
429 |
| - else: |
430 |
| - shape = x.shape |
431 |
| - x = tslib.ints_to_pydatetime(x.view(np.int64).ravel(), box="timestamp") |
432 |
| - x = x.reshape(shape) |
| 391 | + if axis == 1: |
| 392 | + # TODO(EA2D): kludge not necessary with 2D EAs |
| 393 | + to_concat = [x.reshape(1, -1) if x.ndim == 1 else x for x in to_concat] |
433 | 394 |
|
434 |
| - elif x.dtype == TD64NS_DTYPE: |
435 |
| - shape = x.shape |
436 |
| - x = tslibs.ints_to_pytimedelta(x.view(np.int64).ravel(), box=True) |
437 |
| - x = x.reshape(shape) |
| 395 | + result = type(to_concat[0])._concat_same_type(to_concat, axis=axis) |
438 | 396 |
|
439 |
| - return x |
| 397 | + if result.ndim == 2 and is_extension_array_dtype(result.dtype): |
| 398 | + # TODO(EA2D): kludge not necessary with 2D EAs |
| 399 | + assert result.shape[0] == 1 |
| 400 | + result = result[0] |
| 401 | + return result |
440 | 402 |
|
441 | 403 |
|
442 |
| -def _concat_datetimetz(to_concat, name=None): |
| 404 | +def _wrap_datetimelike(arr): |
443 | 405 | """
|
444 |
| - concat DatetimeIndex with the same tz |
445 |
| - all inputs must be DatetimeIndex |
446 |
| - it is used in DatetimeIndex.append also |
| 406 | + Wrap datetime64 and timedelta64 ndarrays in DatetimeArray/TimedeltaArray. |
| 407 | +
|
| 408 | + DTA/TDA handle .astype(object) correctly. |
447 | 409 | """
|
448 |
| - # Right now, internals will pass a List[DatetimeArray] here |
449 |
| - # for reductions like quantile. I would like to disentangle |
450 |
| - # all this before we get here. |
451 |
| - sample = to_concat[0] |
452 |
| - |
453 |
| - if isinstance(sample, ABCIndexClass): |
454 |
| - return sample._concat_same_dtype(to_concat, name=name) |
455 |
| - elif isinstance(sample, ABCDatetimeArray): |
456 |
| - return sample._concat_same_type(to_concat) |
| 410 | + from pandas.core.construction import array as pd_array, extract_array |
| 411 | + |
| 412 | + arr = extract_array(arr, extract_numpy=True) |
| 413 | + if isinstance(arr, np.ndarray) and arr.dtype.kind in ["m", "M"]: |
| 414 | + arr = pd_array(arr) |
| 415 | + return arr |
457 | 416 |
|
458 | 417 |
|
459 | 418 | def _concat_sparse(to_concat, axis=0, typs=None):
|
|
0 commit comments