時(shí)間數(shù)據(jù)
時(shí)間格式是數(shù)據(jù)類型中基礎(chǔ)也不容忽視的一類鸽斟。不像整數(shù)那樣大道至簡(jiǎn)也不像字符串那樣包羅萬象,卻獨(dú)有魅力利诺,時(shí)間數(shù)據(jù)本身除了加減富蓄、比較運(yùn)算外,也有下周慢逾、去年立倍、時(shí)區(qū)等更專項(xiàng)的時(shí)間切換灭红。在各類編程語言里都提供時(shí)間對(duì)象的支持,在MySQL里也有DATETIME類型口注。商業(yè)里的DAU变擒、GMV、LTV也少不了時(shí)間限定和時(shí)間屬性寝志,因此數(shù)據(jù)分析時(shí)少不了對(duì)時(shí)間數(shù)據(jù)類型的處理與轉(zhuǎn)換娇斑。
Python通過套件time、datetime材部、timeit處理時(shí)間類型數(shù)據(jù)毫缆,但面對(duì)一些情況時(shí)會(huì)不夠靈活和易用,在時(shí)間序列生成和截?cái)喾矫孀浇笠娭饫值迹谑钦Q生了Arrow苦丁、Pendulum、Maya等庫增強(qiáng)了Python的時(shí)間處理能力物臂。本篇對(duì)4個(gè)標(biāo)準(zhǔn)庫和6大第三方模塊進(jìn)行介紹旺拉,在面對(duì)需求時(shí)能拿到最趁手的工具。
模塊概覽
在Python中進(jìn)行時(shí)間類型數(shù)據(jù)處理能用到的模塊有:
- time:Python內(nèi)置時(shí)間庫棵磷,通過時(shí)間戳或元組表示時(shí)間蛾狗;
- datetime:內(nèi)置日期庫,處理日期時(shí)間對(duì)象和屬性泽本;
- dateutil:基于datetime庫的實(shí)用拓展淘太,增強(qiáng)了對(duì)時(shí)間間隔和時(shí)間序列的處理姻僧;
- pd.Timestamp:pandas庫用于時(shí)間處理的類规丽;
- Arrow:優(yōu)秀的Python時(shí)間庫,簡(jiǎn)化了時(shí)間類型數(shù)據(jù)的解析和輸出撇贺;
- Pendulum:可以和Arrow對(duì)標(biāo)的時(shí)間處理庫赌莺,pendulum意為鐘擺;
- Delorean:在dateutil基礎(chǔ)上進(jìn)一步拓展的時(shí)間庫松嘶,以《回到未來》中的時(shí)間旅行車命名艘狭;
- moment:靈感來源于Moment.js,目前相對(duì)原始翠订;
- Maya:和Arrow等庫對(duì)標(biāo)巢音,增強(qiáng)了對(duì)時(shí)區(qū)的處理,有調(diào)用pendulum的部分功能尽超;
在深入這些庫的使用之前官撼,先補(bǔ)充一些先驗(yàn)知識(shí):
epoch:時(shí)間基準(zhǔn)點(diǎn)至特定時(shí)間的總秒數(shù),一般用一個(gè)浮點(diǎn)數(shù)值記錄似谁,這個(gè)基準(zhǔn)點(diǎn)在Unix及類Unix系統(tǒng)中是格林威治時(shí)間1970年01月01日00時(shí)0分0秒傲绣,因此也稱為Unix時(shí)間戳(Timestamp)掠哥。因?yàn)榈厍蚴且粋€(gè)橢球體,當(dāng)英國是中午時(shí)中國北京已經(jīng)在吃晚飯了秃诵,不同經(jīng)度地區(qū)的0點(diǎn)相對(duì)于格林威治的0點(diǎn)有一個(gè)時(shí)差续搀,也就有時(shí)區(qū)(timezone)的區(qū)分,以UTC(世界協(xié)調(diào)時(shí))作為基準(zhǔn)菠净,中國采用的東八區(qū)就可表示為UTC+8禁舷,對(duì)應(yīng)北京時(shí)間減8個(gè)小時(shí)就是UTC時(shí)間。
基于以上需要考慮的問題毅往,在時(shí)間類中榛了,表示一個(gè)時(shí)間有兩種基本選擇:
一是用浮點(diǎn)數(shù)記錄一個(gè)時(shí)間戳epoch,時(shí)間小于1970年則是負(fù)數(shù)煞抬,二是用元組或字典記錄年月日時(shí)分秒時(shí)區(qū)等霜大,在Python的time模塊就是記錄了epoch和一個(gè)元組叫struct_time,這兩者之間可以互相轉(zhuǎn)換革答。
模塊特性與實(shí)踐
time&datetime
time是Python內(nèi)置的時(shí)間庫战坤,功能簡(jiǎn)約但實(shí)用,通常和同為內(nèi)置庫的datetime残拐、pytz及calendar互相配合解決各類時(shí)間表示途茫、計(jì)算、輸出等需求溪食。
time的常用方法有:
- time.time():得到當(dāng)前時(shí)間戳Timestamp囊卜,是一個(gè)浮點(diǎn)數(shù);
- time.localtime([secs]):將一個(gè)時(shí)間戳轉(zhuǎn)換為當(dāng)前時(shí)區(qū)的struct_time错沃。secs參數(shù)未提供栅组,則以當(dāng)前時(shí)間為準(zhǔn),相當(dāng)于獲取當(dāng)前時(shí)間now()枢析;
- time.gmtime(ts):時(shí)間戳轉(zhuǎn)struct_time玉掸;struct_time是一個(gè)包含了9個(gè)元素的元組,對(duì)應(yīng)著改時(shí)間對(duì)象的年月日醒叁、本年第幾天等屬性司浪;
- time.mktime(t):struct_time轉(zhuǎn)時(shí)間戳;
- time.strftime("%Y-%m-%d",t):struct_time轉(zhuǎn)格式化字符串把沼;
- time.strptime('2020-12-7',"%Y-%m-%d"):字符串轉(zhuǎn)struct_time啊易;
import time
time.time() #type(time.time())==float
#Out[]:1607319973.764
time.localtime()
# time.struct_time(tm_year=2020, tm_mon=12, tm_mday=7, tm_hour=13, tm_min=46, tm_sec=13, tm_wday=0, tm_yday=342, tm_isdst=0)
st=time.gmtime(time.time())
st.tm_year #獲取屬性,st是元組饮睬,不能修改
# 2020
基于time模塊生成的時(shí)間對(duì)象t租谈,如果是時(shí)間戳形式表示的,是不能直接得到t是在哪一年等屬性的续捂,需要先轉(zhuǎn)struct_time形式垦垂,然后就可以寫st.tm_year獲取所在年宦搬。st是元組,不能修改劫拗,即不能用st.tm_year=2019來修改的st的實(shí)際值间校。
t=time.strptime('2020-12-7 13:52:15',"%Y-%m-%d %H:%M:%S")
# time.struct_time(tm_year=2020, tm_mon=12,...)
time.strftime("%Y-%m-%d %H:%M:%S",t)
# 2020-12-7 13:52:15
從文件中讀取數(shù)據(jù)時(shí)常需要從字符串形式變成時(shí)間對(duì)象,就會(huì)用到strptime页慷,是string parse time的簡(jiǎn)寫憔足,即從字符串?dāng)?shù)據(jù)類型中解析成時(shí)間類型。strftime是把時(shí)間類型格式化為字符串酒繁,是strptime的逆操作滓彰,f是format的縮寫。
時(shí)間類型格式化有一套特定的占位符州袒,下面介紹的符號(hào)在其他時(shí)間模塊里也通用揭绑,因此常用的占位符還是需要心里有數(shù)才能靈活“組裝”出自己需要的字符串效果的。下面表格列出了常用的時(shí)間格式化占位符郎哭,更全面的表可查閱time模塊文檔他匪。
time模塊常和datetime模塊組合使用,time側(cè)重在時(shí)間夸研,datetime在日期方面方法更豐富邦蜜,且datetime會(huì)和pytz及calendar配合處理時(shí)間對(duì)象。
在datetime里也有strftime和strptime亥至,不過需要注意的是悼沈,兩個(gè)庫輸入?yún)?shù)順序的區(qū)別,datetime的strftime姐扮,格式化字符串在后絮供,代碼實(shí)例如下。
from datetime import datetime
dt=datetime.strptime('2020-12-7 13:52:15',"%Y-%m-%d %H:%M:%S")
datetime.strftime(dt,"%Y-%m-%d %H:%M:%S") #
# 2020-12-7 13:52:15
time.strftime("%Y-%m-%d %H:%M:%S",t)
# datetime庫內(nèi)部也是調(diào)用time的striptime
# datetime.strftime -> _wrap_strftime ->_time.strftime
在datetime中新建時(shí)間對(duì)象可以直接使用datetime(y, m,d,tzinfo)
輸入?yún)?shù)溶握,用datetime.now()
獲得當(dāng)前時(shí)間杯缺,通過datetime.fromtimestamp(ts)
可以將時(shí)間戳ts轉(zhuǎn)為時(shí)間對(duì)象,生成的datetime時(shí)間對(duì)象在獲取屬性時(shí)用到的語句類似dt.year
睡榆,有year/month/day/hour/second/tzinfo等可以用。tzinfo是時(shí)區(qū)屬性袍榆,datetime在時(shí)區(qū)相關(guān)處理時(shí)通常用到pytz胀屿。
import pytz
sh=pytz.timezone('Asia/Shanghai') #新建一個(gè)時(shí)區(qū)
dt=datetime(2020,12,7,hour=8,tzinfo=sh)
datetime.fromtimestamp(time.time())
#datetime.datetime(2020,12,8,16,59,42,797401)
dt.year #返回給定datetime對(duì)象的年份
#Out[]: 2020
#屬性有.hour .minute .second .microsecond 等
datetime.weekday() #返回星期幾,星期一為 0包雀,星期天為 6
#方法還有 .isoweekday() .toordinal() 等
datetime.combine(dt.date(),dt.time())
#combine:將一個(gè)date對(duì)象和一個(gè)time對(duì)象組合成一個(gè)datetime對(duì)象
from datetime import timezone #如果不使用pytz庫
d1=datetime(2020, 11, 21,tzinfo=timezone(timedelta(hours=8)))
tdt=dt-d1
# datetime.timedelta(days=16)
dt+timedelta(20)
兩個(gè)datetime日期相減得到的是一個(gè)時(shí)間間隔對(duì)象(imedelta)宿崭,timedelta可以和數(shù)值進(jìn)行乘法和整除運(yùn)算,兩個(gè)timedelta對(duì)象之間可以進(jìn)行加減運(yùn)算才写,但不能比較大小葡兑,datetime對(duì)象可以和timedelta對(duì)象進(jìn)行加減得到新的datetime實(shí)現(xiàn)時(shí)間偏移奖蔓。
datetime也會(huì)和內(nèi)置的calendar庫進(jìn)行配合,顧名思義讹堤,calendar庫主要用來處理和輸出整年吆鹤、整月的日歷。
print(calendar.calendar(2020)) #打印2020年日歷
#calendar.prcal(2020) #兩個(gè)語句效果相同
calendar.prmonth(2019,2) #打印2019年2月的日歷
calendar.isleap(2020) #是否閏年
# True
calendar.weekday(2020,11,20) #指定日期為星期幾洲守,
#4 代表星期五
這幾個(gè)庫其他的實(shí)用方法有:
- time.sleep(secs):線程推遲指定的時(shí)間運(yùn)行疑务,單位為秒;
- time.asctime([t]) :把一個(gè)表示時(shí)間的元組或者struct_time表示為這種形式:'Sun Jun 20 23:21:05 1993'梗醇,如沒有參數(shù)知允,將會(huì)將time.localtime()作為參數(shù)傳入;
- time.ctime([secs]):把一個(gè)時(shí)間戳(按秒計(jì)算的浮點(diǎn)數(shù))轉(zhuǎn)化為time.asctime()的形式叙谨。如果參數(shù)未給或者為None的時(shí)候温鸽,將會(huì)默認(rèn)time.time()為參數(shù)。它的作用相當(dāng)于time.asctime(time.localtime(secs))手负;
- calendar.leapdays(n,m):年份n到m之間的閏年數(shù)量嗤朴;
dateutil
dateutil模塊是基于datetime庫的實(shí)用拓展,增強(qiáng)了對(duì)時(shí)間間隔和時(shí)間序列的處理虫溜,因此dateutil類型直接繼承了datetime類型雹姊,dateutil庫生成的時(shí)間對(duì)象就是datetime。Anaconda下該庫已經(jīng)安裝衡楞,模塊里有parser吱雏、easter、relativedelta瘾境、rrule等實(shí)用類進(jìn)行時(shí)間處理歧杏。
import dateutil #anaconda下已經(jīng)安裝,直接import
dt=dateutil.parser.parse('April 29 2020 14:20')
#可以從字符串解析迷守,不需要手動(dòng)寫匹配的占位符犬绒。
dt=dateutil.parser.parse('April 29') #會(huì)取當(dāng)前年
# datetime.datetime(2020, 4, 29, 0, 0)
dt=dateutil.parser.parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
dateutil的parser類用于更方便地從字符串解析為datetime對(duì)象,parser.parse(string)
可以從各種類型的字符串例如一句自然語言中解析出日期兑凿,但輸入的參數(shù)string必須是字符串凯力,輸入時(shí)間戳不行(這個(gè)和下面提到的Arrow等庫不同)。
因?yàn)榻馕鰹閐atetime類型的對(duì)象礼华,所以可以使用datetime的各種方法和屬性咐鹤,例如需要知道是哪一年仍然使用dt.year
獲取。
一些datetime類的方法可以基于dt實(shí)例使用圣絮,要實(shí)現(xiàn)從時(shí)間戳轉(zhuǎn)時(shí)間對(duì)象祈惶,就可以使用dt.fromtimestamp(ts)
,獲取當(dāng)前時(shí)間,就可以使用dt.now()
捧请。
dt.fromtimestamp(dt.timestamp()) #時(shí)間戳與時(shí)間對(duì)象互轉(zhuǎn)
dt.strftime('%Y-%m-%d') #只能輸入一個(gè)參數(shù)
#時(shí)間對(duì)象轉(zhuǎn)字符串
dateutil.easter.easter(2020,method=3)
#計(jì)算輸入年份復(fù)活節(jié)的日期
dateutil計(jì)算時(shí)間間隔的方法封裝在relativedelta里凡涩,通過輸入?yún)?shù)months等明確間隔的時(shí)間距離,tz用于處理時(shí)區(qū)疹蛉。
dt+dateutil.relativedelta.relativedelta(months=1, weeks=1)
#時(shí)間偏移
# datetime.datetime(2021, 1, 14, 14, 15, 39, 173204)
relativedelta(datetime(2003, 10, 24, 10, 0),dt) #得到一個(gè)時(shí)間間隔
relativedelta(NOW, johnbirthday) #得到一個(gè)人的年齡
#下周五對(duì)應(yīng)的時(shí)間
dt+relativedelta(weekday=FR)
rrule類用于生成和處理一個(gè)時(shí)間序列活箕。rrule的主要參數(shù)有:
- freq:聲明序列重復(fù)的周期;
- count:生成多少個(gè)時(shí)間對(duì)象氧吐;
- dtstart:開始的時(shí)間點(diǎn)讹蘑;
list(dateutil.rrule.rrule(freq=dateutil.rrule.MONTHLY, count=4, dtstart=datetime(2020, 12,7)))
# [datetime.datetime(2020, 12, 7, 0, 0),datetime.datetime(2021, 1, 7, 0, 0),...]
list(dateutil.rrule.rrulestr("""
DTSTART:20201207T090000
RRULE:FREQ=DAILY;INTERVAL=10;COUNT=4
"""))
#效果同上,rrulestr是根據(jù)字符串規(guī)則生成時(shí)間序列
以上例子生成的是一個(gè)由4個(gè)時(shí)間對(duì)象組成的序列筑舅,開始時(shí)間是2020年12月7號(hào)座慰,每月重復(fù)一條記錄。rrule.rrulestr()是把字符串輸入當(dāng)參數(shù)翠拣。
pandas
實(shí)際在進(jìn)行數(shù)據(jù)分析時(shí)版仔,通常都會(huì)用到pandas庫卻不一定會(huì)導(dǎo)入datetime等庫,而pandas模塊也提供了Timestamp误墓、Timedelta等類用于時(shí)間類型數(shù)據(jù)的處理轉(zhuǎn)換蛮粮。直接使用pd.Timestamp也更容易進(jìn)行廣播運(yùn)算。
pandas的Timestamp對(duì)象用法和datetime庫基本一致谜慌,各種dt.year
屬性都有然想,也有dt.isleapyear
用于判斷是否是閏年。pd.Timedelta對(duì)應(yīng)datetime的timedelta欣范,表示時(shí)間間隔变泄。
df['時(shí)間']=pd.to_datetime(df['dt'])
df['years']=df['時(shí)間'].apply(lambda x:x.year)
sdf=df.loc[df['years']==2018]
ddr=dd/(pd.Timestamp('2018-12-31')-pd.Timestamp('2018-1-1')).days
df['tfs']=df['時(shí)間'].apply(lambda x:x.hour+x.minute/60+x.second/3600)
《用pandas處理時(shí)間格式數(shù)據(jù)》講述了一個(gè)處理Excel文件中時(shí)間數(shù)據(jù)的案例。
Arrow
Arrow是一個(gè)優(yōu)秀的Python時(shí)間處理庫恼琼,現(xiàn)在其他有追求的第三方時(shí)間處理庫都喜歡在文檔里對(duì)標(biāo)Arrow妨蛹,足矣見Arrow的影響力。Arrow通過收束接口增強(qiáng)了其易用性晴竞,可以快速上手使用蛙卤,get統(tǒng)籌各種輸入的解析,replace負(fù)責(zé)各種時(shí)間要素的修改噩死,format解決各類格式化輸出的需求颤难,range處理時(shí)間序列生成問題。
Arrow解析字符串或datetime對(duì)象得到的是一個(gè)自定義時(shí)間對(duì)象甜滨,通過dt.time乐严、dt.datetime、dt.timestamp等將時(shí)間數(shù)據(jù)從Arrow內(nèi)置對(duì)象轉(zhuǎn)為time等庫的時(shí)間對(duì)象衣摩,一些例子如下。
import arrow #在Anaconda下已經(jīng)安裝
arrow.get('2020-12-08 17:31:20')
#Out[]: <Arrow [2020-12-08T17:31:20+00:00]>
dt=arrow.get(1607334506) #get可輸入U(xiǎn)nix時(shí)間戳,也可輸入datetime對(duì)象
dt.datetime #轉(zhuǎn)為dateime類型
dt.naive #轉(zhuǎn)為當(dāng)?shù)貢r(shí)區(qū)的datetime類型
dt.floor('hour') #從小時(shí)處截?cái)喟纾r(shí)之后的數(shù)清零
d1.replace(hour=3)
d1.shift(weeks=+4) #當(dāng)前時(shí)間4周后
d1.to('Asia/Shanghai') #換時(shí)區(qū)
dt.format('YYYY-MM-DD') #輸出格式化字符串
arrow.Arrow.range('hour',arrow.now(),arrow.now().shift(hours=5))
#arrow生成時(shí)間序列
dt.humanize() #dt的自然語言表示
Arrow的具體用法可參考前文《Python處理時(shí)間數(shù)據(jù)的另一種選擇既琴,在標(biāo)準(zhǔn)庫之外》。
Pendulum
Pendulum也是一款很優(yōu)秀的Python時(shí)間處理模塊泡嘴,其內(nèi)置數(shù)據(jù)類型拓展自datetime甫恩,與datetime有著很好的兼容性。Pendulum比dateutil功能更豐富酌予,足矣和Arrow對(duì)標(biāo)磺箕。Arrow的易用性體現(xiàn)在接口簡(jiǎn)潔,Pendulum的易用性表現(xiàn)在很多datetime的方法都兼容抛虫,而且Pendulum的文檔頁面也更美觀漂亮松靡。Pendulum[?pend??l?m]意為鐘擺,是很好的時(shí)間意向建椰。Pendulum通過其內(nèi)置的DateTime對(duì)象實(shí)現(xiàn)和拓展datetime.datetime的功能雕欺,同時(shí)封裝出Duration、Period及Timezones處理時(shí)間偏移棉姐、時(shí)區(qū)屠列、時(shí)間序列。
import pendulum
dt=pendulum.now() #獲取本地時(shí)區(qū)的當(dāng)前時(shí)間
#DateTime(2020,12,8,18,0,8,697484,tzinfo=Timezone('Asia/Shanghai'))
pendulum.tomorrow() #明天的這個(gè)時(shí)候
dt.year # 2020
dt.week_of_year #dt所在周是本年第幾周
dt.age #dt對(duì)應(yīng)日期目前的年齡
dt.strftime('%Y-%m-%d')
d2=dt.set(year=2019) #把年份變成2019
dt.add(years=-1) #把時(shí)間變成1年前伞矩,注意是years不是year
period = pendulum.period(dt, dt.add(days=8))
list(period.range('days',2)) #時(shí)間序列
其他的一些實(shí)用方法如下:
- pendulum.datetime(2020,5,7):輸入年月日等生成DateTime笛洛,對(duì)應(yīng)著datetime.datetime()的寫法;
- pendulum.today():獲取當(dāng)天時(shí)間, .tomorrow() .yesterday() 等可以用乃坤;
- pendulum.local(args):獲取當(dāng)?shù)貢r(shí)間的對(duì)象苛让,可以輸入年月日等;
- pendulum.parse(text):從文本中解析出時(shí)間對(duì)象侥袜,有個(gè)類似的方法是pendulum.from_format(text,s)蝌诡;
- pendulum.from_timestamp(ts):把時(shí)間戳ts轉(zhuǎn)為時(shí)間對(duì)象;
- dt.int_timestamp:把dt表示為整數(shù)的timestamp枫吧,對(duì)應(yīng)的還有.float_timestamp浦旱;
- pendulum.timezone("Europe/Paris"):生成一個(gè)時(shí)區(qū)對(duì)象;
- d2.diff_for_humans(dt):將時(shí)間間隔按自然語言輸出九杂;
Pendulum的一些函數(shù)需要輸入DateTime作為參數(shù)時(shí)颁湖,輸入datetime對(duì)象也兼容,例如Period時(shí)期對(duì)象的start例隆、end對(duì)象輸入DateTime對(duì)象或datetime對(duì)象都可以甥捺,更詳細(xì)的Pendulum特性可閱讀《挑戰(zhàn)Arrow,需要怎樣的實(shí)力镀层?Pendulum使用筆記》镰禾。
Delorean
dateutil庫在datetime庫基礎(chǔ)上進(jìn)行拓展,Delorean站在dateutil的肩膀上進(jìn)一步增強(qiáng)了時(shí)間處理能力,其接口更偏向面向?qū)ο蟮膶懛ㄎ庹欤瑫r(shí)間戳使用epoch定義屋休,其時(shí)間對(duì)象和datetime對(duì)象兼容性也很高,并且內(nèi)置時(shí)間對(duì)象可以直接和datetime.timedelta進(jìn)行運(yùn)算备韧。
Delorean是《回到未來》中的主角的時(shí)間旅行車劫樟,作為一個(gè)以epoch表示時(shí)間的程序庫挺契合的。
Delorean抽象了多個(gè)接口用于解析和轉(zhuǎn)換其他格式數(shù)據(jù)為時(shí)間對(duì)象织堂,解析字符串用parse叠艳、處理時(shí)間戳用epoch、輸入的是datetime對(duì)象直接用Delorean()易阳。獲取對(duì)象的年月日等屬性附较,需轉(zhuǎn)datetime再使用datetime的接口。
from delorean import Delorean
dt=Delorean() #獲取當(dāng)前時(shí)間闽烙,相當(dāng)于now
dt=delorean.parse('2020/12/07')
dt.datetime.year #獲取年份
dt.replace(hour=8) #改時(shí)間
dt.shift('US/Eastern') #改時(shí)區(qū)
dt - timedelta(hours=2) #兩小時(shí)之前
list(delorean.stops(freq=delorean.DAILY,count=10))
Delorean修改時(shí)間要素是用replace翅睛,而改時(shí)區(qū)是使用的shift。除了用stops生成時(shí)間序列外黑竞,還有range_daily()捕发、range_hourly()等快速方法生成每天或每小時(shí)的時(shí)間序列。Delorean和datetime的協(xié)作很方便很魂,但接口不夠簡(jiǎn)潔和成體系扎酷,獲取屬性還需要轉(zhuǎn)為datetime,顯得常用的功能卻沒有優(yōu)先封裝遏匆,與Arrow法挨、Pendulum等庫還有些差距,是一個(gè)值得了解的Python時(shí)間庫幅聘,詳細(xì)了解其用法可看前文《設(shè)定基準(zhǔn)點(diǎn)去時(shí)間旅行|Delorean使用筆記》凡纳。
moment
和Arrow類似,moment也是靈感來自Moment.js庫帝蒿。moment是一個(gè)在發(fā)展中的庫荐糜,基本功能不缺,但也不是很完善葛超,其文檔 建議優(yōu)先考慮Arrow及Pendulum庫暴氏。
moment將數(shù)據(jù)的輸入封裝在moment.date里,在解析能力上绣张,比Arrow的get更進(jìn)一步答渔,例如get傳入tomorrow或者2 weeks ago是會(huì)報(bào)錯(cuò)的,這是arrow的get還不支持的寫法侥涵,但moment.date可以解析沼撕。
import moment
moment.date('2020-12-07 14:20:10')
#<Moment(2020-12-07T14:20:10)>
moment.date("2 weeks ago")
dt=moment.date("December 18, 2020")
moment.unix(1355875153626)
dt.year #獲取dt所在年份
moment的時(shí)間對(duì)象也是自定義的對(duì)象宋雏,獲取其屬性使用dt.year
的寫法,和其他庫一致端朵,進(jìn)行時(shí)間偏移用的add和subtract方法好芭,同時(shí)也有replace的接口燃箭,而且寫dt.replace(day=2)
或者dt.replace(days=2)
都沒出問題冲呢。輸出格式化的字符串使用format。通過dt.datetime
轉(zhuǎn)為dateime類型杈抢,而輸出時(shí)間戳是用dt.epoch()
方法壳快。
dt=moment.now() #還有utcnow()可以用
dt.add(days=2) #.subtract()
dt.replace(day=5)
dt.replace(days=5)
dt.format('YYYY-MM-DD')
dt.datetime #轉(zhuǎn)datetime對(duì)象
moment目前的接口還是偏少协饲,生成一個(gè)時(shí)間序列目前還不能實(shí)現(xiàn)。
使用moment時(shí)乘凸,一個(gè)小問題是用pip install moment
可能會(huì)安裝不上,需要通過pip install moment --user
去安裝累榜。
Maya
Maya站在datetime营勤、pendulum、snaptime等模塊的肩膀上發(fā)展有一定特色的時(shí)間處理能力壹罚,Maya自定義對(duì)象MayaDT也是通過epoch定義時(shí)間葛作,能很好地避免一些時(shí)區(qū)問題。
Maya的時(shí)間創(chuàng)建能力上排名前列猖凛,有豐富的接口用于從各種數(shù)據(jù)中解析出時(shí)間對(duì)象赂蠢,when和parse可以從一些自然語言字符串中解析出時(shí)間要素,這方面和moment不遑多讓辨泳,例如寫maya.when('tomorrow')
和.when('2 weeks ago')
等虱岂;當(dāng)然從time/datetime對(duì)象、時(shí)間戳轉(zhuǎn)Maya對(duì)象也是沒有壓力菠红。
import maya
maya.when('tomorrow') #明天的這個(gè)時(shí)候,直接從自然語言轉(zhuǎn)MayaDT
maya.parse('2020-12-08T03:15') #字符串轉(zhuǎn)maya時(shí)間對(duì)象
dt=maya.now() #獲取當(dāng)前時(shí)間
maya.MayaDT.from_datetime(datetime.now()) #datetime對(duì)象轉(zhuǎn)MayaDT
maya.MayaDT.from_struct(time.gmtime())
maya.MayaDT(1606533154) #時(shí)間戳轉(zhuǎn)Maya時(shí)間對(duì)象
dt.from_iso8601(text) #從符合ISO-8601標(biāo)準(zhǔn)的字符串中解析時(shí)間
在輸出和轉(zhuǎn)換方面第岖,有dt.datetime()
方法將MayaDT對(duì)象轉(zhuǎn)為datetime對(duì)象,也能直接通過dt.year
獲取MayaDT對(duì)象的屬性试溯,有dt.iso8601()
輸出滿足ISO-8601標(biāo)準(zhǔn)的時(shí)間字符串蔑滓,和from_iso8601相對(duì)應(yīng)。幾個(gè)優(yōu)秀庫都有的輸出為自然語言功能在Maya里封裝為dt.slang_time()
耍共,并且還有slang_date也能使用烫饼,slang是俚語的意思。
dt=maya.when('2020, 12, 7')
dt.slang_time()
# '8 hours ago'
dt.add(days=10).slang_time()
# 'in 1 week'
list(maya.intervals(start=maya.now(),
end=maya.now().add(days=1),
interval=60*60))
#生成start到end的每小時(shí)間隔的時(shí)間值序列
Maya的很多方法調(diào)用了其他時(shí)間庫试读,例如dt.year等屬性用了datetime庫杠纵、snap方法是調(diào)用了snaptime庫、parse和add用到了Pendulum庫钩骇,很多需求Maya沒有自己去造輪子比藻,同時(shí)也顯得依賴項(xiàng)有些多铝量,要深入了解Maya的用法可以翻看前文《博采眾長(zhǎng)穿梭時(shí)空|Maya庫使用筆記》。
在程序運(yùn)行方面银亲,除了時(shí)間數(shù)據(jù)本身慢叨,量測(cè)代碼運(yùn)行時(shí)間、模擬特定時(shí)間環(huán)境务蝠,都是編程語言層的使用場(chǎng)景拍谐。在Python中,timeit庫用于量測(cè)一段代碼的運(yùn)行時(shí)間馏段,即可以方便地計(jì)算代碼跑一次的耗時(shí)轩拨,也能計(jì)算多次重復(fù)運(yùn)行的平均耗時(shí),在進(jìn)行代碼評(píng)測(cè)時(shí)小巧實(shí)用院喜。FreezeGun 是在進(jìn)行測(cè)試時(shí)常用的時(shí)間庫亡蓉,主要應(yīng)用場(chǎng)景是做測(cè)試時(shí)保證輸入的一致性;功能是調(diào)用freeze_time后喷舀,程序運(yùn)行返回的時(shí)間就是凍結(jié)所在的時(shí)間砍濒,相當(dāng)于測(cè)試任務(wù)是在那個(gè)時(shí)間運(yùn)行的。
from timeit import timeit
timeit('x=1') #看執(zhí)行1000000次x=1的時(shí)間
def func():
s=[i for i in range(1000)]
return s
timeit('func()', number=1) #執(zhí)行函數(shù)func 一次的時(shí)間
from timeit import repeat
#repeat和timeit用法相似硫麻,多了一個(gè)repeat參數(shù)爸邢,表示重復(fù)測(cè)試的次數(shù)(可以不寫,默認(rèn)值為3.)庶香,返回值為一個(gè)時(shí)間的列表甲棍。
t = repeat('func()', 'from __main__ import func', number=100, repeat=5)
#在命令行中使用:
python -m timeit '"-".join(str(n) for n in range(100))'
from freezegun import freeze_time
@freeze_time("2012-01-14")
def test():
return datetime.now()
test()
#FakeDatetime(2012, 1, 14, 0, 0)
總結(jié)
在數(shù)據(jù)處理和數(shù)據(jù)分析過程中,主要需要解決的數(shù)據(jù)需求有以下幾點(diǎn):
- 生成時(shí)間對(duì)象赶掖,從字符串或者寫賦值語句得到一個(gè)時(shí)間對(duì)象感猛;從內(nèi)置的time/datetime對(duì)象轉(zhuǎn)更容易處理的時(shí)間對(duì)象,如數(shù)據(jù)列是從Excel讀入的奢赂,去解析該列為時(shí)間對(duì)象陪白;
- 對(duì)特定時(shí)間對(duì)象t,獲取年月日膳灶、分鐘等時(shí)間要素咱士;
- 時(shí)間運(yùn)算;
- 時(shí)間間隔Timedelta轧钓,兩個(gè)時(shí)間對(duì)象相減序厉;
- 一個(gè)時(shí)間對(duì)象+一個(gè)差值后得到新的時(shí)間對(duì)象,例如獲取t一周后的時(shí)間t2,
- 時(shí)間對(duì)象轉(zhuǎn)為特定格式的字符串毕箍;
- 時(shí)間序列的整體移動(dòng)與抽樣弛房;
- 非結(jié)構(gòu)日期處理,從自然語言中解析時(shí)間而柑;
各個(gè)庫解決該需求的方式總結(jié)如下表文捶。