Spec-Zone.ru › NumPy 1.16

Даты и временные интервалы

Новое в версии 1.7.0.

Начиная с NumPy 1.7, существуют основные типы данных массивов, которые изначально поддерживают функциональность дат и времени. Тип данных называется “datetime64”, так как “datetime” уже используется в библиотеке datetime, включённой в Python.

Примечание

API datetime в версии 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')

Указание только месяца, но принудительное использование единицы ‘days’:

>>> 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 представляет один момент во времени. Если у двух 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 позволяет вычитать две даты datetime, операция, которая генерирует число с единицей измерения времени. Поскольку 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
>>> np.timedelta64(1,'W') % np.timedelta64(10,'D')
numpy.timedelta64(7,'D')

Существует две единицы измерения Timedelta (‘Y’, годы и ‘M’, месяцы), которые обрабатываются особым образом, потому что то, сколько времени они представляют, зависит от того, где они используются. В то время как единица измерения дней timedelta эквивалентна 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

Вот несколько примеров значений custom 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. Однако полученное datetime не учитывает часовой пояс:

>>> 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')

В качестве следствия этого изменения, мы больше не запрещаем преобразование между датами с единицами дат и датами с единицами времени. При использовании datetime без учёта часового пояса правило для преобразования из дат в время больше не является неоднозначным.

Различия между 1.6 и 1.7 датами и временем

Выпуск 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.16.1/reference/arrays.datetime.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API