2015-07-14 4 views
5

В Python У меня есть массив дат, сгенерированных (или прочитанных из CSV-файла) с помощью pandas, и я хочу добавить один год к каждой дате. Я могу заставить его работать с помощью панд, но не используя numpy. Что я делаю не так? Или это ошибка как панд, так и numpy?numpy и pandas timedelta error

Спасибо!

import numpy as np 
import pandas as pd 
from pandas.tseries.offsets import DateOffset 

# Generate range of dates using pandas. 
dates = pd.date_range('1980-01-01', '2015-01-01') 

# Add one year using pandas. 
dates2 = dates + DateOffset(years=1) 

# Convert result to numpy. THIS WORKS! 
dates2_np = dates2.values 

# Convert original dates to numpy array. 
dates_np = dates.values 

# Add one year using numpy. THIS FAILS! 
dates3 = dates_np + np.timedelta64(1, 'Y') 

# TypeError: Cannot get a common metadata divisor for NumPy datetime metadata [ns] and [Y] because they have incompatible nonlinear base time units 

ответ

5

Добавление np.timedelta64(1, 'Y') в массив DTYPE datetime64[ns] не работает, потому что год не соответствует фиксированным числом наносекунд. Иногда год составляет 365 дней, иногда 366 дней, иногда даже второй прыжок. (Обратите внимание на дополнительные прыжковые секунды, например, тот, который произошел в 2015-06-30 23:59:60, не могут быть представлены как NumPy datetime64.)

Самый простой способ, который я знаю, добавить год к массиву NumPy datetime64[ns] чтобы разбить его на составные части, такие как годы, месяцы и дни, делать вычисления на целых массивов, а затем перекомпонуйте datetime64 массив:

def year(dates): 
    "Return an array of the years given an array of datetime64s" 
    return dates.astype('M8[Y]').astype('i8') + 1970 

def month(dates): 
    "Return an array of the months given an array of datetime64s" 
    return dates.astype('M8[M]').astype('i8') % 12 + 1 

def day(dates): 
    "Return an array of the days of the month given an array of datetime64s" 
    return (dates - dates.astype('M8[M]'))/np.timedelta64(1, 'D') + 1 

def combine64(years, months=1, days=1, weeks=None, hours=None, minutes=None, 
       seconds=None, milliseconds=None, microseconds=None, nanoseconds=None): 
    years = np.asarray(years) - 1970 
    months = np.asarray(months) - 1 
    days = np.asarray(days) - 1 
    types = ('<M8[Y]', '<m8[M]', '<m8[D]', '<m8[W]', '<m8[h]', 
      '<m8[m]', '<m8[s]', '<m8[ms]', '<m8[us]', '<m8[ns]') 
    vals = (years, months, days, weeks, hours, minutes, seconds, 
      milliseconds, microseconds, nanoseconds) 
    return sum(np.asarray(v, dtype=t) for t, v in zip(types, vals) 
       if v is not None) 

# break the datetime64 array into constituent parts 
years, months, days = [f(dates_np) for f in (year, month, day)] 
# recompose the datetime64 array after adding 1 to the years 
dates3 = combine64(years+1, months, days) 

урожайности

In [185]: dates3 
Out[185]: 
array(['1981-01-01', '1981-01-02', '1981-01-03', ..., '2015-12-30', 
     '2015-12-31', '2016-01-01'], dtype='datetime64[D]') 

Несмотря на появление настолько много кода, это на самом деле быстрее, чем добавление DateOff набор 1 года:

In [206]: %timeit dates + DateOffset(years=1) 
1 loops, best of 3: 285 ms per loop 

In [207]: %%timeit 
    .....: years, months, days = [f(dates_np) for f in (year, month, day)] 
    .....: combine64(years+1, months, days) 
    .....: 
100 loops, best of 3: 2.65 ms per loop 

Конечно, pd.tseries.offsets предлагает целый арсенал сдвигов, которые не имеют аналогов легко при работе с NumPy datetime64s.

+0

Я поражен скоростью и деталями этого ответа! Поскольку я читал свои данные с помощью панд, мне легче просто продолжать использовать параметр DateOffset для pandas, чтобы преобразовать даты, прежде чем выполнять свои вычисления с помощью numpy. Но я подумал о том, как сделать конверсию, как вы предлагаете, поэтому теперь у меня есть исходный код, если он мне понадобится. Благодаря! – questiondude

1

Вот что он говорит в NumPy documentation:

Есть два Timedelta единиц («Y», года и «М», месяцы), которые рассматриваются специально, потому что, сколько времени они представляют изменения в зависимости от того, когда они используются. В то время как единица timedelta day эквивалентна 24 часам, нет возможности конвертировать месячную единицу в дни, потому что разные месяцы имеют разное количество дней.

дни и недели, кажется, работает, хотя:

dates4 = dates_np + np.timedelta64(1, 'D') 
dates5 = dates_np + np.timedelta64(1, 'W') 
+0

Да, я прочитал это и в документации. Это не устранило мою путаницу, хотя :-) – questiondude

Смежные вопросы