精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何高效的使用Python和pandas清理非結構化文本字段技巧

數據分析與開發 ? 來源:數據分析與開發 ? 作者:數據分析與開發 ? 2021-04-06 13:43 ? 次閱讀

介紹

大家都知道數據清洗是數據分析過程中的一個重要部分。pandas有多種清洗文本字段的方法,可以用來為進一步分析做準備。隨著數據集越來越大,文本清洗的過程會逐漸變長,尋找一個能在合理時間內有效運行并可維護的方法變得非常重要。

本文將展示清洗大數據文件中文本字段的示例,幫助大家學習使用 Python 和 pandas 高效清理非結構化文本字段的技巧。

問題

假設你有一批全新工藝的威士忌想出售。你所在的愛荷華州,剛好有一個公開的數據集顯示了該州所有的酒類銷售情況。這似乎是一個很好的機會,你可以利用你的分析技能,看看誰是這個州最大的客戶。有了這些數據,你甚至可以為每個客戶規劃銷售流程。

你對這個機會感到興奮,但下載了數據后發現它相當大。這個數據集是一個565MB的CSV文件,包含24列和2.3百萬行。它雖然不是我們平時說的“大數據”,但它依然足夠大到可以讓Excel卡死。同時它也大到讓一些pandas方法在比較慢的筆記本電腦上運行地非常吃力。

本文中,我們將使用包括2019年所有銷售額的數據。當然你也可以從網站上下載其他不同時間段的數據。

我們從導入模塊和讀取數據開始,我會使用sidetable包來查看數據的概覽。這個包雖然不能用來做清洗,但我想強調一下它對于這些數據探索場景其實很有用。

數據

讀取數據:

import pandas as pd

import numpy as np

import sidetable

df = pd.read_csv(‘2019_Iowa_Liquor_Sales.csv’)

數據長這樣:

7240cd9e-9531-11eb-8b86-12bb97331649.png

我們大概率要做的第一件事是看每一家商店的購買量,并將它們從大到小排序。資源有限所以我們應該集中精力在那些我們能從中獲得最好回報的地方。我們更應該打電話給幾個大公司的賬戶,而不是那些夫妻小店。

sidetable是以可讀格式匯總數據的快捷方式。另一種方法是groupby加上其他操作。

df.stb.freq([‘Store Name’], value=‘Sale (Dollars)’, style=True, cum_cols=False)

724cd544-9531-11eb-8b86-12bb97331649.png

很明顯在大多數情況下,每個位置的商店名稱都是唯一的。理想情況下我們希望看到的是Hy-Vee, Costco, Sam’s 等聚合在一起的內容。

看來我們需要清洗數據了。

清洗嘗試·1

我們可以研究的第一種方法是使用.loc以及str的布爾過濾器來搜索Store Name列中的相關字符串。

df.loc[df[‘Store Name’].str.contains(‘Hy-Vee’, case=False), ‘Store_Group_1’] = ‘Hy-Vee’

上述代碼使用不區分大小寫的方式來搜索字符串“Hy Vee”,并將值“Hy Vee”存儲在名為Store_Group_1的新列中。這個代碼可以有效地將“Hy Vee#3/BDI/Des Moines”或“Hy Vee Food Store/Urbandale”等名稱轉換為正常的“Hy Vee”。

用%%timeit來計算此操作的時間:

1.43 s ± 31.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我們不想過早地進行優化,但我們可以使用regex=False參數來稍微加速一下:

df.loc[df[‘Store Name’].str.contains(‘Hy-Vee’, case=False, regex=False), ‘Store_Group_1’] = ‘Hy-Vee’

804 ms ± 27.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

來看下新列的情況:

df[‘Store_Group_1’].value_counts(dropna=False)

NaN 1617777

Hy-Vee 762568

Name: Store_Group_1, dtype: int64

可以看到我們已經清理了Hy-Vee,但還有很多其他值需要我們處理。

.loc方法內部包含大量代碼,速度其實可能很慢。我們可以利用這個思想,來尋找一些更易于執行和維護的替代方案。

清洗嘗試·2

另一種非常有效和靈活的方法是使用np.select來進行多匹配并在匹配時指定值。

有幾個很好的資源可以幫你學習如何使用np.select。這篇來自Dataquest的文章就是一個很好的概述。Nathan Cheever的這篇演講也十分有趣,內容豐富。我建議你們可以看下這兩篇文章。

關于np.select的作用最簡單的解釋是,它計算一個條件列表,如果有條件為真,就應用相應值的列表。

在我們的例子中,我們想查找不同的字符串,來替換為我們想要的規范值。

瀏覽完我們的數據后,我們把條件和值列表總結在store_patterns列表中。列表中的每個元組都是一個str.contains()方法,來查找和替換對應的我們想要做聚合的規范值。

store_patterns = [

(df[‘Store Name’].str.contains(‘Hy-Vee’, case=False, regex=False), ‘Hy-Vee’),

(df[‘Store Name’].str.contains(‘Central City’,

case=False, regex=False), ‘Central City’),

(df[‘Store Name’].str.contains(“Smokin‘ Joe’s”,

case=False, regex=False), “Smokin‘ Joe’s”),

(df[‘Store Name’].str.contains(‘Walmart|Wal-Mart’,

case=False), ‘Wal-Mart’),

(df[‘Store Name’].str.contains(‘Fareway Stores’,

case=False, regex=False), ‘Fareway Stores’),

(df[‘Store Name’].str.contains(“Casey‘s”,

case=False, regex=False), “Casey’s General Store”),

(df[‘Store Name’].str.contains(“Sam‘s Club”, case=False, regex=False), “Sam’s Club”),

(df[‘Store Name’].str.contains(‘Kum & Go’, regex=False, case=False), ‘Kum & Go’),

(df[‘Store Name’].str.contains(‘CVS’, regex=False, case=False), ‘CVS Pharmacy’),

(df[‘Store Name’].str.contains(‘Walgreens’, regex=False, case=False), ‘Walgreens’),

(df[‘Store Name’].str.contains(‘Yesway’, regex=False, case=False), ‘Yesway Store’),

(df[‘Store Name’].str.contains(‘Target Store’, regex=False, case=False), ‘Target’),

(df[‘Store Name’].str.contains(‘Quik Trip’, regex=False, case=False), ‘Quik Trip’),

(df[‘Store Name’].str.contains(‘Circle K’, regex=False, case=False), ‘Circle K’),

(df[‘Store Name’].str.contains(‘Hometown Foods’, regex=False,

case=False), ‘Hometown Foods’),

(df[‘Store Name’].str.contains(“Bucky‘s”, case=False, regex=False), “Bucky’s Express”),

(df[‘Store Name’].str.contains(‘Kwik’, case=False, regex=False), ‘Kwik Shop’)

使用np.select很容易遇到條件和值不匹配的情況。所以我們將其合并為元組,以便更容易地跟蹤數據匹配。

想使用這種數據結構,我們需要將元組分成兩個單獨的列表。使用zip來把store_patterns分為store_criteria和store_values:

store_criteria, store_values = zip(*store_patterns)

df[‘Store_Group_1’] = np.select(store_criteria, store_values, ‘other’)

上述代碼將用文本值填充每個匹配項。如果沒有匹配項,我們給它賦值‘other’。

數據現在長這樣:

df.stb.freq([‘Store_Group_1’], value=‘Sale (Dollars)’, style=True, cum_cols=False)

72fa307c-9531-11eb-8b86-12bb97331649.png

看起來比之前好,但仍然有32.28%的‘other’。

思考下這樣做是不是更好:如果帳戶不匹配,我們使用Store Name字段,而不是‘other’。這樣來實現:

df[‘Store_Group_1’] = np.select(store_criteria, store_values, None)

df[‘Store_Group_1’] = df[‘Store_Group_1’].combine_first(df[‘Store Name’])

這里使用了combine_first方法來將Store Name填充None值,這是清理數據時要記住的一個簡便技巧。

再來看下數據:

df.stb.freq([‘Store_Group_1’], value=‘Sale (Dollars)’, style=True, cum_cols=False)

73267eb6-9531-11eb-8b86-12bb97331649.png

這樣看起來更好了,我們可以根據需要繼續細化分組。例如我們可能需要為Costco構建一個字符串查找。

對于這個大型數據集來說,性能也還不錯:

13.2 s ± 328 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

這個方法的好處是你可以使用np.select來做數值分析或者上面展示的文本示例,非常靈活。這個方法也有一個弊端,那就是代碼量很大。如果你要清理的數據集非常大,那么用這個方法可能導致很多數據和代碼混合在一起。那么有沒有其他方法可以有差不多的性能,代碼更整潔一些?

清洗嘗試·3

這里要介紹的解決方案基于Matt Harrison的優秀代碼示例,他開發了一個可以做匹配和清洗的generalize函數。我做了一些修改,讓這個方法可以在這個示例中使用,我想給Matt一個大大的贊。如果沒有他前期99%的工作,我永遠不會想到這個解決方案!

def generalize(ser, match_name, default=None, regex=False, case=False):

“”“ Search a series for text matches.

Based on code from https://www.metasnake.com/blog/pydata-assign.html

ser: pandas series to search

match_name: tuple containing text to search for and text to use for normalization

default: If no match, use this to provide a default value, otherwise use the original text

regex: Boolean to indicate if match_name contains a regular expression

case: Case sensitive search

Returns a pandas series with the matched value

”“”

seen = None

for match, name in match_name:

mask = ser.str.contains(match, case=case, regex=regex)

if seen is None:

seen = mask

else:

seen |= mask

ser = ser.where(~mask, name)

if default:

ser = ser.where(seen, default)

else:

ser = ser.where(seen, ser.values)

return ser

這個函數可以在pandas上調用,傳參是一個元組列表。第一個元組項是要搜索的值,第二個是要為匹配值填充的值。

以下是等效的模式列表:

store_patterns_2 = [(‘Hy-Vee’, ‘Hy-Vee’), (“Smokin‘ Joe’s”, “Smokin‘ Joe’s”),

(‘Central City’, ‘Central City’),

(‘Costco Wholesale’, ‘Costco Wholesale’),

(‘Walmart’, ‘Walmart’), (‘Wal-Mart’, ‘Walmart’),

(‘Fareway Stores’, ‘Fareway Stores’),

(“Casey‘s”, “Casey’s General Store”),

(“Sam‘s Club”, “Sam’s Club”), (‘Kum & Go’, ‘Kum & Go’),

(‘CVS’, ‘CVS Pharmacy’), (‘Walgreens’, ‘Walgreens’),

(‘Yesway’, ‘Yesway Store’), (‘Target Store’, ‘Target’),

(‘Quik Trip’, ‘Quik Trip’), (‘Circle K’, ‘Circle K’),

(‘Hometown Foods’, ‘Hometown Foods’),

(“Bucky‘s”, “Bucky’s Express”), (‘Kwik’, ‘Kwik Shop’)]

這個方案的一個好處是,與前面的store_patterns示例相比,維護這個列表要容易得多。

我對generalize函數做的另一個更改是,如果沒有提供默認值,那么將保留原始值,而不是像上面那樣使用combine_first函數。最后,為了提高性能,我默認關閉了正則匹配。

現在數據都設置好了,調用它很簡單:

df[‘Store_Group_2’] = generalize(df[‘Store Name’], store_patterns_2)

性能如何?

15.5 s ± 409 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

比起上面它稍微有一點慢,但我認為它是一個更優雅的解決方案,如果我要做一個類似的文本清理工作,我會用這個方法。

這種方法的缺點是,它只能做字符串清洗。而np.select也可以應用于數值,所以應用范圍更廣。

關于數據類型

在pandas的最新版本中,有一個專用的字符串類型。我嘗試將Store Name轉換為該字符串類型,想看是否有性能優化。結果沒有看到任何變化。不過,未來有可能會有速度的提升,這點大家可以關注一下。

雖然string類型沒有什么區別,但是category類型在這個數據集上顯示了很大的潛力。有關category數據類型的詳細信息,可以參閱我的上一篇文章:https://pbpython.com/pandas_dtypes_cat.html。

我們可以使用astype將數據轉換為category類型:

df[‘Store Name’] = df[‘Store Name’].astype(‘category’)

然后我們跟之前那樣在運行np.select的方法

df[‘Store_Group_3’] = np.select(store_criteria, store_values, None)

df[‘Store_Group_3’] = df[‘Store_Group_1’].combine_first(df[‘Store Name’])

786 ms ± 108 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我們只做了一個簡單的改動,運行時間從13秒到不到1秒。太神了!效果這么明顯的原因其實很簡單。當pandas將列轉換為分組類型時,它只會對每個唯一的文本值調用珍貴的str.contains()函數。因為這個數據集有很多重復的數據,所以我們得到了巨大的性能提升。

讓我們看看這是否適用于我們的generalize函數:

df[‘Store_Group_4’] = generalize(df[‘Store Name’], store_patterns_2)

不幸的是報錯了:

ValueError: Cannot setitem on a Categorical with a new category, set the categories first

這個錯誤讓我回憶起我過去處理分組數據時遇到的一些挑戰。當你合并和關聯分組數據時,你很容易遇到這些錯誤。

我試圖找到一個比較好的方法來修改generage(),想讓它起作用,但目前還沒找到。如果有任何讀者能找到方法,可以聯系我獲得獎金。這里,我們通過構建一個查找表來復制Category方法。

查找表

正如我們通過分類方法了解到的,這個數據集有很多重復的數據。我們可以構建一個查找表,每個字符串處理一次資源密集型函數。

為了說明它是如何在字符串上工作的,我們將值從category轉換回字符串類型:

df[‘Store Name’] = df[‘Store Name’].astype(‘string’)

首先,我們構建一個包含所有唯一值的lookup DataFrame并運行generalize函數:

lookup_df = pd.DataFrame()

lookup_df[‘Store Name’] = df[‘Store Name’].unique()

lookup_df[‘Store_Group_5’] = generalize(lookup_df[‘Store Name’], store_patterns_2)

7338d782-9531-11eb-8b86-12bb97331649.png

我們可以把它合并到最終的DataFrame:

df = pd.merge(df, lookup_df, how=‘left’)

1.38 s ± 15.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

比起np.select使用分組數據的方法稍慢一些,但是代碼可讀性和易維護程度更高,性能和這兩者之間其實需要掌握一個平衡。

此外,中間的lookup_df可以很好的輸出給分析師共享,從而讓分析師幫助你清洗更多數據。這可能節省你幾小時的時間!

總結

根據我的經驗,通過本文中概述的清洗示例,你可以了解很多關于底層數據的信息。

我推測你會在你的日常分析中發現很多需要進行文本清理的案例,就像我在本文中展示的那樣。

下面是本文解決方案的簡要總結:

解決方案執行時間注釋

np.select13s可用于非文本分析

generalize15s只支持文本

分組數據和np.select786ms在合并和關聯時,分組數據可能會變得棘手

查找表和generalize1.3s查找表可以由其他人維護

對于一些數據集來說,性能不是問題,所以你可以隨意選擇。

然而,隨著數據規模的增長(想象一下對50個州的數據進行分析),你需要了解如何高效地使用pandas進行文本清洗。我建議你可以收藏這篇文章,當你面對類似的問題時可以再回來看看。

當然,如果你有一些其他的建議,可能會對別人有用,可以寫在評論里。如果你知道如何使我的generalize函數與分組數據一起工作,也記得告訴我。
編輯:lyn

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據分析
    +關注

    關注

    2

    文章

    1427

    瀏覽量

    34015
  • python
    +關注

    關注

    56

    文章

    4782

    瀏覽量

    84453

原文標題:用 pandas 高效清洗文本數據

文章出處:【微信號:DBDevs,微信公眾號:數據分析與開發】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    結構化布線在AI數據中心的關鍵作用

    AI 正在不斷顛覆各行各業,推動從電影制作到金融行業等各個領域的創新。而在 AI 系統的背后,隱藏著這樣一位無名英雄:結構化布線。
    的頭像 發表于 11-21 16:51 ?209次閱讀

    使用ReMEmbR實現機器人推理與行動能力

    視覺語言模型(VLM)通過將文本和圖像投射到同一個嵌入空間,將基礎大語言模型(LLM)強大的語言理解能力與視覺 transformer(ViT)的視覺能力相結合。VLM 可以處理結構化的多模態數據
    的頭像 發表于 11-19 15:37 ?142次閱讀
    使用ReMEmbR實現機器人推理與行動能力

    如何在文本字段中使用上標、下標及變量

    在KiCad的任何文本字段中,都可以通過以下的方式實現上標、下標、上劃線以及顯示變量及字段值的描述: 文本變量“文本變量”可以在 原理圖設置->工程->
    的頭像 發表于 11-12 12:23 ?56次閱讀
    如何在<b class='flag-5'>文本字段</b>中使用上標、下標及變量

    基于分布式對象存儲WDS的信托結構化數據整合平臺

    基于分布式對象存儲WDS的信托結構化數據整合平臺
    的頭像 發表于 08-28 09:56 ?285次閱讀
    基于分布式對象存儲WDS的信托<b class='flag-5'>非</b><b class='flag-5'>結構化</b>數據整合平臺

    定期維護結構化布線對于辦公室得重要性

    定期維護結構化布線對于辦公室的順利運行至關重要。結構化布線是指支持建筑物內各種數據、語音和視頻系統的標準基礎設施。它包括電纜、連接器、機架和其他構成網絡主干的組件。 通過正確維護結構化
    的頭像 發表于 06-14 10:44 ?221次閱讀

    什么是結構化網絡布線?結構化網絡布線有哪些好處?

    在電纜領域,結構化網絡布線這個術語經常被提及。人們將其用作流行語,但它的真正含義是什么?結構化布線到底是什么? 為了了解真正的含義,讓我們看它的一些相關定義。 根據光纖協會的說法,結構化布線是由
    的頭像 發表于 04-11 11:54 ?488次閱讀

    結構化布線的好處多嗎

    結構化布線是網絡系統中的重要組成部分,因為它為數據傳輸提供了強大、可擴展且可靠的基礎。通過遵守全球公認的標準,結構化布線可促進高速連接、簡化故障排除并確保未來的可擴展性。考慮到這些優勢,企業應優先
    的頭像 發表于 04-07 11:15 ?407次閱讀

    什么是網絡系統中的結構化布線?

    結構化布線在網絡系統中發揮著至關重要的作用,為組織內的無縫通信和數據傳輸提供了堅實的基礎。這種綜合基礎設施旨在支持廣泛的應用程序和技術。本文將深入探討它是什么、為什么它很重要以及它為組織提供的好處
    的頭像 發表于 04-07 10:58 ?350次閱讀

    arcgis值類型與字段類型不兼容

    ArcGIS是一個地理信息系統軟件,可以用來處理、分析和可視地理數據。在ArcGIS中,值類型和字段類型之間需要相互匹配,否則會導致不兼容的錯誤。 在ArcGIS中,值類型是指存儲在數據字段
    的頭像 發表于 02-25 11:14 ?1442次閱讀

    科通技術推出基于FPGA的應用設計結構化技術

    隨著汽車技術的飛速發展,汽車功能的復雜性對處理芯片的算力及IO端口數量提出了更高的要求。作為一家正在進行IPO排隊的公司,深圳市科通技術股份有限公司(以下簡稱:科通技術)積極應對市場挑戰,針對新一代汽車輔助駕駛的需求,研發了一系列基于FPGA的應用設計結構化技術。
    的頭像 發表于 02-02 09:34 ?508次閱讀

    CFD 設計利器:結構化結構化網格的組合使用

    (曾用名Autogrid),非常適合帶有幾何形狀葉片的渦輪機械應用。01結構化or結構化網格選擇,兩難境地?隨著幾何形狀的復雜性不斷增加(現在通常有超過10k個曲面
    的頭像 發表于 12-23 08:12 ?1710次閱讀
    CFD 設計利器:<b class='flag-5'>結構化</b>和<b class='flag-5'>非</b><b class='flag-5'>結構化</b>網格的組合使用

    使用關系數據庫中的半結構化數據

    NoSQL革命已經進入了關系世界。您可能正在使用關系數據庫,但仍必須查詢和理解隱藏在文本列、JSON或 XML文檔中的半結構化數據。
    的頭像 發表于 12-20 10:46 ?628次閱讀
    使用關系數據庫中的半<b class='flag-5'>結構化</b>數據

    Python利用pandas讀寫Excel文件

    使用pandas模塊讀取Excel文件可以更為方便和快捷。pandas可以將Excel文件讀取為一個DataFrame對象,方便進行數據處理和分析。
    的頭像 發表于 12-16 11:22 ?1275次閱讀
    <b class='flag-5'>Python</b>利用<b class='flag-5'>pandas</b>讀寫Excel文件

    Python編程的十大依賴庫有哪些

    Pandas庫是數據科學家的得力工具,它提供了強大的數據結構和數據分析功能。無論您需要進行數據清洗、分析還是可視Pandas都能幫您事半功倍。
    的頭像 發表于 12-13 10:29 ?812次閱讀

    使用pandas進行數據選擇和過濾的基本技術和函數

    Python pandas庫提供了幾種選擇和過濾數據的方法,如loc、iloc、[]括號操作符、query、isin、between等等
    的頭像 發表于 12-01 10:14 ?339次閱讀
    使用<b class='flag-5'>pandas</b>進行數據選擇和過濾的基本技術和函數