前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Pandas&NumPy进行数据清洗的6大常用方法

使用Pandas&NumPy进行数据清洗的6大常用方法

作者头像
Python数据科学
发布2018-08-06 18:17:14
3.1K0
发布2018-08-06 18:17:14
举报
文章被收录于专栏:Python数据科学Python数据科学

数据科学家花了大量的时间清洗数据集,并将这些数据转换为他们可以处理的格式。事实上,很多数据科学家声称开始获取和清洗数据的工作量要占整个工作的80%。

因此,如果你正巧也在这个领域中,或者计划进入这个领域,那么处理这些杂乱不规则数据是非常重要的,这些杂乱数据包括一些缺失值,不连续格式,错误记录,或者是没有意义的异常值。

在这个教程中,我们将利用Python的PandasNumpy包来进行数据清洗。

主要内容如下:

  • 删除 DataFrame 中的不必要 columns
  • 改变 DataFrame 的 index
  • 使用 .str() 方法来清洗 columns
  • 使用 DataFrame.applymap() 函数按元素清洗整个数据集
  • 重命名 columns 为一组更易识别的标签
  • 滤除 CSV文件中不必要的 rows

下面是要用到的数据集:

  • BL-Flickr-Images-Book.csv : 一份来自英国图书馆包含关于书籍信息的CSV文档
  • university_towns.txt : 一份包含美国各大洲大学城名称的text文档
  • olympics.csv : 一份总结了各国家参加夏季与冬季奥林匹克运动会情况的CSV文档

你可以从Real Python 的 GitHub repository 下载数据集来进行下面的例子。

注意:建议使用Jupter Notebooks来学习下面的知识。

学习之前假设你已经有了对Pandas和Numpy库的基本认识,包括Pandas的工作基础SeriesDataFrame对象,应用到这些对象上的常用方法,以及熟悉了NumPy的NaN值。

让我们导入这些模块开始我们的学习。

代码语言:javascript
复制
1>>> import pandas as pd
2>>> import numpy as np

删除DataFrame的列

经常的,你会发现数据集中不是所有的字段类型都是有用的。例如,你可能有一个关于学生信息的数据集,包含姓名,分数,标准,父母姓名,住址等具体信息,但是你只想分析学生的分数。

这个情况下,住址或者父母姓名信息对你来说就不是很重要。这些没有用的信息会占用不必要的空间,并会使运行时间减慢。

Pandas提供了一个非常便捷的方法drop()函数来移除一个DataFrame中不想要的行或列。让我们看一个简单的例子如何从DataFrame中移除列。

首先,我们引入BL-Flickr-Images-Book.csv文件,并创建一个此文件的DataFrame。在下面这个例子中,我们设置了一个pd.read_csv的相对路径,意味着所有的数据集都在Datasets文件夹下的当前工作目录中:

代码语言:javascript
复制
 1>>> df = pd.read_csv('Datasets/BL-Flickr-Images-Book.csv')
 2>>> df.head()
 3    Identifier             Edition Statement      Place of Publication  \
 40         206                           NaN                    London
 51         216                           NaN  London; Virtue & Yorston
 62         218                           NaN                    London
 73         472                           NaN                    London
 84         480  A new edition, revised, etc.                    London
 9  Date of Publication              Publisher  \
100         1879 [1878]       S. Tinsley & Co.
111                1868           Virtue & Co.
122                1869  Bradbury, Evans & Co.
133                1851          James Darling
144                1857   Wertheim & Macintosh
15                                               Title     Author  \
160                  Walter Forbes. [A novel.] By A. A      A. A.
171  All for Greed. [A novel. The dedication signed...  A., A. A.
182  Love the Avenger. By the author of “All for Gr...  A., A. A.
193  Welsh Sketches, chiefly ecclesiastical, to the...  A., E. S.
204  [The World in which I live, and my place in it...  A., E. S.
21                                   Contributors  Corporate Author  \
220                               FORBES, Walter.               NaN
231  BLAZE DE BURY, Marie Pauline Rose - Baroness               NaN
242  BLAZE DE BURY, Marie Pauline Rose - Baroness               NaN
253                   Appleyard, Ernest Silvanus.               NaN
264                           BROOME, John Henry.               NaN
27   Corporate Contributors Former owner  Engraver Issuance type  \
280                     NaN          NaN       NaN   monographic
291                     NaN          NaN       NaN   monographic
302                     NaN          NaN       NaN   monographic
313                     NaN          NaN       NaN   monographic
324                     NaN          NaN       NaN   monographic
33                                          Flickr URL  \
340  http://www.flickr.com/photos/britishlibrary/ta...
351  http://www.flickr.com/photos/britishlibrary/ta...
362  http://www.flickr.com/photos/britishlibrary/ta...
373  http://www.flickr.com/photos/britishlibrary/ta...
384  http://www.flickr.com/photos/britishlibrary/ta...
39                            Shelfmarks
400    British Library HMNTS 12641.b.30.
411    British Library HMNTS 12626.cc.2.
422    British Library HMNTS 12625.dd.1.
433    British Library HMNTS 10369.bbb.15.
444    British Library HMNTS 9007.d.28.

我们使用了head()方法得到了前五个行信息,这些列提供了对图书馆有帮助的辅助信息,但是并不能很好的描述这些书籍:Edition Statement, Corporate Author, Corporate Contributors, Former owner, Engraver, Issuance type and Shelfmarks

因此,我们可以用下面的方法移除这些列:

代码语言:javascript
复制
1>>> to_drop = ['Edition Statement',
2...            'Corporate Author',
3...            'Corporate Contributors',
4...            'Former owner',
5...            'Engraver',
6...            'Contributors',
7...            'Issuance type',
8...            'Shelfmarks']
9>>> df.drop(to_drop, inplace=True, axis=1)

在上面,我们定义了一个包含我们不要的列的名称列表。接着,我们在对象上调用drop()函数,其中inplace参数是Trueaxis参数是1。这告诉了Pandas,我们想要直接在我们的对象上发生改变,并且它应该可以寻找对象中被移除列的信息。

我们再次看一下DataFrame,我们会看到不想要的信息已经被移除了。

代码语言:javascript
复制
 1>>> df.head()
 2   Identifier      Place of Publication Date of Publication  \
 30         206                    London         1879 [1878]
 41         216  London; Virtue & Yorston                1868
 52         218                    London                1869
 63         472                    London                1851
 74         480                    London                1857
 8               Publisher                                              Title  \
 90       S. Tinsley & Co.                  Walter Forbes. [A novel.] By A. A
101           Virtue & Co.  All for Greed. [A novel. The dedication signed...
112  Bradbury, Evans & Co.  Love the Avenger. By the author of “All for Gr...
123          James Darling  Welsh Sketches, chiefly ecclesiastical, to the...
134   Wertheim & Macintosh  [The World in which I live, and my place in it...
14      Author                                         Flickr URL
150      A. A.  http://www.flickr.com/photos/britishlibrary/ta...
161  A., A. A.  http://www.flickr.com/photos/britishlibrary/ta...
172  A., A. A.  http://www.flickr.com/photos/britishlibrary/ta...
183  A., E. S.  http://www.flickr.com/photos/britishlibrary/ta...
194  A., E. S.  http://www.flickr.com/photos/britishlibrary/ta...

同样的,我们也可以通过给columns参数赋值直接移除列,而就不用分别定义to_drop列表和axis了。

代码语言:javascript
复制
1>>> df.drop(columns=to_drop, inplace=True)

这种语法更直观更可读。我们这里将要做什么就很明显了。

改变DataFrame的索引

Pandas索引index扩展了Numpy数组的功能,以允许更多多样化的切分和标记。在很多情况下,使用唯一的值作为索引值识别数据字段是非常有帮助的。

例如,仍然使用上一节的数据集,可以想象当一个图书管理员寻找一个记录,他们也许会输入一个唯一标识来定位一本书。

代码语言:javascript
复制
1>>> df['Identifier'].is_unique
2True

让我们用set_index把已经存在的索引改为这个列。

代码语言:javascript
复制
 1>>> df = df.set_index('Identifier')
 2>>> df.head()
 3                Place of Publication Date of Publication  \
 4206                           London         1879 [1878]
 5216         London; Virtue & Yorston                1868
 6218                           London                1869
 7472                           London                1851
 8480                           London                1857
 9                        Publisher  \
10206              S. Tinsley & Co.
11216                  Virtue & Co.
12218         Bradbury, Evans & Co.
13472                 James Darling
14480          Wertheim & Macintosh
15                                                        Title     Author  \
16206                         Walter Forbes. [A novel.] By A. A      A. A.
17216         All for Greed. [A novel. The dedication signed...  A., A. A.
18218         Love the Avenger. By the author of “All for Gr...  A., A. A.
19472         Welsh Sketches, chiefly ecclesiastical, to the...  A., E. S.
20480         [The World in which I live, and my place in it...  A., E. S.
21                                                   Flickr URL
22206         http://www.flickr.com/photos/britishlibrary/ta...
23216         http://www.flickr.com/photos/britishlibrary/ta...
24218         http://www.flickr.com/photos/britishlibrary/ta...
25472         http://www.flickr.com/photos/britishlibrary/ta...
26480         http://www.flickr.com/photos/britishlibrary/ta...

技术细节:不像在SQL中的主键一样,pandas的索引不保证唯一性,尽管许多索引和合并操作将会使运行时间变长如果是这样。

我们可以用一个直接的方法loc[]来获取每一条记录。尽管loc[]这个词可能看上去没有那么直观,但它允许我们使用基于标签的索引,这个索引是行的标签或者不考虑位置的记录。

代码语言:javascript
复制
1>>> df.loc[206]
2Place of Publication                                               London
3Date of Publication                                           1879 [1878]
4Publisher                                                S. Tinsley & Co.
5Title                                   Walter Forbes. [A novel.] By A. A
6Author                                                              A. A.
7Flickr URL              http://www.flickr.com/photos/britishlibrary/ta...
8Name: 206, dtype: object

换句话说,206是索引的第一个标签。如果想通过位置获取它,我们可以使用df.iloc[0],是一个基于位置的索引。

之前,我们的索引是一个范围索引:从0开始的整数,类似Python的内建range。通过给set_index一个列名,我们就把索引变成了Identifier中的值。

你也许注意到了我们通过df = df.set_index(...)的返回变量重新给对象赋了值。这是因为,默认的情况下,这个方法返回一个被改变对象的拷贝,并且它不会直接对原对象做任何改变。我们可以通过设置参数inplace来避免这个问题。

代码语言:javascript
复制
1df.set_index('Identifier', inplace=True)

清洗数据字段

到现在为止,我们移除了不必要的列并改变了我们的索引,让它们变得更有意义。这个部分,我们将清洗特殊的列,并使它们变成统一的格式,这样可以更好的理解数据集和加强连续性。特别的,我们将清洗Date of PublicationPlace of Publication

根据上面观察,所有的数据类型都是objectdtype类型,差不多类似于Python中的str。

它包含了一些不能被适用于数值或是分类的数据。这也正常,因为我们正在处理这些初始值就是杂乱无章字符串的数据。

代码语言:javascript
复制
1>>> df.get_dtype_counts()
2object    6

一个需要被改变为数值的的字段是date of publication所以我们做如下操作:

代码语言:javascript
复制
 1>>> df.loc[1905:, 'Date of Publication'].head(10)
 2Identifier
 31905           1888
 41929    1839, 38-54
 52836        [1897?]
 62854           1865
 72956        1860-63
 82957           1873
 93017           1866
103131           1899
114598           1814
124884           1820
13Name: Date of Publication, dtype: object

一本书只能有一个出版日期data of publication。因此,我们需要做以下的一些事情:

  • 移除在方括号内的额外日期,任何存在的:1879[1878]。
  • 将日期范围转化为它们的起始日期,任何存在的:1860-63;1839,38-54。
  • 完全移除我们不关心的日期,并用Numpy的NaN替换:[1879?]。
  • 将字符串nan转化为Numpy的NaN值。

考虑这些模式,我们可以用一个简单的正则表达式来提取出版日期:

代码语言:javascript
复制
1regex = r'^(\d{4})'

上面正则表达式的意思在字符串开头寻找任何四位数字,符合我们的情况。

\d代表任何数字,{4}重复这个规则四次。^符号匹配一个字符串最开始的部分,圆括号表示一个分组,提示pandas我们想要提取正则表达式的部分。

让我们看看运行这个正则在数据集上之后会发生什么。

代码语言:javascript
复制
1>>> extr = df['Date of Publication'].str.extract(r'^(\d{4})', expand=False)
2>>> extr.head()
3Identifier
4206    1879
5216    1868
6218    1869
7472    1851
8480    1857
9Name: Date of Publication, dtype: object

其实这个列仍然是一个object类型,但是我们可以使用pd.to_numeric轻松的得到数字的版本:

代码语言:javascript
复制
1>>> df['Date of Publication'] = pd.to_numeric(extr)
2>>> df['Date of Publication'].dtype
3dtype('float64')

这个结果中,10个值里大约有1个值缺失,这让我们付出了很小的代价来对剩余有效的值做计算。

代码语言:javascript
复制
1>>> df['Date of Publication'].isnull().sum() / len(df)
20.11717147339205986

结合str方法与Numpy清洗列

上面,你可以观察到df['Date of Publication'].str.的使用。这个属性是pandas里的一种提升字符串操作速度的方法,并有大量的Python字符串或编译的正则表达式上的小操作,例如.split(),.replace(),和.capitalize()

为了清洗Place of Publication字段,我们可以结合pandas的str方法和numpy的np.where函数配合完成。 它的语法如下:

代码语言:javascript
复制
1>>> np.where(condition, then, else)

这里,condition可以使一个类数组的对象,也可以是一个布尔表达。如果condition值为真,那么then将被使用,否则使用else

它也可以嵌套使用,允许我们基于多个条件进行计算。

代码语言:javascript
复制
1>>> np.where(condition1, x1, 
2        np.where(condition2, x2, 
3            np.where(condition3, x3, ...)))

我们将使用这两个函数来清洗Place of Publication由于这列有字符串对象。以下是这个列的内容:

代码语言:javascript
复制
 1>>> df['Place of Publication'].head(10)
 2Identifier
 3206                                  London
 4216                London; Virtue & Yorston
 5218                                  London
 6472                                  London
 7480                                  London
 8481                                  London
 9519                                  London
10667     pp. 40. G. Bryan & Co: Oxford, 1898
11874                                 London]
121143                                 London
13Name: Place of Publication, dtype: object

我们看到,对于一些行,place of publication还被一些其它没有用的信息围绕着。如果我们看更多的值,我们发现这种情况只适用于place of publication是"London"或者"Oxford"的行。

让我们看看两个特殊的:

代码语言:javascript
复制
 1>>> df.loc[4157862]
 2Place of Publication                                  Newcastle-upon-Tyne
 3Date of Publication                                                  1867
 4Publisher                                                      T. Fordyce
 5Title                   Local Records; or, Historical Register of rema...
 6Author                                                        T.  Fordyce
 7Flickr URL              http://www.flickr.com/photos/britishlibrary/ta...
 8Name: 4157862, dtype: object
 9>>> df.loc[4159587]
10Place of Publication                                  Newcastle upon Tyne
11Date of Publication                                                  1834
12Publisher                                                Mackenzie & Dent
13Title                   An historical, topographical and descriptive v...
14Author                                               E. (Eneas) Mackenzie
15Flickr URL              http://www.flickr.com/photos/britishlibrary/ta...
16Name: 4159587, dtype: object

这两本书在同一个地方出版,但是一个有连字符,另一个没有。

为了一次性清洗这个列,我们使用str.contains()来获取一个布尔值。

我们清洗的列如下:

代码语言:javascript
复制
 1>>> pub = df['Place of Publication']
 2>>> london = pub.str.contains('London')
 3>>> london[:5]
 4Identifier
 5206    True
 6216    True
 7218    True
 8472    True
 9480    True
10Name: Place of Publication, dtype: bool
11>>> oxford = pub.str.contains('Oxford')

我们将它与np.where结合。

代码语言:javascript
复制
 1df['Place of Publication'] = np.where(london, 'London',
 2                                      np.where(oxford, 'Oxford',
 3                                               pub.str.replace('-', ' ')))
 4>>> df['Place of Publication'].head()
 5Identifier
 6206    London
 7216    London
 8218    London
 9472    London
10480    London
11Name: Place of Publication, dtype: object

这里,np.where函数在一个嵌套的结构中被调用,condition是一个通过st.contains()得到的布尔的Seriescontains()方法与Python内建的in关键字一样,用于发现一个个体是否发生在一个迭代器中。

使用的替代物是一个代表我们期望的出版社地址字符串。我们也使用str.replace()将连字符替换为空格,然后给DataFrame中的列重新赋值。

尽管数据集中还有更多的不干净数据,但是我们现在仅讨论这两列。

让我们看看前五行,现在看起来比我们刚开始的时候好点了。

代码语言:javascript
复制
 1>>> df.head()
 2           Place of Publication Date of Publication              Publisher  \
 3206                      London                1879        S. Tinsley & Co.
 4216                      London                1868           Virtue & Co.
 5218                      London                1869  Bradbury, Evans & Co.
 6472                      London                1851          James Darling
 7480                      London                1857   Wertheim & Macintosh
 8                                                        Title    Author  \
 9206                         Walter Forbes. [A novel.] By A. A        AA
10216         All for Greed. [A novel. The dedication signed...   A. A A.
11218         Love the Avenger. By the author of “All for Gr...   A. A A.
12472         Welsh Sketches, chiefly ecclesiastical, to the...   E. S A.
13480         [The World in which I live, and my place in it...   E. S A.
14                                                   Flickr URL
15206         http://www.flickr.com/photos/britishlibrary/ta...
16216         http://www.flickr.com/photos/britishlibrary/ta...
17218         http://www.flickr.com/photos/britishlibrary/ta...
18472         http://www.flickr.com/photos/britishlibrary/ta...
19480         http://www.flickr.com/photos/britishlibrary/ta...

在这一点上,Place of Publication就是一个很好的需要被转换成分类数据的类型,因为我们可以用整数将这相当小的唯一城市集编码。(分类数据的使用内存与分类的数量以及数据的长度成正比)

使用applymap方法清洗整个数据集

在一定的情况下,你将看到并不是仅仅有一条列不干净,而是更多的。

在一些实例中,使用一个定制的函数到DataFrame的每一个元素将会是很有帮助的。pandasapplymap()方法与内建的map()函数相似,并且简单的应用到一个DataFrame中的所有元素上。

让我们看一个例子。我们将基于"university_towns.txt"文件创建一个DataFrame

代码语言:javascript
复制
 1$ head Datasets/univerisity_towns.txt
 2Alabama[edit]
 3Auburn (Auburn University)[1]
 4Florence (University of North Alabama)
 5Jacksonville (Jacksonville State University)[2]
 6Livingston (University of West Alabama)[2]
 7Montevallo (University of Montevallo)[2]
 8Troy (Troy University)[2]
 9Tuscaloosa (University of Alabama, Stillman College, Shelton State)[3][4]
10Tuskegee (Tuskegee University)[5]
11Alaska[edit]

我们可以看到每个state后边都有一些在那个state的大学城:StateA TownA1 TownA2 StateB TownB1 TownB2...。如果我们仔细观察state名字的写法,我们会发现它们都有"[edit]"的自字符串。

我们可以利用这个特征创建一个含有(state,city)元组的列表,并将这个列表嵌入到DdataFrame中,

代码语言:javascript
复制
 1>>> university_towns = []
 2>>> with open('Datasets/university_towns.txt') as file:
 3...     for line in file:
 4...         if '[edit]' in line:
 5...             # Remember this `state` until the next is found
 6...             state = line
 7...         else:
 8...             # Otherwise, we have a city; keep `state` as last-seen
 9...             university_towns.append((state, line))
10>>> university_towns[:5]
11[('Alabama[edit]\n', 'Auburn (Auburn University)[1]\n'),
12 ('Alabama[edit]\n', 'Florence (University of North Alabama)\n'),
13 ('Alabama[edit]\n', 'Jacksonville (Jacksonville State University)[2]\n'),
14 ('Alabama[edit]\n', 'Livingston (University of West Alabama)[2]\n'),
15 ('Alabama[edit]\n', 'Montevallo (University of Montevallo)[2]\n')]

我们可以在DataFrame中包装这个列表,并设列名为"State"和"RegionName"。pandas将会使用列表中的每个元素,然后设置State到左边的列,RegionName到右边的列。

最终的DataFrame是这样的:

代码语言:javascript
复制
1>>> towns_df = pd.DataFrame(university_towns,
2...                         columns=['State', 'RegionName'])
3>>> towns_df.head()
4 State                                         RegionName
50  Alabama[edit]\n                    Auburn (Auburn University)[1]\n
61  Alabama[edit]\n           Florence (University of North Alabama)\n
72  Alabama[edit]\n  Jacksonville (Jacksonville State University)[2]\n
83  Alabama[edit]\n       Livingston (University of West Alabama)[2]\n
94  Alabama[edit]\n         Montevallo (University of Montevallo)[2]\n

我们可以像上面使用for loop来进行清洗,但是pandas提供了更简单的办法。我们只需要state name和town name,然后就可以移除所以其他的了。这里我们可以再次使用pandas的.str()方法,同时我们也可以使用applymap()将一个python callable映射到DataFrame中的每个元素上。

我们一直在使用"元素"这个摄于,但是我们到底是什么意思呢?看看下面这个"toy"的DataFrame:

代码语言:javascript
复制
1        0           1
20    Mock     Dataset
31  Python     Pandas
42    Real     Python
53   NumPy     Clean

在这个例子中,每个单元 (‘Mock’, ‘Dataset’, ‘Python’, ‘Pandas’, etc.) 都是一个元素。因此,applymap()将分别应用一个函数到这些元素上。让我们定义这个函数。

代码语言:javascript
复制
1>>> def get_citystate(item):
2...     if ' (' in item:
3...         return item[:item.find(' (')]
4...     elif '[' in item:
5...         return item[:item.find('[')]
6...     else:
7...         return item

pandas的applymap()只用一个参数,就是要应用到每个元素上的函数(callable)。

代码语言:javascript
复制
1>>> towns_df =  towns_df.applymap(get_citystate)

首先,我们定义一个函数,它将从DataFrame中获取每一个元素作为自己的参数。在这个函数中,检验元素中是否有一个(或者[

基于上面的检查,函数返回相应的值。最后,applymap()函数被用在我们的对象上。现在DataFrame就看起来更干净了。

代码语言:javascript
复制
1>>> towns_df.head()
2     State    RegionName
30  Alabama        Auburn
41  Alabama      Florence
52  Alabama  Jacksonville
63  Alabama    Livingston
74  Alabama    Montevallo

applymap()方法从DataFrame中提取每个元素,传递到函数中,然后覆盖原来的值。就是这么简单!

技术细节:虽然.applymap是一个方便和灵活的方法,但是对于大的数据集它将会花费很长时间运行,因为它需要将python callable应用到每个元素上。一些情况中,使用Cython或者NumPY的矢量化的操作会更高效。

重命名列和移除行

经常的,你处理的数据集会有让你不太容易理解的列名,或者在头几行或最后几行有一些不重要的信息,例如术语定义,或是附注。

这种情况下,我们想重新命名列和移除一定的行以让我们只留下正确和有意义的信息。

为了证明我们如何处理它,我们先看一下"olympics.csv"数据集的头5行:

代码语言:javascript
复制
1$ head -n 5 Datasets/olympics.csv
20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
3,? Summer,01 !,02 !,03 !,Total,? Winter,01 !,02 !,03 !,Total,? Games,01 !,02 !,03 !,Combined total
4Afghanistan (AFG),13,0,0,2,2,0,0,0,0,0,13,0,0,2,2
5Algeria (ALG),12,5,2,8,15,3,0,0,0,0,15,5,2,8,15
6Argentina (ARG),23,18,24,28,70,18,0,0,0,0,41,18,24,28,70

现在我们将它读入pandas的DataFrame。

代码语言:javascript
复制
 1>>> olympics_df = pd.read_csv('Datasets/olympics.csv')
 2>>> olympics_df.head()
 3                   0         1     2     3     4      5         6     7     8  \
 40                NaN  ? Summer  01 !  02 !  03 !  Total  ? Winter  01 !  02 !
 51  Afghanistan (AFG)        13     0     0     2      2         0     0     0
 62      Algeria (ALG)        12     5     2     8     15         3     0     0
 73    Argentina (ARG)        23    18    24    28     70        18     0     0
 84      Armenia (ARM)         5     1     2     9     12         6     0     0
 9      9     10       11    12    13    14              15
100  03 !  Total  ? Games  01 !  02 !  03 !  Combined total
111     0      0       13     0     0     2               2
122     0      0       15     5     2     8              15
133     0      0       41    18    24    28              70
144     0      0       11     1     2     9              12

这的确有点乱!列名是以整数的字符串形式索引的,以0开始。本应该是列名的行却处在olympics_df.iloc[0]。发生这个是因为CSV文件以0, 1, 2, …, 15起始的。

同样,如果我们去数据集的源文件观察,上面的NaN真的应该是像"Country"这样的,? Summer应该代表"Summer Games", 而01 !应该是"Gold"之类的。

因此,我们需要做两件事:

  • 移除第一行并设置header为第一行
  • 重新命名列

当我们读CSV文件的时候,可以通过传递一些参数到read_csv函数来移除行和设置列名称。

这个函数有很多可选参数,但是这里我们只需要header来移除第0行:

代码语言:javascript
复制
 1>>> olympics_df = pd.read_csv('Datasets/olympics.csv', header=1)
 2>>> olympics_df.head()
 3          Unnamed: 0  ? Summer  01 !  02 !  03 !  Total  ? Winter  \
 40        Afghanistan (AFG)        13     0     0     2      2         0
 51            Algeria (ALG)        12     5     2     8     15         3
 62          Argentina (ARG)        23    18    24    28     70        18
 73            Armenia (ARM)         5     1     2     9     12         6
 84  Australasia (ANZ) [ANZ]         2     3     4     5     12         0
 9   01 !.1  02 !.1  03 !.1  Total.1  ? Games  01 !.2  02 !.2  03 !.2  \
100       0       0       0        0       13       0       0       2
111       0       0       0        0       15       5       2       8
122       0       0       0        0       41      18      24      28
133       0       0       0        0       11       1       2       9
144       0       0       0        0        2       3       4       5
15   Combined total
160               2
171              15
182              70
193              12
204              12

我们现在有了设置为header的正确行,并且所有没用的行都被移除了。记录一下pandas是如何将包含国家的列名NaN改变为Unnamed:0的。

为了重命名列,我们将使用DataFrame的rename()方法,允许你以一个映射(这里是一个字典)重新标记一个轴。

让我们开始定义一个字典将现有的列名称(键)映射到更多的可用列名称上(字典的值)。

代码语言:javascript
复制
 1>>> new_names =  {'Unnamed: 0': 'Country',
 2...               '? Summer': 'Summer Olympics',
 3...               '01 !': 'Gold',
 4...               '02 !': 'Silver',
 5...               '03 !': 'Bronze',
 6...               '? Winter': 'Winter Olympics',
 7...               '01 !.1': 'Gold.1',
 8...               '02 !.1': 'Silver.1',
 9...               '03 !.1': 'Bronze.1',
10...               '? Games': '# Games',
11...               '01 !.2': 'Gold.2',
12...               '02 !.2': 'Silver.2',
13...               '03 !.2': 'Bronze.2'}

我们在对象上调用rename()函数:

代码语言:javascript
复制
1>>> olympics_df.rename(columns=new_names, inplace=True)

设置inplaceTrue可以让我们的改变直接反映在对象上。让我们看看是否正确:

代码语言:javascript
复制
 1>>> olympics_df.head()
 2                   Country  Summer Olympics  Gold  Silver  Bronze  Total  \
 30        Afghanistan (AFG)               13     0       0       2      2
 41            Algeria (ALG)               12     5       2       8     15
 52          Argentina (ARG)               23    18      24      28     70
 63            Armenia (ARM)                5     1       2       9     12
 74  Australasia (ANZ) [ANZ]                2     3       4       5     12
 8   Winter Olympics  Gold.1  Silver.1  Bronze.1  Total.1  # Games  Gold.2  \
 90                0       0         0         0        0       13       0
101                3       0         0         0        0       15       5
112               18       0         0         0        0       41      18
123                6       0         0         0        0       11       1
134                0       0         0         0        0        2       3
14   Silver.2  Bronze.2  Combined total
150         0         2               2
161         2         8              15
172        24        28              70
183         2         9              12
194         4         5              12

Python数据清洗:回顾

这个教程中,你学会了从数据集中如何使用drop()函数去除不必要的信息,也学会了如何为数据集设置索引,以让items可以被容易的找到。

更多的,你学会了如何使用.str()清洗对象字段,以及如何使用applymap对整个数据集清洗。最后,我们探索了如何移除CSV文件的行,并且使用rename()方法重命名列。

掌握数据清洗非常重要,因为它是数据科学的一个大的部分。你现在应该有了一个如何使用pandas和numpy进行数据清洗的基本理解了。更多内容可参考pandas和numpy官网。

参考:https://realpython.com/python-data-cleaning-numpy-pandas/


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python数据科学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 删除DataFrame的列
  • 改变DataFrame的索引
  • 清洗数据字段
  • 结合str方法与Numpy清洗列
  • 使用applymap方法清洗整个数据集
  • 重命名列和移除行
  • Python数据清洗:回顾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档