在自然語言處理中,詞性標註(Part-of-Speech Tagging)是一項基礎但極為重要的任務。當我們需要深入理解文字結構,或為後續的機器學習模型準備特徵時,詞性標註能夠提供關鍵的語法訊息。

使用NLTK進行詞性標註

NLTK提供了預訓練的詞性標註器,使用起來相當直觀:

# 載入必要的函式庫from nltk import pos_tag
from nltk import word_tokenize

# 建立測試文字
text_data = "Chris loved outdoor running"

# 使用預訓練的詞性標註器
text_tagged = pos_tag(word_tokenize(text_data))

# 顯示詞性標註結果
print(text_tagged)
# 輸出: [('Chris', 'NNP'), ('loved', 'VBD'), ('outdoor', 'RP'), ('running', 'VBG')]

這段程式碼示了NLTK的詞性標註功能。首先使用word_tokenize將文字分割成單詞,然後用pos_tag為每個單詞標註詞性。結果是一個元組列表,每個元組包含單詞和對應的詞性標籤。

NLTK使用Penn Treebank標籤集,常見的標籤包括:

  • NNP: 專有名詞(如"Chris")
  • NN: 普通名詞
  • VBD: 過去式動詞(如"loved")
  • VBG: 動名詞或現在分詞(如"running")
  • JJ: 形容詞
  • RB: 副詞
  • PRP: 人稱代詞

有了這些標籤,我們可以篩選出特定詞性的單詞:

# 篩選出所有名詞
nouns = [word for word, tag in text_tagged if tag in ['NN','NNS','NNP','NNPS']]
print(nouns)  # 輸出: ['Chris']

將詞性轉換為機器學習特徵

在實際應用中,我們經常需要將文字的詞性訊息轉換為機器學習模型可用的特徵。以下是如何處理多個推文並將其詞性標註結果轉換為特徵的例子:

import nltk
from sklearn.preprocessing import MultiLabelBinarizer

# 建立測試推文
tweets = ["I am eating a burrito for breakfast",
          "Political science is an amazing field",
          "San Francisco is an awesome city"]

# 為每條推文的每個單詞標註詞性
tagged_tweets = []
for tweet in tweets:
    tweet_tag = nltk.pos_tag(word_tokenize(tweet))
    tagged_tweets.append([tag for word, tag in tweet_tag])

# 使用獨熱編碼將標籤轉換為特徵
one_hot_multi = MultiLabelBinarizer()
features = one_hot_multi.fit_transform(tagged_tweets)

print(features)
# 輸出:
# array([[1, 1, 0, 1, 0, 1, 1, 1, 0],
#        [1, 0, 1, 1, 0, 0, 0, 0, 1],
#        [1, 0, 1, 1, 1, 0, 0, 0, 1]])

# 顯示特徵名稱
print(one_hot_multi.classes_)
# 輸出: array(['DT', 'IN', 'JJ', 'NN', 'NNP', 'PRP', 'VBG', 'VBP', 'VBZ'], dtype=object)

這段程式碼示了一個更實際的應用場景。我們對多個推文進行詞性標註,然後使用MultiLabelBinarizer將每條推文的詞性標籤轉換為二元特徵向量。每個特徵表示某個詞性是否出現在推文中。

這種方法非常適合將文字轉換為機器學習模型可用的特徵,特別是在情感分析、文字分類別任務中。例如,形容詞(JJ)的出現頻率可能與情感極性相關。

自訓練詞性標註器

雖然NLTK的預訓練標註器對大多數英文字效果不錯,但在專業領域(如醫學或法律)可能準確度不夠。這時我們可以訓練自己的標註器:

from nltk.corpus import brown
from nltk.tag import UnigramTagger, BigramTagger, TrigramTagger

# 從Brown語料函式庫已標註的文字
sentences = brown.tagged_sents(categories='news')

# 分割訓練集和測試集
train = sentences[:4000]
test = sentences[4000:]

# 建立回退標註器
unigram = UnigramTagger(train)
bigram = BigramTagger(train, backoff=unigram)
trigram = TrigramTagger(train, backoff=bigram)

# 評估準確率
accuracy = trigram.evaluate(test)
print(f"標註器準確率: {accuracy:.4f}")
# 輸出: 標註器準確率: 0.8179

這段程式碼示瞭如何訓練自定義的詞性標註器。我們使用了回退n-gram標註策略,這是一種分層標註方法:

  1. 首先嘗試使用TrigramTagger,考慮前兩個單詞的標籤
  2. 如果沒有足夠訊息,回退到BigramTagger,只考慮前一個單詞
  3. 最後回退到UnigramTagger,只考慮單詞本身

這種回退策略在處理未見過的單詞或罕見序列時特別有效。在測試集上達到了約82%的準確率,這對於許多NLP任務來說已經相當不錯。

在實際專案中,我發現訓練自定義標註器的最大挑戰是取得高品質的標註語料函式庫果你正在處理專業領域文字,通常需要工作者來標註部分資料,然後用它來訓練你的標註器。

詞袋模型:文字的數值化表示

在機器學習中處理文字時,我們需要將文字轉換為數值特徵。詞袋模型(Bag of Words)是最基本也最常用的方法之一。

使用scikit-learn建立詞袋模型

scikit-learn的CountVectorizer讓我們能夠輕鬆地將文字轉換為詞袋特徵:

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer

# 建立測試文字
text_data = np.array(['I love Brazil. Brazil!',
                      'Sweden is best',
                      'Germany beats both'])

# 建立詞袋特徵矩陣
count = CountVectorizer()
bag_of_words = count.fit_transform(text_data)

# 顯示特徵矩陣
print(bag_of_words.toarray())
# 輸出:
# array([[0, 0, 0, 2, 0, 0, 1, 0],
#        [0, 1, 0, 0, 0, 1, 0, 1],
#        [1, 0, 1, 0, 1, 0, 0, 0]], dtype=int64)

# 顯示特徵名稱
print(count.get_feature_names())
# 輸出: ['beats', 'best', 'both', 'brazil', 'germany', 'is', 'love', 'sweden']

這段程式碼示瞭如何使用CountVectorizer建立詞袋模型。每個檔案(這裡是每個句子)被轉換為一個向量,向量的每個元素表示某個詞在該檔案中出現的次數。

例如,第一個句子"I love Brazil. Brazil!“被轉換為[0, 0, 0, 2, 0, 0, 1, 0],其中第四個元素(索引3)的值為2,表示"brazil"出現了兩次;第七個元素(索引6)的值為1,表示"love"出現了一次。

詞袋模型雖然簡單,但在許多文字分類別務中表現出色。它完全忽略了單詞的順序和語法結構,只關注單詞的出現頻率。

稀疏矩陣的重要性

在實際應用中,詞袋特徵矩陣通常非常稀疏(大多數元素為0),因為:

  1. 大多數檔案只包含詞彙表中的一小部分單詞
  2. 詞彙表可能非常大(成千上萬個單詞)

CountVectorizer預設輸出稀疏矩陣,這大節省了記憶體只有非零值被儲存,其餘值被假定為0。在處理大型文字語料函式庫這一點至關重要。

詞袋模型的進階用法

CountVectorizer提供了多種引數來自定義詞袋模型:

# 建立包含2-gram和3-gram的特徵矩陣,同時去除英文停用詞,只考慮"brazil"
count_2gram = CountVectorizer(ngram_range=(1, 2),
                              stop_words="english",
                              vocabulary=['brazil'])
bag = count_2gram.fit_transform(text_data)

# 檢視特徵矩陣
print(bag.toarray())
# 輸出: array([[2], [0], [0]])

# 檢視詞彙表
print(count_2gram.vocabulary_)
# 輸出: {'brazil': 0}

這個例子展示了CountVectorizer的三個重要引數:

  1. ngram_range:除了單個單詞(1-gram)外,還可以包含連續的2個單詞(2-gram)或3個單詞(3-gram)作為特徵
  2. stop_words:去除常見但訊息量少的單詞(如"the”, “is”, “and"等)
  3. vocabulary:限制只考慮指定的單詞集合

n-gram特別有用,因為它捕捉了一些單詞順序訊息。例如,“not good"作為2-gram比分別看"not"和"good"能提供更準確的情感訊息。

在實際應用中,我經常使用1-gram和2-gram的組合,同時去除停用詞。這提供了良好的特徵表示,同時保持了計算效率。

詞重要性加權:超越純詞頻

雖然詞袋模型很有用,但純詞頻統計存在一個明顯問題:常見但不重要的詞(如"the”, “is”)會獲得較高的權重,而稀少但重要的詞可能被忽視。TF-IDF(詞頻-逆檔案頻率)是解決這一問題的經典方法。

使用TF-IDF加權詞重要性

scikit-learn的TfidfVectorizer讓我們能夠直接建立TF-IDF加權的特徵矩陣:

from sklearn.feature_extraction.text import TfidfVectorizer

# 建立TF-IDF特徵矩陣
tfidf = TfidfVectorizer()
text_tfidf = tfidf.fit_transform(text_data)

# 顯示特徵矩陣
print(text_tfidf.toarray())
# 輸出類別:
# array([[0.        , 0.        , 0.        , 0.89442719, 0.        ,
#         0.        , 0.4472136 , 0.        ],
#        [0.        , 0.57735027, 0.        , 0.        , 0.        ,
#         0.57735027, 0.        , 0.57735027],
#        [0.57735027, 0.        , 0.57735027, 0.        , 0.57735027,
#         0.        , 0.        , 0.        ]])

# 顯示特徵名稱
print(tfidf.get_feature_names())
# 輸出: ['beats', 'best', 'both', 'brazil', 'germany', 'is', 'love', 'sweden']

TF-IDF結合了兩個因素:

  1. TF(詞頻):詞在檔案中出現的頻率
  2. IDF(逆檔案頻率):log(總檔案數/包含該詞的檔案數)

TF-IDF的核心思想是:如果一個詞在特設定檔案中頻繁出現,但在整個語料函式庫少出現,那麼這個詞可能對該檔案的內容有很強的指示意義。

在上面的例子中,“brazil"在第一個檔案中出現了兩次,與在整個語料函式庫對罕見(只出現在1/3的檔案中),因此獲得了較高的TF-IDF值(0.89442719)。

TF-IDF的

文字重要性權重分析:TF-IDF 技術實戰

在處理自然語言時,我們常需要判斷哪些詞彙對特設定檔案最具代表性。單純計算詞頻並不足夠,因為像「的」、「是」這類別見詞幾乎出現在所有檔案中,實際上並不能代表檔案特性。這時,TF-IDF(詞頻-逆檔案頻率)技術就顯得尤為重要。

TF-IDF 原理剖析

TF-IDF 結合了兩個關鍵指標:

  1. 詞頻(Term Frequency, TF):詞彙在檔案中出現的頻率,代表該詞對檔案的重要性
  2. 逆檔案頻率(Inverse Document Frequency, IDF):衡量詞彙的普遍性,計算方式為檔案總數除以包含該詞的檔案數的對數值

當詞彙在某篇檔案中頻繁出現(高 TF),但在其他檔案中較少出現(高 IDF),這個詞彙的 TF-IDF 值就會較高,表明它對該檔案具有較強的代表性。

使用 scikit-learn 實作 TF-IDF

scikit-learn 提供了 TfidfVectorizer 類別,讓我們能夠輕鬆實作 TF-IDF 轉換:

# 載入必要的函式庫
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer

# 建立文字資料
text_data = np.array(['I love Brazil. Brazil!',
                      'Sweden is best',
                      'Germany beats both'])

# 建立 TF-IDF 特徵矩陣
tfidf = TfidfVectorizer()
feature_matrix = tfidf.fit_transform(text_data)

這段程式碼首先匯入了 NumPy 和 scikit-learn 的 TF-IDF 向量化工具。接著建立了一個包含三個簡短檔案的文字資料陣列。然後使用 TfidfVectorizer() 建立向量化器,並透過 fit_transform() 方法將文字資料轉換成 TF-IDF 特徵矩陣。

檢視 TF-IDF 特徵矩陣

由於 TF-IDF 矩陣通常是稀疏的(大部分元素為零),scikit-learn 預設以壓縮格式儲存。我們可以使用 .toarray() 方法將其轉換為密集矩陣以便觀察:

# 以密集矩陣形式顯示 TF-IDF 特徵矩陣
feature_matrix.toarray()

輸出結果:

array([[ 0.        , 0.        , 0.        , 0.89442719, 0.        ,
         0.        , 0.4472136 , 0.        ],
       [ 0.        , 0.57735027, 0.        , 0.        , 0.        ,
         0.57735027, 0.        , 0.57735027],
       [ 0.57735027, 0.        , 0.57735027, 0.        , 0.57735027,
         0.        , 0.        , 0.        ]])

要理解這些數值代表什麼,我們可以檢視每個特徵對應的詞彙:

# 顯示特徵名稱
tfidf.vocabulary_

輸出結果:

{'beats': 0,
 'best': 1,
 'both': 2,
 'brazil': 3,
 'germany': 4,
 'is': 5,
 'love': 6,
 'sweden': 7}

從結果可以看出,矩陣中每一列代表一個詞彙,每一行代表一個檔案。例如,第一個檔案中 “brazil” 的 TF-IDF 值為 0.89442719,這是最高的值,表明 “brazil” 對於第一個檔案具有很強的代表性。這符合直覺,因為 “Brazil” 在第一個檔案中出現了兩次,但在其他檔案中沒有出現。

TF-IDF 計算原理深入解析

在 scikit-learn 中,TF-IDF 的計算方式如下:

  1. 詞頻(TF):簡單計算詞彙在檔案中出現的次數

  2. 逆檔案頻率(IDF):使用以下公式計算:

    idf(t) = log((1 + n_d)/(1 + df(d, t))) + 1
    

    其中 n_d 是檔案總數,df(d, t) 是包含詞彙 t 的檔案數

  3. 正規化:scikit-learn 預設使用 L2 正規化(歐幾裡得範數)來標準化 TF-IDF 向量

TF-IDF 值越高,表示該詞對檔案的重要性越大。在實際應用中,這種方法能有效識別出每個檔案的關鍵字,對檔案分類別搜尋引擎最佳化內容推薦等任務非常有用。

時間序列資料處理技巧

在機器學習預處理中,時間序列資料處理是另一個常見的挑戰。無論是銷售時間記錄還是公共衛生統計年份,我們都需要正確處理日期和時間資料。以下探討幾種處理時間序列資料的關鍵技巧。

將字串轉換為日期

處理時間資料的第一步通常是將字串格式的日期轉換為可處理的日期時間物件。pandas 提供了強大的 to_datetime 函式來完成這項工作:

# 載入必要的函式庫
import numpy as np
import pandas as pd

# 建立日期字串
date_strings = np.array(['03-04-2005 11:35 PM',
                         '23-05-2010 12:01 AM',
                         '04-09-2009 09:09 PM'])

# 轉換為日期時間
[pd.to_datetime(date, format='%d-%m-%Y %I:%M %p') for date in date_strings]

輸出結果:

[Timestamp('2005-04-03 23:35:00'),
 Timestamp('2010-05-23 00:01:00'),
 Timestamp('2009-09-04 21:09:00')]

這段程式碼將三個不同的日期時間字串轉換為 pandas 的 Timestamp 物件。format 引數指定了日期時間字串的格式:%d 表示日,%m 表示月,%Y 表示年(四位數),%I 表示小時(12小時制),%M 表示分鐘,%p 表示上午/下午。

我們也可以加入 errors 引數來處理可能出現的問題:

# 轉換為日期時間並處理錯誤
[pd.to_datetime(date, format="%d-%m-%Y %I:%M %p", errors="coerce")
 for date in date_strings]

errors="coerce" 時,任何轉換過程中出現的問題都不會引發錯誤(這是預設行為),而是會將導致錯誤的值設為 NaT(相當於缺失值)。

處理時區資訊

在全球化的資料分析中,處理不同時區的時間資料是常見挑戰。pandas 提供了多種方法來處理時區:

建立時間時指定時區

# 建立帶有時區的日期時間
pd.Timestamp('2017-05-01 06:00:00', tz='Europe/London')

輸出結果:

Timestamp('2017-05-01 06:00:00+0100', tz='Europe/London')

為現有日期時間增加時區

# 建立日期時間
date = pd.Timestamp('2017-05-01 06:00:00')

# 設定時區
date_in_london = date.tz_localize('Europe/London')

# 顯示日期時間
date_in_london

輸出結果:

Timestamp('2017-05-01 06:00:00+0100', tz='Europe/London')

轉換時區

# 改變時區
date_in_london.tz_convert('Africa/Abidjan')

輸出結果:

Timestamp('2017-05-01 05:00:00+0000', tz='Africa/Abidjan')

處理多個日期的時區

對於 pandas Series 物件,我們可以使用 dt.tz_localizedt.tz_convert 方法對每個元素應用時區操作:

# 建立三個日期
dates = pd.Series(pd.date_range('2/2/2002', periods=3, freq='M'))

# 設定時區
dates.dt.tz_localize('Africa/Abidjan')

輸出結果:

0 2002-02-28 00:00:00+00:00
1 2002-03-31 00:00:00+00:00
2 2002-04-30 00:00:00+00:00
dtype: datetime64[ns, Africa/Abidjan]

這些程式碼展示瞭如何處理時區資訊。首先展示如何在建立時間戳時直接指定時區,然後示範如何為現有時間戳增加時區資訊,接著說明如何將時間從一個時區轉換到另一個時區。最後展示如何對一系列日期同時應用時區操作。

pandas 支援兩組表示時區的字串,但我建議使用 pytz 函式庫串。我們可以透過匯入 all_timezones 來檢視所有時區字串:

# 載入函式庫
from pytz import all_timezones

# 顯示兩個時區
all_timezones[0:2]

輸出結果:

['Africa/Abidjan', 'Africa/Accra']

選擇日期和時間

在資料分析中,我們常需要選取特定時間範圍的資料。pandas 提供了多種方法來實作這一點:

使用布林條件選取

# 載入函式庫
import pandas as pd

# 建立資料框
dataframe = pd.DataFrame()

# 建立日期時間
dataframe['date'] = pd.date_range('1/1/2001', periods=100000, freq='H')

# 選取兩個日期時間之間的觀測值
dataframe[(dataframe['date'] > '2002-1-1 01:00:00') & 
          (dataframe['date'] <= '2002-1-1 04:00:00')]

輸出結果:

                  date
8762 2002-01-01 02:00:00
8763 2002-01-01 03:00:00
8764 2002-01-01 04:00:00

使用索引切片選取

另一種方法是將日期列設為 DataFrame 的索引,然後使用 loc 進行切片:

# 設定索引
dataframe = dataframe.set_index(dataframe['date'])

# 選取兩個日期時間之間的觀測值
dataframe.loc['2002-1-1 01:00:00':'2002-1-1 04:00:00']

輸出結果:

                         date
date                        
2002-01-01 01:00:00 2002-01-01 01:00:00
2002-01-01 02:00:00 2002-01-01 02:00:00
2002-01-01 03:00:00 2002-01-01 03:00:00
2002-01-01 04:00:00 2002-01-01 04:00:00

這些程式碼展示了兩種選取特定時間範圍資料的方法。第一種方法使用布林條件來選取符合時間範圍的資料;第二種方法則先將日期列設為索引,然後使用 loc 方法進行時間範圍切片。選擇哪種方法取決於具體情況。如果需要進行複雜的時間序列操作,將日期列設為索引可能更有利,但如果只需進行簡單的選取,使用布林條件可能更直接。

日期時間格式化程式碼考

在使用 pandas 的 to_datetime 函式時,正確指定日期時間格式是關鍵。以下是一些常用的日期和時間格式化程式碼

程式碼描述範例
%Y完整年份2001
%m帶零填充的月份04

日期時間資料的特徵分解與處理技巧

時間資料在資料分析和機器學習中扮演著至關重要的角色,然而原始的時間戳記通常需要進一步處理才能發揮其最大價值。在多年的資料科學專案中,玄貓發現妥善處理時間資料往往能顯著提升模型效能,特別是在處理具有時間週期性的資料時更為明顯。

日期資料拆分為多個特徵

在建構機器學習模型時,將日期時間拆分為年、月、日、時、分等元件常能夠幫助模型捕捉時間模式。這種拆分使模型能夠學習到與特定時間單位相關的模式,例如月度季節性趨勢或每週迴圈模式。

以下是如何使用 pandas 的 Series.dt 屬性來拆分日期資料:

# 載入必要的函式庫
import pandas as pd

# 建立資料框
dataframe = pd.DataFrame()

# 建立包含150個日期的序列,頻率為每週
dataframe['date'] = pd.date_range('1/1/2001', periods=150, freq='W')

# 建立年、月、日、時、分特徵
dataframe['year'] = dataframe['date'].dt.year
dataframe['month'] = dataframe['date'].dt.month
dataframe['day'] = dataframe['date'].dt.day
dataframe['hour'] = dataframe['date'].dt.hour
dataframe['minute'] = dataframe['date'].dt.minute

# 顯示前三筆資料
dataframe.head(3)

這段程式碼展示瞭如何將日期時間資料拆分為多個時間特徵。首先我們建立一個包含日期的 DataFrame,使用 pd.date_range() 函式生成一個從 2001 年 1 月 1 日開始,每週一筆,共 150 筆的時間序列。接著,我們利用 pandas 中 Series.dt 的屬性來提取每個日期的年、月、日、時、分資訊,將它們分別存入新的欄位中。

這種拆分策略在實務上非常有用。例如,當分析零售銷售資料時,將日期拆分可以幫助我們識別出某些月份的銷售高峰,或是一週中的哪一天顧客購買意願較高。這樣的特徵工程能讓模型更容易捕捉到時間相關的模式。

計算日期之間的差異

在許多實際應用中,我們需要計算兩個時間點之間的差異。例如,計算顧客在網站的停留時間、訂單處理時間,或是專案的完成週期等。pandas 提供了簡單的方法來處理這類別算。

# 載入必要的函式庫
import pandas as pd

# 建立資料框
dataframe = pd.DataFrame()

# 建立兩個日期時間特徵
dataframe['Arrived'] = [pd.Timestamp('01-01-2017'), pd.Timestamp('01-04-2017')]
dataframe['Left'] = [pd.Timestamp('01-01-2017'), pd.Timestamp('01-06-2017')]

# 計算兩個特徵之間的時間差
dataframe['Left'] - dataframe['Arrived']

若只需要間隔的天數,而不需要完整的 timedelta 物件,可以這樣處理:

# 只計算天數差異
pd.Series(delta.days for delta in (dataframe['Left'] - dataframe['Arrived']))

這段程式碼示範瞭如何計算兩個日期時間之間的差異。我們建立了一個包含 ArrivedLeft 兩個時間欄位的 DataFrame,代表到達和離開的時間。然後,我們直接使用減法運算元來計算兩者之間的時間差,結果是一個 timedelta64[ns] 類別的序列。

第二個程式碼片段展示瞭如何只提取天數差異,這在許多業務分析中更為實用。例如,在計算酒店住宿天數、租車天數或服務使用期限時,通常只需要知道整數天數而非精確到秒的時間差。

實際應用中,這種時間差異計算可以幫助我們建立重要的衍生特徵。例如,在分析客戶行為時,我們可以計算客戶首次和最近一次購買之間的天數,藉此評估客戶活躍度。

編碼星期幾資訊

星期幾的資訊對於捕捉週期性模式非常有價值。例如,餐廳在週末的客流量通常高於平日,或者某些服務在工作日的使用頻率可能更高。

# 載入必要的函式庫
import pandas as pd

# 建立日期序列
dates = pd.Series(pd.date_range("2/2/2002", periods=3, freq="M"))

# 顯示星期幾的名稱
dates.dt.weekday_name

若希望得到數值型態的結果(對機器學習更有用),可以使用:

# 顯示星期幾的數值表示(星期一為0)
dates.dt.weekday

這段程式碼展示瞭如何從日期中提取星期幾的資訊。我們首先建立了一個日期序列,然後使用 dt.weekday_name 屬性取得星期幾的名稱(如 “Monday”、“Tuesday” 等)。

第二個範例使用 dt.weekday 屬性,這會回傳整數值,其中 0 代表星期一,6 代表星期日。這種數值表示對於機器學習模型來說更為適用,因為大多數演算法需要數值輸入。

在實務中,星期幾的資訊常被用來建立虛擬變數(dummy variables)或迴圈特徵,以捕捉週期性模式。例如,當分析交通流量時,工作日和週末的模式往往有顯著差異,將星期幾編碼為特徵可以幫助模型識別這些模式。

建立延遲特徵

延遲特徵(lagged feature)是時間序列分析中的重要工具,它可以幫助模型學習時間依賴性。例如,預測明天的股價時,今天的股價通常是一個有價值的特徵。

# 載入必要的函式庫
import pandas as pd

# 建立資料框
dataframe = pd.DataFrame()

# 建立資料
dataframe["dates"] = pd.date_range("1/1/2001", periods=5, freq="D")
dataframe["stock_price"] = [1.1, 2.2, 3.3, 4.4, 5.5]

# 建立延遲一天的特徵
dataframe["previous_days_stock_price"] = dataframe["stock_price"].shift(1)

# 顯示資料框
dataframe

這段程式碼說明瞭如何建立延遲特徵。我們首先建立了一個包含日期和股價的 DataFrame,然後使用 shift(1) 方法建立了一個延遲一天的股價特徵。這個新特徵 previous_days_stock_price 代表前一天的股價。

值得注意的是,因為第一筆資料沒有「前一天」的值,所以延遲特徵的第一個值是 NaN(Not a Number)。在實際應用中,我們可能需要處理這些缺失值,例如使用填充方法或者捨棄第一筆資料。

延遲特徵在時間序列預測中非常有用。例如,在預測銷售量時,前幾天或前幾週的銷售量通常是重要的預測因子。透過 shift 函式,我們可以輕鬆建立不同時間延遲的特徵(例如 shift(7) 可以獲得一週前的值),以捕捉不同時間尺度的依賴關係。

使用滾動時間視窗

滾動時間視窗(rolling time window)是分析時間序列資料的強大工具,它可以幫助我們計算移動平均、移動標準差等統計量,用於平滑資料或捕捉趨勢。

# 載入必要的函式庫
import pandas as pd

# 建立時間索引
time_index = pd.date_range("01/01/2010", periods=5, freq="M")

# 建立資料框,設定索引
dataframe = pd.DataFrame(index=time_index)

# 建立特徵
dataframe["Stock_Price"] = [1, 2, 3, 4, 5]

# 計算滾動平均(視窗大小為2)
dataframe.rolling(window=2).mean()

這段程式碼展示瞭如何使用滾動時間視窗來計算滾動統計量。我們首先建立了一個以月為單位的時間索引,然後建立了一個包含股價的 DataFrame。接著,使用 rolling(window=2) 方法定義了一個大小為 2 的滾動視窗,並計算視窗內的平均值。

滾動視窗的概念可以這樣理解:想像一個固定大小的「視窗」在資料上移動,每次移動都計算視窗內資料的某種統計量。例如,如果視窗大小為 3,我們會計算:

  1. 第 1、2、3 個值的統計量
  2. 第 2、3、4 個值的統計量
  3. 第 3、4、5 個值的統計量
  4. 依此類別

滾動平均常用於平滑時間序列資料,減少短期波動的影響,更清晰地展示長期趨勢。例如,在分析股票價格時,10 天或 30 天的滾動平均可以幫助識別價格趨勢,過濾掉日常的波動。

除了平均值外,rolling 方法還支援其他統計函式,如 max()(最大值)、min()(最小值)、std()(標準差)和 corr()(相關性)等,提供了豐富的分析工具。

處理時間序列中的缺失資料

時間序列資料中的缺失值是常見的問題,可能由於感測器故障、資料收集問題或其他原因導致。處理這些缺失值對於準確分析至關重要。

# 載入必要的函式庫
import pandas as pd
import numpy as np

# 建立時間索引
time_index = pd.date_range("01/01/2010", periods=5, freq="M")

# 建立資料框,設定索引
dataframe = pd.DataFrame(index=time_index)

# 建立包含缺失值的特徵
dataframe["Sales"] = [1.0, 2.0, np.nan, np.nan, 5.0]

# 使用內插法填充缺失值
dataframe.interpolate()

也可以使用前向填充(使用前一個已知值):

# 前向填充
dataframe.ffill()

或者使用後向填充(使用後一個已知值):

# 後向填充
dataframe.bfill()

這段程式碼展示了處理時間序列缺失值的三種主要方法。我們首先建立了一個包含缺失值(np.nan)的時間序列資料。

第一種方法是使用 interpolate() 內插法,它會根據缺失值前後的已知值「畫出」一條線或曲線,並使用這條線來預測合理的缺失值。在我們的例子中,2.0 和 5.0 之間有兩個缺失值,內插法會將它們填充為 3.0 和 4.0,即在 2.0 和 5.0 之間均勻分佈的值。

第二種方法是使用 ffill()(前向填充),它會使用前一個已知值來填充缺失值。在我們的例子中,兩個缺失值都會被填充為 2.0(前一個已知值)。

第三種方法是使用 bfill()(後向填充),它會使用後一個已知值來填充缺失值。在我們的例子中,兩個缺失值都會被填充為 5.0(後一個已知值)。

選擇哪種方法取決於資料的性質和分析目的。內插法在資料有明顯趨勢與缺失間隔較小時效果較好;前向填充適用於當我們認為最近的已知值是最佳預測時;後向填充則在某些特殊情況下有用,例如當未來的值已知與與缺失值相關時。