手把手教你從有限的資料樣本中發掘價值(附程式碼)

資料派THU發表於2019-05-17

[ 導讀 ]本文是系列文章中的一篇,作者對滑鐵盧地區的Freedom of Information Requests資料集進行探索分析,展示了在實踐中拿到一批資料時(尤其像本文中的情況,資料很稀缺時),該如何一步步進行分析從而得到一些見解。作者的同事也對該資料集使用其他方法進行了分析,建議對NLP感興趣的讀者也一併閱讀,將大有裨益

最近我碰到了滑鐵盧地區的Open Data專案,連同它的Freedom of Information Requests資料集。我的同事Scott Jones已經在一系列文章中使用機器學習(ML)技術對其進行了分析。由於資料不足,ML表現不佳。雖然Scott做了在這種情況下應該做的事情,即尋找更多資料。儘管資料很稀缺,但我仍然很好奇這些資料還能告訴我什麼。畢竟資料總是有價值的。

在進入這段8分鐘的閱讀旅程之前,我想說你可以在Github上找到Jupyter notebook裡的所有程式碼和對這些資料的更多見解,由於內容太多,文章裡無法一一介紹。如果你不想閱讀notebook,可以在下面連結的相關檔案中找到全部圖形結果。

Github相關連結:

https://github.com/brodriguezmilla/foir

相關檔案:

https://github.com/brodriguezmilla/foir/blob/master/foir_all_figures.pdf

接下來,我向你介紹其中幾個分析的要點。

瞭解資料

我們使用pandas庫來實現這一步,以下是Open Data中的檔案之一: 

手把手教你從有限的資料樣本中發掘價值(附程式碼)1999年的Freedom of Information Requests檔案樣本 我們有18個檔案,從1999年至2016年每年一個,總共有576個請求(Requests),令人驚訝地是全部都有相同的六列。我們將只使用三個主要列,來源(Source),請求摘要(Summary_of_Request)和決策(Decision)。 

  • Source。 這是發出請求的實體,即請求者。 通過檢視多年來的資訊,能夠將其合併為“Business”,“Individual”,“Individual by Agent”,“Media”,“Business by Agent”和“Individual for dependant”。

  • Summary_of_Request。 包含已由記錄員編輯過的請求。

  • Decision。合併後的類別包括:“All information disclosed”,“Information disclosed in part”,“No records exist”,“Request withdrawn”,“Partly non-existent”,“No information disclosed”,“Transferred”,“Abandoned”, “Correction refused”,“Correction granted”,“No additional records exist”。

這些列的相互之間關係如何?

描述性統計和探索性資料分析

在本節中,我們將重點關注Source和Decision列。稍後我們將使用一些NLP工具分析這些請求。以下是資料的分佈:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

大約60%的請求是“All information disclosed”或“Information disclosed in part”。 至少有七種型別的決策少於25個例項,其中一個最重要的決策是“No information disclosed”。 因此,我們不僅資料量有限,而且還存在不平衡的情況。 對於機器學習來說這都不太好。

通過另一種資料檢視,即Source 與 Decision的透視表,我們看到大多數請求都是由“Business”,“Individual”和“Individual by Agent”發起的。

手把手教你從有限的資料樣本中發掘價值(附程式碼)

將每個來源的數字進行處理,使每一行加起來等於1,我們看到主要的三個來源表現良好,因為“All information disclosed”每個都超過30%,“Information disclosed in part”則增加了18%至34%,甚至更多。 兩者相加在50%以上。此外,“Individual by Agent”的成功率高於“Individual”。幾乎沒有請求的“Media”表現不佳,只有10%的請求被決策為“All information disclosed”。

手把手教你從有限的資料樣本中發掘價值(附程式碼)

自然語言處理(NLP)

現在我們繼續分析Summary_of_Requests列。為此,我們轉投自然語言處理庫,例如NLTK和spaCy,以及scikit-learn的幫助。 

從廣義上講,在分析任何文字之前,需要做的步驟其實很少(參見Susan Li的帖子):

https://towardsdatascience.com/topic-modelling-in-python-with-nltk-and-gensim-4ef03213cd21

  • 對文字進行分詞:將文字分解為單個特殊實體/單詞,即token。

  • 刪除任何不需要的字元,比如回車換行和標點符號,像' - ','...','“'等。

  • 刪除網址或將其替換為某個單詞,例如“URL”。

  • 刪除網名或用某個單詞替換“@”,例如“screen_name”。

  • 刪除單詞的大小寫。

  • 刪除少於等於n個字元的單詞。在本例中,n = 3。

  • 刪除停用詞,即某種語言中含義不大的詞。這些詞可能無助於對我們的文字進行分類。例如“a”,“the”,“and”等詞。但並沒有一個通用的停用詞列表。

  • 詞形還原,它是將單詞的變種形式歸併在一起的過程,這樣它們就可以作為單個詞項進行分析,就可以通過單詞的詞目(lemma)或詞典形式來識別。

因此,在編寫了處理函式之後,我們可以對文字進行轉換:

def prepare_text_tlc(the_text):

    text = clean_text(the_text)

    text = parse_text(text)

    tokens = tokenize(text)

    tokens = replace_urls(tokens)

    tokens = replace_screen_names(tokens)

    tokens = lemmatize_tokens(tokens)

    tokens = remove_short_strings(tokens, 3)

    tokens = remove_stop_words(tokens)

    tokens = remove_symbols(tokens)

    return tokens

由於我們會持續處理此文字,因此我們將預處理過的文字作為新列“Edited_Summary”新增到dataframe中。
手把手教你從有限的資料樣本中發掘價值(附程式碼)N元語法(N-grams)和詞雲還能如何分析和視覺化我們的文字呢?作為第一步,我們可以找到最常用的單詞和短語,即我們可以獲得一元語法(單個tokens)和 n元語法(n-tokens組)及它們在文字中的頻率。 

def display_top_grams(gram, gram_length, num_grams):

    gram_counter = Counter(gram)

    if gram_length is 1:

        name = 'unigrams'

    elif gram_length is 2:

        name = 'bigrams'

    elif gram_length is 3:

        name = 'trigrams'

    else:

        name = str(gram_length) + '-grams'    

    print("No. of unique {0}: {1}".format(name, len(gram_counter)))

for grams in gram_counter.most_common(num_grams):

        print(grams)

    return None 

所以對於我們的一元語法:

並使用WordCloud:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

那為什麼“remove”這個詞如此突出?事實證明,出於隱私原因,原始請求中寫入的所有姓名,日期和位置都已刪除,並在Open Data的檔案中被替換為“{location removed}”或“{date removed}”等短語。這種替換共有30多種變體。 使用正規表示式(regEx)來清理文字,我們得到了一個更好的詞雲。這一次,我們也加入了二元語法。
手把手教你從有限的資料樣本中發掘價值(附程式碼)

看一下上面的詞雲和三元語法:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

我們看到有一些常見的短語,例如“ontario works”,“environmental site”,“grand river transit”,“rabies control”,“public health inspection”和“food bear illness”(亦如'food borne illness ' – 還記得我們之前曾把tokens進行了詞形還原)。 那麼,這些短語在我們的文字中有多常見?包含這些短語的請求資訊是否影響請求被批准的可能性?事實證明,46%的資料是那些型別的請求,這些短語沒有一個得到“No information disclosed”的決策,並且有明顯的趨勢:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

例如,“rabies control”約有95%披露了全部或部分資訊,而5%被轉移了。

對Summary_of_Request和Edited_Summary 列統計

我們已經知道現有資料量是有限的,但到底多有限呢?好吧,只有7個請求超過100個單詞,而分詞後只剩1個。平均每個請求有21個單詞,而中位數為15,而分詞後平均為9個單詞,中位數為7。

手把手教你從有限的資料樣本中發掘價值(附程式碼)

手把手教你從有限的資料樣本中發掘價值(附程式碼)

詞性(POS)標記

在這裡,我們使用spaCy來識別該文字是如何由名詞,動詞,形容詞等組成的。 我們還使用函式spacy.explain()來找出這些標記的含義。

full_text_nlp = nlp(full_text)    # spaCy nlp()

tags = [] 

for token in full_text_nlp:

    tags.append(token.tag_)

tags_df = pd.DataFrame(data=tags, columns=['Tags'])

print("Number of unique tag values:\

      {0}".format(tags_df['Tags'].nunique()))

print("Total number of words: {0}".format(len(tags_df['Tags'])))

# Make a dataframe out of unique values

tags_value_counts = tags_df['Tags'].value_counts(dropna=True,

                    sort=True)

tags_value_counts_df = tags_value_counts.rename_axis(

                       'Unique_Values').reset_index(name='Counts')

# And normalizing the count values

tags_value_counts_df['Normalized_Count'] = tags_value_counts_df['Counts'] / len(tags_df['Tags'])

uv_decoded = []

for val in tags_value_counts_df['Unique_Values']:

    uv_decoded.append(spacy.explain(val))

tags_value_counts_df['Decoded'] = uv_decoded

tags_value_counts_df.head(10)手把手教你從有限的資料樣本中發掘價值(附程式碼)

同時將類別合併,例如“名詞,單數或大量”和“名詞,複數”,以形成更通用的版本,以下是這些請求的組成方式:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

使用scikit-learn,Bokeh和t-SNE進行主題建模

在notebook中,我們使用不同的主題建模技術,包括scikit-learn的隱含狄利克雷分佈(LDA)函式,潛在語義分析(LSA),並且用 CountVectorizer()和TfidfVectorizer()做對比,gensim的LDA,使用t-SNE用於降維,Bokeh和pyLDAvis用於視覺化。 我們不會在此處附上完整程式碼,所以鼓勵你去親自檢視完整的notebook。鑑於我們資料的侷限性,所有工具都還表現得不錯。下圖是一個亮點:

幾乎所有最常見的短語都在主題中出現了。正如預期的那樣,一些主題是明確的,例如“ontario works”或“environmental site”,而其他聚類則不然。

手把手教你從有限的資料樣本中發掘價值(附程式碼)

機器學習

我們已經知道機器學習效果不會很好,但鑑於這是一個學習練習,我們仍然要試一下。在notebook中,我們比較了三種不同情況下的八種不同機器學習模型。我們無法按原樣比較完整資料,因為某些情況只有極少數例項。例如,只有一個請求被“Correction granted”,因此當我們訓練模型時,該情況將要麼在訓練集中,要麼在測試集中。只有一個案例並不能提供一個良好的基礎。我們的選擇很少:

我們可以刪除少於15個例項的請求,稱之為“Over-15”。

我們將全部決議分成三個基本類別:

All information disclosed(加上“Correction granted”。)

Information disclosed in part(加上“Partly non-existent”。)

No information disclosed(加上' Transferred',' No records exist',' Correction refused',' No additional records exist',' Withdrawn'和' Abandoned'。)這會使我們的資料集更平衡。

我們可以刪掉少於15個例項的請求,並且刪掉沒有實際結果的決策,即撤回或拋棄的情況,稱之為“Independent”。

以下是結果:

手把手教你從有限的資料樣本中發掘價值(附程式碼)

總體而言,邏輯迴歸和多項式樸素貝葉斯分類器結合tf-idf給出了更好的結果。 對我們的類別進行分箱(binning)似乎是最合乎邏輯的方法。

可以在以下Github連結中找到程式碼和完整的結果:

Github連結:

https://github.com/brodriguezmilla/foi

原文標題:

When Data is Scarce… Ways to Extract Valuable Insights

原文連結:

https://towardsdatascience.com/when-data-is-scarce-ways-to-extract-valuable-insights-f73eca652009


相關文章