輸入文檔的部分則以 python list 表示,每個元素表示一個文檔,先以簡單的英文文字示範,文檔如下。
1 2 3 4 5 6 7
documents = [ "I want to adopt the dog", "a apple a day keeps doctor away", "I have a pen I have an apple", "who is your daddy", "daddy daddy daddy daddy daddy" ]
def_document_to_freq_dict(self, document): returndict([(word, document.count(word)) for word in document]) def_count_doc_freq_by_word(self, word): doc_count = 0 for document in self.documents: if word in document: doc_count += 1 return doc_count defcompute_tf_idf(self, word, document): freq_dict = self._document_to_freq_dict(document) tf = freq_dict.get(word, 0) / sum(freq_dict.values()) idf = math.log10(len(self.documents) / (1 + self._count_doc_freq_by_word(word))) return tf * idf
deftokenize(documents): documents = [document.split(" ") for document in documents] return documents
defmain(): documents = [ "I want to adopt the dog", "a apple a day keeps doctor away", "I have a pen I have an apple", "who is your daddy", "daddy daddy daddy daddy daddy" ] # 分詞前處理 documents = tokenize(documents)
a 2018-03-18 -0.159224 2018-03-190.836197 2018-03-200.168442 2018-03-210.147137 2018-03-22 -0.605849
可以輸入以下程式碼來將時間間隔放大,從一天變兩天。
1
df.resample('2D').asfreq()
可以發現間隔變為兩天,同時資料量減少。
1 2 3 4
a 2018-03-18 -0.159224 2018-03-200.168442 2018-03-22 -0.605849
同樣的道理間隔變小,則產生的記錄會變多了,如以下改成每 12 小時一個區間如下
1
df.resample('12H').asfreq()
asfreq 只會單純的把時間序列拉長,不對資料數值做修改,因此新產生紀錄值會保持空的。
1 2 3 4 5 6 7 8 9 10
a 2018-03-1800:00:00 -0.159224 2018-03-1812:00:00 NaN 2018-03-1900:00:000.836197 2018-03-1912:00:00 NaN 2018-03-2000:00:000.168442 2018-03-2012:00:00 NaN 2018-03-2100:00:000.147137 2018-03-2112:00:00 NaN 2018-03-2200:00:00 -0.605849
iloc iloc 是以 position-based index 的資料選擇方法,以數字來做為行與列的選擇。 loc loc 則是以 label-based index 的方式選擇,將 index 以字串匹配的方式來選擇第幾行與第幾列。
如下參考圖所示:
首先令測試的 df 如下所示,維度為 4 x 4 的資料。
1 2 3 4 5
a b c d 0 -0.4003631.1414850.8211020.237137 1 -0.1222300.293221 -1.3182230.765636 21.530837 -0.730529 -0.614539 -0.639149 3 -0.2013291.2798751.8752100.466313
使用 iloc 選擇資料範圍到前 2 列,欄位第 1, 3 欄程式碼如下,可以發現 iloc 的 position 如同一般的陣列一樣 index 從 0 開始。
1
df.iloc[:2,[0,2]]
以下成功的選取到指定的資料。
1 2 3
a c 01.574904 -0.263963 1 -1.3779692.188388
改用 loc 時,要以 label 的思維來進行資料的選取,以下是選用與 iloc 同樣行列範圍的程式寫法。
1
df.loc[:1,['a', 'c']]
結果如下,亦可以成功的選取到與上例一樣的資料,但要注意的事當 df 的 index 是數值表示時 (如同本例的 df index: [0,1,2,3]),loc 一樣會把看似數值表示的 index 以 label 字串匹配的角度來進行資料選取。這也是為什麼 loc 的 row number range 是 1,而 iloc 卻是到 2,因為 loc 把 1 當成是字串 label 來選取了。
1 2 3
a c 01.574904 -0.263963 1 -1.3779692.188388
合併 concat 合併也是 pandas 常見的任務,利用合併更多的資料欄位來進行分析來取得更好的結果。以下來紀錄 pandas 中 concat 涵式的用法。 定義以下的兩個 df,為度分別為 4 X 5以及6 X 4來進行合併操作。
InfluxDB 是專門做用於 time series 的資料庫,由 go 語言編寫,擁有對 time series data 操作的強大特性,據統計也是目前最流行的 time series 資料庫,以下記錄我一些學習的心得。
nfluxDB 存儲的數據從邏輯上劃分可以分成以下四部分:
measurement 可以把 measurement 想成對應到 sql 的資料表,表示整個 time series data。
time 即表示時間戳記,視為 primary index。
field 表示一個或多個 <key,value> 的欄位,儲存該時間點所在的屬性與其數值,像是 <temparture, 28.5> 是 InfluxDB 真正儲存資料的地方,不可以省略該欄位,每一個記錄都對應一筆 timestamp。
tag 也表示一個或多個 <key,value> 的欄位,但型態皆是字串,與 field 不同。用來描述該筆時間點記錄的其他性質。會有這個 tag 欄位屬性主要是因為 time series data 除了每筆記錄都有 timestamp 外,往往還伴隨著其他屬性,像是 IOT 感測器的 device_id 金融資料的 acount_id 等。這個欄位也是 optional 的,用來輔助 query 資料用。
以上的四部分所組成的一筆記錄就視為 time series 中的一個資料點,以下為一筆記錄的表示形式:
1
measurement-name tag-set field-settimestamp
對應實際的例子如下:
1
cpu host=serverA,region=uswest idle=23,user=42,system=121464623548s
了解了以上的時間序列資料特性後就可以做時間序列預測 (time series forecasting) 的任務了,一些著名的統計預測方法,像是 ARIMA 就是基於以上的時間序列特性的,而時間序列要如何預測詳細待下回分解。
程式處理技巧
由於自己常常在處理 IOT sensor 的時間序列資料,也記錄一下常用的技巧。
time series data 區間間隔標準化 sensor 資料的收集往往都會遇到網路延遲的問題,所以難保每筆記錄的時間間隔都是一樣的,pandas 提供了方便的技巧可以處理這類問題,如下程式碼所示。
1 2 3 4
# regular time series interval to 1 min 且用最近的一筆記錄補值 train_series_regular = train_series.resample('1T').bfill() # 間隔標準化後可能會產生缺值,因此在做內插 train_series_regular = train_series_regular.interpolate(method='time', limit_direction='both')
經過一方折騰終於發現問題出在哪了。以下是取自官方文件說明: Rollover occurs whenever the current log file is nearly maxBytes in length; but if either of maxBytes or backupCount is zero, rollover never occurs, so you generally want to set backupCount to at least 1, and have a non-zero maxBytes. 原因就是我沒有指定 backupCount, 而這個參數預設就是 0,沒有備份檔根本就不能將舊的記錄寫入,所以很自然的 log 檔就會越長越大了。 所以非常重要的一點是必須要加上 backupCount 這個參數,且值至少為 1,RotatingFileHandler 才可以根據 maxBytes 設定來將舊紀錄寫入備份檔。 再來值得注意的是,如果 log 是寫在 django 的話,則 django develop server 所對應的執行指令必須要加上 --noreload 參數,RotatingFileHandler 的設定才會有用,原因是 django 再 develop server 模式下會有兩個 thread,一個是執行 django 服務的主體,另一個則是監聽程式碼有沒有被修改,有修改則 reload 服務。所以若不把 reload 服務的 thread 關掉,則 log 檔就會被這個 thread 占住了,無法讓 RotatingFileHandler 對 log 檔進行大小限制處理,詳細指令如以下所示:
這兩個套件可以說是構成 python 網路爬蟲的基礎,requests 套件用來拜訪要爬資料的網站,並且取得爬資料網頁的整個 html 內容,BeautifulSoup 套件則是更進一步的剖析取回的 html 文檔內容,像是選擇特定幾個 tag 來取回想爬的資料。 以下為簡單的爬蟲範例,目標是爬一個登山資訊網站的資訊紀錄內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import requests from user_agent import generate_user_agent import bs4
接下來就是建立 model 的部分了,這裡建立了一個很簡單的 LSTM 模型,新版 keras 2.0 的 recurrent layers API 已可以不限制 input timesteps 的長度,所以 input_shape=(None,1),而 batch size 這裏則是在 fit 資料時設定。
1 2 3 4 5 6
# Define the LSTM model model = Sequential() model.add(LSTM(10, input_shape=(None,1))) model.add(Dense(1)) model.compile(loss="mse", optimizer="adam") model.summary()