2015-03-26 3 views
3

Мне нужно быстро преобразовать строку даты и времени ISO 8601 - без часовой пояс в строке, но, как известно, в часовом поясе США/Тихого океана - в numpy datetime64 объект.Быстро проанализировать дату и время Python в нелокальном часовом поясе, корректируя переход на летнее время

Если бы моя машина находилась в США/Тихоокеанском регионе, я мог бы просто запустить numpy.datetime64(s). Однако это предполагает, что строки без часовых поясов находятся в локальном часовом поясе. Кроме того, я не могу легко указать часовой пояс США/Тихого океана в формате ISO 8601, потому что иногда бывает -0800, а иногда -0700 в зависимости от летнего времени.

Пока что самое быстрое решение у меня есть numpy.datetime64(pandas.Timestamp(s).tz_localize(tz='US/Pacific', ambiguous=True)). Это занимает 70 мкс на моей машине. Было бы хорошо, если бы я мог получить это, по крайней мере, на порядок быстрее (numpy.datetime64(s) в местное время занимает 4 мкс, но неверно, как описано выше). Это возможно?

+2

Если производительность является проблемой, вы должны делать это миллионы раз, не так ли? Является ли смещение одинаковым каждый раз? Если это так, возможно, просто используйте 'numpy.datetime64 (s)' для всех из них, а затем используйте numpy-арифметику, чтобы сдвинуть все их на одну и ту же величину смещения за один падающий удар. – unutbu

+0

@unutbu: К сожалению, смещение не обязательно одно и то же: некоторые из дат во время периодов летнего времени, а некоторые из них - нет. Кроме того, похоже, что даже вычисление DST-корректировки за время-времени было бы подвержено ошибкам? –

+2

Я не уверен. Я не нашел более быстрый способ. Но в любом случае будьте осторожны: 'np.datetime64 (pd.Timestamp (s, tz = 'US/Pacific'))' может не дать желаемого результата. Рассмотрим строку даты и времени ISO 8601 ''2000-04-02T03: 00: 00-07: 00''. Без часовой пояс у вас будет 's = '2000-04-02T03: 00: 00''. Но 'np.datetime64 (pd.Timestamp (s, tz = 'US/Pacific'))' возвращает 'numpy.datetime64 ('2000-04-02T04: 00: 00.000000-0700')', поэтому это не round- правильное срабатывание. – unutbu

ответ

3

Прежде всего обратите внимание, что без смещения некоторые локальные моменты и, следовательно, их дата-время строки неоднозначны. Так, например, ISO 8601 DateTime строки

2000-10-29T01:00:00-07:00 
2000-10-29T01:00:00-08:00 

как карта для одной и той же строке 2000-10-29T01:00:00, когда смещение удаляется.

Так что не всегда возможно восстановить уникальную информацию о времени, отличном от часового пояса, datetime из строки datetime без смещения.

Однако мы могли бы сделать выбор в этих неопределенных ситуациях и принять, что не все двусмысленные даты будут правильно преобразованы.


Если вы используете Unix, вы можете использовать time.tzset изменить локальный часовой пояс процесса в:

import os 
import time 
os.environ['TZ'] = tz 
time.tzset() 

Затем можно преобразовать даты-строки в NumPy datetime64, используя

def using_tzset(date_strings, tz): 
    os.environ['TZ'] = tz 
    time.tzset() 
    return np.array(date_strings, dtype='datetime64[ns]') 

Обратите внимание, что using_tzset не всегда производит то же значение, что и метод, который вы предложили:

import os 
import time 
import numpy as np 
import pandas as pd 

tz = 'US/Pacific' 
N = 10**5 
dates = pd.date_range('2000-1-1', periods=N, freq='H', tz=tz) 
date_strings_tz = dates.format(formatter=lambda x: x.isoformat()) 
date_strings = [d.rsplit('-', 1)[0] for d in date_strings_tz] 

def orig(date_strings, tz): 
    return [np.datetime64(pd.Timestamp(s, tz=tz)) for s in date_strings] 

def using_tzset(date_strings, tz): 
    os.environ['TZ'] = tz 
    time.tzset() 
    return np.array(date_strings, dtype='datetime64[ns]') 

npdates = dates.asi8.view('datetime64[ns]') 
x = np.array(orig(date_strings, tz)) 
y = using_tzset(date_strings, tz) 
df = pd.DataFrame({'dates': npdates, 'str': date_strings_tz, 'orig': x, 'using_tzset': y}) 

Это указывает на то, что оригинальный метод, orig, не в состоянии восстановить первоначальную дату 172 раз:

print((df['dates'] != df['orig']).sum()) 
172 

пока using_tzset терпит неудачу в 11 раз:

print((df['dates'] != df['using_tzset']).sum()) 
11 

Обратите внимание, однако, что 11 раз, что using_tzset терпит неудачу, объясняются двусмысленностью локальных дат из-за DST.

Это показывает некоторые несоответствия:

mask = df['dates'] != df['using_tzset'] 
idx = np.where(mask.shift(1) | mask)[0] 
print(df[['dates', 'str', 'using_tzset']].iloc[idx]).head(6) 

#      dates      str   using_tzset 
# 7248 2000-10-29 08:00:00 2000-10-29T01:00:00-07:00 2000-10-29 08:00:00 
# 7249 2000-10-29 09:00:00 2000-10-29T01:00:00-08:00 2000-10-29 08:00:00 
# 15984 2001-10-28 08:00:00 2001-10-28T01:00:00-07:00 2001-10-28 08:00:00 
# 15985 2001-10-28 09:00:00 2001-10-28T01:00:00-08:00 2001-10-28 08:00:00 
# 24720 2002-10-27 08:00:00 2002-10-27T01:00:00-07:00 2002-10-27 08:00:00 
# 24721 2002-10-27 09:00:00 2002-10-27T01:00:00-08:00 2002-10-27 08:00:00 

Как вы можете увидеть несоответствия возникают, когда дата строка в столбце str становится неоднозначной, когда смещение удаляются.


Вот timeit тест сравнения orig и using_tzset:

In [95]: %timeit orig(date_strings, tz) 
1 loops, best of 3: 5.43 s per loop 

In [96]: %timeit using_tzset(date_strings, tz) 
10 loops, best of 3: 41.7 ms per loop 

Так using_tzset закончилась 100x быстрее, чем orig при N = 10 ** 5.

+0

Удивительный! Я не понимал, что можно установить часовой пояс, и Numpy поднимет его. Вы сказали «местный часовой пояс машины», но это фактически часовой пояс * процесса *, верно? Он не будет мешать другим вещам на машине? –

+0

Да; спасибо за исправление. 'tzset' изменяет понятие процесса локального часового пояса. – unutbu

+0

Awesome. Похоже, это так, как мы пойдем, пока не получится дать numpy языку для разбора. –

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

  • Нет связанных вопросов^_^