前言
pandas所包含的数据结构和数据处理工具的设计使得在Python中进行数据清洗和分析非常快捷。pandas支持大部分NumPy语言风格的数组计算,尤其是数组函数以及没有for循环的各种数据处理。尽管pandas采用了很多NumPy的代码风格,但最大的不同在于pandas是用来处理表格型或异质型数据的。而NumPy则相反,它更适合处理同质型的数值类数组数据。
一、pandas数据结构介绍
pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。为了入门pandas,需要先了解两个常用的工具数据结构:Series和DataFrame。别忘了在代码实现中,导入pandas:
import pandas as pd
1.1 Series
Series是一种一维的数组型对象,它包含了一个值序列,并且包含了数据标签,称为索引(index)。最简单的序列可以仅仅由一个数组组成:
n = pd.Series([4, 5, -7, 1])
print(n)
----------------------------------------------------------------
0 4
1 5
2 -7
3 1
dtype: int64
交互式环境中Series的字符串表示,索引在左边,值在右边。由于我们不为数据指定索引,默认生成的索引是从0到N-1( N是数据长度)。你可以通过values属性和index属性分别获得Series对象的值和索引:
n1 = n.values
print(n1)
n1 = n.index
print(n1)
----------------------------------------------------------------
[ 4 5 -7 1]
RangeIndex(start=0, stop=4, step=1)
我们通常需要创建一个索引序列,用标签标识每个数据点:
n2 = pd.Series([4, 5, -7, 3], index=['a', 'b', 'c', 'd'])
print(n2)
n3 = n2.index
print(n3)
----------------------------------------------------------------
a 4
b 5
c -7
d 3
dtype: int64
Index(['a', 'b', 'c', 'd'], dtype='object')
你可以在从数据中选择数据的时候使用标签来进行索引:
n3 = n2['a']
print(n3)
n2['d'] = 6
print(n2[['b', 'c', 'd']])
----------------------------------------------------------------
4
b 5
c -7
d 6
dtype: int64
若是使用NumPy的函数或NumPy风格的操作,比如使用布尔值数组进行过滤,与标量相乘,或是应用数学函数,这些操作将保存索引值连接:
n3 = n2[n2>0]
print(n3)
n3 = n2 * 2
print(n3)
n3 = np.exp(n2)
print(n3)
----------------------------------------------------------------
a 4
b 5
d 6
dtype: int64
a 8
b 10
c -14
d 12
dtype: int64
a 54.598150
b 148.413159
c 0.000912
d 403.428793
dtype: float64
从另一个角度考虑Series,可以认为它是一个长度固定且有序的字典,因为它将索引值和数据值按位置配对。如果你已经有数据包含在Python字典中,你可以使用字典生成一个Series:
sdata = {'oi': 35000, 'texas': 71000, 'oregon': 16000, 'utah': 5000}
n3 = pd.Series(sdata)
print(n3)
----------------------------------------------------------------
oi 35000
texas 71000
oregon 16000
utah 5000
dtype: int64
当你把字典传递给Series构造函数时,产生的Series的索引将是排序好的字典键。你可以将字典键按照你所想要的顺序传递给构造函数,从而使生成的Series的索引顺序符号你的预期:
stats = ['california', 'oi', 'oregon', 'texas']
n4 = pd.Series(sdata,index=stats)
print(n4)
----------------------------------------------------------------
california NaN
oi 35000.0
oregon 16000.0
texas 71000.0
dtype: float64
在这个例子中sdata中的三个值被放置在正确的位置,但是因为‘california‘没有出现在sdata的键中,因此它对应的是NaN,这是pandas中标记缺失值或NA值的方式。而因为sdata中的utah并不在stats中,被排除在结果对象外。
当遇到缺失数据时,pandas中使用isnull和notnull函数来检查缺失数据:
n5 = pd.isnull(n4)
print(n5)
n5 = n4.notnull()
print(n5)
----------------------------------------------------------------
california True
oi False
oregon False
texas False
dtype: bool
california False
oi True
oregon True
texas True
dtype: bool
对于很多应用来说,在数学操作中自动对齐索引是Series的一个非常有用的特性:
n5 = n3 + n4
print(n5)
----------------------------------------------------------------
california NaN
oi 70000.0
oregon 32000.0
texas 142000.0
utah NaN
dtype: float64
Series对象自身和其索引都有name属性,这个特性与pandas其他重要功能集成在一起:
n6 = n4.name
print(n6)
n7 = n4.index.name
print(n7)
n4.name = 'population'
n4.index.name = 'state'
print(n4)
----------------------------------------------------------------
None
None
state
california NaN
oi 35000.0
oregon 16000.0
texas 71000.0
Name: population, dtype: float64
不过一开始Series对象自身和索引的name属性都为None。
Series的索引也可以通过按位赋值的方式进行改变:
n4.index=['1','2','4','3']
print(n4)
----------------------------------------------------------------
1 NaN
2 35000.0
4 16000.0
3 71000.0
Name: population, dtype: float64
1.2 DataFrame
DataFrame表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔型等)。DataFrame既有行索引也有列索引,它可以视为一个共享相同索引的Series的字典。在DataFrame中,数据被存储为一个以上的二维块,不是列表,字典或者其他一维数组的集合。
有多种方式可以构建DataFrame,其中最常用的方式是利用包含等长度列表或NumPy数组的字典来形成DataFrame:
data = {'state': ['zhangshang', 'lishi', 'lishi', 'lishi', 'lizi', 'lizi'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)
print(frame)
----------------------------------------------------------------
state year pop
0 zhangshang 2000 1.5
1 lishi 2001 1.7
2 lishi 2002 3.6
3 lishi 2001 2.4
4 lizi 2002 2.9
5 lizi 2003 3.2
对于大型的DataFrame,head方法将会只选出头部的五行:
p1 = frame.head()
print(p1)
----------------------------------------------------------------
state year pop
0 zhangshang 2000 1.5
1 lishi 2001 1.7
2 lishi 2002 3.6
3 lishi 2001 2.4
4 lizi 2002 2.9
如果指定了列的顺序,DataFrame的列将会按照指定顺序排列:
frame1 = pd.DataFrame(data, columns=['year', 'pop', 'state'])
print(frame1)
----------------------------------------------------------------
year pop state
0 2000 1.5 zhangshang
1 2001 1.7 lishi
2 2002 3.6 lishi
3 2001 2.4 lishi
4 2002 2.9 lizi
5 2003 3.2 lizi
如果你传的列不包含在字典里,将会在结果中出现缺失值:
frame2 = pd.DataFrame(data, columns=['year', 'pop', 'state', 'debt'],
index=['one', 'two', 'three', 'four', 'five', 'six'])
print(frame2)
----------------------------------------------------------------
year pop state debt
one 2000 1.5 zhangshang NaN
two 2001 1.7 lishi NaN
three 2002 3.6 lishi NaN
four 2001 2.4 lishi NaN
five 2002 2.9 lizi NaN
six 2003 3.2 lizi NaN
DataFrame中的一列,可以按字典型标记或属性那样检索为Series:
p2 = frame2['state']
print(p2)
p2 = frame2.year
print(p2)
----------------------------------------------------------------
one zhangshang
two lishi
three lishi
four lishi
five lizi
six lizi
Name: state, dtype: object
one 2000
two 2001
three 2002
four 2001
five 2002
six 2003
Name: year, dtype: int64
注意frame2[colum]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。
行也可以通过位置或者特殊属性loc进行选取:
p2 = frame2.loc['three']
print(p2)
----------------------------------------------------------------
year 2002
pop 3.6
state lishi
debt NaN
Name: three, dtype: object
列的引用是可以修改的。例如,空的’debt‘列可以赋值为标量值或者值数组:
frame2['debt'] = np.arange(6.)
print(frame2)
----------------------------------------------------------------
year pop state debt
one 2000 1.5 zhangshang 0.0
two 2001 1.7 lishi 1.0
three 2002 3.6 lishi 2.0
four 2001 2.4 lishi 3.0
five 2002 2.9 lizi 4.0
six 2003 3.2 lizi 5.0
当你想要将列表或者数组赋给一个列时,值的长度必须和DataFrame的长度相匹配。如果你将Series赋值给一列时,Series的索引将会按照DataFrame的索引重新排列,并在空缺的地方填充缺失值:
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val
print(frame2)
----------------------------------------------------------------
year pop state debt
one 2000 1.5 zhangshang NaN
two 2001 1.7 lishi -1.2
three 2002 3.6 lishi NaN
four 2001 2.4 lishi -1.5
five 2002 2.9 lizi -1.7
six 2003 3.2 lizi NaN
如果被赋值的列并不存在,则会生成一个新的列。在下面这个例子中,我们首先增加一列,这一列是布尔值,判断条件是state列是否为’lishi‘:
frame2['eastern'] = (frame2.state == 'lishi')
print(frame2)
----------------------------------------------------------------
year pop state debt eastern
one 2000 1.5 zhangshang NaN False
two 2001 1.7 lishi -1.2 True
three 2002 3.6 lishi NaN True
four 2001 2.4 lishi -1.5 True
five 2002 2.9 lizi -1.7 False
six 2003 3.2 lizi NaN False
del关键字可以像在字典中那样对DataFrame删除列。del方法可以用于移除之前新建的列:
del frame2['eastern']
p2 = frame2.columns
print(p2)
----------------------------------------------------------------
Index(['year', 'pop', 'state', 'debt'], dtype='object')
从DataFrame中选取的列是数据的视图,而不是拷贝。因此对Series的修改会映射到DataFrame中。如果需要复制,则应当使用Series的copy方法。
另一种常用的数据形式是包含字典的嵌套字典。如果嵌套字典被赋值给DataFrame,pandas会将字典的键作为列,将内部字典的键作为行索引:
pop = {'nevada': {2001: 2.4, 2002: 2.9},
'ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop)
print(frame3)
----------------------------------------------------------------
nevada ohio
2001 2.4 1.7
2002 2.9 3.6
2000 NaN 1.5
可以对DataFrame进行转置操作(调换行和列):
frame4 = frame3.T
print(frame4)
----------------------------------------------------------------
2001 2002 2000
nevada 2.4 2.9 NaN
ohio 1.7 3.6 1.5
包含Series的字典也可以用于构造DataFrame:
pdata = {'ohio' : frame3['ohio'][:-1],
'nevada':frame3['nevada'][:2]}
frame5 = pd.DataFrame(pdata)
print(frame5)
----------------------------------------------------------------
ohio nevada
2001 1.7 2.4
2002 3.6 2.9
可以向DataFrame构造函数传递的对象列表见下列表:
DataFrame构造函数的有效输入表:
类型 | 注释 |
---|---|
2D ndarray | 数据的矩阵,行和列的标签是可选参数 |
数组、列表和元组构成的字典 | 每个序列成为DataFrame的一列,所有序列的长度必须相等 |
NumPy结构化、记录化数组 | 与数组构成一致 |
Series构成的字典 | 每个值成为一列,每个Series的索引联合起来形成结果的行索引,也可以显示地传递索引 |
字典构成的字典 | 每一个内部字典成为一列,键联合起来形成结果的行索引 |
字典或Series构成的列表 | 列表中的一个元素形成DataFrame的一行,字典键或Series索引联合起来形成DataFrame的列标签 |
列表或元组构成的列表 | 与2Dndarry的情况一致 |
其他DataFrame | 如果不显示传递索引,则会使用原DataFrame的索引 |
NumPy MaskedArray | 与2D ndarry的情况类似,但隐藏值会在结果DataFrame中成为NA/缺失值 |
如果DataFrame的索引和列拥有name属性,则这些name属性也会被显示:
frame3.index.name = 'year'
frame3.columns.name = 'state'
print(frame3)
----------------------------------------------------------------
state nevada ohio
year
2001 2.4 1.7
2002 2.9 3.6
2000 NaN 1.5
和Series类似,DataFrame的values属性会将包含在DataFrame中的数据以二维ndarry的形式返回:
n1 = frame3.values
print(n1)
----------------------------------------------------------------
[[2.4 1.7]
[2.9 3.6]
[nan 1.5]]
如果DataFrame的列是不同的dtypes,则values的dtype会自动选择适合所有列的类似:
n1 = frame2.values
print(n1)
----------------------------------------------------------------
[[2000 1.5 'zhangshang' nan]
[2001 1.7 'lishi' -1.2]
[2002 3.6 'lishi' nan]
[2001 2.4 'lishi' -1.5]
[2002 2.9 'lizi' -1.7]
[2003 3.2 'lizi' nan]]
1.3 索引对象
pandas中的索引对象是用于存储轴标签和其他元数据的(例如轴名称或标签)。在构造Series或DataFrame时,你所使用的任意数组或标签序列都可以在内部转换为索引对象:
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
print(index)
print(index[1:])
----------------------------------------------------------------
Index(['a', 'b', 'c'], dtype='object')
Index(['b', 'c'], dtype='object')
注意:索引对象是不可变的,因此用户是无法修改索引对象的
与Python集合不同,pandas索引对象可以包含重复标签:
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
print(dup_labels)
----------------------------------------------------------------
Index(['foo', 'foo', 'bar', 'bar'], dtype='object')
根据重复标签进行筛选,会选取所有重复标签对应的数据。
每个索引都有一些集合逻辑的方法和属性,这些方法和属性解决了关于它所包含的数据的其他常见问题。下表中总结了这些方法和属性中常用的一部分。
方法 | 描述 |
---|---|
append | 将额外的索引对象粘贴到原索引后,产生一个新的索引 |
difference | 计算两个索引的差集 |
intersection | 计算两个索引的交集 |
union | 计算两个索引的并集 |
isin | 计算表示每一个值是否存在传值容器中的布尔数组 |
delete | 将位置i的元素删除,并产生新的索引 |
drop | 根据传参删除指定索引值,并产生新的索引 |
insert | 在位置i插入元素,并产生新的索引 |
is_monotonic | 如果索引序列递增则返回True |
is_unique | 如果索引序列唯一则返回True |
unique | 计算索引的唯一值序列 |
10月5日结束!