Даты и временные интервалы
Новая версия 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')
Использование UTC «Zulu» времени:
>>> np.datetime64('2005-02-25T03:30Z')
numpy.datetime64('2005-02-24T21:30-0600')
ISO 8601 определяет использование местного часового пояса, если он не указан явно:
>>> np.datetime64('2005-02-25T03:30')
numpy.datetime64('2005-02-25T03:30-0600')
При создании массива дат и времени из строки все еще можно автоматически выбрать единицу из входных данных, используя тип 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
Важное исключение из этого правила — между datetime с единицами дат и datetime с единицами времени. Это связано с тем, что для такого преобразования обычно требуется выбор часового пояса и конкретного времени суток в заданную дату.
Пример
>>> np.datetime64('2003-12-25', 's')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cannot parse "2003-12-25" as unit 's' using casting rule 'same_kind'
>>> np.datetime64('2003-12-25') == np.datetime64('2003-12-25T00Z')
False
Арифметика дат и временных интервалов
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
Существует две единицы 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 time (хотя предлагается режим 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,9e12 лет | [2,9e9 до н. э., 2,9e9 н. э.] |
| ms | миллисекунда | +/- 2,9e9 лет | [2,9e6 до н. э., 2,9e6 н. э.] |
| us | микросекунда | +/- 2,9e6 лет | [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"
Различия между 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
Реализация 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–2016 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.10.1/reference/arrays.datetime.html