Подтвердить что ты не робот

Преобразовать pandas с учетом времени DateTimeIndex в наивную временную метку, но в определенный часовой пояс

Вы можете использовать функцию tz_localize, чтобы узнать временную зону Timestamp или DateTimeIndex, но как вы можете сделать обратное: как вы можете преобразовать временную метку времени в наивную, сохраняя ее часовой пояс?

Пример:

In [82]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10, freq='s', tz="Europe/Brussels")

In [83]: t
Out[83]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels

Я могу удалить часовой пояс, установив его в None, но затем результат преобразуется в UTC (12 часов становилось 10):

In [86]: t.tz = None

In [87]: t
Out[87]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 10:00:00, ..., 2013-05-18 10:00:09]
Length: 10, Freq: S, Timezone: None

Есть ли другой способ преобразовать DateTimeIndex в наименьший часовой пояс, но сохраняя часовой пояс, он был установлен в?


Некоторый контекст по причине, о которой я спрашиваю: я хочу работать с часовыми наивными таймингами (чтобы избежать лишних хлопот с часовыми поясами, и мне не нужны они для случая, над которым я работаю).
Но по какой-то причине мне приходится иметь дело с таймсерами с учетом часовых поясов в моем местном часовом поясе (Европа/Брюссель). Поскольку все мои другие данные являются наименее часовыми (но представленными в моем местном часовом поясе), я хочу преобразовать эти таймеры в наивную для дальнейшей работы с ней, но ее также нужно представлять в моем локальном часовом поясе (так что просто удаляйте информацию о часовом поясе, без преобразования видимого пользователем времени в UTC).

Я знаю, что время фактически внутренне хранится как UTC и только преобразуется в другой часовой пояс, когда вы его представляете, поэтому должно быть какое-то преобразование, когда я хочу "делокализовать" его. Например, с модулем datetime python вы можете "удалить" часовой пояс следующим образом:

In [119]: d = pd.Timestamp("2013-05-18 12:00:00", tz="Europe/Brussels")

In [120]: d
Out[120]: <Timestamp: 2013-05-18 12:00:00+0200 CEST, tz=Europe/Brussels>

In [121]: d.replace(tzinfo=None)
Out[121]: <Timestamp: 2013-05-18 12:00:00> 

Итак, основываясь на этом, я мог бы сделать следующее, но я полагаю, что это будет не очень эффективно при работе с более крупными серверами времени:

In [124]: t
Out[124]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels

In [125]: pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Out[125]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: None, Timezone: None
4b9b3361

Ответ 1

Чтобы ответить на мой собственный вопрос, эта функциональность была добавлена ​​к pandas тем временем. Начиная с от pandas 0.15.0, вы можете использовать tz_localize(None) для удаления часового пояса, приводящего к местному времени.
См. Whatsnew запись: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements

Итак, мой пример сверху:

In [4]: t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H',
                          tz= "Europe/Brussels")

In [5]: t
Out[5]: DatetimeIndex(['2013-05-18 12:00:00+02:00', '2013-05-18 13:00:00+02:00'],
                       dtype='datetime64[ns, Europe/Brussels]', freq='H')

с помощью tz_localize(None) удаляет информацию о часовом поясе, в результате чего наивное локальное время:

In [6]: t.tz_localize(None)
Out[6]: DatetimeIndex(['2013-05-18 12:00:00', '2013-05-18 13:00:00'], 
                      dtype='datetime64[ns]', freq='H')

Кроме того, вы можете также использовать tz_convert(None) для удаления информации о часовом поясе, но конвертирования в UTC, что дает наивное время UTC:

In [7]: t.tz_convert(None)
Out[7]: DatetimeIndex(['2013-05-18 10:00:00', '2013-05-18 11:00:00'], 
                      dtype='datetime64[ns]', freq='H')

Это намного более результативно, чем решение datetime.replace:

In [31]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10000, freq='H',
                           tz="Europe/Brussels")

In [32]: %timeit t.tz_localize(None)
1000 loops, best of 3: 233 µs per loop

In [33]: %timeit pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
10 loops, best of 3: 99.7 ms per loop

Ответ 2

Я думаю, что вы не можете достичь того, чего хотите, более эффективным способом, чем вы предлагали.

Основная проблема заключается в том, что временные метки (как вам кажется) состоят из двух частей. Данные, представляющие время UTC, и часовой пояс tz_info. Информация о часовом поясе используется только для показа при печати временной шкалы на экране. Во время отображения данные смещаются соответственно, а в строку добавляется +01: 00 (или аналогичный). Снятие значения tz_info (с использованием tz_convert (tz = None)) фактически не изменяет данные, представляющие наивную часть метки времени.

Итак, единственный способ сделать то, что вы хотите, - изменить базовые данные (pandas не позволяет это... DatetimeIndex неизменяемы - см. справку по DatetimeIndex) или создать новый набор временные объекты и обернуть их в новый DatetimeIndex. Ваше решение делает последнее:

pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])

Для справки: replace метод Timestamp (см. tslib.pyx):

def replace(self, **kwds):
    return Timestamp(datetime.replace(self, **kwds),
                     offset=self.offset)

Вы можете обратиться к документам на datetime.datetime, чтобы увидеть, что datetime.datetime.replace также создает новый объект.

Если вы можете, лучшим вариантом для эффективности является изменение источника данных, чтобы он (некорректно) сообщал отметки времени без их часового пояса. Вы упомянули:

Я хочу работать с наименьшими тайм-зонами с часовым поясом (чтобы избежать лишних хлопот с часовыми поясами, и я не нуждаюсь в них для случая, над которым я работаю)

Мне было бы любопытно, что лишние хлопоты вы имеете в виду. Я рекомендую в качестве общего правила для всей разработки программного обеспечения, сохраняйте ваши наивные значения timestamp в UTC. Существует немного хуже, чем смотреть на два разных значения int64, интересующихся, к какому временному диапазону они относятся. Если вы всегда, всегда, всегда используете UTC для внутреннего хранилища, тогда вы избежите бесчисленных головных болей. Моя мантра Часовые пояса предназначены только для ввода/вывода людей.

Ответ 3

Настройка атрибута tz индекса явно работает:

ts_utc = ts.tz_convert("UTC")
ts_utc.index.tz = None

Ответ 4

Основываясь на D.A. что "единственный способ сделать то, что вы хотите, - изменить базовые данные" и использовать numpy для изменения базовых данных...

Это работает для меня и довольно быстро:

def tz_to_naive(datetime_index):
    """Converts a tz-aware DatetimeIndex into a tz-naive DatetimeIndex,
    effectively baking the timezone into the internal representation.

    Parameters
    ----------
    datetime_index : pandas.DatetimeIndex, tz-aware

    Returns
    -------
    pandas.DatetimeIndex, tz-naive
    """
    # Calculate timezone offset relative to UTC
    timestamp = datetime_index[0]
    tz_offset = (timestamp.replace(tzinfo=None) - 
                 timestamp.tz_convert('UTC').replace(tzinfo=None))
    tz_offset_td64 = np.timedelta64(tz_offset)

    # Now convert to naive DatetimeIndex
    return pd.DatetimeIndex(datetime_index.values + tz_offset_td64)

Ответ 5

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

>>> pd.Timestamp.now()  # naive local time
Timestamp('2019-10-07 10:30:19.428748')

>>> pd.Timestamp.utcnow()  # tz aware UTC
Timestamp('2019-10-07 08:30:19.428748+0000', tz='UTC')

>>> pd.Timestamp.now(tz='Europe/Brussels')  # tz aware local time
Timestamp('2019-10-07 10:30:19.428748+0200', tz='Europe/Brussels')

>>> pd.Timestamp.now(tz='Europe/Brussels').tz_localize(None)  # naive local time
Timestamp('2019-10-07 10:30:19.428748')

>>> pd.Timestamp.now(tz='Europe/Brussels').tz_convert(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')

>>> pd.Timestamp.utcnow().tz_localize(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')

>>> pd.Timestamp.utcnow().tz_convert(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')

Ответ 6

Самое важное - добавить tzinfo при определении объекта datetime.

from datetime import datetime, timezone
from tzinfo_examples import HOUR, Eastern
u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc)
for i in range(4):
     u = u0 + i*HOUR
     t = u.astimezone(Eastern)
     print(u.time(), 'UTC =', t.time(), t.tzname())