पांडा टाइमज़ोन-जागरूक DateTimeIndex को भोले टाइमस्टैम्प में परिवर्तित करें, लेकिन निश्चित समय क्षेत्र में


99

आप tz_localizeटाइमस्टैम्प या डेटाइम टाइमेक्स टाइमजोन को अवगत कराने के लिए फ़ंक्शन का उपयोग कर सकते हैं , लेकिन आप इसके विपरीत कैसे कर सकते हैं: आप अपने टाइमज़ोन को संरक्षित करते हुए टाइमस्टैम्प जागरूक टाइमस्टैम्प को भोले में कैसे बदल सकते हैं?

एक उदाहरण:

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

मैं किसी को भी इसे सेट करके टाइमज़ोन को निकाल सकता था, लेकिन फिर परिणाम यूटीसी (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 को समय-समय पर भोले में परिवर्तित कर सकता हूं, लेकिन समय-सीमा को संरक्षित करते हुए इसे सेट किया गया था?


कारण के बारे में कुछ संदर्भ मैं यह पूछ रहा हूं: मैं टाइमज़ोन भोले समय के साथ काम करना चाहता हूं (टाइमज़ोन के साथ अतिरिक्त परेशानी से बचने के लिए, और मुझे उस मामले की आवश्यकता नहीं है जो मैं काम कर रहा हूं)।
लेकिन किसी कारण से, मुझे अपने स्थानीय समयक्षेत्र (यूरोप / ब्रुसेल्स) में एक टाइमज़ोन-जागरूक समय से निपटना पड़ता है। जैसा कि मेरे सभी अन्य डेटा टाइमज़ोन भोले हैं (लेकिन मेरे स्थानीय समयक्षेत्र में दर्शाए गए हैं), मैं इस समय को इसके साथ आगे काम करने के लिए भोली में बदलना चाहता हूं, लेकिन इसे मेरे स्थानीय समय क्षेत्र में भी प्रस्तुत किया जाना है (इसलिए सिर्फ टाइमज़ोन जानकारी निकालें) उपयोगकर्ता-दृश्यमान समय को यूटीसी में परिवर्तित किए बिना )।

मुझे पता है कि समय वास्तव में यूटीसी के रूप में आंतरिक रूप से संग्रहीत होता है और जब आप इसका प्रतिनिधित्व करते हैं तो केवल एक और टाइमज़ोन में परिवर्तित हो जाता है, इसलिए जब मुझे इसे "delocalize" करना होता है तो किसी प्रकार का रूपांतरण होना चाहिए। उदाहरण के लिए, अजगर डेटाइम मॉड्यूल के साथ आप टाइमजोन को इस तरह "हटा" सकते हैं:

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

टाइमजोन = कोई भी यूटीसी का मतलब नहीं है ... मुझे यकीन नहीं है कि मैं समझता हूं कि आप यहां क्या पूछ रहे हैं।
एंडी हेडन

मैंने कुछ स्पष्टीकरण जोड़ा। मैं समय को एक उपयोगकर्ता के रूप में 'देखना' रखना चाहता हूं। मुझे उम्मीद है कि यह इसे थोड़ा स्पष्ट करता है।
जोरिस

आह हा, यह करता है, मुझे नहीं पता था कि आप ऐसा कर सकते थे replace
एंडी हेडन

@AndyHayden तो वास्तव में मैं जो चाहता हूं उसका सटीक उलटा है, tz_localizeजो डेटाटाइम्स के लिए replace(tzinfo=None)करता है, लेकिन यह वास्तव में बहुत स्पष्ट तरीका नहीं है।
जॉरिस

जवाबों:


123

मेरे स्वयं के प्रश्न का उत्तर देने के लिए, इस कार्यक्षमता को इस बीच पांडा में जोड़ा गया है। पांडा 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

1
मामले में आप कुछ पहले से ही यूटीसी है कि और यह स्थानीय समय में कनवर्ट और करने की आवश्यकता के साथ काम कर रहे हैं तो : समय क्षेत्र छोड़ from tzlocal import get_localzone, tz_here = get_localzone(),<datetime object>.tz_convert(tz_here).tz_localize(None)
नाथन लॉयड

3
यदि आपके पास एक उपयोगी इंडेक्स नहीं है, तो आपको आवश्यकता हो सकती है t.dt.tz_localize(None)या t.dt.tz_convert(None)। ध्यान दें .dt
एक्यूमेनस

2
यह समाधान केवल तभी काम करता है जब श्रृंखला में एक अद्वितीय tz हो। यदि आपके पास एक ही श्रृंखला में कई अलग-अलग tz हैं, तो यहां देखें (और upvote) समाधान :-): stackoverflow.com/a/59204751/1054154
tozCSS

14

मुझे लगता है कि आप जो प्रस्तावित करते हैं उससे अधिक कुशल तरीके से आप जो चाहते हैं उसे प्राप्त नहीं कर सकते।

अंतर्निहित समस्या यह है कि टाइमस्टैम्प (जैसा कि आप जानते हैं) दो भागों से बने होते हैं। वह डेटा जो UTC समय और समयक्षेत्र tz_info का प्रतिनिधित्व करता है। समयक्षेत्र की जानकारी का उपयोग केवल प्रदर्शन उद्देश्यों के लिए किया जाता है जब स्क्रीन पर टाइमज़ोन को प्रिंट किया जाता है। प्रदर्शन के समय, डेटा को उचित रूप से ऑफसेट किया जाता है और स्ट्रिंग में +01: 00 (या समान) जोड़ा जाता है। Tz_info मान को अलग करना (tz_convert (tz = none) का उपयोग करके) वास्तव में डेटा को परिवर्तित नहीं करता है जो टाइमस्टैम्प के भोले हिस्से का प्रतिनिधित्व करता है।

तो, जो आप करना चाहते हैं उसका एकमात्र तरीका अंतर्निहित डेटा को संशोधित करना है (पांडा यह अनुमति नहीं देता है ... DatetimeIndex अपरिवर्तनीय हैं - DatetimeIndex पर मदद देखें), या टाइमस्टैम्प ऑब्जेक्ट का एक नया सेट बनाने और उन्हें लपेटने के लिए। एक नए DatetimeIndex में। आपका समाधान बाद में करता है:

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

संदर्भ के लिए, यहां (tslib.pyx देखें) की replaceविधि है Timestamp:

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

आप डॉक्स को संदर्भित कर सकते हैं यह datetime.datetimeदेखने के लिए कि datetime.datetime.replaceएक नई वस्तु भी बनाता है।

यदि आप कर सकते हैं, तो दक्षता के लिए आपकी सबसे अच्छी शर्त डेटा के स्रोत को संशोधित करना है ताकि यह (गलत तरीके से) टाइमस्टैम्प को बिना किसी समय के रिपोर्ट कर सके। आपने उल्लिखित किया था:

मैं टाइमज़ोन भोले समय के साथ काम करना चाहता हूं (टाइमज़ोन के साथ अतिरिक्त परेशानी से बचने के लिए, और मुझे उस मामले की आवश्यकता नहीं है जिस पर मैं काम कर रहा हूं)

मैं उत्सुक हूँ कि आप किस अतिरिक्त परेशानी का उल्लेख कर रहे हैं। मैं सभी सॉफ्टवेयर विकास के लिए एक सामान्य नियम के रूप में सलाह देता हूं, अपने टाइमस्टैम्प 'भोले मूल्यों' को यूटीसी में रखें। दो अलग-अलग int64 मूल्यों को देखकर थोड़ा बुरा लगता है कि वे किस समयक्षेत्र के हैं। यदि आप हमेशा, हमेशा, हमेशा आंतरिक भंडारण के लिए यूटीसी का उपयोग करते हैं, तो आप अनगिनत सिरदर्द से बचेंगे। मेरा मंत्र टाइमज़ोन मानव I / O के लिए ही है


3
उत्तर के लिए धन्यवाद, और एक देर से जवाब: मेरा मामला एक आवेदन नहीं है, बस मेरे अपने काम के लिए एक वैज्ञानिक विश्लेषण है (इसलिए जैसे दुनिया भर में सहयोगियों के साथ कोई साझा नहीं)। और उस स्थिति में, भोले टाइमस्टैम्प के साथ काम करना आसान हो सकता है, लेकिन आपके स्थानीय समय में। इसलिए मुझे समय क्षेत्रों के बारे में चिंता करने की ज़रूरत नहीं है और बस टाइमस्टैम्प को स्थानीय समय के रूप में व्याख्या कर सकते हैं (अतिरिक्त 'परेशानी' उदाहरण के लिए हो सकता है कि सब कुछ तब टाइमज़ोन में होना चाहिए, अन्यथा आपको "ऑफसेट की तुलना नहीं कर सकते" जैसी चीजें मिल सकती हैं। अनुभवहीन और ऑफसेट-अवगत डेटाटाइम्स ")। लेकिन अधिक जटिल अनुप्रयोगों से निपटने पर मैं आपसे पूरी तरह सहमत हूं।
जोरिस

12

क्योंकि मैं हमेशा याद रखने के लिए संघर्ष करता हूं, इनमें से प्रत्येक का एक त्वरित सारांश:

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

7

tzसूचकांक की विशेषता को स्पष्ट रूप से काम करना लगता है:

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

3
देर से टिप्पणी, लेकिन मैं चाहता हूं कि परिणाम स्थानीय समय क्षेत्र में प्रतिनिधित्व किया जाए, यूटीसी में नहीं। और जैसा कि मैंने प्रश्न में दिखाया है, किसी tzको भी सेट करना भी इसे यूटीसी में परिवर्तित नहीं करता है।
जॉरिस

इसके अलावा, समय-समय पर पहले से ही समय क्षेत्र के बारे में पता है, इसलिए इस tz_convertपर कॉल करने से त्रुटि पैदा होगी।
जॉरिस

4

किसी श्रृंखला में कई अलग-अलग टाइमज़ोन होने पर स्वीकृत समाधान काम नहीं करता है। यह फेंकता हैValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True

इसका उपाय applyविधि का उपयोग करना है।

कृपया नीचे दिए गए उदाहरण देखें:

# Let's have a series `a` with different multiple timezones. 
> a
0    2019-10-04 16:30:00+02:00
1    2019-10-07 16:00:00-04:00
2    2019-09-24 08:30:00-07:00
Name: localized, dtype: object

> a.iloc[0]
Timestamp('2019-10-04 16:30:00+0200', tz='Europe/Amsterdam')

# trying the accepted solution
> a.dt.tz_localize(None)
ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True

# Make it tz-naive. This is the solution:
> a.apply(lambda x:x.tz_localize(None))
0   2019-10-04 16:30:00
1   2019-10-07 16:00:00
2   2019-09-24 08:30:00
Name: localized, dtype: datetime64[ns]

# a.tz_convert() also does not work with multiple timezones, but this works:
> a.apply(lambda x:x.tz_convert('America/Los_Angeles'))
0   2019-10-04 07:30:00-07:00
1   2019-10-07 13:00:00-07:00
2   2019-09-24 08:30:00-07:00
Name: localized, dtype: datetime64[ns, America/Los_Angeles]

3

डीए के सुझाव पर निर्माण कि " जो आप चाहते हैं वह करने के लिए एकमात्र तरीका अंतर्निहित डेटा को संशोधित करना है " और अंतर्निहित डेटा को संशोधित करने के लिए 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)

आपके उत्तर के लिए धन्यवाद! हालाँकि, मुझे लगता है कि यह तभी काम करेगा जब डेटासेट की अवधि में कोई समरटाइम / विंटरटाइम संक्रमण न हो।
जॉरिस

@ जोरिस आह, अच्छी पकड़! मैंने ऐसा नहीं माना था! मैं ASAP की इस स्थिति को संभालने के लिए अपने समाधान को संशोधित करूँगा।
जैक केली

मेरा मानना ​​है कि यह अभी भी गलत है क्योंकि आप केवल पहली बार ऑफसेट की गणना कर रहे हैं, न कि यह पूरे समय में प्रगति के रूप में। यह आपको दिन के समय की बचत के समय को याद करने और उस दी गई तारीख के अनुसार समायोजित न करने का कारण बनेगा।
पियरे-ल्यूक बर्ट्रेंड

2

देर से योगदान लेकिन सिर्फ पायथन डेटटाइम में कुछ समान आया और पांडा एक ही तारीख के लिए अलग-अलग टाइमस्टैम्प देते हैं

यदि आपके पास तकनीकी रूप से टाइमज़ोन-अवगत डेटाटाइम है pandas, तो POSIX टाइमस्टैम्प को बदल देता है (जो आंतरिक रूप से उपयोग किया जाता है) जैसे कि टाइमस्टैम्प से स्थानीय समय UTC था। इस संदर्भ में स्थानीय का अर्थ है निर्दिष्ट समय क्षेत्र में स्थानीय । उदाहरण के लिए:tz_localize(None)

import pandas as pd

t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H', tz="US/Central")
# DatetimeIndex(['2013-05-18 12:00:00-05:00', '2013-05-18 13:00:00-05:00'], dtype='datetime64[ns, US/Central]', freq='H')

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

# offset in seconds according to timezone:
(t_loc.values-t.values)//1e9
# array([-18000, -18000], dtype='timedelta64[ns]')

ध्यान दें कि यह आपको डीएसटी संक्रमण के दौरान अजीब चीजों के साथ छोड़ देगा , जैसे

t = pd.date_range(start="2020-03-08 01:00:00", periods=2, freq='H', tz="US/Central")
(t.values[1]-t.values[0])//1e9
# numpy.timedelta64(3600,'ns')

t_loc = t.tz_localize(None)
(t_loc.values[1]-t_loc.values[0])//1e9
# numpy.timedelta64(7200,'ns')

इसके विपरीत, tz_convert(None)आंतरिक टाइमस्टैम्प को संशोधित नहीं करता है, यह बस को हटा देता है tzinfo

t_utc = t.tz_convert(None)
(t_utc.values-t.values)//1e9
# array([0, 0], dtype='timedelta64[ns]')

मेरी निचली रेखा यह होगी: यदि आप या केवल उपयोग कर सकते हैं t.tz_convert(None)जो अंतर्निहित POSIX टाइमस्टैम्प को संशोधित नहीं करता है तो टाइमज़ोन-अवगत डेटाटाइम के साथ रहें । बस ध्यान रखें कि आप व्यावहारिक रूप से यूटीसी के साथ काम कर रहे हैं।

(विंडोज 10, pandasv1.0.5 पर पायथन 3.8.2 x64 ।)


0

सबसे महत्वपूर्ण बात यह है कि tzinfoजब आप एक डेटाइम ऑब्जेक्ट परिभाषित करते हैं।

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())
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.