python数据处理基础之pandas基础数据结构 -- Series

本章开始,我们来一起学习Pandas这个库,之前介绍过,pandas是数据处理过程中非常重要的一个基础库。它拥有SeriesDataFrame两大超实用的数据结构,使之可以方便地处理表格和其它复杂数据。pandas工作主要在数据清洗与分析上。但是,说得高大上,实质上,Pandas就是对各种表进行了抽象,以方便大家对表格进行处理的工具。

在介绍Pandas之前我们先来看看一张数据表,同时了解下pandas两个主要的数据结构DataFrame & Series,本章将先详细介绍Series,下一章再介绍DataFrame

一个图示看懂Series与DataFrame

从上面的图可以看出pandas就是对数据表进行处理的工具嘛~说得这么高大上,什么“清洗数据”、“加工数据”,其实就是牛逼点的代码版Excel~~

  • DataFrame

其中,DataFrame代表一个表结构,比如上面的表,有表头(体检结果:身高、体重、年龄等),有表内容(各个学生的体检结果)。这其中,每一行内容我们称之为记录(比如Peter这一行的体验结果就是一条记录)。而每一列的表头我们称之为字段(字段是用来索引的key),比如我们可以说Peter这条记录中的“tall”这个字段结果是120。

  • Series

Series就是用来抽象一行记录的数据结果(比如我在图中圈出来的)。也就是单看这条记录,我们可以用Series封装之。其中,记录的值称之为value,如120、70这些数据。而其对应的字段则称之为index。而上面说过,字段是用来索引的key,所以在Series中,其索性称为index了。比如Peter这个Series,我们要获取他的体重,直接使用index索引获得,如peter['weight'] # output => 70

当然,Series也可以用来抽象列记录,比如我们竖着看那张表,以weight这条列来切分,则[peter、key、anna]则变成了index,所以说这个数据结构还是抽象得非常灵活非常到位的。

下面我们就来深入学习下Series,看下怎么利用它来处理数据。

1 导图

(建议看每一章时,先把此导图打印出来对着看,好让自己对整体内容有个大概的把握与知道自己学习到哪个位置:)我自己做此笔记也是这样,把导图都打印出来,然后对着自己的笔记回顾,看完哪里就去哪里打个勾表示复习过一遍了)

2 Series的创建与基本操作

Series最主要的两个属性就是indexvalue。其中,index就是我们上面说的索引(也就是”字段”)。value就是每个字段的值。其中value是使用numpy的ndarray结构表示的;index是pandas.core.indexes.base.Index这个数据结构。比如我们下面创建一个Series细看下:

2.1 Series的创建

上面说了,Series最主要的两个属性就是indexvalue

本章所有示例都得先导入相应的库及修改别名,后面就不一一赘述了
1
2
3
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
Series的创建(文章后面有完整代码示例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 本文代码段中多次引用的log是我写的一个简单的输出对象的函数,
# 文章结尾的源码部分都有,后面都是如此,就不重复解释了。

peter = pd.Series(data=[120, 70, 10], index=['tall', 'weight', 'age'])
log('指定values and index', peter)
log("peter's value", peter.values)
log("Peter's index", peter.index)

# ==> 指定values and index: <class 'pandas.core.series.Series'>
# tall 120
# weight 70
# age 10
# dtype: int64
# 上面是系统输出的Series,可以看出是以左边一列index,右边一列values的形式输出。dtype是values元素的数据结构。
#
# ==> peter's value: <class 'numpy.ndarray'>
# [120 70 10]
#
# ==> Peter's index: <class 'pandas.core.indexes.base.Index'>
# Index(['tall', 'weight', 'age'], dtype='object')

可以看出,其主要的构造函数pd.Series可以传入data与index,分别表示values与index这两个重要的数据结构。同时,这两个重要的数据结构也可以直接访问之。

2.1.1 指定Series的name,或其index的name

Series本身及index都可以有name属性。其中,Series本身的Name你可以理解为整个表记录的名字,而index的name则理解为表头的名字:

比如,peter.name='student',表示这是一张学生体检表,每条记录代表一名学生。peter.index.name ='result',表示表头是体检的结果。

Series本身及index都可以有name属性
1
2
3
4
5
6
7
8
9
10
peter.name = 'student' # 表示这是一张学生体检表,每条记录代表一名学生。
peter.index.name ='result' # 表示表头是体检的结果。
print(peter) # 我们输出下指定了name的Series会变成怎样

# ==> <class 'pandas.core.series.Series'>
# result <-- index的name,你可以理解为表头名,说明index['tall', 'weight']等就是体验的结果
# tall 120
# weight 70
# age 10
# Name: student <-- Series名说明了这条记录是一条学生的体检记录, dtype: int64

2.1.2 使用ndarray数组来初始化Series

也可以直接使用数组来初始化Series,Pandas库会自动为之生成从0开始的序列作为index

直接用数组初始化series
1
2
3
4
5
6
7
8
9
series = Series([1,3,5,7,9])

# ==> 使用ndarray创建Series: <class 'pandas.core.series.Series'>
# 0 1
# 1 3
# 2 5
# 3 7
# 4 9
# dtype: int64

可见,左边那一列就是pandas自动生成的0开始的序列。

2.1.3 使用字典初始化Series

我们再仔细观察下Series后会发现,这个数据结构和py的字典dist结构有几分相似。Bingo!你除了可以将之视为一条记录切面之外,还可以将之视为一个定长的有序字典~比如你可以这样使用字典来初始化之:

使用字典来初始化Series
1
2
3
4
5
6
7
8
9
10
11
12
13
data = {
'a': 1,
'b': 3,
'c': 5
}
series = Series(data)
log('使用字典来初始化Series', series)

# ==> 使用字典来初始化Series: <class 'pandas.core.series.Series'>
# a 1
# b 3
# c 5
# dtype: int64

其中,字典的keys变成了所生成的Series的index。而value就是Series的values啦~

既然如此,你就会想到,Series是否也能为字典一样索引访问呢? —- 又Bingo!Series的索引访问不仅如字典一样方便,甚至还要更加强大!!

3 索引访问

Series的索引访问可以根据index的值访问到对应的value(注意这个方向,只能根据index找value,不能反过来,这点和dict结构也挺像的!)。

然后,Series的索引访问增加了dict所没有的一些常用的功能,比如:过滤、包含判断等。我们直接通过下面的源码来一探究竟。

索引访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 根据 index 取值
log("Peta's tall is peter['tall']", peter['tall'])
# 索引访问也可以像类成员引用或字典索引一样使用 "."下标符号
log("Peta's tall is peter.tall", peter.tall)

# 过滤索引
data = Series([1,3,5,7], index=['a', 'b', 'c', 'd'])
log("data > 3", data > 3)
log("data[data > 3]", data[data > 3])
log('np.power(data, 2)', np.power(data, 2))

# ==> data > 3: <class 'pandas.core.series.Series'>
# a False
# b False
# c True
# d True
# dtype: bool
#
# ==> data[data > 3]: <class 'pandas.core.series.Series'>
# c 5
# d 7
# dtype: int64

# 包含判断
log("'b' in data", 'b' in data) # 可以判断index, out=>True
log("5 in data", 5 in data) # 不能判断values, out=>False

4 Series提供的数据处理方法

Series提供了许多数据处理方法,下面介绍的一些处理方法很多会涉及到以后会介绍的数据清洗、数据聚合等,大家可以先大致看下有个大概有印象即可。记住,学习时整个知识体系是需要过N遍的,而前面的几遍尽量做到不求甚解,只对整体做了解与把握。

4.1 缺失值的处理

当我们构造Series或是在实际项目中运用时,难免会遇到缺失值。比如下面的示例,这是一张简单的key-value二维表,表示各大洲的选票数量。但是有个洲的选票就缺失了:

缺失值的出现
1
2
3
4
5
6
7
8
9
10
11
vote = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Ohio', 'Oregon', 'Texas']
s = Series(data=vote, index=states)
log('s', s)

# ==> s: <class 'pandas.core.series.Series'>
# California NaN
# Ohio 35000.0
# Oregon 16000.0
# Texas 71000.0
# dtype: float64

Pandas中,NaN表示缺失值(Not a Number)。我们可以使用pd.isnull(s) 或是 pd.notnull(s)方法将之“捉出来”:

缺失值的处理-isnull() & notnull()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
log('pd.isnull(s)', pd.isnull(s))  # 也可以直接 s.isnull()
log('pd.notnull(s)', pd.isnull(s)) # 也可以 s.notnull()

# ==> pd.isnull(s): <class 'pandas.core.series.Series'>
# California True
# Ohio False
# Oregon False
# Texas False
# dtype: bool
#
# ==> pd.notnull(s): <class 'pandas.core.series.Series'>
# California True
# Ohio False
# Oregon False
# Texas False
# dtype: bool

结果会返回一个新的Series,value表示原Series的值在此处是否为空。

这样如果我们要将有空值的key-value去掉怎么做呢?还记得前面说的过滤索引吗~~直接s[s.notnull()],这就是所谓的数据清洗了!

数据清洗
1
2
3
4
5
6
7
log('数据清洗', s[s.notnull()])

# ==> 数据清洗: <class 'pandas.core.series.Series'>
# Ohio 35000.0
# Oregon 16000.0
# Texas 71000.0
# dtype: float64

5 小结

作为小结和对本章的练习,大家可以试着在纸上算算下面程序的输出,并亲自复制到源文件中执行对比看自己对了多少:

本章源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# -*- coding: UTF-8 -*-
"""
pandas基础数据结构 Series and DataFrame.

Copyright 2018 luochenxun https://luochenxun.com
"""

import numpy as np
import pandas as pd
from pandas import Series, DataFrame

def log(msgOrOjb, obj=None):
"""一个实用的log方法."""
if isinstance(msgOrOjb, str):
if obj is not None:
print('\n==> ' + msgOrOjb + ':' , type(obj))
print(obj)
else:
print('\n==> ' + msgOrOjb)
else:
print('\n==> ', type(msgOrOjb))
print(msgOrOjb)


def main():
#---- Series的创建 ----

peter = pd.Series(data=[120, 70, 10], index=['tall', 'weight', 'age'])
log('指定values and index', peter)
log("peter's value", peter.values)
log("Peter's index", peter.index)

series = Series([1,3,5,7,9])
log('使用ndarray创建Series', series)
log('输出Series属性之values', series.values)
log('输出Series属性之index', series.index)

# 使用字典来初始化Series
data = {
'a': 1,
'b': 3,
'c': 5,
}
series = Series(data)
log('使用字典来初始化Series', series)

# Series本身及index都可以有个name属性。
# Series本身的Name你可以理解为整个表记录的名字,而index的name则理解为表头的名字
# 比如, peter.name='student',表示这是一张学生体检表,每条记录代表一名学生。
# peter.index.name ='result',表示表头是体检的结果。
peter.name = 'student' # 表示这是一张学生体检表,每条记录代表一名学生。
peter.index.name ='result' # 表示表头是体检的结果。

#---- Series的索引访问 ----
# 根据 index 取值
log("Peta's tall is peter['tall']", peter['tall'])
log("Peta's tall is peter.tall", peter.tall)
# 过滤索引
data = Series([1,3,5,7], index=['a', 'b', 'c', 'd'])
log("data > 3", data > 3)
log("data[data > 3]", data[data > 3])
log('np.power(data, 2)', np.power(data, 2))
# 包含判断
log("'b' in data", 'b' in data) # 可以判断index, out=>True
log("5 in data", 5 in data) # 不能判断values, out=>False

# --- Series 方法 ---
# 缺失值处理
# isnull(), notnull()
vote = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Ohio', 'Oregon', 'Texas']
s = Series(data=vote, index=states)
log('s', s)
log('pd.isnull(s)', pd.isnull(s))
log('pd.notnull(s)', pd.isnull(s))
log('数据清洗', s[s.notnull()])

if __name__ == '__main__':
main()


6 引用

  1. 《利用python进行数据分析》 - WesMcKinney著,SeanCheney译 - WesMcKinney是pandas的开发者,所以必须是讲解numpy、pandas等数据处理工具最权威的专家了,他的这本书写的很简洁很易读,SeanCheney翻译的也是非常完美。十分值得推荐的。