- 原文地址:Pythonic Data Cleaning With NumPy and Pandas
- 原文作者:Malay Agarwal
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:bambooom
- 校對者:luochen1992,Hopsken
使用 NumPy 和 Pandas 進行 Python 式資料清理
資料科學家花費大量時間清理資料集,將它們清理為可以工作的形式。事實上,很多資料科學家表示,80% 的工作都是獲取和清理資料。
因此,不管你是剛剛進入這個領域或者計劃進入,那麼處理混亂資料的能力會非常重要,無論這意味著缺失值、格式不一致、格式錯誤還是無意義的異常值。
在此教程中,我們將利用 Pandas 和 NumPy 這兩個庫來清理資料。
我們將介紹以下內容:
- 刪除
DataFrame
中不必要的列 - 更改
DataFrame
的索引 - 用
.str()
方法清理列 - 使用
DataFrame.applymap()
函式以元素方式清理資料集 - 將列重新命名為更易識別的標籤
- 跳過 CSV 檔案中不必要的行
這些是我們將要用到的資料集:
- BL-Flickr-Images-Book.csv – 包含大英圖書館書籍資訊的 CSV 檔案
- university_towns.txt – 包含美國各州大學城名稱的文字檔案
- olympics.csv – 彙總所有國家夏季和冬季奧運會參與情況的 CSV 檔案
你可以從 Real Python 的 GitHub 倉庫 下載所有資料集,以便檢視以下示例。
注意:我推薦使用 Jupyter Notebook 來進行以下步驟。
本教程假設你對 Pandas 和 NumPy 庫有基本的瞭解,包括 Pandas 的主要工作物件 Series
和 DataFrame
,應用於它們的常用方法,以及熟悉 NumPy 的 NaN
值。
讓我們從 import 這些模組開始吧!
>>> import pandas as pd
>>> import numpy as np
複製程式碼
刪除 DataFrame
中不必要的列
你經常會發現資料集中並非所有類別的資料都對你有用。例如,你可能有一個資料集包含了學生資訊(名字、成績、標準、父母姓名和住址),但你想要專注於分析學生的成績。
在這種情況下,住址和父母姓名對你來說並不重要,保留這些類別將佔用不必要的空間,並可能拖累執行時間。
Pandas 提供了一個很方便的 drop()
函式來從 DataFrame
中移除列或行。我們來看一個簡單的例子,從 DataFrame
中刪除一些列。
首先,我們從 CSV 檔案 “BL-Flickr-Images-Book.csv” 中建立一個 DataFrame
。在下面的例子中,我們把相對路徑傳遞給 pd.read_csv
,當前工作路徑下,所有的資料集都存放在 Datasets
資料夾中:
>>> df = pd.read_csv('Datasets/BL-Flickr-Images-Book.csv')
>>> df.head()
Identifier Edition Statement Place of Publication \
0 206 NaN London
1 216 NaN London; Virtue & Yorston
2 218 NaN London
3 472 NaN London
4 480 A new edition, revised, etc. London
Date of Publication Publisher \
0 1879 [1878] S. Tinsley & Co.
1 1868 Virtue & Co.
2 1869 Bradbury, Evans & Co.
3 1851 James Darling
4 1857 Wertheim & Macintosh
Title Author \
0 Walter Forbes. [A novel.] By A. A A. A.
1 All for Greed. [A novel. The dedication signed... A., A. A.
2 Love the Avenger. By the author of “All for Gr... A., A. A.
3 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
4 [The World in which I live, and my place in it... A., E. S.
Contributors Corporate Author \
0 FORBES, Walter. NaN
1 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
2 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
3 Appleyard, Ernest Silvanus. NaN
4 BROOME, John Henry. NaN
Corporate Contributors Former owner Engraver Issuance type \
0 NaN NaN NaN monographic
1 NaN NaN NaN monographic
2 NaN NaN NaN monographic
3 NaN NaN NaN monographic
4 NaN NaN NaN monographic
Flickr URL \
0 http://www.flickr.com/photos/britishlibrary/ta...
1 http://www.flickr.com/photos/britishlibrary/ta...
2 http://www.flickr.com/photos/britishlibrary/ta...
3 http://www.flickr.com/photos/britishlibrary/ta...
4 http://www.flickr.com/photos/britishlibrary/ta...
Shelfmarks
0 British Library HMNTS 12641.b.30.
1 British Library HMNTS 12626.cc.2.
2 British Library HMNTS 12625.dd.1.
3 British Library HMNTS 10369.bbb.15.
4 British Library HMNTS 9007.d.28.
複製程式碼
當我們使用 head()
方法檢視前五條資料時,我們可以看到一些列提供了對圖書館來說有用的輔助資訊,但是對描述書籍本身並沒有太多幫助: Edition Statement
,Corporate Author
,Corporate Contributors
,Former owner
,Engraver
,Issuance type
和 Shelfmarks
。
我們可以這樣刪除這些列:
>>> to_drop = ['Edition Statement',
... 'Corporate Author',
... 'Corporate Contributors',
... 'Former owner',
... 'Engraver',
... 'Contributors',
... 'Issuance type',
... 'Shelfmarks']
>>> df.drop(to_drop, inplace=True, axis=1)
複製程式碼
這裡,我們定義了一個列表,其中包含了我們想要刪除的列的名字。然後呼叫 drop()
函式,傳入 inplace
引數為 True
,以及 axis
引數為 1
。這兩個引數告訴 Pandas 我們想要讓改變直接作用在物件上,並且我們需要刪除的是列。
再次檢視 DataFrame
,可以發現不想要的列已經被移除了:
>>> df.head()
Identifier Place of Publication Date of Publication \
0 206 London 1879 [1878]
1 216 London; Virtue & Yorston 1868
2 218 London 1869
3 472 London 1851
4 480 London 1857
Publisher Title \
0 S. Tinsley & Co. Walter Forbes. [A novel.] By A. A
1 Virtue & Co. All for Greed. [A novel. The dedication signed...
2 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr...
3 James Darling Welsh Sketches, chiefly ecclesiastical, to the...
4 Wertheim & Macintosh [The World in which I live, and my place in it...
Author Flickr URL
0 A. A. http://www.flickr.com/photos/britishlibrary/ta...
1 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
2 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
3 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
4 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
複製程式碼
或者,我們可以通過直接將列傳遞給 columns
引數來刪除列,不用單獨指定刪除的標籤以及刪除列還是行:
>>> df.drop(columns=to_drop, inplace=True)
複製程式碼
這種方法更直觀易讀,這一步做了什麼是非常明顯的。
如果你事先知道那些列是你需要保留的,另外一個選擇是將列作為 usecols
的引數傳給 pd.read_csv
。
更改 DataFrame
的索引
Pandas 的 Index
擴充套件了 NumPy 的陣列功能,從而可以實現更多功能的擷取和標籤。在多數情況下,使用資料唯一有價值的標識欄位作為索引是很有幫助的。
例如,在上一節使用的資料集中,可以想象到,圖書管理員如果需要搜尋記錄,他也許輸入的是書籍的唯一識別符號(Identifier
列):
>>> df['Identifier'].is_unique
True
複製程式碼
讓我們用 set_index
來替換現有的索引:
>>> df = df.set_index('Identifier')
>>> df.head()
Place of Publication Date of Publication \
206 London 1879 [1878]
216 London; Virtue & Yorston 1868
218 London 1869
472 London 1851
480 London 1857
Publisher \
206 S. Tinsley & Co.
216 Virtue & Co.
218 Bradbury, Evans & Co.
472 James Darling
480 Wertheim & Macintosh
Title Author \
206 Walter Forbes. [A novel.] By A. A A. A.
216 All for Greed. [A novel. The dedication signed... A., A. A.
218 Love the Avenger. By the author of “All for Gr... A., A. A.
472 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
480 [The World in which I live, and my place in it... A., E. S.
Flickr URL
206 http://www.flickr.com/photos/britishlibrary/ta...
216 http://www.flickr.com/photos/britishlibrary/ta...
218 http://www.flickr.com/photos/britishlibrary/ta...
472 http://www.flickr.com/photos/britishlibrary/ta...
480 http://www.flickr.com/photos/britishlibrary/ta...
複製程式碼
技術細節: 與 SQL 中的主鍵不同,Pandas 的 Index
不保證是唯一的,儘管許多索引及合併操作在唯一的情況下執行時會加速。
我們可以使用 loc[]
直接訪問每條記錄。儘管 loc[]
可能不具有直觀的名稱,但它允許我們執行基於標籤的索引,即標記某一行或某一條記錄而不用考慮其位置:
>>> df.loc[206]
Place of Publication London
Date of Publication 1879 [1878]
Publisher S. Tinsley & Co.
Title Walter Forbes. [A novel.] By A. A
Author A. A.
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 206, dtype: object
複製程式碼
換句話說,206 是索引的第一個標籤。如要按位置訪問它,我們可以使用 df.iloc[0]
,它執行基於位置的索引。
技術細節:.loc[]
在技術上來說是一個類例項,它有一些特殊的語法不完全符合大多數普通 Python 例項方法。
一開始,我們的索引是一個 RangeIndex,也就是從 0 開始的整數,類似於 Python 內建的 range
。通過把列的名稱傳給 set_index
,我們將索引改成了 Identifier
中的值。
你可能注意到,我們使用 df = df.set_index(...)
將此方法返回的值重新賦值給變數。這是因為預設情況下,此方法會返回一個修改後的副本,並不會直接對原本的物件進行更改,索引可以通過設定 inplace
引數來避免這種情況:
df.set_index('Identifier', inplace=True)
複製程式碼
整理資料中的欄位
到這裡,我們已經刪除了不必要的列,並將 DataFrame
的索引更改為更有意義的列。在這一節,我們將會清理特定的列,使其成為統一的格式,以便更好地理解資料集並強化一致性。具體來說,我們將清理 Date of Publication
和 Place of Publication
這兩列。
經過檢查,所有的資料型別都是 object
dtype,它與 Python 中的 str
類似。
它封裝了任何不適用於數字或分類資料的欄位。這是有道理的,因為我們使用的資料最初只是一堆雜亂的字元:
>>> df.get_dtype_counts()
object 6
複製程式碼
其中出版日期一列,如果將其轉化為數字型別更有意義,所以我們可以進行如下計算:
>>> df.loc[1905:, 'Date of Publication'].head(10)
Identifier
1905 1888
1929 1839, 38-54
2836 [1897?]
2854 1865
2956 1860-63
2957 1873
3017 1866
3131 1899
4598 1814
4884 1820
Name: Date of Publication, dtype: object
複製程式碼
一本書只能有一個出版日期,因此我們需要做到以下幾點:
- 除去方括號內的多餘日期,不管出現在哪裡,例如:1879 [1878]
- 將日期範圍轉換為“開始日期”,例如:1860-63; 1839, 38-54
- 完全移除任何不確定的日期,並用 NumPy 的
NaN
值替代:[1897?] - 將字串
nan
也轉換為 NumPy 的NaN
綜合以上,我們實際上可以利用一個正規表示式來提取出版年份:
regex = r'^(\d{4})'
複製程式碼
這個正規表示式意圖在字串的開頭找到四位數字,這足以滿足我們的要求。上面是一個原始字串(這意味著反斜槓不再是轉義字元),這是正規表示式的標準做法。
\d
表示任何數字,{4}
表示重複 4 次,^
表示匹配字串的開頭,括號表示一個捕獲組,它向 Pandas 表明我們想要提取正規表示式的這部分。(我們希望用 ^
來避免字串從 [
開始的情況。)
現在讓我們來看看我們在資料集中執行這個表示式時會發生什麼:
>>> extr = df['Date of Publication'].str.extract(r'^(\d{4})', expand=False)
>>> extr.head()
Identifier
206 1879
216 1868
218 1869
472 1851
480 1857
Name: Date of Publication, dtype: object
複製程式碼
對正則不熟悉?你可以在 regex101.com 這個網站上檢視上面這個正規表示式,也可以閱讀更多 Python 正規表示式 HOWTO 上的教程。
從技術上講,這一列仍然是 object
dtype,但是我們用 pd.to_numeric
即可輕鬆獲取數字:
>>> df['Date of Publication'] = pd.to_numeric(extr)
>>> df['Date of Publication'].dtype
dtype('float64')
複製程式碼
這麼做會導致十分之一的值丟失,但這相對於能夠對剩餘的有效值上進行計算而已,是一個比較小的代價:
>>> df['Date of Publication'].isnull().sum() / len(df)
0.11717147339205986
複製程式碼
很好!本節完成了!
結合 NumPy 以及 str
方法來清理列
上一部分,你可能已經注意到我們使用了 df['Date of Publication'].str
。這個屬性是訪問 Pandas 的快速字串操作的一種方式,它主要模仿了原生 Python 中的字串或編譯的正規表示式方法,例如 .split()
、.replace()
、.capitalize()
。
為了清理 Place of Publication
欄位,我們可以結合 Pandas 的 str
方法以及 NumPy 的 np.where
函式,這個函式基本上是 Excel 裡的 IF()
巨集的向量化形式。它的語法如下:
>>> np.where(condition, then, else)
複製程式碼
這裡,condition
可以是一個類似陣列的物件或者一個布林遮罩,如果 condition
為 True
,則使用 then
值,否則使用 else
值。
從本質上來說,.where()
函式對物件中的每個元素進行檢查,看 condition
是否為 True
,並返回一個 ndarray
物件,包含then
或者 else
的值。
它也可以被用於巢狀的 if-then 語句中,允許我們根據多個條件進行計算:
>>> np.where(condition1, x1,
np.where(condition2, x2,
np.where(condition3, x3, ...)))
複製程式碼
我們將用這兩個函式來清理 Place of Publication
一列,因為此列包含字串。以下是該列的內容:
>>> df['Place of Publication'].head(10)
Identifier
206 London
216 London; Virtue & Yorston
218 London
472 London
480 London
481 London
519 London
667 pp. 40. G. Bryan & Co: Oxford, 1898
874 London]
1143 London
Name: Place of Publication, dtype: object
複製程式碼
我們發現某些行中,出版地被其他不必要的資訊包圍著。如果觀察更多值,我們會發現只有出版地包含 ‘London’ 或者 ‘Oxford’ 的行才會出現這種情況。
我們來看看兩條特定的資料:
>>> df.loc[4157862]
Place of Publication Newcastle-upon-Tyne
Date of Publication 1867
Publisher T. Fordyce
Title Local Records; or, Historical Register of rema...
Author T. Fordyce
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 4157862, dtype: object
>>> df.loc[4159587]
Place of Publication Newcastle upon Tyne
Date of Publication 1834
Publisher Mackenzie & Dent
Title An historical, topographical and descriptive v...
Author E. (Eneas) Mackenzie
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 4159587, dtype: object
複製程式碼
這兩本書在用一個地方出版,但是一個地名中間包含連字元,另一個沒有。
想要一次性清理這一列,我們可以用 str.contains()
來獲得一個布林掩碼。
我們按如下方式清理此列:
>>> pub = df['Place of Publication']
>>> london = pub.str.contains('London')
>>> london[:5]
Identifier
206 True
216 True
218 True
472 True
480 True
Name: Place of Publication, dtype: bool
>>> oxford = pub.str.contains('Oxford')
複製程式碼
與 np.where
結合:
df['Place of Publication'] = np.where(london, 'London',
np.where(oxford, 'Oxford',
pub.str.replace('-', ' ')))
>>> df['Place of Publication'].head()
Identifier
206 London
216 London
218 London
472 London
480 London
Name: Place of Publication, dtype: object
複製程式碼
這裡,np.where
函式在巢狀結果中被呼叫,condition
是從 str.contains()
返回的布林值的 Series
物件。contains()
方法類似原生 Python 中內建的 in
關鍵字,它被用來查詢一個迭代器中某個實體是否出現(或者字串中是否有某子字串)。
替換的是我們想要的出版地點的字串。我們也用 str.replace()
方法將連字元替換成了空格然後重新賦值給 DataFrame
的列。
雖然這個資料集中還有很多髒資料,我們現在只討論這兩列。
讓我們來重新看看前五項,看起來比最開始的時候清晰多了:
>>> df.head()
Place of Publication Date of Publication Publisher \
206 London 1879 S. Tinsley & Co.
216 London 1868 Virtue & Co.
218 London 1869 Bradbury, Evans & Co.
472 London 1851 James Darling
480 London 1857 Wertheim & Macintosh
Title Author \
206 Walter Forbes. [A novel.] By A. A AA
216 All for Greed. [A novel. The dedication signed... A. A A.
218 Love the Avenger. By the author of “All for Gr... A. A A.
472 Welsh Sketches, chiefly ecclesiastical, to the... E. S A.
480 [The World in which I live, and my place in it... E. S A.
Flickr URL
206 http://www.flickr.com/photos/britishlibrary/ta...
216 http://www.flickr.com/photos/britishlibrary/ta...
218 http://www.flickr.com/photos/britishlibrary/ta...
472 http://www.flickr.com/photos/britishlibrary/ta...
480 http://www.flickr.com/photos/britishlibrary/ta...
複製程式碼
注意:到這裡,Place of Publication
會是一個很好轉化為 Categorical
dtype 的列,因為我們可以用整數對比較小的唯一的城市進行編碼。(分類資料型別的記憶體使用量與類別數目加上資料長度成正比,dtype 物件的大小是一個常數乘以資料長度。)
使用 applymap
函式清理整個資料集
在某些情況下,你會發現不僅是某一列裡有髒資料,而是分散在整個資料集。
有時如果可以對 DataFrame
裡的每個單元或元素都應用一個自定義函式會很有幫助。Pandas 的 .applymap()
函式類似內建的 map()
函式,只是它將應用於 DataFrame
中的所有元素。
讓我們來看個例子,我們將從 “university_towns.txt” 檔案中建立 DataFrame
。
$ head Datasets/univerisity_towns.txt
Alabama[edit]
Auburn (Auburn University)[1]
Florence (University of North Alabama)
Jacksonville (Jacksonville State University)[2]
Livingston (University of West Alabama)[2]
Montevallo (University of Montevallo)[2]
Troy (Troy University)[2]
Tuscaloosa (University of Alabama, Stillman College, Shelton State)[3][4]
Tuskegee (Tuskegee University)[5]
Alaska[edit]
複製程式碼
我們發現州名後面跟著大學城的名字這樣週期性出現:StateA TownA1 TownA2 StateB TownB1 TownB2…
,如果我們在檔案中檢視州名的寫法,會發現所有都有一個 “[edit]” 子字串。
我們可以利用這個模式建立一個 (state, city)
元組列表,並將它放入 DataFrame
。
>>> university_towns = []
>>> with open('Datasets/university_towns.txt') as file:
... for line in file:
... if '[edit]' in line:
... # Remember this `state` until the next is found
... state = line
... else:
... # Otherwise, we have a city; keep `state` as last-seen
... university_towns.append((state, line))
>>> university_towns[:5]
[('Alabama[edit]\n', 'Auburn (Auburn University)[1]\n'),
('Alabama[edit]\n', 'Florence (University of North Alabama)\n'),
('Alabama[edit]\n', 'Jacksonville (Jacksonville State University)[2]\n'),
('Alabama[edit]\n', 'Livingston (University of West Alabama)[2]\n'),
('Alabama[edit]\n', 'Montevallo (University of Montevallo)[2]\n')]
複製程式碼
我們可以將這個列表包入 DataFrame 中,並將列起名為 “State” 和 “RegionName”。Pandas 會獲取每個列表中的元素,將左邊的值放入 State
列,右邊的值放入 RegionName
列。
生成的 DataFrame 如下:
>>> towns_df = pd.DataFrame(university_towns,
... columns=['State', 'RegionName'])
>>> towns_df.head()
State RegionName
0 Alabama[edit]\n Auburn (Auburn University)[1]\n
1 Alabama[edit]\n Florence (University of North Alabama)\n
2 Alabama[edit]\n Jacksonville (Jacksonville State University)[2]\n
3 Alabama[edit]\n Livingston (University of West Alabama)[2]\n
4 Alabama[edit]\n Montevallo (University of Montevallo)[2]\n
複製程式碼
儘管我們可以使用 for 迴圈來清理上面的字串,但是使用 Pandas 會更加方便。我們只需要州名和城鎮名字,其他都可以刪除。雖然這裡也可以再次使用 .str() 方法,但我們也可以使用 applymap() 方法將一個 Python 可呼叫方法對映到 DataFrame 的每個元素上。
我們一直在使用元素這個術語,但實際上到底是指什麼呢?看一下以下這個 DataFrame 例子:
0 1
0 Mock Dataset
1 Python Pandas
2 Real Python
3 NumPy Clean
複製程式碼
在這個例子中,每個單元格(‘Mock’、‘Dataset’、‘Python’、‘Pandas’ 等)都是一個元素。所以 applumap()
方法將函式作用於每個元素上。假設定義函式為:
>>> def get_citystate(item):
... if ' (' in item:
... return item[:item.find(' (')]
... elif '[' in item:
... return item[:item.find('[')]
... else:
... return item
複製程式碼
Pandas 的 .applymap()
只接受一個引數,也就是將會作用於每個元素上的函式(可呼叫):
>>> towns_df = towns_df.applymap(get_citystate)
複製程式碼
首先,我們定義一個 Python 函式,它以 DataFrame
中的元素作為引數。在函式內部,執行元素是否包含 (
或 [
的檢查。
函式返回的值取決於這個檢查。最後,applymap()
函式在我們的 DataFrame
物件上被呼叫。現在我們的 DataFrame
物件更加簡潔了。
>>> towns_df.head()
State RegionName
0 Alabama Auburn
1 Alabama Florence
2 Alabama Jacksonville
3 Alabama Livingston
4 Alabama Montevallo
複製程式碼
applymap()
方法從 DataFrame 中獲取每個元素,將它傳遞給函式,然後將原來的值替換為函式返回的值。就是這麼簡單!
技術細節:雖然它是一個方便多功能的方法,但 .applymap()
對於較大的資料集會有明顯的執行時間,因為它將可呼叫的 Python 函式對映到每個單獨元素。某些情況下,使用 Cython 或者 NumPy (呼叫 C 語言)裡的向量化操作更高效。
列的重新命名以及跳過行
通常,需要處理的資料集可能包含不易理解的列名,或者某些包含不重要資訊的行,它們可能是最前面的有關術語定義的幾行,或者最末尾的腳註。
在這種情況下,我們希望重新命名列以及跳過某些行,以便我們可以只對必要的資訊以及有意義的標籤進行深入分析。
為了說明我們如何做到這一點,我們先來看一看 “olympics.csv” 資料集的前五行:
$ head -n 5 Datasets/olympics.csv
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
,? Summer,01 !,02 !,03 !,Total,? Winter,01 !,02 !,03 !,Total,? Games,01 !,02 !,03 !,Combined total
Afghanistan (AFG),13,0,0,2,2,0,0,0,0,0,13,0,0,2,2
Algeria (ALG),12,5,2,8,15,3,0,0,0,0,15,5,2,8,15
Argentina (ARG),23,18,24,28,70,18,0,0,0,0,41,18,24,28,70
複製程式碼
然後,將它讀入 Pandas 的 DataFrame 中:
>>> olympics_df = pd.read_csv('Datasets/olympics.csv')
>>> olympics_df.head()
0 1 2 3 4 5 6 7 8 \
0 NaN ? Summer 01 ! 02 ! 03 ! Total ? Winter 01 ! 02 !
1 Afghanistan (AFG) 13 0 0 2 2 0 0 0
2 Algeria (ALG) 12 5 2 8 15 3 0 0
3 Argentina (ARG) 23 18 24 28 70 18 0 0
4 Armenia (ARM) 5 1 2 9 12 6 0 0
9 10 11 12 13 14 15
0 03 ! Total ? Games 01 ! 02 ! 03 ! Combined total
1 0 0 13 0 0 2 2
2 0 0 15 5 2 8 15
3 0 0 41 18 24 28 70
4 0 0 11 1 2 9 12
複製程式碼
這確實很凌亂!列是從 0 開始索引的字串形式的數字。應該是頭部的行(也就是應該設定為列名的行)位於 olympics_df.iloc[0]
。發生這種情況是因為我們的 csv 檔案是以 0、1、2…15 開頭的。
另外,如果我們去檢視資料集的來源,會發現 NaN
應該是類似 “Country”,?Summer
應該代表的是 “Summer Games”,而 01!
應該是 “Gold” 等等。
所以,我們需要做以下兩件事:
- 跳過一行,將第一行(索引為 0)設定為 header
- 重新命名這些列
我們可以在讀取 CSV 檔案時通過傳遞一些引數給 read_csv()
函式來跳過某行並設定 header。
這個函式有很多可選的引數,但這個情況裡,我們只需要一個引數(header
)來移除第 0 行:
>>> olympics_df = pd.read_csv('Datasets/olympics.csv', header=1)
>>> olympics_df.head()
Unnamed: 0 ? Summer 01 ! 02 ! 03 ! Total ? Winter \
0 Afghanistan (AFG) 13 0 0 2 2 0
1 Algeria (ALG) 12 5 2 8 15 3
2 Argentina (ARG) 23 18 24 28 70 18
3 Armenia (ARM) 5 1 2 9 12 6
4 Australasia (ANZ) [ANZ] 2 3 4 5 12 0
01 !.1 02 !.1 03 !.1 Total.1 ? Games 01 !.2 02 !.2 03 !.2 \
0 0 0 0 0 13 0 0 2
1 0 0 0 0 15 5 2 8
2 0 0 0 0 41 18 24 28
3 0 0 0 0 11 1 2 9
4 0 0 0 0 2 3 4 5
Combined total
0 2
1 15
2 70
3 12
4 12
複製程式碼
我們現在已經有了正確的 header 行,以及移除了所有不必要的行。注意 Pandas 將包含國家名字的列的名字從 NaN
變成了 Unnames:0
。
要重新命名列,我們將利用 rename()
方法,這個方法允許你基於一個對映(本例中,指字典)來重新標記軸的名字。
讓我們從定義一個新的字典開始,它將現在的列的名字作為 key,對映到可用性更強的名字(字典值)。
>>> new_names = {'Unnamed: 0': 'Country',
... '? Summer': 'Summer Olympics',
... '01 !': 'Gold',
... '02 !': 'Silver',
... '03 !': 'Bronze',
... '? Winter': 'Winter Olympics',
... '01 !.1': 'Gold.1',
... '02 !.1': 'Silver.1',
... '03 !.1': 'Bronze.1',
... '? Games': '# Games',
... '01 !.2': 'Gold.2',
... '02 !.2': 'Silver.2',
... '03 !.2': 'Bronze.2'}
複製程式碼
然後呼叫 rename()
函式:
>>> olympics_df.rename(columns=new_names, inplace=True)
複製程式碼
將 inplace
引數設定為 True
可以將變化直接作用於我們的 DataFrame
物件上。讓我們看看是否生效:
>>> olympics_df.head()
Country Summer Olympics Gold Silver Bronze Total \
0 Afghanistan (AFG) 13 0 0 2 2
1 Algeria (ALG) 12 5 2 8 15
2 Argentina (ARG) 23 18 24 28 70
3 Armenia (ARM) 5 1 2 9 12
4 Australasia (ANZ) [ANZ] 2 3 4 5 12
Winter Olympics Gold.1 Silver.1 Bronze.1 Total.1 # Games Gold.2 \
0 0 0 0 0 0 13 0
1 3 0 0 0 0 15 5
2 18 0 0 0 0 41 18
3 6 0 0 0 0 11 1
4 0 0 0 0 0 2 3
Silver.2 Bronze.2 Combined total
0 0 2 2
1 2 8 15
2 24 28 70
3 2 9 12
4 4 5 12
複製程式碼
Python 資料清理:回顧以及其他資源
在本教程中,你學習瞭如何使用 drop()
函式刪除不必要的資訊,以及如何給你的資料集設定索引以便更加方便的引用其他的項。
此外,你也學習瞭如何使用 .str()
清理物件欄位,以及如何使用 applymap()
函式清理整個資料集。最後,我們探索了一下如何跳過 CSV 檔案中某些列以及使用 rename()
方法重新命名列。
瞭解資料清理非常重要,因為這是資料科學很重要的一部分。你現在已經對如何使用 Pandas 以及 NumPy 清理資料集有了基本的瞭解。
檢視以下連結可以幫你找到更多的資源繼續你的 Python 資料科學之旅:
- Pandas 文件
- NumPy 文件
- Python 資料分析 由 Pandas 的創造者 Wes McKinney 撰寫
- Pandas Cookbook 由資料科學教練和顧問 Ted Petrou 撰寫
Real Python 的每一個教程都是由一組開發人員建立,所以它符合我們的高質量標準。參與本教程的團隊成員是 Malay Agarwal (作者)以及 Brad Solomon (編輯)。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。