[譯] 使用 NumPy 和 Pandas 進行 Python 式數據清理

使用 NumPy 和 Pandas 進行 Python 式數據清理

數據科學家花費大量時間清理數據集,將它們清理爲能夠工做的形式。事實上,不少數據科學家表示,80% 的工做都是獲取和清理數據。html

所以,無論你是剛剛進入這個領域或者計劃進入,那麼處理混亂數據的能力會很是重要,不管這意味着缺失值、格式不一致、格式錯誤仍是無心義的異常值。前端

在此教程中,咱們將利用 Pandas 和 NumPy 這兩個庫來清理數據。python

咱們將介紹如下內容:android

  • 刪除 DataFrame 中沒必要要的列
  • 更改 DataFrame 的索引
  • .str() 方法清理列
  • 使用 DataFrame.applymap() 函數以元素方式清理數據集
  • 將列重命名爲更易識別的標籤
  • 跳過 CSV 文件中沒必要要的行

這些是咱們將要用到的數據集:ios

你能夠從 Real Python 的 GitHub 倉庫 下載全部數據集,以便查看如下示例。git

注意:我推薦使用 Jupyter Notebook 來進行如下步驟。github

本教程假設你對 Pandas 和 NumPy 庫有基本的瞭解,包括 Pandas 的主要工做對象 SeriesDataFrame,應用於它們的經常使用方法,以及熟悉 NumPy 的 NaN 值。正則表達式

讓咱們從 import 這些模塊開始吧!後端

>>> import pandas as pd
>>> import numpy as np
複製代碼

刪除 DataFrame 中沒必要要的列

你常常會發現數據集中並不是全部類別的數據都對你有用。例如,你可能有一個數據集包含了學生信息(名字、成績、標準、父母姓名和住址),但你想要專一於分析學生的成績。api

在這種狀況下,住址和父母姓名對你來講並不重要,保留這些類別將佔用沒必要要的空間,並可能拖累運行時間。

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 StatementCorporate AuthorCorporate ContributorsFormer ownerEngraverIssuance typeShelfmarks

咱們能夠這樣刪除這些列:

>>> 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 PublicationPlace 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 能夠是一個相似數組的對象或者一個布爾遮罩,若是 conditionTrue,則使用 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、一、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 數據科學之旅:

Real Python 的每個教程都是由一組開發人員建立,因此它符合咱們的高質量標準。參與本教程的團隊成員是 Malay Agarwal (做者)以及 Brad Solomon (編輯)。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索