[译] 使用 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前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索