以下内容主要学习自《利用Python进行数据分析》
第5章 Pandas入门(1)
pandas所包含的数据结构和数据处理工具,使得在pandas中进行数据清洗和分析非常快捷。从2010年成为开源项目以来,pandas的开发者社区已经有超过800个代码贡献者,他们帮助pandas成熟、成为在数据领域被广泛应用的大型类库。
pandas支持大部分NumPy风格的数组计算,尤其是数据函数以及没有for循环的各种数据处理。但他们也有差异:
- pandas是用来处理表格型或异质型数据的(想想关系型数据库中的表);
- 而NumPy更适合处理同质型的、数值类的数组。
作为约定,我会使用下面的快捷方式导入pandas:
import pandas as pd
因此,只要你看到pd.
,就表示对pandas的引用。

数据结构
为了入门pandas,你需要熟悉两个常用的数据结构:Series和DataFrame。
为便于理解,可以把DataFrame看成是关系型数据库的一个表、把Serise看成其中的一个列。但需要警惕,它们并不是完全相同,如:尽管DataFrame通常情况下是二维的,但可以用分层索引在DataFrame中展现更高维度的数据。
Series
Series是一种一维的数组型对象,它包含了一个值序列,并包含了数据标签,称为索引(index)。
创建Series
从一个Python列表创建Series
In [1]: import pandas as pd
In [2]: obj = pd.Series([4, 7, -5, 3])
In [3]: obj
Out[3]:
0 4
1 7
2 -5
3 3
dtype: int64
如上所示,Series对象的索引在左侧、值在右侧,默认的索引是从0到n-1的数字索引(n是数据的总量)。如果不使用默认的数字索引,可以自定义标签索引,如下:
In [4]: obj = pd.Series([4,7, -5, 3], index=['d', 'b', 'a', 'c'])
In [5]: obj
Out[5]:
d 4
b 7
a -5
c 3
dtype: int64
您还可以修改Series对象的标签索引,但必须保证索引个数与元素个数相等:
# 为Series对象指定一个名字
In [6]: obj.name = 'my datas'
# 为Series对象指定新的标签索引
In [7]: obj.index = ['张三','李四','王五','杨六']
In [8]: obj
Out[8]:
张三 4
李四 7
王五 -5
杨六 3
Name: my datas, dtype: int64
如果已经有一个包含数据的Python字典,那么也可以生成一个Series,默认情况下字典的键将成为Series的标签索引,如下:
In [6]: dict_datas = {'Ohio':3500, 'Texas':7100, 'Oregon':1600}
In [7]: obj = pd.Series(dict_datas)
In [8]: obj
Out[8]:
Ohio 3500
Texas 7100
Oregon 1600
dtype: int64
把Python字典对象转换为Series对象时,仍然可以指定标签索引。Series将会自动匹配字典键,对于缺失的将赋值为NaN(not a number),如下:
In [1]: dic_data = {'2018':-0.715068, '2019':-0.068092,'2020':0.448901}
In [2]: years = ['2017','2018','2019','2020']
In [3]: obj = pd.Series(dic_data, index=years)
In [4]: obj # 因为字典中没有2017,所以,被赋值为NaN
Out[4]:
2017 NaN
2018 -0.715068
2019 -0.068092
2020 0.448901
dtype: float64
访问Series
可以通过values属性
和index属性
分别获得Series对象的值和索引:
In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In [2]: obj.values
Out[2]: array([ 4, 7, -5, 3], dtype=int64)
In [3]: obj.index
Out[3]: Index(['d', 'b', 'a', 'c'], dtype='object')
可以使用数字索引
、也可以使用标签索引
来获取Series中元素的值:
In [4]: obj[0] # 使用数字索引,获取单个元素值
Out[4]: 4
In [5]: obj['d'] # 使用标签索引,获取单个元素值
Out[5]: 4
In [6]: obj[0:2] # 使用数字索引,获取多个元素值
Out[6]:
d 4
b 7
dtype: int64
In [7]: obj['d':'b'] # 使用标签索引,获取多个元素值
Out[7]:
d 4
b 7
dtype: int64
某些情景中,您可以使用in关键字
来判断Series的索引是否包含某个值:
In [8]: 'b' in obj
Out[8]: True
In [9]: 'e' in obj
Out[9]: False
还可以使用bool值作为滤条件,得到Series对象的子集:
In [10]: obj[obj > 0] # 选择大于0的元素
Out[10]:
d 4
b 7
c 3
dtype: int64
修改Series的值
通过Series对象的索引(数字或标签均可),可以修改Series对象的值:
In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In [2]: obj['b'] = -1 # 通过标签索引修改值
In [3]: obj[2:4] = 0 # 通过数字索引段,修改值
In [4]: obj
Out[4]:
d 4
b -1
a 0
c 0
dtype: int64
添加Series元素
如果要给Series对象添加单个元素,那么直接为新标签索引赋值即可:
In [5]: obj['e'] = 7
In [6]: obj
Out[6]:
d 4
b -1
a 0
c 0
e 7
dtype: int64
使用Series对象的append方法
可以把多个元素添加到现有的Series对象中:
In [7]: obj2 = pd.Serise([5, -2, 8])
In [8]: obj = obj.append(obj2)
In [9]: obj
Out[9]:
d 4
b -1
a 0
c 0
e 7
0 5
1 -2
2 8
dtype: int64
删除Series元素
使用drop方法
、并指定要删除的标签索引,将删除Series对象中的元素:
In [10]: obj.drop(labels=[0, 'c'])
Out[10]:
d 4
b -1
a 0
e 7
1 -2
2 8
dtype: int64
运算
可以对Series对象进行四则运算,如下:
In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In [3]: obj * 2 # 与标量的四则运算,乘2。
Out[3]:
d 8
b 14
a -10
c 6
dtype: int64
可以对Series对象应用数学函数,如下:
In [4]: import numpy as np
In [5]: np.exp(obj) # 求自然指数
Out[5]:
d 54.598150
b 1096.633158
a 0.006738
c 20.085537
dtype: float64
两个Series对象可以用+操作符
进行所谓的“数据对齐”操作,你可以认为这个特性与数据库的join
操作相似。
In [1]: obj1 = pd.Series([350, 160, 710, 500],
...: index=['张三','李四', '王五', '杨六'])
In [2]: obj2 = pd.Series([np.NaN, 350, 160],
...: index=['陈七', '张三', '李四'])
In [3]: obj = obj1 + obj2
In [4]: obj
Out[4]:
张三 700.0
李四 320.0
杨六 NaN
王五 NaN
陈七 NaN
dtype: float64
可以使用isnull方法
或notnull方法
判断Series对象是否包含空值。这两个方法,即是Series的实例方法、也是pandas的顶层方法。
# 使用实例方法判断“空值”
In [5]: obj.isnull()
Out[5]:
张三 False
李四 False
杨六 True
王五 True
陈七 True
dtype: bool
# 使用pandas顶层方法判断“非空值”
In [6]: pd.notnull(obj)
Out[6]:
张三 True
李四 True
杨六 False
王五 False
陈七 False
dtype: bool
DataFrame
DataFrame表示的是矩阵的数据表,每一列可以是不同的数据类型。DataFrame既有行索引也有列索引。DataFrame的每一列都是一个Series对象。
尽管不完全正确,但可以用关系数据库表来类比:行索引相当于主键、列索引相当于列名。
尽管DataFrame是二维的,但你可以利用分层索引在DataFrame中展现更高维度的数据,将在后续章节进行讨论。
创建DataFrame
下面的例子演示了用ndarray创建一个最简单的DataFrame。
# 引用numpy、pandas
In [1]: import numpy as np
In [2]: import pandas as pd
# 构建了一个3X4的二维ndarray
In [3]: arr = np.arange(12).reshape(3,4)
In [4]: arr
Out[4]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 用ndarray创建DataFrame
In [5]: df = pd.DataFrame(arr)
In [6]: df
Out[6]:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
注意上面的示例,DataFrame对象除了数据,还包含列索引和行索引,缺省情况下,它们都是从0开始编号的顺序值。
如果我们不想使用默认的行/列索引,可以用index属性
和columns属性
修改DataFrame对象的行/列索引。
In [7]: df.index = ['a','b','c']
In [8]: df.columns = ['cn1','cn2','cn3','cn4']
# 经过以上的修改,DataFrame有了新的行列索引
In [9]: df
Out[9]:
cn1 cn2 cn3 cn4
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
# 可以给DataFrame的name属性赋值,类似于定义表名
In [10]: df.name = 'my datas'
当然,index、columns可以在构造时就指定,以下代码的效果与上面是一致的。
# 引用numpy、pandas
In [1]: import numpy as np
In [2]: import pandas as pd
# 构建了一个3X4的二维ndarray
In [3]: arr = np.arange(12).reshape(3,4)
# 用ndarray创建DataFrame
In [24]: df = pd.DataFrame(arr,
...: index = ['a','b','c'],
...: columns = ['cn1','cn2','cn3','cn4'])
In [25]: df
Out[25]:
cn1 cn2 cn3 cn4
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
包含等长度列表或NumPy数组的字典,也可以用来构建DataFrame。字典的键将被pandas视为列名。请参考如下的代码:
In [1]: data = {'state': ['Ohio','Ohio','Ohio','Nevada','Nevada','Nevada'],
...: 'year': [2000,2001,2002,2001,2002,2003],
...: 'pop': [1.5,1.7,3.6,2.4,2.9,3.2]}
In [2]: df = pd.DataFrame(data)
In [3]: df
Out[3]:
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
用字典构建DataFrame时,如果传入columns参数,pandas会按照columns的顺序对列排序;如果字典的键不包含在colums中,pandas会填充缺失值,如下:
In [4]: df = pd.DataFrame(data, columns=['year','state','pop','debt'],
...: index=['one','tow','three','four','five','six'])
In [5]: df
Out[5]:
year state pop debt
one 2000 Ohio 1.5 NaN
tow 2001 Ohio 1.7 NaN
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 NaN
five 2002 Nevada 2.9 NaN
six 2003 Nevada 3.2 NaN
在上面的示例中,我们还传入了index参数,用来取代pandas默认的、从0开始的数字行索引。
使用DataFrame构造函数
创建DataFrame对象的有效输入小结如下:
类型 | 说明 |
---|---|
2D ndarray | 二维数组,行和列的标签是可选参数 |
数组、列表、元组构成的字典 | 每个序列成为DataFrame的一列,所有序列必须长度相同 |
NumPy结构化数组 | 与数组构成的字典一致 |
Series构成的字典 | 每个值成为一列,每个Series的索引联合起来形成行索引 |
字典构成的字典 | 每个内部字典成为一列,键联合起来形成行索引 |
字典或Series构成的列表 | 列表中的一个元素形成一行,字典键或Series索引联合起来形成列标签 |
列表或元组构成的列表 | 与2D ndarray一致 |
其它DataFrame | 默认使用原DataFrame的行列索引 |
NumPy MaskedArray | 与2D ndarry一致,但隐藏值会成为NaN缺失值 |
访问数据
在科学计算中,数据量往往非常大。如果仅要看看DataFrame的结构,可以调用head方法
获取头部的5行数据
In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])
In [3]: df
Out[3]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
In [4]: df.head()
Out[4]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
下面的示例演示了获取DataFrame的行数、列数、行索引、列索引等:
# 获取行索引
In [5]: df.index
Out[5]: Index(['ont', 'tow', 'three', 'four', 'five', 'six'], dtype='object')
# 得到行数
In [6]: len(df.index)
Out[6]: 6
# 获取列索引
In [7]: df.columns
Out[7]: Index(['year', 'state', 'pop'], dtype='object')
# 得到列数
In [8]: len(df.columns)
Out[8]: 3
获取行数据,采用与NumPy一致的索引方式,索引值可以是数字索引或标签索引,如下:
In [9]: df[1:3] # 用数字索引获取第1、2行数据
Out[9]:
year state pop
two 2001 Ohio 1.7
three 2002 Ohio 3.6
In [10]: df['four':] # 用标签索引获取行数据
Out[10]:
year state pop
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
获取列数据,采用与字典键值索引一致的语法,按列索引得到的结果是Series对象:
In [11]: df['year']
Out[11]:
ont 2000
two 2001
three 2002
four 2001
five 2002
six 2003
Name: year, dtype: int64
获取单元数据,需要同时传入列索引和行索引,如下:
In [12]: df['year']['three']
Out[12]: 2002
获取切片数据,就是得到部分的行列数据子集,关键的语法是:起始行索引用冒号分割,列索引要明确指定并封装为一个列表。至于行索引在前还是列索引在前,都无所谓。如下:
In [13]: df['three':'five'][['state','pop']]
Out[13]:
state pop
three Ohio 3.6
four Nevada 2.4
five Nevada 2.9
# 行列索引的先后次序没有关系,下面的语句与如上的等效
In [12]: df[['state','pop']]['three':'five']
调用DataFrame对象的values属性
,可以得到ndarray类型的数据集(没有行列索引):
In [14]: df.values
Out[14]:
array([[2000, 'Ohio', 1.5],
[2001, 'Ohio', 1.7],
[2002, 'Ohio', 3.6],
[2001, 'Nevada', 2.4],
[2002, 'Nevada', 2.9],
[2003, 'Nevada', 3.2]], dtype=object)
修改数据
初始化dataframe如下:
In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])
In [3]: df
Out[3]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
修改行数据,可以通过指定行索引为dataframe的行赋值,下面的例子把一个固定值赋值给行:
# 通过指定行索引为第一行数据赋值
In [4]: df[0:1] = 0
In [5]: df
Out[5]:
year state pop
one 0 0 0.0
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
您可以看出,上面的示例能够正确运行,按并不是一个好的示例。因为每列的数据类型并不相同,为某行的所有列赋值为同一个常量会导致有歧义。
更好些的方式,是为某行赋值一个列表,如下所示:
In [6]: df[0:1] = [2000,'Ohio',3.8]
In [7]: df
Out[7]:
year state pop
one 2000 Ohio 3.8
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
修改列数据
可以把常数赋值给某一列
In [8]: df['pop']=0
In [9]: df
Out[9]:
year state pop
one 2000 Ohio 0
tow 2001 Ohio 0
three 2002 Ohio 0
four 2001 Nevada 0
five 2002 Nevada 0
six 2003 Nevada 0
也可以把一个列表赋值给某一列:
In [10]: df['pop']= [3.3, 1.2, 3.1, 1.9, 2.4, 2.7]
In [11]: df
Out[11]:
year state pop
one 2000 Ohio 3.3
tow 2001 Ohio 1.2
three 2002 Ohio 3.1
four 2001 Nevada 1.9
five 2002 Nevada 2.4
six 2003 Nevada 2.7
还可以在列上直接使用标量运算:
In [12]: df['pop'] = df['pop'] * 3
In [13]: df
Out[13]:
year state pop
one 2000 Ohio 9.9
tow 2001 Ohio 3.6
three 2002 Ohio 9.3
four 2001 Nevada 5.7
five 2002 Nevada 7.2
six 2003 Nevada 8.1
修改单元数据,需要同时指定行索引和列索引。
下面的示例演示了如何修改一个单元格数据
In [14]: df[0:1]['pop'] = 8.8
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.Try using .loc[row_indexer,col_indexer] = value instead
In [15]: df
Out[15]:
year state pop
one 2000 Ohio 8.8
tow 2001 Ohio 3.6
three 2002 Ohio 9.3
four 2001 Nevada 5.7
five 2002 Nevada 7.2
six 2003 Nevada 8.1
请注意,虽然上面的语句可以完成单元格赋值,但引发了一个警告,该警告说明:比起使用上面的方法,使用.loc[row_indexer,col_indexer] = value
更好,在下方的整数索引章节会有更详细的说明。
添加数据
初始化dataframe如下:
In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])
In [3]: df
Out[3]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
添加行数据
下面的示例使用字典对象为dataframe添加一行
In [4]: df.append({'year': 2001, 'state': 'Kilo', 'pop': 2.5}, ignore_index=True)
Out[4]:
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
5 2003 Nevada 3.2
6 2001 Kilo 2.5
在上面的代码中,如果不使用ignore_index=True
参数,会报告错误“TypeError: Can only append a Series if ignore_index=True or if the Series has a name”。
下面的代码演示如何用Series对象为dataframe添加行数据,其效果与上面是一样的。
In [5]: rd = pd.Series({'year': 2001, 'state': 'Kilo', 'pop': 2.5} , name='seven')
In [6]: df.append(rd)
Out[6]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
seven 2001 Kilo 2.5
另外,如果两个dataframe的结构一致,也可以使用append方法
追加行数据。
添加列数据
使用insert方法
为dataframe对象添加一列。
# 把名为memo的数据添加到第二列
In [7]: df.insert(1, 'memo', ['m1','m2','m3','m4','m5','m6'])
In [8]: df
Out[8]:
year memo state pop
one 2000 m1 Ohio 1.5
tow 2001 m2 Ohio 1.7
three 2002 m3 Ohio 3.6
four 2001 m4 Nevada 2.4
five 2002 m5 Nevada 2.9
six 2003 m6 Nevada 3.2
删除数据
使用drop方法
删除行列,该方法中的参数axis为1表示删除列,0表示删除行。inplace为True表示直接对原表修改。
下面的示例演示了如何删除列
In [9]: df.drop('memo', axis=1, inplace=True)
In [10]: df
Out[10]:
year state pop
one 2000 Ohio 1.5
tow 2001 Ohio 1.7
three 2002 Ohio 3.6
four 2001 Nevada 2.4
five 2002 Nevada 2.9
six 2003 Nevada 3.2
下面的示例演示了如何删除行
In [11]: df.drop(['two','three','five'], axis=0, inplace=True)
In [12]: df
Out[12]:
year state pop
one 2000 Ohio 1.5
four 2001 Nevada 2.4
six 2003 Nevada 3.2
运算
使用dataframe对象的stack方法
和unstack方法
可以行列转置,如下:
In [13]: df.stack()
Out[13]:
one year 2000
state Ohio
pop 1.5
four year 2001
state Nevada
pop 2.4
six year 2003
state Nevada
pop 3.2
dtype: object
In [14]: df.stack().unstack(0)
Out[14]:
one four six
year 2000 2001 2003
state Ohio Nevada Nevada
pop 1.5 2.4 3.2
index
索引对象
pandas中的索引对象是用于存储轴标签和其它元数据的。大多数时候,我们并不常用索引对象提供的功能,但是因为一些操作会产生包含索引化数据的结果,因此有必要理解索引是如何工作的。
在构造Series或DataFrame时,你所使用的任意数组或标签都可以在内部转换为索引对象
In [1]: import numpy as np
In [2]: import pandas as pd
In [3]: obj = pd.Series(range(3), index=['a', 'b', 'c'])
In [4]: index = obj.index
In [5]: index
Out[5]: Index(['a', 'b', 'c'], dtype='object')
In [6]: index[1:]
Out[6]: Index(['b', 'c'], dtype='object')
注意:索引对象是不可改变的,也就是说用户无法修改索引对象。不变性使得在多种数据结构中分享索引对象更为安全。另外,pandas索引对象可以包含重复的标签数据。
方法和属性
索引对象提供了一些方法和属性,如下表所示:
方法 | 描述 |
---|---|
append | 将额外的索引对象追加到原索引对象后,产生一个新的索引 |
difference | 计算两个索引的差集 |
intersection | 计算两个索引的交集 |
union | 计算两个索引的并集 |
isin | 计算表示每一个值是否在容器中 |
delete | 将位置i的元素删除,并产生新的索引 |
drop | 根据传参删除指定索引值,并产生新的索引 |
insert | 在位置i处插入元素,并产生新的索引 |
is_monotonic | 如果索引序列递增,返回True |
is_unique | 如果索引序列值唯一,返回True |
unique | 计算索引的唯一值序列 |
网友评论