王樹義 讀完需要 速讀僅需8分鐘 本文為你介紹 Pandas 存取數(shù)據(jù)的3種主要格式,以及使用中的注意事項(xiàng)。 問題在數(shù)據(jù)分析的過程里,你已經(jīng)體會到 Python 生態(tài)系統(tǒng)的強(qiáng)大了吧? 數(shù)據(jù)采集、整理、可視化、統(tǒng)計(jì)分析……一直到深度學(xué)習(xí),都有相應(yīng)的 Python 包支持。 但是你會發(fā)現(xiàn),沒有任何一個(gè) Python 軟件包,是全能的。 這是一種非常好的設(shè)計(jì)思維——用優(yōu)秀的工具,做專業(yè)的事兒;用許多優(yōu)秀工具組成的系統(tǒng),來有條不紊地處理復(fù)雜問題。 所以,在這個(gè)過程中,你大概率會經(jīng)常遇到數(shù)據(jù)的交換問題。 有時(shí)候,是把分析結(jié)果存起來,下次讀取回來繼續(xù)使用。 更重要的時(shí)候,是把一個(gè)工具的分析結(jié)果導(dǎo)出,導(dǎo)入到另一個(gè)工具包中。 這些數(shù)據(jù)存取的功能,幾乎分布在每一個(gè) Python 數(shù)據(jù)科學(xué)軟件包之內(nèi)。 但是,其中有一個(gè)最重要的樞紐,那就是 Pandas 。 我不止一次跟你提起過,學(xué)好 Pandas 的重要性。 很多情況下,看似復(fù)雜的數(shù)據(jù)整理與可視化,Pandas 只需要一行語句就能搞定。 回顧我們的教程里,也曾使用過各種不同的格式讀取數(shù)據(jù)到 Pandas 進(jìn)行處理。 然而,當(dāng)你需要自己獨(dú)立面對軟件包的格式要求時(shí),也許僅僅是因?yàn)椴涣私馊绾握_生成或讀取某種格式,結(jié)果導(dǎo)致出錯(cuò),甚至?xí)鼓銌适剿鞯男判呐c興趣。 這篇教程里,我以咱們介紹過多次的情感分類數(shù)據(jù)作為例子,用最小化的數(shù)據(jù)集,詳細(xì)為你介紹若干種常見的存取數(shù)據(jù)格式。 有了這些知識與技能儲備,你就可以應(yīng)對大多數(shù)同類數(shù)據(jù)分析問題的場景了。 環(huán)境為了方便你完整重現(xiàn)我教程中的代碼,我使用 Google Colab 撰寫和運(yùn)行,并且存儲副本到了 Github 里面。 請?jiān)谖业墓娞枴坝駱渲ヌm”(nkwangshuyi)后臺輸入“export”,就可以獲得本教程相應(yīng)的 Github 鏈接,以及代碼運(yùn)行環(huán)境的使用說明了。 數(shù)據(jù)為了盡量簡化問題,我們這里手動輸入兩條文本,構(gòu)建一個(gè)超小型的評論情感數(shù)據(jù)集。 str1 = '這是個(gè)好電影,\n我喜歡!' (猜猜看,其中 你看到了,這里我加了一些特殊符號進(jìn)去。 其中:
我們打印一下兩個(gè)字符串,看是否正確輸入:
這是個(gè)好電影, 換行符正確顯示了。下面我們看看制表符。
這部劇的 第八季 糟透了! 好了,下面我們分別賦予兩句話情感標(biāo)記,然后用 Pandas 構(gòu)建數(shù)據(jù)框。
我們建立了一個(gè)字典(dict),分別將文本和標(biāo)記列表放到 df = pd.DataFrame({'text': [str1, str2], 'label': [1, 0]}) 顯示效果如下: 好了,數(shù)據(jù)已經(jīng)正確存儲到 Pandas 里面了。下面我們分別看看幾種輸出格式如何導(dǎo)出,以及它們的特點(diǎn)和常見問題。 CSV/TSV我們來看最常見的兩種格式,分別是:
先嘗試把 Pandas 數(shù)據(jù)框?qū)С鰹?csv 文件。
注意這里我們使用了一個(gè) 回顧剛才的輸出: 上圖中標(biāo)紅色的地方,就是索引(index)。如果我們不加入 將生成的 csv 文件拖入文本編輯器內(nèi),效果如下: 你可以清楚地看到,逗號分割了表頭和數(shù)據(jù)。 有意思的是,因?yàn)榈谝痪湓u論里包含了換行符,所以就真的記錄到兩行上面。而文本的兩端,有引號包裹。 第二句話,制表符(縮進(jìn))也是正確顯示了。但是這句話兩端,卻沒有引號。 這么亂七八糟的結(jié)果,Pandas 還能夠正確讀回來嗎? 我們試試看。 pd.read_csv('data.csv') 一切正常。 看來,在讀取 csv 的過程里,Pandas 還是很有適應(yīng)能力的。 下面我們來看看頗為類似的 tsv 格式。 Pandas 并不提供一個(gè)單獨(dú)的 只不過,這次我們添加一個(gè)參數(shù)
生成的文件名為 對比一下剛剛的 csv 格式,你發(fā)現(xiàn)了什么? 大體上二者差不多。 只是逗號都變成了制表符縮進(jìn)而已。 但是不知你是否發(fā)現(xiàn),第二句話此時(shí)也被引號包裹起來了。 為什么呢? 對,因?yàn)檫@句話里面含有制表符。如果不包裹,讀取的時(shí)候可就要出問題了。程序就會傻乎乎地把 “第八季” 當(dāng)成標(biāo)記,扔掉后面的內(nèi)容了。 你看現(xiàn)在編輯器的著色,實(shí)際上已經(jīng)錯(cuò)誤判斷分列了。 我們試著用 Pandas 把它讀取回來。 注意,這里我們依然指定了,分割符是 pd.read_csv('data.tsv', sep='\t') 沒有差別,效果依然很好。 這兩種數(shù)據(jù)導(dǎo)出格式,非常直觀簡潔,用文本編輯器就可以打開查看。而且導(dǎo)出讀取都很方便。 這是不是意味著,我們只要會用這兩種格式就可以了呢? 別忙,我們再來看一個(gè)使用案例。 在處理中文文本信息時(shí),我們經(jīng)常需要做的一件事情,就是分詞。 這里,我們把之前兩句話進(jìn)行分詞后,再嘗試保存和讀取。 為了分詞,我們先安裝一個(gè)jieba分詞包。
然后把它讀取進(jìn)來。 import jieba 前面我們給自己挖了個(gè)坑——為了說明特殊符號的存儲,我們加了換行符和制表符?,F(xiàn)在問題來了,分詞之后,我們肯定不想要這些符號。 怎么辦呢? 我們來編寫一個(gè)定制化的分詞函數(shù)就好了。 這個(gè)函數(shù)里,我們分別清除掉制表符和換行符,然后再用結(jié)巴分詞切割。分詞這里,我們用的是默認(rèn)參數(shù)。 因?yàn)榉衷~后的結(jié)果實(shí)際上是個(gè)生成器(generator),而我們是需要真正的列表(list)的,所以利用
我們生成一個(gè)新的數(shù)據(jù)框 df_list = df.copy() 然后,我們把分詞的結(jié)果,存到新的數(shù)據(jù)框
看看分詞后的效果: df_list 怎么證明 我們來讀取一下其中的第一個(gè)元素好了。
結(jié)果顯示為: '這' 很好。此時(shí)的數(shù)據(jù)框可以正確存儲預(yù)處理(分詞)的結(jié)果。 下面我們還是仿照原先的方式,把這個(gè)處理結(jié)果數(shù)據(jù)導(dǎo)出,然后再導(dǎo)入。 先嘗試 csv 格式。
導(dǎo)出過程一切正常。 我們來看看生成的 csv 文件。 在存儲的過程中,列表內(nèi)部,每個(gè)元素都用單引號包裹。整體列表的外部,被雙引號包裹。 至于分割符嘛,依然是逗號。 看著是不是很正常? 我們來嘗試把它讀取回來。當(dāng)然我們希望讀取回來的格式,跟當(dāng)時(shí)導(dǎo)出的一模一樣。 pd.read_csv('data_list.csv') 結(jié)果是這樣的: 初看起來,很好??! 但是,我們把它和導(dǎo)出之前的數(shù)據(jù)框?qū)Ρ纫幌拢銇硗鎯阂粋€(gè)“大家來找茬”游戲吧。 注意,導(dǎo)出之前,列表當(dāng)中的每一個(gè)元素,都沒有引號包裹的。 但是重新讀取回來的內(nèi)容,每一個(gè)元素多了個(gè)單引號。 這看起來,似乎也不是什么大毛病啊。 然而,我們需要驗(yàn)證一下:
這次程序給我們返回的第一行文本分割的第一個(gè)元素,是這樣的: '[' 不應(yīng)該是“這”嗎? 我們來看看下一個(gè)元素是“這”嗎?
答案是: ''' 看到這里,你可能已經(jīng)恍然大悟。原來導(dǎo)出 csv 的時(shí)候,原先的分詞列表被當(dāng)成了字符串;導(dǎo)入進(jìn)來的時(shí)候,干脆就是個(gè)字符串了。 可是我們需要的是個(gè)列表啊,這個(gè)字符串怎么用? 來看看 tsv 格式是不是對我們的問題有幫助。
打開導(dǎo)出的 tsv 文件。 列表就是列表,兩邊并沒有用雙引號包裹。 這次興許能成! 我們趕緊讀回來看看。 pd.read_csv('data_list.tsv', sep='\t') 這結(jié)果,立刻讓人心里涼了一半。 因?yàn)榱斜砝锩婷總€(gè)元素兩旁的單引號都在啊。 抱著一絲僥幸的心理,我們嘗試一下驗(yàn)證第一個(gè)元素。
果不其然,還是中括號。 這意味著讀回來的,還是一個(gè)字符串。 任務(wù)失敗。 看來,依靠 csv/tsv 格式把列表導(dǎo)出導(dǎo)入,是不合適的。 那我們該怎么辦呢? pickle好消息是,我們可以用 pickle 。 pickle 是一種二進(jìn)制格式,在 Python 生態(tài)系統(tǒng)中,擁有廣泛的支持。 例如 PyTorch 的預(yù)訓(xùn)練模型,就可以用它來存儲和讀取。 在 Pandas 里面使用 pickle,非常簡單,和 csv 一樣有專門的命令,而且連參數(shù)都可以不用修改添加。 df_list.to_pickle('data.pickle') 讀取回來,也很方便。
我們來看看讀取回來的數(shù)據(jù)是否正確: df_list_loaded 這次看著好多了,那些讓我們煩惱的引號都不見了。 驗(yàn)證一下第一行列表的第一個(gè)元素:
結(jié)果是: '這' 很讓人欣喜的結(jié)果??! 看來 pickle 格式果然靠譜。 不過,當(dāng)我們試圖在文本編輯器里打開 pickle 格式的時(shí)候,會有警告。 如果我們忽略警告,一意孤行。那么確實(shí)還是可以打開的。 只不過,你看得懂嗎? 反正我是看不懂的。 這就是二進(jìn)制存儲方式的問題——只適合機(jī)器來看,人讀起來如同天書。 但這其實(shí)還不是 pickle 格式最大的問題。 最大的問題,在于不同軟件包之間的交互。 我們在做數(shù)據(jù)分析的時(shí)候,難免會調(diào)用 Pandas 以外的軟件包,繼續(xù)分析我們用 Pandas 預(yù)處理后的文件。 這個(gè)時(shí)候,就要看對方支持的文件格式有哪些了。 一個(gè)最常見的例子,是 PyTorch 的文本工具包 torchtext 。 用它讀取數(shù)據(jù)的時(shí)候,格式列表里面不包含 pickle 。 這可糟糕了。我們前面需要 Pandas 來預(yù)處理分詞,后面又需要使用 Torchtext 來劃分訓(xùn)練集和驗(yàn)證集,生成迭代(iteration)數(shù)據(jù)流,以便輸入模型做訓(xùn)練。 可在二者中間,我們卻被交換格式問題卡住了。 好在,天無絕人之路。 你看,這里列出的格式列表,除了 csv 和 tsv (已被我們驗(yàn)證過不適合處理分詞列表)之外,還有一個(gè) JSON 。 JSONJSON 絕對是數(shù)據(jù)交換界的一等公民。 它不僅可以存儲結(jié)構(gòu)化數(shù)據(jù)(也就是我們例子里面的數(shù)據(jù)框,或者你更常見的 Excel 表格),也可以存儲非結(jié)構(gòu)化數(shù)據(jù)。 如果你跟著我的教程了解過一些 API 的 Python 調(diào)用方法,那你對 JSON 格式應(yīng)該并不陌生。 本例中我們使用的,是一種特殊的 JSON 格式,叫做 JSON Lines。 之所以用它,是因?yàn)榍懊嫖覀兘榻B的 torchtext 包,要求使用這種格式。 所以,在 Pandas 的
輸出的結(jié)果,是這個(gè)樣子的。 由于中文采用了 unicode 方式存儲,所以此處我們無法直接識別每一個(gè)漢字。 但是,存儲的格式,以及其他類型的數(shù)據(jù)記錄,還是能看得一清二楚的。 我們來嘗試讀入。方法與輸出類似,也是用同樣的參數(shù)。 df_list_loaded_json = pd.read_json('data.json', orient='records', lines=True) 看看讀入效果:
首先,你會發(fā)現(xiàn)列的位置發(fā)生了調(diào)換。好在對于數(shù)據(jù)框來說,這不是問題,因?yàn)榱兄g的相對位置本來也沒有特殊含義。 其次,你能看到,那些引號都沒有出現(xiàn)。 為了進(jìn)一步驗(yàn)證,我們還是調(diào)取第一行列表的第一個(gè)元素。 df_list_loaded_json.text.iloc[0][0] 顯示為:
太棒了! 這樣一來, Pandas 就可以和 torchtext 等軟件包之間,建立順暢而牢固的數(shù)據(jù)交換通道了。 小結(jié)通過閱讀本文,希望你已經(jīng)掌握了以下知識點(diǎn):
希望這些知識和技能,可以幫助你解決研究和工作中遇到的實(shí)際問題。 祝深度學(xué)習(xí)愉快! |
|