|
6 | 6 | # necessary to enforce truediv in Python 2.X
|
7 | 7 | from __future__ import division
|
8 | 8 | import operator
|
9 |
| -import warnings |
| 9 | + |
10 | 10 | import numpy as np
|
11 | 11 | import pandas as pd
|
12 |
| -import datetime |
13 | 12 |
|
14 | 13 | from pandas._libs import (lib, index as libindex,
|
15 |
| - tslib as libts, algos as libalgos, iNaT) |
| 14 | + algos as libalgos) |
16 | 15 |
|
17 | 16 | from pandas import compat
|
18 | 17 | from pandas.util._decorators import Appender
|
19 | 18 |
|
20 | 19 | from pandas.compat import bind_method
|
21 | 20 | import pandas.core.missing as missing
|
22 | 21 |
|
23 |
| -from pandas.errors import PerformanceWarning, NullFrequencyError |
| 22 | +from pandas.errors import NullFrequencyError |
24 | 23 | from pandas.core.common import _values_from_object, _maybe_match_name
|
25 | 24 | from pandas.core.dtypes.missing import notna, isna
|
26 | 25 | from pandas.core.dtypes.common import (
|
27 | 26 | needs_i8_conversion,
|
28 | 27 | is_datetimelike_v_numeric,
|
29 | 28 | is_integer_dtype, is_categorical_dtype,
|
30 | 29 | is_object_dtype, is_timedelta64_dtype,
|
31 |
| - is_datetime64_dtype, is_datetime64tz_dtype, is_datetime64_ns_dtype, |
32 |
| - is_bool_dtype, is_datetimetz, |
33 |
| - is_list_like, is_offsetlike, |
| 30 | + is_datetime64_dtype, is_datetime64tz_dtype, |
| 31 | + is_bool_dtype, |
| 32 | + is_list_like, |
34 | 33 | is_scalar,
|
35 | 34 | _ensure_object)
|
36 | 35 | from pandas.core.dtypes.cast import (
|
|
39 | 38 | from pandas.core.dtypes.generic import (
|
40 | 39 | ABCSeries,
|
41 | 40 | ABCDataFrame,
|
42 |
| - ABCIndex, ABCDatetimeIndex, |
| 41 | + ABCIndex, |
43 | 42 | ABCPeriodIndex)
|
44 | 43 |
|
45 | 44 | # -----------------------------------------------------------------------------
|
@@ -294,287 +293,6 @@ def add_flex_arithmetic_methods(cls, flex_arith_method,
|
294 | 293 | exclude=exclude)
|
295 | 294 |
|
296 | 295 |
|
297 |
| -class _Op(object): |
298 |
| - |
299 |
| - """ |
300 |
| - Wrapper around Series arithmetic operations. |
301 |
| - Generally, you should use classmethod ``_Op.get_op`` as an entry point. |
302 |
| -
|
303 |
| - This validates and coerces lhs and rhs depending on its dtype and |
304 |
| - based on op. See _TimeOp also. |
305 |
| -
|
306 |
| - Parameters |
307 |
| - ---------- |
308 |
| - left : Series |
309 |
| - lhs of op |
310 |
| - right : object |
311 |
| - rhs of op |
312 |
| - name : str |
313 |
| - name of op |
314 |
| - na_op : callable |
315 |
| - a function which wraps op |
316 |
| - """ |
317 |
| - |
318 |
| - fill_value = np.nan |
319 |
| - wrap_results = staticmethod(lambda x: x) |
320 |
| - dtype = None |
321 |
| - |
322 |
| - def __init__(self, left, right, name, na_op): |
323 |
| - self.left = left |
324 |
| - self.right = right |
325 |
| - |
326 |
| - self.name = name |
327 |
| - self.na_op = na_op |
328 |
| - |
329 |
| - self.lvalues = left |
330 |
| - self.rvalues = right |
331 |
| - |
332 |
| - @classmethod |
333 |
| - def get_op(cls, left, right, name, na_op): |
334 |
| - """ |
335 |
| - Get op dispatcher, returns _Op or _TimeOp. |
336 |
| -
|
337 |
| - If ``left`` and ``right`` are appropriate for datetime arithmetic with |
338 |
| - operation ``name``, processes them and returns a ``_TimeOp`` object |
339 |
| - that stores all the required values. Otherwise, it will generate |
340 |
| - either a ``_Op``, indicating that the operation is performed via |
341 |
| - normal numpy path. |
342 |
| - """ |
343 |
| - is_timedelta_lhs = is_timedelta64_dtype(left) |
344 |
| - |
345 |
| - if not is_timedelta_lhs: |
346 |
| - return _Op(left, right, name, na_op) |
347 |
| - else: |
348 |
| - return _TimeOp(left, right, name, na_op) |
349 |
| - |
350 |
| - |
351 |
| -class _TimeOp(_Op): |
352 |
| - """ |
353 |
| - Wrapper around Series datetime/time/timedelta arithmetic operations. |
354 |
| - Generally, you should use classmethod ``_Op.get_op`` as an entry point. |
355 |
| - """ |
356 |
| - fill_value = iNaT |
357 |
| - |
358 |
| - def __init__(self, left, right, name, na_op): |
359 |
| - super(_TimeOp, self).__init__(left, right, name, na_op) |
360 |
| - |
361 |
| - lvalues = self._convert_to_array(left, name=name) |
362 |
| - rvalues = self._convert_to_array(right, name=name, other=lvalues) |
363 |
| - |
364 |
| - # left |
365 |
| - self.is_timedelta_lhs = is_timedelta64_dtype(lvalues) |
366 |
| - assert self.is_timedelta_lhs |
367 |
| - |
368 |
| - # right |
369 |
| - self.is_offset_rhs = is_offsetlike(right) |
370 |
| - self.is_datetime64_rhs = is_datetime64_dtype(rvalues) |
371 |
| - self.is_datetime64tz_rhs = is_datetime64tz_dtype(rvalues) |
372 |
| - self.is_datetime_rhs = (self.is_datetime64_rhs or |
373 |
| - self.is_datetime64tz_rhs) |
374 |
| - self.is_timedelta_rhs = is_timedelta64_dtype(rvalues) |
375 |
| - self.is_integer_rhs = rvalues.dtype.kind in ('i', 'u') |
376 |
| - self.is_floating_rhs = rvalues.dtype.kind == 'f' |
377 |
| - |
378 |
| - self._validate(lvalues, rvalues, name) |
379 |
| - self.lvalues, self.rvalues = self._convert_for_datetime(lvalues, |
380 |
| - rvalues) |
381 |
| - |
382 |
| - def _validate_timedelta(self, name): |
383 |
| - # assumes self.is_timedelta_lhs |
384 |
| - |
385 |
| - if self.is_integer_rhs or self.is_floating_rhs: |
386 |
| - # timedelta and integer mul/div |
387 |
| - self._check_timedelta_with_numeric(name) |
388 |
| - elif self.is_timedelta_rhs or self.is_offset_rhs: |
389 |
| - # 2 timedeltas |
390 |
| - if name not in ('__div__', '__rdiv__', '__truediv__', |
391 |
| - '__rtruediv__', '__add__', '__radd__', '__sub__', |
392 |
| - '__rsub__', '__floordiv__', '__rfloordiv__'): |
393 |
| - raise TypeError("can only operate on a timedeltas for addition" |
394 |
| - ", subtraction, and division, but the operator" |
395 |
| - " [{name}] was passed".format(name=name)) |
396 |
| - elif self.is_datetime_rhs: |
397 |
| - if name not in ('__add__', '__radd__', '__rsub__'): |
398 |
| - raise TypeError("can only operate on a timedelta/DateOffset " |
399 |
| - "with a rhs of a datetime for addition, " |
400 |
| - "but the operator [{name}] was passed" |
401 |
| - .format(name=name)) |
402 |
| - else: |
403 |
| - raise TypeError('cannot operate on a series without a rhs ' |
404 |
| - 'of a series/ndarray of type datetime64[ns] ' |
405 |
| - 'or a timedelta') |
406 |
| - |
407 |
| - def _validate(self, lvalues, rvalues, name): |
408 |
| - return self._validate_timedelta(name) |
409 |
| - |
410 |
| - def _check_timedelta_with_numeric(self, name): |
411 |
| - if name not in ('__div__', '__truediv__', '__mul__', '__rmul__'): |
412 |
| - raise TypeError("can only operate on a timedelta and an " |
413 |
| - "integer or a float for division and " |
414 |
| - "multiplication, but the operator [{name}] " |
415 |
| - "was passed".format(name=name)) |
416 |
| - |
417 |
| - def _convert_to_array(self, values, name=None, other=None): |
418 |
| - """converts values to ndarray""" |
419 |
| - from pandas.core.tools.timedeltas import to_timedelta |
420 |
| - |
421 |
| - ovalues = values |
422 |
| - supplied_dtype = None |
423 |
| - if not is_list_like(values): |
424 |
| - values = np.array([values]) |
425 |
| - |
426 |
| - # if this is a Series that contains relevant dtype info, then use this |
427 |
| - # instead of the inferred type; this avoids coercing Series([NaT], |
428 |
| - # dtype='datetime64[ns]') to Series([NaT], dtype='timedelta64[ns]') |
429 |
| - elif (isinstance(values, (pd.Series, ABCDatetimeIndex)) and |
430 |
| - (is_timedelta64_dtype(values) or is_datetime64_dtype(values))): |
431 |
| - supplied_dtype = values.dtype |
432 |
| - |
433 |
| - inferred_type = lib.infer_dtype(values) |
434 |
| - if (inferred_type in ('datetime64', 'datetime', 'date', 'time') or |
435 |
| - is_datetimetz(inferred_type)): |
436 |
| - # if we have a other of timedelta, but use pd.NaT here we |
437 |
| - # we are in the wrong path |
438 |
| - if (supplied_dtype is None and other is not None and |
439 |
| - (other.dtype in ('timedelta64[ns]', 'datetime64[ns]')) and |
440 |
| - isna(values).all()): |
441 |
| - values = np.empty(values.shape, dtype='timedelta64[ns]') |
442 |
| - values[:] = iNaT |
443 |
| - |
444 |
| - elif isinstance(values, ABCDatetimeIndex): |
445 |
| - # a datelike |
446 |
| - pass |
447 |
| - elif isinstance(ovalues, datetime.datetime): |
448 |
| - # datetime scalar |
449 |
| - values = pd.DatetimeIndex(values) |
450 |
| - # datetime array with tz |
451 |
| - elif is_datetimetz(values): |
452 |
| - if isinstance(values, ABCSeries): |
453 |
| - values = values._values |
454 |
| - elif not (isinstance(values, (np.ndarray, ABCSeries)) and |
455 |
| - is_datetime64_dtype(values)): |
456 |
| - values = libts.array_to_datetime(values) |
457 |
| - elif (is_datetime64_dtype(values) and |
458 |
| - not is_datetime64_ns_dtype(values)): |
459 |
| - # GH#7996 e.g. np.datetime64('2013-01-01') is datetime64[D] |
460 |
| - values = values.astype('datetime64[ns]') |
461 |
| - |
462 |
| - elif inferred_type in ('timedelta', 'timedelta64'): |
463 |
| - # have a timedelta, convert to to ns here |
464 |
| - values = to_timedelta(values, errors='coerce', box=False) |
465 |
| - if isinstance(other, ABCDatetimeIndex): |
466 |
| - # GH#13905 |
467 |
| - # Defer to DatetimeIndex/TimedeltaIndex operations where |
468 |
| - # timezones are handled carefully. |
469 |
| - values = pd.TimedeltaIndex(values) |
470 |
| - elif inferred_type == 'integer': |
471 |
| - # py3 compat where dtype is 'm' but is an integer |
472 |
| - if values.dtype.kind == 'm': |
473 |
| - values = values.astype('timedelta64[ns]') |
474 |
| - elif isinstance(values, pd.PeriodIndex): |
475 |
| - values = values.to_timestamp().to_series() |
476 |
| - elif name not in ('__truediv__', '__div__', '__mul__', '__rmul__'): |
477 |
| - raise TypeError("incompatible type for a datetime/timedelta " |
478 |
| - "operation [{name}]".format(name=name)) |
479 |
| - elif inferred_type == 'floating': |
480 |
| - if (isna(values).all() and |
481 |
| - name in ('__add__', '__radd__', '__sub__', '__rsub__')): |
482 |
| - values = np.empty(values.shape, dtype=other.dtype) |
483 |
| - values[:] = iNaT |
484 |
| - return values |
485 |
| - elif is_offsetlike(values): |
486 |
| - return values |
487 |
| - else: |
488 |
| - raise TypeError("incompatible type [{dtype}] for a " |
489 |
| - "datetime/timedelta operation" |
490 |
| - .format(dtype=np.array(values).dtype)) |
491 |
| - |
492 |
| - return values |
493 |
| - |
494 |
| - def _convert_for_datetime(self, lvalues, rvalues): |
495 |
| - from pandas.core.tools.timedeltas import to_timedelta |
496 |
| - |
497 |
| - mask = isna(lvalues) | isna(rvalues) |
498 |
| - |
499 |
| - # datetimes require views |
500 |
| - if self.is_datetime_rhs: |
501 |
| - |
502 |
| - # datetime subtraction means timedelta |
503 |
| - if self.is_datetime64tz_rhs: |
504 |
| - self.dtype = rvalues.dtype |
505 |
| - else: |
506 |
| - self.dtype = 'datetime64[ns]' |
507 |
| - |
508 |
| - # if adding single offset try vectorized path |
509 |
| - # in DatetimeIndex; otherwise elementwise apply |
510 |
| - def _offset(lvalues, rvalues): |
511 |
| - if len(lvalues) == 1: |
512 |
| - rvalues = pd.DatetimeIndex(rvalues) |
513 |
| - lvalues = lvalues[0] |
514 |
| - else: |
515 |
| - warnings.warn("Adding/subtracting array of DateOffsets to " |
516 |
| - "Series not vectorized", PerformanceWarning) |
517 |
| - rvalues = rvalues.astype('O') |
518 |
| - |
519 |
| - # pass thru on the na_op |
520 |
| - self.na_op = lambda x, y: getattr(x, self.name)(y) |
521 |
| - return lvalues, rvalues |
522 |
| - |
523 |
| - if self.is_offset_rhs: |
524 |
| - rvalues, lvalues = _offset(rvalues, lvalues) |
525 |
| - else: |
526 |
| - |
527 |
| - # with tz, convert to UTC |
528 |
| - if self.is_datetime64tz_rhs: |
529 |
| - rvalues = rvalues.tz_convert('UTC').tz_localize(None) |
530 |
| - |
531 |
| - lvalues = lvalues.view(np.int64) |
532 |
| - rvalues = rvalues.view(np.int64) |
533 |
| - |
534 |
| - # otherwise it's a timedelta |
535 |
| - else: |
536 |
| - |
537 |
| - self.dtype = 'timedelta64[ns]' |
538 |
| - |
539 |
| - # convert Tick DateOffset to underlying delta |
540 |
| - if self.is_offset_rhs: |
541 |
| - rvalues = to_timedelta(rvalues, box=False) |
542 |
| - |
543 |
| - lvalues = lvalues.astype(np.int64) |
544 |
| - if not self.is_floating_rhs: |
545 |
| - rvalues = rvalues.astype(np.int64) |
546 |
| - |
547 |
| - # time delta division -> unit less |
548 |
| - # integer gets converted to timedelta in np < 1.6 |
549 |
| - if ((self.is_timedelta_lhs and self.is_timedelta_rhs) and |
550 |
| - not self.is_integer_rhs and |
551 |
| - self.name in ('__div__', '__rdiv__', |
552 |
| - '__truediv__', '__rtruediv__', |
553 |
| - '__floordiv__', '__rfloordiv__')): |
554 |
| - self.dtype = 'float64' |
555 |
| - self.fill_value = np.nan |
556 |
| - lvalues = lvalues.astype(np.float64) |
557 |
| - rvalues = rvalues.astype(np.float64) |
558 |
| - |
559 |
| - # if we need to mask the results |
560 |
| - if mask.any(): |
561 |
| - |
562 |
| - def f(x): |
563 |
| - |
564 |
| - # datetime64[ns]/timedelta64[ns] masking |
565 |
| - try: |
566 |
| - x = np.array(x, dtype=self.dtype) |
567 |
| - except TypeError: |
568 |
| - x = np.array(x, dtype='datetime64[ns]') |
569 |
| - |
570 |
| - np.putmask(x, mask, self.fill_value) |
571 |
| - return x |
572 |
| - |
573 |
| - self.wrap_results = f |
574 |
| - |
575 |
| - return lvalues, rvalues |
576 |
| - |
577 |
| - |
578 | 296 | def _align_method_SERIES(left, right, align_asobject=False):
|
579 | 297 | """ align lhs and rhs Series """
|
580 | 298 |
|
@@ -678,26 +396,22 @@ def wrapper(left, right, name=name, na_op=na_op):
|
678 | 396 | index=left.index, name=res_name,
|
679 | 397 | dtype=result.dtype)
|
680 | 398 |
|
681 |
| - converted = _Op.get_op(left, right, name, na_op) |
682 |
| - |
683 |
| - lvalues, rvalues = converted.lvalues, converted.rvalues |
684 |
| - dtype = converted.dtype |
685 |
| - wrap_results = converted.wrap_results |
686 |
| - na_op = converted.na_op |
| 399 | + elif is_timedelta64_dtype(left): |
| 400 | + result = dispatch_to_index_op(op, left, right, pd.TimedeltaIndex) |
| 401 | + res_name = _get_series_op_result_name(left, right) |
| 402 | + return construct_result(left, result, |
| 403 | + index=left.index, name=res_name, |
| 404 | + dtype=result.dtype) |
687 | 405 |
|
| 406 | + lvalues = left.values |
| 407 | + rvalues = right |
688 | 408 | if isinstance(rvalues, ABCSeries):
|
689 |
| - lvalues = getattr(lvalues, 'values', lvalues) |
690 | 409 | rvalues = getattr(rvalues, 'values', rvalues)
|
691 |
| - # _Op aligns left and right |
692 |
| - else: |
693 |
| - if (hasattr(lvalues, 'values') and |
694 |
| - not isinstance(lvalues, ABCDatetimeIndex)): |
695 |
| - lvalues = lvalues.values |
696 | 410 |
|
697 |
| - result = wrap_results(safe_na_op(lvalues, rvalues)) |
| 411 | + result = safe_na_op(lvalues, rvalues) |
698 | 412 | res_name = _get_series_op_result_name(left, right)
|
699 | 413 | return construct_result(left, result,
|
700 |
| - index=left.index, name=res_name, dtype=dtype) |
| 414 | + index=left.index, name=res_name, dtype=None) |
701 | 415 |
|
702 | 416 | return wrapper
|
703 | 417 |
|
|
0 commit comments