Spec-Zone.ru › NumPy 1.13

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

Новое в версии 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')

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

>>> 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 датами и временем

Релиз 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]')

© 2008–2017 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.datetime.html

Spec-Zone.ru

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