文章目录
Pandas数据处理
DataFrame本质上是一种带行标签和列标签、支持相同类型数据和缺失值的多维数组。
1. Series对象
Pandas 的 Series对象是一个带索引数据构成的一维数组,可以用一个数组创建Series对象。
import pandas as pd
import numpy as np
data = pd.Series([1,2,3,4])
print(data)
"""
0 1
1 2
2 3
3 4
dtype: int64
"""
print(data.values)
#[1 2 3 4]
print(data.index)
"""
RangeIndex(start=0, stop=4, step=1)
"""
#获取数据
print(data[1])
print(data[1:3])
"""
2
1 2
2 3
dtype: int64
"""
Series是通用的Numpy数组
但两者间的本质差异是索引:Numpy数组通过隐式定义的整数索引获取数值,而Pandas的Series对象用一种显式定义的索引与数值关联。
显式索引
data = pd.Series([1,2,3,4],index=['a','b','c','d'])
print(data)
"""
a 1
b 2
c 3
d 4
dtype: int64
"""
#获取数值的方式
print(data['c'])
# 3
Series对象其实是一种将类型键映射到一组类型值得数据结构。
我们可以直接用python字典创建一个Series对象:
dict_demo = {
'a':1,
'b':2,
'c':3,
'd':4
}
ser_demo = pd.Series(dict_demo)
print(ser_demo)
"""
a 1
b 2
c 3
d 4
dtype: int64
"""
data可以是一个字典,默认是排序的字典键,但是可以通过显示指定索引筛选需要的结果:
ser_demo = pd.Series({2:'a',1:'b',3:'c'},index=[3,2])
print(ser_demo)
"""
3 c
2 a
dtype: object
"""
2. DataFrame对象
如果将Series类比为带灵活索引的一维数组,那么DataFrame就可以看作是一种既有灵活的行索引,又有灵活列名的二维数组。就像你可以把二维数组看成是有序排列的一维数组一样,你也可以把DataFrame看成是有序排列的若干Series对象,这里的“排列”指的是它们拥有共同的索引。
area = {'a':2,'b':4,'c':6}
area_ser = pd.Series(area)
population = {'a':100,'b':200,'c':300}
population_ser = pd.Series(population)
df_demo = pd.DataFrame({'area':area_ser,'population':population_ser})
print(df_demo)
print(df_demo.index)#获取行标签
print(df_demo.columns)#获取列标签
"""
area population
a 2 100
b 4 200
c 6 300
Index(['a', 'b', 'c'], dtype='object')
Index(['area', 'population'], dtype='object')
"""
#获取数据
print(df_demo['area'])
"""
a 2
b 4
c 6
Name: area, dtype: int64
"""
注意:在Numpy的二维数组里,data[0]返回第一行,而在DataFrame中,data[‘col1’]返回第一列。
2.1 创建DataFrame对象
#1. 通过单个Series对象创建。DataFrame是一组Series对象的集合,可以用单个Series创建一个单列的DataFrame
population = {'a':100,'b':200,'c':300}
population_ser = pd.Series(population)
df_single_col = pd.DataFrame(population_ser,columns=['population'])
print(df_single_col)
"""
population
a 100
b 200
c 300
"""
#2. 通过字典列表创建
data = [{'a':i,'b':2*i} for i in range(3)]
pd_by_dict = pd.DataFrame(data)
print(pd_by_dict)
"""
a b
0 0 0
1 1 2
2 2 4
"""
#3. 通过Series对象字典创建。如示例在前面
#4. 通过Numpy二维数组创建。
df_by_np = pd.DataFrame(np.random.random((3,2)),index=[1,2,3],columns=[1,3])
print(df_by_np)
"""
1 3
1 0.388610 0.687721
2 0.797138 0.763878
3 0.047219 0.901602
"""
Pandas的Index是一个很有趣的数据结构,可以将它看作是一个不可变数组或有序集合。
ind = pd.Index([1,2,3,4])
print(ind)
print(ind[1])
print(ind[::2])
print(ind.size,ind.shape,ind.ndim,ind.dtype)
"""
Int64Index([1, 2, 3, 4], dtype='int64')
2
Int64Index([1, 3], dtype='int64')
4 (4,) 1 int64
"""
#index对象与Numpy数组之间的不同在于,Index对象的索引是不可变的,一旦确定,不可以对它进行赋值操作。
Index对象遵循Python标准库的集合(set)数据结构的许多习惯用法,包括并集、交集、差集等:
indA = pd.Index([1,2,3,4,5])
indB = pd.Index([3,4,5,6,7])
print(indA&indB)
print(indA.intersection(indB))
"""
Int64Index([3, 4, 5], dtype='int64')
Int64Index([3, 4, 5], dtype='int64')
"""
2.2 数据取值与选择
(1) series 数据操作
data = pd.Series([1,3,4,5],index=['a','b','c','d'])
print(data['a'])
print('a' in data)
print(data.keys())#索引值
print(list(data.items()))#[('a', 1), ('b', 3), ('c', 4), ('d', 5)]
data['e'] = 1#增加新的索引值扩展字典
data['a':'c'] #显式索引作为切片
data[0:2]#隐式整数索引作为切片
data[(data>0.3) & (data<0.8)]#掩码操作
data[['a','e']]#花哨的索引
#第一种索引器是loc属性,表示取值和切片都是显式的:
data = pd.Series([1,2,3,4],index=[1,2,3,4])
data.loc[1]#1
data.loc[1:3]
#第二种是iloc属性,表示取值和切片都是python形式的隐式索引:
data.iloc[1]#2
data.iloc[1:3]
#第三种混合索引,忽略
(2) DataFrame 数据操作
area = {'a':2,'b':4,'c':6}
area_ser = pd.Series(area)
population = {'a':100,'b':200,'c':300}
population_ser = pd.Series(population)
df_demo = pd.DataFrame({'area':area_ser,'population':population_ser})
print(df_demo['area'])
print(df_demo.area)#这种方法不通用
df_demo['list'] = 23#增加一列为list
print(df_demo.T)
print(df_demo.values[0])#获取一行数据
print(df_demo['list'])#获取一列数据
df_demo.iloc[:3,:2]#获取前3行,2列数据
df_demo.loc[:'b',:2]#显式获取
df_demo.ix[]#混合获取,忽略
df_demo.loc[df_demo.area>2,['population']]#掩码和花哨获取
#任何一种取值方式都可以用于调整数据。
df_demo['area':'population']#取多行数据,和一列要互相区别
df_demo[:3]#取多行数据
df_demo[df_demo.area>1]
2.3 数值运算方法
因为pandas是建立在numpy基础之上的,所以numpy的通用函数同样适用于pandas的Series和DataFrame对象。
rng = np.random.RandomState(43)
ser = pd.Series(rng.randint(0,10,4))
df = pd.DataFrame(rng.randint(0,10,(3,4)),columns=
['A','B','C','D'])
print(ser)
print(df)
np.exp(ser)
np.sin(df*np.pi/4)
#索引对齐
A = pd.Series([2,4,6],index=[0,1,2])
B = pd.Series([1,2,3],index=[1,2,3])
A+B#会出现NaN,因为索引不对齐
A.add(B,fill_value=0)#用0填充缺失值
#DataFrame类似
df = pd.DataFrame(rng.randint(0,20,(2,2)),columns=list('AB'))
#计算df的均值需要用stack将二维压缩成一维
df_mean = df.stack().mean()
python运算符 | pandas方法 |
---|---|
+ | add() |
- | sub()、subtract() |
* | mul()、multiply() |
/ | truediv()、div()、divide() |
// | floordiv() |
% | mod() |
** | pow() |
2.4 处理缺失值
缺失值主要有三种形式:null、NaN和NA
在数据表或DataFrame中有很多识别缺失值的方法。一般情况下可以分为两种:一种方法是通过一个覆盖全局的掩码表示缺失值,另一种方法是用一个标签值(sentinel value)表示缺失值。
Pandas选择用标签方法表示缺失值,包括两种Python原有的缺失值:浮点数据类型的NaN值,以及Python的None对象。
可以把NaN看作是一个数据类病毒–它会将与它接触过的数据同化,无论和NaN进行何种操作,最终结果都是NaN。
#在Series里使用的isnull()和notnull()同样使用于DataFrame
#筛选出缺失值
data = pd.Series([1,np.nan,'hello',None])
print(data.isnull())
print(data.notnull())
#剔除缺失值
print(data.dropna())
df = pd.DataFrame(np.random.randint(1,10,(3,3)))
df.iloc[0,1] = np.nan
df.iloc[2,0] = np.nan
print(df.dropna())#相当于df.dropna(axis=0)
print(df.dropna(axis=1))
#还可以通过参数how或thresh参数来满足,默认设置how='any',你可以设置how='all',这样只会剔除全部是缺失值的行货列
#thres参数设置行货列中非缺失值的最小数量
#填充缺失值
# print(df.fillna(0))#用0填充
# print(df.fillna(method='ffill'))#用缺失值前面的有效值来从前往后填充(forward-fill)
print(df.fillna(method='bfill',axis=1))#用缺失值后面的有效值从后往前填充(back-fill)
2.5 合并数据集:Concat与Append操作
pd.concat()函数和np.concatenate语法类似。可以简单地合并一维的Series或DataFrame。
pd.concat([x,y],ignore_index=True)#忽略索引,如果索引重复,可以用参数verify_integrity=True检查
#也可以通过参数join设置合并的方式。通过join_axes设置索引对象构成的列表
df1.append(ddf2)#相当于pd.concat([df1,df2])
#append()不直接更新原有对象的值,而是为合并后的数据创建一个新对象。
2.6 合并数据集:合并与连接
Pandas的基本特性之一就是高性能的内存式数据连接(join)与合并(merge)操作。
数据连接的类型
pd.merge()函数实现了三种数据连接的类型:一对一、多对一和多对多。
一对一连接
df1 = pd.DataFrame({'employee':['Bob','Jake','Lisa','Sue'],
'group':['Accounting','Engineering','Engineering','HR']})
df2 = pd.DataFrame({'employee':['Bob','Jake','Lisa','Sue'],
'hire_date': [2004, 2008, 2012, 2014]})
df3 = pd.merge(df1,df2)
print(df3)
"""
employee group hire_date
0 Bob Accounting 2004
1 Jake Engineering 2008
2 Lisa Engineering 2012
3 Sue HR 2014
"""
#pd.merge()方法会发现两个DataFrame都有employee列,并会自动以这列作为键进行连接。
多对一连接
多对一连接是指,在需要连接的两个列中,有一列的值有重复,通过多对一连接获得的结果DataFrame将会保留重复值。
df1 = pd.DataFrame({'employee':['Bob','Jake','Lisa','Sue'],
'group':['Accounting','Engineering','Engineering','HR']})
df2 = pd.DataFrame({'employee':['Bob','Jake','Lisa','Sue'],
'hire_date': [2004, 2008, 2012, 2014]})
df3 = pd.merge(df1,df2)
# print(df3)
df4 = pd.DataFrame({'group':['Accounting','Engineering','HR'],
'supervisor':['Carly','Guido','Steve']})
df5 = pd.merge(df3,df4)
print(df5)
"""
employee group hire_date supervisor
0 Bob Accounting 2004 Carly
1 Jake Engineering 2008 Guido
2 Lisa Engineering 2012 Guido
3 Sue HR 2014 Steve
"""
多对多连接
如果左右两个输入的共同列都包含重复值,那么合并的结果就是一种多对多连接。
df5 = pd.DataFrame({'group':['Accounting','Accounting','Engineering',
'Engineering','HR','HR'],
'skills':['math','spreadsheets','coding','linux',
'spreadsheets','organization']})
df6 = pd.merge(df1,df5)
print(df6)
"""
employee group skills
0 Bob Accounting math
1 Bob Accounting spreadsheets
2 Jake Engineering coding
3 Jake Engineering linux
4 Lisa Engineering coding
5 Lisa Engineering linux
6 Sue HR spreadsheets
7 Sue HR organization
"""
设置数据合并的键
pd.merge()的默认行为:将两个输入的一个或多个共同列作为键进行合并。但是由于两个输入要合并的列通常都不是同名的,因此pd.merge()提供了一些参数处理这个问题
参数on的用法
最简单的方法就是直接将参数on设置为一个列名字符串或者一个包含多列名称的列表。on=’col_name’
left_on与right_on参数
有时需要合并两个列名不同的数据集。这时候就可以用left_on和right_on参数来指定列名:
left_on = ‘left_col_name’,right_on = ‘right_col_name’
这个结果中,两个对应的列名都存在,也就是多了一个列名,可以使用drop()函数将多余的列去除
df4 = pd.DataFrame({'employee':['Bob','Jake','Lisa','Sue'],
'group':['Accounting','Engineering','Engineering','HR']})
df5 = pd.DataFrame({'g':['Accounting','Accounting','Engineering',
'Engineering','HR','HR'],
'skills':['math','spreadsheets','coding','linux',
'spreadsheets','organization']})
df6 = pd.merge(df4,df5,left_on='group',right_on='g').drop('g',axis=1)
print(df6)
left_index和right_index参数
除了合并列之外,你可能还需要合并索引。
为了方便考虑,DataFrame实现了join()方法,它可以按照索引进行数据合并:
df1.join(df2)
设置数据连接的集合操作规则
默认情况下,pd.merge()结果中只会包含两个输入集合的交集,这种连接方式被称为内连接(inner join),我们可以用how参数设置连接方式,默认值是‘inner’。(其他值有:outer,left,right)
重复列名:suffixes参数
当2个有重复列名的数据集进行连接的时候,系统会自动生成新的列名,我们也可以通过参数suffixes=[‘col_1’,‘col_2’]进行设置
2.7 累计和分组
在对较大数据进行分析时,一项基本的工作就是有效的数据累计:计算累计(aggregation)指标,如sum()、mean()、median()、min()和max()。
Pandas的累计方法
指标 | 描述 |
---|---|
count() | 计数项 |
first(),last() | 第一项和最后一项 |
mean(),median() | 均值和中位数 |
min(),max() | 最小值和最大值 |
std(),var() | 标准差和方差 |
mad() | 均值绝对偏差 |
prod() | 所有项乘积 |
sum() | 所有项求和 |
2.7 GroupBy:分割、应用和组合
df.groupby(‘key’)
GroupBy对象
GroupBy对象是一种非常灵活的抽象类型。
(1) 累计。aggregate()可以支持更复杂的操作,比如字符串、函数或者函数列表。并且能一次性计算所有累计值。
df.groupby(‘key’).aggregate([‘min’,np.median,max])
3 处理时间序列
工具:datetime和dateutil
4 eval()与query()
pd.eval(‘df1+df2+df3’)
目前pd.eval(0还不支持函数调用、条件语句、循环以及更复杂的运算。
df.eval()方法还支持通过@符号使用python的局部变量。@只能在df.eval()中使用,不能再pandas.eval()中使用。
query()
支持@符号引用局部变量