整理總結 python 中時間日期類數據處理與類型轉換(含 pandas)

我自學 python 編程並付諸實戰,迄今三個月。 pandas多是我最高頻使用的庫,基於它的易學、實用,我也很是建議朋友們去嘗試它。——尤爲當你自己不是程序員,但多少跟表格或數據打點交道時,pandasexcelVBA 簡單優雅多了。html

pandas 善於處理表格類數據,而我平常接觸的數據自然帶有時間日期屬性,好比用戶行爲日誌、爬蟲爬取到的內容文本等。因而,使用 pandas 也就意味着至關頻繁地與時間日期數據打交道。這篇筆記將從個人實戰經驗出發,整理我經常使用的時間日期類數據處理、類型轉換的方法。python

與此相關的三個庫以下。mysql

import time
import datetime
import pandas as pd

複製代碼

其中,timedatetime都是 python 自帶的,pandas則是一個第三方庫。換言之,前二者無需額外安裝,第三方庫則須要經過pip install pandas命令行自行安裝。如何檢查本身是否安裝了某個庫,如何安裝它,又如何查看和更新版本,對新手來講是一個比較大的話題,也是基礎技能,值得另外整理一篇筆記,就不在這裏佔篇幅了。固然,若是你不想本身本地折騰,也可電腦瀏覽器訪問xue.cn 這樣的網站,網頁上直接寫代碼並運行它們。git

1、time模塊

time模塊,我最經常使用到的功能就三個:程序員

  • 指定程序休眠;
  • 獲取當前時間戳;
  • 時間戳與本地時間的互相轉換

time.sleep(s) 指定程序休眠 s 秒

指定程序休眠時間,一般是在長時間運行的循環任務中進行。好比爬蟲任務,控制讀取網頁的時間間隔;自循環任務的時間間隔,調用瀏覽器打開網頁的時間間隔等等。github

先用兩個打印語句,輔助觀察和理解time.sleep()的效果:sql

print(datetime.datetime.now())
time.sleep(5)
print(datetime.datetime.now())
複製代碼

至於長時間運行的循環任務,我一般是把核心業務邏輯封裝好,利用jupyter lab自帶的多進程特定,建一個 notebook 放入下面這個函數去持續運行。編程

def repeat_myself(how_many_times = 10):
    print('--------',how_many_times,'----------')
    # 被封裝的核心代碼
    your_main_def() 

    # 自循環減 1 ;若是剩餘次數是0,則終止自循環
    how_many_times += -1
    if how_many_times == 0:
        print(datetime.datetime.now(),'stop it.')
        return

    # 每次調用設定一個時間間隔
    print(datetime.datetime.now(),'have a rest')
    how_long = random.randint(30,120)
    time.sleep(how_long)
    return repeat_myself(how_many_times)

repeat_myself(12)

複製代碼

time.time()獲取當前時間戳

最初我認爲無需急於掌握時間戳這個技能點,但實戰中,1) 個人爬蟲有時爬取到時間戳類型的數據,爲了易讀,要把它轉換爲正常人能看懂的方式;2) 使用 mysql 時我關心存儲所佔用的空間以及讀寫效率,並獲知一個時間數據存成 char 不如時間戳更節省空間。好吧,實戰須要,那麼趕忙掌握起這個小技能吧。瀏覽器

先了解下如何生成時間戳。經過time.time()獲得的時間戳,是一個有着10位整數位 + 6位小數位的浮點數,可根據須要簡單運算轉換爲須要的 十、1三、16 位整數時間戳。app

# 獲取當前時間戳

# 值是 1569642653.1041737 ,float
a = time.time()
# 1569642653,獲得 10位時間戳,int
b = int(a)
# 1569642653104,獲得 13位時間戳,int
c = int(a * 1000)
# 1569642653104173,獲得 16位時間戳,int
d = int(a * 1000000)

複製代碼

接下來,瞭解一下時間戳和人類易讀的時間之間的轉換。

時間戳與人類易讀的時間互相轉換

如上面所示,時間戳是一個floatint類型的數值,至少有 10 位整數。把時間戳轉換爲人類易讀的時間,用到的是localtime(),與其相反的是mktime()能把人類易讀的時間轉換爲時間戳。

# 時間戳轉換爲人類易讀的時間
# 結果是:time.struct_time(tm_year=2019, tm_mon=9, tm_mday=28, tm_hour=12, tm_min=12, tm_sec=1, tm_wday=5, tm_yday=271, tm_isdst=0)
# 數據類型是 time.struct_time
e = time.localtime(a)
f = time.localtime(b)
g = time.localtime(c//1000)
h = time.localtime(d//1000000)

# 人類易讀的時間轉換爲時間戳
# 結果是:1569643921.0,float
i = time.mktime(e)
j = time.mktime(f)
k = time.mktime(g)
l = time.mktime(h)

複製代碼

type()檢查,localtime() 獲得的結果,是 time.struct_time 類型,直觀可見這個類型對人類依然不是最友好的。最友好的表達將用到 strftimestrptime 這兩個方法,處理 time.struct_timestring字符串 兩個類型的互換。

# 把 struct_time 轉換爲指定格式的字符串
# '2019-09-28 12:12:01 Saturday'
good = time.strftime("%Y-%m-%d %H:%M:%S %A", e)

# 把字符串轉換爲 struct_time
# 結果是:time.struct_time(tm_year=2019, tm_mon=9, tm_mday=28, tm_hour=12, tm_min=12, tm_sec=1, tm_wday=5, tm_yday=271, tm_isdst=-1)
nice = time.strptime(good,"%Y-%m-%d %H:%M:%S %A")

複製代碼

在個人筆記中,僅整理總結本身經常使用的方法,至於我本身從未用到或不多用到的方法,並不羅列其中。若有小夥伴但願系統完整地瞭解,可直接搜:time site:python.org 或點擊訪問官方文檔 能查看完整說明。

2、datetime 模塊

datetime獲取到的時間數據是很是易讀的,在和人交互時,比 time 更好用一些。我一般把 datetime 用於如下 2 個場景。

場景A:log時間戳,打印信息監控代碼運行狀況

新手寫代碼,變相就是寫bug,以我本身來講,使用不熟模塊或寫新業務時,寫代碼和調試修復錯誤,佔用時間經常各半。採用 jupter lab的 notebook,讓寫代碼和調試方便許多,但依然須要 print() 打印信息方便監控代碼運行狀況。好比下方這個代碼片斷:

# 顯示效果:2019-09-28 12:44:36.574576 df_rlt ...
print(datetime.datetime.now(),'df_rlt ...')
for one in df_rlt.values:
    print(datetime.datetime.now(),one,'for circle ...')
    try:
        sql_insert = 'INSERT INTO questions(q_id,q_title,q_description,q_keywords,q_people,q_pageview,time) VALUES( "'\
            + str(quesition_id) + '", "' + str(one[0])+ '", "' + str(one[1]) + '", "' + str(one[2]) + '", "' \
            + str(one[3]) + '", "' + str(one[4]) + '", "' + str(datetime.datetime.now()) + '");' 
        sql_update = 'update topic_monitor SET is_title="1" where question_id = "' + str(quesition_id) + '";'
        cursor.execute(sql_insert)
        cursor.execute(sql_update)
        conn.commit()
        print(datetime.datetime.now(),'sql_insert ...')
    except:
        print(datetime.datetime.now(),'sql_insert error...')
        continue

複製代碼

場景B:文件名時間戳,文件名中增長當前日期

文件名中增長當前日期做爲參數,既避免文件相互覆蓋(好比數據天天更新,天天導出一次),也方便直觀地查看文件版本。固然啦,若是處理的是超級頻繁導出的文件,精確到天並不知足需求,可自行精確到時分秒,或直接用int(time.time())時間戳做爲文件名中的參數。

# 效果:'d:/out_put/xuecn_comments_statistics_2019-09-28.xlsx'
comms_file = output_path + 'xuecn_comments_statistics_' + str(datetime.datetime.now())[:10] + '.xlsx'
複製代碼

直接搜:datetime site:python.org 或者點擊訪問 python 官方文檔查看超多方法說明。

與官方文檔對比,我已經用到的知識點真是九牛一毛。不過也不要緊,從須要和興趣出發就好,不必硬着頭皮把本身打形成移動字典,不少方法呢都是用多了天然記住了,無需反覆死記硬背。

3、pandas 中的時間處理

我寫這篇筆記,本就是奔着精進 pandas 來的,前面花了很大篇幅先整理了timedatetime這些基礎功,如今進入重頭戲,即 pandas 中與時間相關的時間處理。

前面兩個部分舉例,處理的均是單個值,而在處理 pandasdataframe 數據類型時,事情會複雜一點,但不會複雜太多。我在實戰中遇到的狀況,總結起來無非兩類:

  • 數據類型的互換
  • 索引與列的互換

須要留意的是,數據類型應該靠程序判斷,而非咱們人肉判斷。python pandas 判斷數據類型,經常使用type()df.info() 這兩個方法。

首先,咱們構造一個簡單的數據示例 df

構造這個實例,只是爲了方便後面的展開。構造一個 dataframe 的方法有很是多。這裏就不展開了。

import random
df = pd.DataFrame({
    'some_data' : [random.randint(100,999) for i in range(1,10)],
    'a_col' : '2019-07-12',
    'b_col' : datetime.datetime.now().date(),
    'c_col' : time.time()},
    index=range(1,10))

複製代碼

而後,咱們逐項查看它的數據類型

剛學着用pandas常常會由於想固然地認爲某個對象是某個數據類型,從而代碼運行報錯。後來學乖,特別留心數據類型。

某個數據是什麼類型,如何查看,某個方法對數據類型有什麼要求,如何轉換數據類型,這些都是實戰中特別關心的。

# pandas.core.frame.DataFrame
type(df)
# pandas.core.series.Series
type(df['some_data'])
# numpy.ndarray
type(df['some_data'].values)
# numpy.int64
type(df['some_data'].values[0])
# str
type(df['a_col'].values[0])
# datetime.date
type(df['b_col'].values[0])
# numpy.float64
type(df['c_col'].values[0])

df.info()
""" <class 'pandas.core.frame.DataFrame'> RangeIndex: 9 entries, 1 to 9 Data columns (total 4 columns): some_data 9 non-null int64 a_col 9 non-null object b_col 9 non-null object c_col 9 non-null float64 dtypes: float64(1), int64(1), object(2) memory usage: 420.0+ bytes """

複製代碼

爲何要轉換數據類型,有什麼用途

爲何要把時間日期之類的數據轉換爲 pandas 自帶的 datetime64 類型呢?這固然不是強迫症整潔癖,並且即使不作轉換也不會帶來任何報錯。

最重要的緣由是,數據分析將會高頻用到基於時間的統計,好比:天天有多少用戶註冊、登陸、付費、留言……產品運營一般按日統計,把dt.date改爲dt.weekdt.monthdt.hour就能輸出周統計、月統計、分時統計……固然官方文檔介紹的方法還有更多,我提到的僅是本身高頻使用的方法。

df.groupby(df['c_col'].dt.date).some_data.agg('sum')

複製代碼

次要的緣由是,輸出數據到 excel 表格中發給其它同事時,我們仍是得考慮文件的易讀、簡潔吖。好比,時間戳得轉換爲人能看懂的文本,好比僅顯示日期,無需把後面時分秒之類的冗餘數據也顯示出來等等。

經過不一樣方式拿到的數據類型,一般相互之間並不一致,而咱們想要使用某些方法提升生產力,必須遵循該方法所要求的數據類型。因而數據類型轉換就成了剛需。

如何轉換爲 pandas 自帶的 datetime 類型

在上方示例中,肉眼可見 a_colb_col 這兩列都是日期,但 a_col 的值實際上是string 字符串類型,b_col的值是datatime.date類型。想要用pandas 的按時間屬性分組的方法,前提是轉換爲 pandas 本身的 datetime類型。

轉換方法是一致的:

# 字符串類型轉換爲 datetime64[ns] 類型
df['a_col'] = pd.to_datetime(df['a_col'])
# datetime.date 類型轉換爲 datetime64[ns] 類型
df['b_col'] = pd.to_datetime(df['b_col'])
# 時間戳(float) 類型轉換爲 datetime64[ns] 類型
df['c_col'] = pd.to_datetime(df['c_col'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(x))))

# 查看轉換後的屬性
df.info()
""" <class 'pandas.core.frame.DataFrame'> RangeIndex: 9 entries, 1 to 9 Data columns (total 4 columns): some_data 9 non-null int64 a_col 9 non-null datetime64[ns] b_col 9 non-null datetime64[ns] c_col 9 non-null datetime64[ns] dtypes: datetime64[ns](3), int64(1) memory usage: 420.0 bytes """

複製代碼

其中,難點是 c_col 這列。其實不難,只是幾個嵌套,顯得有點複雜而已:

  1. y = time.localtime(x),把 x 從時間戳(10個整數位+6個小數位的那串數字)類型轉換爲struct_time
  2. z = time.strftime('%Y-%m-%d %H:%M:%S',y) 把上一步獲得的 struct_time 轉換爲 字符串
  3. lambda x:z 匿名函數,輸入一個值x,獲得字符串z
  4. df['c_col'].apply() 對整列每一個值作上述匿名函數所定義的運算,完成後整列值都是字符串類型
  5. pd.to_datetime() 把整列字符串轉換爲 pandas 的 datetime 類型,再從新賦值給該列(至關於更新該列)

我其實很是但願有個過來人告訴我,這個知識點用的頻繁嗎,在什麼時期是否應該掌握?因而我本身寫的筆記,一般都會留意分享本身實戰過來的這個判斷。固然啦,每一個人實戰的方向不太同樣,你們可做參考,無需徹底照搬。具體說來:

  • 第 一、2 步是第一部分 time 模塊總結到基礎技能。
  • 第 3 步的匿名函數 lambda 是至關風騷的知識點,xue.cn 《自學是門手藝》有一節專門講到它,建議掌握。
  • 第 4 步結合匿名函數lambda,是對 dataframe 整列進行統一操做的重要技能點,多用幾回就熟練了。
  • 第 5 步 無需死記硬背。爲啥我總說 pandas 易學好用呢?由於它的不少方法,都能直接見文生義,幾乎沒有記憶負擔。

關於時間日期處理的pandas 官方文檔篇幅也挺長的,沒中文版,你們想要系統瞭解,直接點開查閱吧~

關於索引與列的互換

無論何種緣由致使,一般使用 pandas 時會常常對索引與列進行互換。好比把某列時間數據設爲索引,把時間索引設爲一列……這些操做並無額外的特別之處,都統一在pandas 如何進行索引與列的互換 這個技能點之下。限於篇幅,我這裏就不展開啦。不過索引與列的轉換是高頻操做,值得另寫一篇筆記。

有一點反覆強調都不過爲,即,個人筆記僅記錄本身實戰中頻繁遇到的知識技能,並不是該模塊全貌。如需系統掌握或遇到筆記以外的疑問,請善用搜索技能喲:你的關鍵詞們 site:python.org

若是個人整理帶給你幫助,請點個贊鼓勵我繼續分享。如需勘誤請留言,或挪步到個人 github 提issues

相關文章
相關標籤/搜索