Даты и временные интервалы
Впервые добавлено в версии 1.7.0.
Начиная с NumPy 1.7, существуют основные типы данных массивов, которые изначально поддерживают функциональность работы с датами и временем. Тип данных называется «datetime64», так как «datetime» уже используется в библиотеке datetime, включённой в Python.
Примечание
API для работы с датами и временем в версии 1.7.0 является экспериментальным и может быть изменён в будущих версиях NumPy.
Основные даты и время
Самый простой способ создания дат и времени — из строк в формате даты или времени ISO 8601. Единица для внутреннего хранения автоматически выбирается из формата строки и может быть либо единицей измерения даты, либо единицей измерения времени. Единицы измерения дат — годы (‘Y’), месяцы (‘M’), недели (‘W’) и дни (‘D’), а единицы измерения времени — часы (‘h’), минуты (‘m’), секунды (‘s’), миллисекунды (‘ms’) и некоторые дополнительные единицы измерения секунд с использованием префиксов СИ.
Пример
Простая дата в формате ISO:
>>> np.datetime64('2005-02-25')
numpy.datetime64('2005-02-25')
Использование месяцев в качестве единицы измерения:
>>> np.datetime64('2005-02')
numpy.datetime64('2005-02')
Указание только месяца, но принудительное использование единицы «дни»:
>>> np.datetime64('2005-02', 'D')
numpy.datetime64('2005-02-01')
Из даты и времени:
>>> np.datetime64('2005-02-25T03:30')
numpy.datetime64('2005-02-25T03:30')
При создании массива дат и времени из строки всё ещё можно автоматически выбрать единицу измерения из входных данных, используя тип datetime с общими единицами измерения.
Пример
>>> np.array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='datetime64') array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='datetime64[D]')
>>> np.array(['2001-01-01T12:00', '2002-02-03T13:56:03.172'], dtype='datetime64') array(['2001-01-01T12:00:00.000-0600', '2002-02-03T13:56:03.172-0600'], dtype='datetime64[ms]')
Тип datetime работает со многими общими функциями NumPy, например, arange может быть использован для генерации диапазонов дат.
Пример
Все даты за один месяц:
>>> np.arange('2005-02', '2005-03', dtype='datetime64[D]')
array(['2005-02-01', '2005-02-02', '2005-02-03', '2005-02-04',
'2005-02-05', '2005-02-06', '2005-02-07', '2005-02-08',
'2005-02-09', '2005-02-10', '2005-02-11', '2005-02-12',
'2005-02-13', '2005-02-14', '2005-02-15', '2005-02-16',
'2005-02-17', '2005-02-18', '2005-02-19', '2005-02-20',
'2005-02-21', '2005-02-22', '2005-02-23', '2005-02-24',
'2005-02-25', '2005-02-26', '2005-02-27', '2005-02-28'],
dtype='datetime64[D]')
Объект datetime представляет один момент во времени. Если у двух дат и времени разные единицы измерения, они всё равно могут представлять один и тот же момент времени, и преобразование из большей единицы, такой как месяцы, в меньшую, такую как дни, считается «безопасным» преобразованием, поскольку момент времени всё ещё представляется точно.
Пример
>>> np.datetime64('2005') == np.datetime64('2005-01-01')
True
>>> np.datetime64('2010-03-14T15Z') == np.datetime64('2010-03-14T15:00:00.00Z')
True
Арифметика дат и временных интервалов
NumPy позволяет вычитать две даты, операция, которая даёт число с единицей измерения времени. Поскольку NumPy не имеет физической системы единиц измерения в своём ядре, был создан тип данных timedelta64, который дополняет datetime64.
Даты и временные интервалы работают вместе, чтобы предоставить способы для простых вычислений дат и времени.
Пример
>>> np.datetime64('2009-01-01') - np.datetime64('2008-01-01')
numpy.timedelta64(366,'D')
>>> np.datetime64('2009') + np.timedelta64(20, 'D')
numpy.datetime64('2009-01-21')
>>> np.datetime64('2011-06-15T00:00') + np.timedelta64(12, 'h')
numpy.datetime64('2011-06-15T12:00-0500')
>>> np.timedelta64(1,'W') / np.timedelta64(1,'D') 7.0
Существуют две единицы измерения временных интервалов (‘Y’, годы и ‘M’, месяцы), которые обрабатываются специально, поскольку количество представляемого ими времени зависит от контекста их использования. В то время как единица измерения временного интервала «день» эквивалентна 24 часам, нет способа преобразовать единицу измерения «месяц» в дни, потому что разные месяцы имеют разное количество дней.
Пример
>>> a = np.timedelta64(1, 'Y')
>>> np.timedelta64(a, 'M') numpy.timedelta64(12,'M')
>>> np.timedelta64(a, 'D') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Cannot cast NumPy timedelta64 scalar from metadata [Y] to [D] according to the rule 'same_kind'
Единицы измерения дат
Типы данных Datetime и Timedelta поддерживают большое количество единиц измерения времени, а также общие единицы измерения, которые могут быть преобразованы в любые другие единицы измерения на основе входных данных.
Даты всегда хранятся в формате времени POSIX (хотя предлагается режим TAI, который позволяет учитывать високосные секунды), с эпохой 1970-01-01T00:00Z. Это означает, что поддерживаемые даты всегда являются симметричным интервалом вокруг эпохи, называемым «временным интервалом» в таблице ниже.
Длина интервала — это диапазон 64-битного целого числа умноженный на длину даты или единицы. Например, временной интервал для ‘W’ (неделя) в точности в 7 раз длиннее, чем временной интервал для ‘D’ (день), а временной интервал для ‘D’ (день) в точности в 24 раза длиннее, чем временной интервал для ‘h’ (час).
Вот единицы измерения дат:
| Код | Значение | Временной интервал (относительно) | Временной интервал (абсолютно) |
|---|---|---|---|
| Y | год | +/- 9.2e18 лет | [9.2e18 до н.э., 9.2e18 н.э.] |
| M | месяц | +/- 7.6e17 лет | [7.6e17 до н.э., 7.6e17 н.э.] |
| W | неделя | +/- 1.7e17 лет | [1.7e17 до н.э., 1.7e17 н.э.] |
| D | день | +/- 2.5e16 лет | [2.5e16 до н.э., 2.5e16 н.э.] |
И вот единицы измерения времени:
| Код | Значение | Временной интервал (относительно) | Временной интервал (абсолютно) |
|---|---|---|---|
| h | час | +/- 1.0e15 лет | [1.0e15 до н.э., 1.0e15 н.э.] |
| m | минута | +/- 1.7e13 лет | [1.7e13 до н.э., 1.7e13 н.э.] |
| s | секунда | +/- 2.9e11 лет | [2.9e11 до н.э., 2.9e11 н.э.] |
| ms | миллисекунда | +/- 2.9e8 лет | [ 2.9e8 до н.э., 2.9e8 н.э.] |
| us | микросекунда | +/- 2.9e5 лет | [290301 до н.э., 294241 н.э.] |
| ns | наносекунда | +/- 292 года | [ 1678 н.э., 2262 н.э.] |
| ps | пикосекунда | +/- 106 дней | [ 1969 н.э., 1970 н.э.] |
| fs | фемтосекунда | +/- 2.6 часа | [ 1969 н.э., 1970 н.э.] |
| as | аттосекунда | +/- 9.2 секунды | [ 1969 н.э., 1970 н.э.] |
Функциональность рабочих дней
Для того, чтобы можно было использовать даты в контекстах, где допустимы только определённые дни недели, NumPy включает набор функций «busday» (рабочий день).
По умолчанию для функций busday допустимы только понедельник по пятницу (обычные рабочие дни). Реализация основана на «weekmask», содержащем 7 булевых флагов для указания допустимых дней; возможны пользовательские weekmask, которые указывают другие наборы допустимых дней.
Функции «busday» дополнительно могут проверять список «holiday» (выходных) дат — конкретных дат, которые не являются рабочими днями.
Функция busday_offset позволяет применять сдвиги, указанные в рабочих днях, к датам и времени с единицей измерения ‘D’ (день).
Пример
>>> np.busday_offset('2011-06-23', 1)
numpy.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-23', 2)
numpy.datetime64('2011-06-27')
Когда входная дата попадает на выходные или праздничный день, busday_offset сначала применяет правило для переноса даты на допустимый рабочий день, а затем применяет сдвиг. По умолчанию правило — «raise», которое просто вызывает исключение. Наиболее часто используемые правила — «forward» (вперёд) и «backward» (назад).
Пример
>>> np.busday_offset('2011-06-25', 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Non-business day date in busday_offset
>>> np.busday_offset('2011-06-25', 0, roll='forward')
numpy.datetime64('2011-06-27')
>>> np.busday_offset('2011-06-25', 2, roll='forward')
numpy.datetime64('2011-06-29')
>>> np.busday_offset('2011-06-25', 0, roll='backward')
numpy.datetime64('2011-06-24')
>>> np.busday_offset('2011-06-25', 2, roll='backward')
numpy.datetime64('2011-06-28')
В некоторых случаях необходимо правильное использование сдвига и смещения, чтобы получить желаемый результат.
Пример
Первый рабочий день на или после даты:
>>> np.busday_offset('2011-03-20', 0, roll='forward')
numpy.datetime64('2011-03-21','D')
>>> np.busday_offset('2011-03-22', 0, roll='forward')
numpy.datetime64('2011-03-22','D')
Первый рабочий день строго после даты:
>>> np.busday_offset('2011-03-20', 1, roll='backward')
numpy.datetime64('2011-03-21','D')
>>> np.busday_offset('2011-03-22', 1, roll='backward')
numpy.datetime64('2011-03-23','D')
Функция также полезна для вычисления некоторых видов дней, таких как выходные. В Канаде и США День матери приходится на второе воскресенье мая, что можно вычислить с помощью пользовательского weekmask.
Пример
>>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
numpy.datetime64('2012-05-13','D')
Когда производительность важна для обработки многих дат рабочих дней с определённым выбором weekmask и выходных дней, существует объект busdaycalendar, который хранит необходимые данные в оптимизированной форме.
np.is_busday():
Чтобы проверить значение datetime64, является ли оно допустимым днём, используйте is_busday.
Пример
>>> np.is_busday(np.datetime64('2011-07-15')) # a Friday
True
>>> np.is_busday(np.datetime64('2011-07-16')) # a Saturday
False
>>> np.is_busday(np.datetime64('2011-07-16'), weekmask="Sat Sun")
True
>>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
>>> np.is_busday(a)
array([ True, True, True, True, True, False, False], dtype='bool')
np.busday_count():
Чтобы найти количество допустимых дней в указанном диапазоне дат datetime64, используйте busday_count:
Пример
>>> np.busday_count(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
5
>>> np.busday_count(np.datetime64('2011-07-18'), np.datetime64('2011-07-11'))
-5
Если у вас есть массив значений datetime64 дня, и вы хотите подсчитать, сколько из них являются допустимыми датами, вы можете сделать это:
Пример
>>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
>>> np.count_nonzero(np.is_busday(a))
5
Пользовательские weekmask
Вот несколько примеров пользовательских значений weekmask. Эти примеры указывают «busday» по умолчанию, что понедельник по пятницу — допустимые дни.
Некоторые примеры:
# Positional sequences; positions are Monday through Sunday. # Length of the sequence must be exactly 7. weekmask = [1, 1, 1, 1, 1, 0, 0] # list or other sequence; 0 == invalid day, 1 == valid day weekmask = "1111100" # string '0' == invalid day, '1' == valid day # string abbreviations from this list: Mon Tue Wed Thu Fri Sat Sun weekmask = "Mon Tue Wed Thu Fri" # any amount of whitespace is allowed; abbreviations are case-sensitive. weekmask = "MonTue Wed Thu\tFri"
Изменения в NumPy 1.11
В предыдущих версиях NumPy тип datetime64 всегда хранил время в UTC. По умолчанию создание объекта datetime64 из строки или его вывод конвертировали из или в локальное время:
# old behavior
>>>> np.datetime64('2000-01-01T00:00:00')
numpy.datetime64('2000-01-01T00:00:00-0800') # note the timezone offset -08:00
Консенсус пользователей datetime64 согласен, что такое поведение нежелательно и не соответствует обычному использованию datetime64 (например, библиотекой pandas). В большинстве случаев предпочтительнее использовать тип datetime без часового пояса, аналогично типу datetime.datetime в стандартной библиотеке Python. Соответственно, datetime64 больше не предполагает, что ввод находится в локальном часовом поясе, и не выводит локальное время:
>>>> np.datetime64('2000-01-01T00:00:00')
numpy.datetime64('2000-01-01T00:00:00')
Для обратной совместимости datetime64 всё ещё анализирует смещения часового пояса, обрабатывая их путём преобразования в UTC. Однако полученная дата является датой без часового пояса:
>>> np.datetime64('2000-01-01T00:00:00-08')
DeprecationWarning: parsing timezone aware datetimes is deprecated; this will raise an error in the future
numpy.datetime64('2000-01-01T08:00:00')
В качестве следствия этого изменения, мы больше не запрещаем преобразование между датами с единицами дат и датами с единицами времени. С датами без часового пояса правило преобразования из дат в время больше не является неоднозначным.
Различия между 1.6 и 1.7 Datetimes
Выпуск NumPy 1.6 включает более примитивный тип данных datetime, чем 1.7. Этот раздел документирует многие изменения, произошедшие.
Разбор строк
Парсер строк datetime в NumPy 1.6 очень либерален в том, что он принимает, и молча допускает недопустимые входные данные без подъёма ошибок. Парсер в NumPy 1.7 очень строг в отношении только принятия дат ISO 8601 с несколькими расширениями удобства. 1.6 всегда создаёт единицы измерения микросекунд (us) по умолчанию, тогда как 1.7 определяет единицу измерения на основе формата строки. Вот сравнение:
# NumPy 1.6.1
>>> np.datetime64('1979-03-22')
1979-03-22 00:00:00
# NumPy 1.7.0
>>> np.datetime64('1979-03-22')
numpy.datetime64('1979-03-22')
# NumPy 1.6.1, unit default microseconds
>>> np.datetime64('1979-03-22').dtype
dtype('datetime64[us]')
# NumPy 1.7.0, unit of days detected from string
>>> np.datetime64('1979-03-22').dtype
dtype('<M8[D]')
# NumPy 1.6.1, ignores invalid part of string
>>> np.datetime64('1979-03-2corruptedstring')
1979-03-02 00:00:00
# NumPy 1.7.0, raises error for invalid input
>>> np.datetime64('1979-03-2corruptedstring')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Error parsing datetime string "1979-03-2corruptedstring" at position 8
# NumPy 1.6.1, 'nat' produces today's date
>>> np.datetime64('nat')
2012-04-30 00:00:00
# NumPy 1.7.0, 'nat' produces not-a-time
>>> np.datetime64('nat')
numpy.datetime64('NaT')
# NumPy 1.6.1, 'garbage' produces today's date
>>> np.datetime64('garbage')
2012-04-30 00:00:00
# NumPy 1.7.0, 'garbage' raises an exception
>>> np.datetime64('garbage')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Error parsing datetime string "garbage" at position 0
# NumPy 1.6.1, can't specify unit in scalar constructor
>>> np.datetime64('1979-03-22T19:00', 'h')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function takes at most 1 argument (2 given)
# NumPy 1.7.0, unit in scalar constructor
>>> np.datetime64('1979-03-22T19:00', 'h')
numpy.datetime64('1979-03-22T19:00-0500','h')
# NumPy 1.6.1, reads ISO 8601 strings w/o TZ as UTC
>>> np.array(['1979-03-22T19:00'], dtype='M8[h]')
array([1979-03-22 19:00:00], dtype=datetime64[h])
# NumPy 1.7.0, reads ISO 8601 strings w/o TZ as local (ISO specifies this)
>>> np.array(['1979-03-22T19:00'], dtype='M8[h]')
array(['1979-03-22T19-0500'], dtype='datetime64[h]')
# NumPy 1.6.1, doesn't parse all ISO 8601 strings correctly
>>> np.array(['1979-03-22T12'], dtype='M8[h]')
array([1979-03-22 00:00:00], dtype=datetime64[h])
>>> np.array(['1979-03-22T12:00'], dtype='M8[h]')
array([1979-03-22 12:00:00], dtype=datetime64[h])
# NumPy 1.7.0, handles this case correctly
>>> np.array(['1979-03-22T12'], dtype='M8[h]')
array(['1979-03-22T12-0500'], dtype='datetime64[h]')
>>> np.array(['1979-03-22T12:00'], dtype='M8[h]')
array(['1979-03-22T12-0500'], dtype='datetime64[h]')
Преобразование единиц измерения
Реализация datetime в версии 1.6 не правильно преобразует единицы измерения:
# NumPy 1.6.1, the representation value is untouched
>>> np.array(['1979-03-22'], dtype='M8[D]')
array([1979-03-22 00:00:00], dtype=datetime64[D])
>>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]')
array([2250-08-01 00:00:00], dtype=datetime64[M])
# NumPy 1.7.0, the representation is scaled accordingly
>>> np.array(['1979-03-22'], dtype='M8[D]')
array(['1979-03-22'], dtype='datetime64[D]')
>>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]')
array(['1979-03'], dtype='datetime64[M]')
Арифметика дат
Реализация datetime в версии 1.6 правильно работает только для небольшого подмножества арифметических операций. Здесь показаны некоторые простые случаи:
# NumPy 1.6.1, produces invalid results if units are incompatible >>> a = np.array(['1979-03-22T12'], dtype='M8[h]') >>> b = np.array([3*60], dtype='m8[m]') >>> a + b array([1970-01-01 00:00:00.080988], dtype=datetime64[us]) # NumPy 1.7.0, promotes to higher-resolution unit >>> a = np.array(['1979-03-22T12'], dtype='M8[h]') >>> b = np.array([3*60], dtype='m8[m]') >>> a + b array(['1979-03-22T15:00-0500'], dtype='datetime64[m]') # NumPy 1.6.1, arithmetic works if everything is microseconds >>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]') >>> b = np.array([3*60*60*1000000], dtype='m8[us]') >>> a + b array([1979-03-22 15:00:00], dtype=datetime64[us]) # NumPy 1.7.0 >>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]') >>> b = np.array([3*60*60*1000000], dtype='m8[us]') >>> a + b array(['1979-03-22T15:00:00.000000-0500'], dtype='datetime64[us]')
© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.14.5/reference/arrays.datetime.html