Pandas数据处理
Series对象
Series对象是一个带索引构成的一维数组,可以用一个数组创建Series对象:
import pandas as pd
In [2]: pd.Series([1,2,3,4,5,6])
Out[2]:
0 1
1 2
2 3
3 4
4 5
5 6
dtype: int64
In [5]: data.index
Out[5]: RangeIndex(start=0, stop=6, step=1)
In [7]: data.values
Out[7]: array([1, 2, 3, 4, 5, 6])
Series对象是一种显式定义的索引与数值关联,可以不再仅仅是整数,还可以是任意想要的类型。你可以把Series看成一种特殊的python字典。
DataFrame
创建DataFrame对象
- 通过单个Series对象创建
In [15]: pd.DataFrame(pd.Series([1,2,3]), columns=['test'])
Out[15]:
test
0 1
1 2
2 3
- 通过字典列表创建
In [11]: pd.DataFrame([{'a':1, 'b':2}])
Out[11]:
a b
0 1 2
- 通过Series对象字典创建
In [16]: pd.DataFrame({ 'a':pd.Series([1,2,3]), 'b':pd.Series([2,3,4])})
Out[16]:
a b
0 1 2
1 2 3
2 3 4
- 通过Numpy二维数组创建
In [14]: pd.DataFrame(np.random.rand(3,2), columns=['foo', 'bar'], index=['a','b','c'])
Out[14]:
foo bar
a 0.528223 0.955862
b 0.506073 0.432731
c 0.310693 0.522418
- 通过Numpy结构化数组创建
a = np.zeros(3,dtype=[('A', '<i8'), ('B', '<f8')])
Pandas的Index对象
Pandas的Index对象是一个很有趣的数据结构,可以将它看作是一个不可变数组或有序集合。可以看成index不可变数组和有序集合。
In [17]: ind = pd.Index([2,3,4,5,7])
In [18]: ind
Out[18]: Int64Index([2, 3, 4, 5, 7], dtype='int64')
数据选择方式
Series
- 将Series看作Series看作字典,用键值映射。
- 将Series看作一维数组,可以索引、掩码和花哨。
- 索引器:loc\iloc和ix
前面提到的取值和切片,其实它们的索引方式不同,所以整数索引会产生混淆。loc表示取值和切片都是显式的,iloc表示取值和切片都是Python形式的隐式索(也就是元素所在位置, 一定从0开始)。第三种是ix,两种的混合,常用于DataFrame。
DataFrame
- 将DataFrame看作字典
- 将DataFrame看作二维数组,可以用Values属性按行查看数据。
- 索引器loc,iloc,ix
loc可以结合使用掩码与花哨的索引方法:data.loc[data.density > 100, ['pop', 'density']]
,如果用掩码也可以直接data[data.density > 100]
Pandas数值运算方法
pandas一元运算,通用函数将在输出结果中保留索引和列标签,二元运算,会自动对齐索引。二元运算索引是并集操作,如果缺失会用NaN来代替。
处理缺失值
在数据表或者DataFrame有很多识别缺失值,一般有两种:掩码和标签值。None是python对象的缺失值,NaN是数值类型的缺失值。在Pandas可以看作等价交换的。
In [3]: pd.Series([1,np.nan,2, None])
Out[3]:
0 1.0
1 NaN
2 2.0
3 NaN
dtype: float64
发现/处理缺失值函数
- isnull(): 创建一个布尔类型的掩码标签缺失值
- notnull(): 与isnull()操作相反
- dropna(): 返回一个剔除缺失值的数据
- fillna():填充缺失值
发现缺失值
isnull()和notnull()可以发现缺失值,返回布尔类型掩码数据。
In [4]: data = pd.Series([1, np.nan, 'hello', None])
In [5]: data.isnull()
Out[5]:
0 False
1 True
2 False
3 True
dtype: bool
用布尔掩码数组可以直接作为Series或DataFrame索引使用:
In [6]: data[data.notnull()]
Out[6]:
0 1
2 hello
dtype: object
剔除缺失值
可以用dropna()剔除缺失值
In [7]: data.dropna()
Out[7]:
0 1
2 hello
dtype: object
在DataFrame上使用它们需要设置一些参数,我们没有办法单独剔除一个值,要么剔除缺失值所在整行,要么整列。
In [8]: df = pd.DataFrame([[1,np.nan,2],[2,3,5],[np.nan,4,6]])
In [9]: df
Out[9]:
0 1 2
0 1.0 NaN 2
1 2.0 3.0 5
2 NaN 4.0 6
# 剔除任何包含缺失值整行数据
In [10]: df.dropna()
Out[10]:
0 1 2
1 2.0 3.0 5
# 删除任何包含缺失值的整列数据
In [11]: df.dropna(axis='columns')
Out[11]:
2
0 2
1 5
2 6
可以通过how或者thresh参数设置剔除阈值:
In [12]: df[3] = np.nan
In [13]: df
Out[13]:
0 1 2 3
0 1.0 NaN 2 NaN
1 2.0 3.0 5 NaN
2 NaN 4.0 6 NaN
# how='all'全部是缺失值的行或者列
In [14]: df.dropna(axis='columns', how = 'all')
Out[14]:
0 1 2
0 1.0 NaN 2
1 2.0 3.0 5
2 NaN 4.0 6
thresh设置行列中非缺失值最小数量
In [15]: df.dropna(axis='rows', thresh=3)
Out[15]:
0 1 2 3
1 2.0 3.0 5 NaN
填充缺失值
fillna()填充缺失值
In [16]: data=pd.Series([1, np.nan, 2, None,3], index=list('abcde'))
In [17]: data
Out[17]:
a 1.0
b NaN
c 2.0
d NaN
e 3.0
dtype: float64
用fillna()填充
In [18]: data.fillna(0)
Out[18]:
a 1.0
b 0.0
c 2.0
d 0.0
e 3.0
dtype: float64
可以用前面有效值从前往后填充(forward-fill)
In [19]: data.fillna(method='ffill')
Out[19]:
a 1.0
b 1.0
c 2.0
d 2.0
e 3.0
dtype: float64
可以用后面往前填充(back-fill)
In [20]: data.fillna(method='bfill')
Out[20]:
a 1.0
b 2.0
c 2.0
d 3.0
e 3.0
dtype: float64
DataFrame与Series类似:
In [21]: df
Out[21]:
0 1 2 3
0 1.0 NaN 2 NaN
1 2.0 3.0 5 NaN
2 NaN 4.0 6 NaN
# 如果从前往后填充时,前面没有值,仍然是NaN
In [22]: df.fillna(method='ffill', axis=1)
Out[22]:
0 1 2 3
0 1.0 1.0 2.0 2.0
1 2.0 3.0 5.0 5.0
2 NaN 4.0 6.0 6.0
合并数据:Concat与Append操作
Pandas有一个pd.concat(), concat可以简单合并一维的Series或DataFrame对象
In [26]: ser1 = pd.Series(['A','B', 'C'], index=[1,2,3])
In [27]: ser2 = pd.Series(['D', 'E', 'F'], index=[4,5,6])
In [28]: pd.concat([ser1, ser2])
Out[28]:
1 A
2 B
3 C
4 D
5 E
6 F
dtype: object
In [23]: def make_df(cols,ind):
...: data = {c:[str(c) + str(i) for i in ind] for c in cols}
...: return pd.DataFrame(data, ind)
In [29]: df1 = make_df('AB', [1,2])
In [30]: df2 = make_df('AB', [3,4])
In [31]: print(df1), print(df2), print(pd.concat([df1, df2]))
A B
1 A1 B1
2 A2 B2
A B
3 A3 B3
4 A4 B4
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
# 列拼接
In [35]: print(df3); print(df4);print(pd.concat([df3, df4], axis=1))
A B
0 A0 B0
1 A1 B1
C D
0 C0 D0
1 C1 D1
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
pandas在合并时会保留索引:
In [36]: x = make_df('AB', [0,1])
In [37]: y = make_df('CD', [2,3])
In [38]: y.index = x.index
In [39]: print(x); print(y), print(pd.concat([x,y]))
A B
0 A0 B0
1 A1 B1
A B
0 A2 B2
1 A3 B3
A B
0 A0 B0
1 A1 B1
0 A2 B2
1 A3 B3
如果想捕捉索引重复错误,可以用verify_integrity
:pd.concat([x,y], verify_integrity=True)
可以用ignore_index参数来创建新的整数索引:ignore_index
:pd.concat([x,y], ignore_index=True)
还有一种方式是可以用keys参数设置多级索引标签:pd.concat([x,y], keys=['x', 'y'])
合并DataFrame都是同样的列名,而在实际工作中,可能是数据带不同的列名,这里可以用join合并方式。
In [4]: df5 = make_df('ABC', [1,2])
In [5]: df5 = make_df('ABC', [1,2])
In [6]: df6 = make_df('BCD', [3,4])
In [7]: print(df5);print(df6);print(pd.concat([df5,df6]))
A B C
1 A1 B1 C1
2 A2 B2 C2
B C D
3 B3 C3 D3
4 B4 C4 D4
A B C D
1 A1 B1 C1 NaN
2 A2 B2 C2 NaN
3 NaN B3 C3 D3
4 NaN B4 C4 D4
默认情况下,join是outer方式并集合并,你可以用inner交集合并
In [8]: print(df5);print(df6);print(pd.concat([df5,df6], join='inner'))
A B C
1 A1 B1 C1
2 A2 B2 C2
B C D
3 B3 C3 D3
4 B4 C4 D4
B C
1 B1 C1
2 B2 C2
3 B3 C3
4 B4 C4
合并数据集:合并与连接
pd.merge()函数实现了三种数据连续类型:一对一,多对一和多对多。
In [14]: df1=pd.DataFrame({'employee':['Bob','Jake', 'Lisa','Sue'], 'group':['Accounting', 'Engineering', 'Engineering', 'HR']})
In [15]: df2=pd.DataFrame({'employee':['Bob','Jake', 'Lisa','Sue'], 'hire_date':[2004,2008,2012,2014]})
In [16]: print(df1);print(df2)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
employee hire_date
0 Bob 2004
1 Jake 2008
2 Lisa 2012
3 Sue 2014
In [17]: df3 = pd.merge(df1,df2)
In [18]: df3
Out[18]:
employee group hire_date
0 Bob Accounting 2004
1 Jake Engineering 2008
2 Lisa Engineering 2012
3 Sue HR 2014
merge发现有'employee'列,所以自动以以偏概全作为键进行连接。同时需要注意,merge会默认丢弃原来行索引。
多对一是指同一列有重复值,通过多对一的方式,会保留重复值。
In [19]: df4=pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'], 'supervisor':['Carly','Guido', 'Steve']})
In [20]: print(df3);print(df4);print(pd.merge(df3,df4))
employee group hire_date
0 Bob Accounting 2004
1 Jake Engineering 2008
2 Lisa Engineering 2012
3 Sue HR 2014
group supervisor
0 Accounting Carly
1 Engineering Guido
2 HR Steve
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
多对多是指两个同一列有重复值,通过多对多的方式,会保留重复值。
In [21]: df5=pd.DataFrame({'group':['Accounting', 'Accounting', 'Engineering', 'Engineering','HR','HR'], 'skills':['math','spreadsheets', 'coding', 'linux', 'spreadsheets', 'organization']})
In [22]: print(df1); print(df5); print(pd.merge(df1,df5))
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
group skills
0 Accounting math
1 Accounting spreadsheets
2 Engineering coding
3 Engineering linux
4 HR spreadsheets
5 HR organization
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
设置数据合并的键
merge()默认行为它会将两个输入的一个或多个共同列作为键进行合并,但由于输入要合并列通常不同名,所以提供了一些参数处理。如果合并两个不同列名的数据集,可以用left_on
和right_on
来指定。
n [23]: df3 = pd.DataFrame({'name':['Bob','Jake','Lisa','Sue'], 'salary':[7000,8000,12000,90000]})
In [24]: print(df1);print(df2);print(pd.merge(df1,df3,left_on="employee", right_on="name"))
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
employee hire_date
0 Bob 2004
1 Jake 2008
2 Lisa 2012
3 Sue 2014
employee group name salary
0 Bob Accounting Bob 7000
1 Jake Engineering Jake 8000
2 Lisa Engineering Lisa 12000
3 Sue HR Sue 90000
还可以索引合并:
In [25]: df1a = df1.set_index('employee')
In [26]: df2a = df2.set_index('employee')
In [27]: print(df1a);print(df2a);print(pd.merge(df1a,df2a,left_index=True, right_index=True))
group
employee
Bob Accounting
Jake Engineering
Lisa Engineering
Sue HR
hire_date
employee
Bob 2004
Jake 2008
Lisa 2012
Sue 2014
group hire_date
employee
Bob Accounting 2004
Jake Engineering 2008
Lisa Engineering 2012
Sue HR 2014
当然也可以混用:
In [28]: pd.merge(df1a,df3,left_index=True, right_on='name')
Out[28]:
group name salary
0 Accounting Bob 7000
1 Engineering Jake 8000
2 Engineering Lisa 12000
3 HR Sue 90000
设置数据连接的集合操作规则
默认情况下,merge是用inner join内连接,我们可以用how来控制(outer,left,right)
In [29]: df6=pd.DataFrame({'name':['Peter','Paul','Mary'], 'food':['fish', 'beans','bread']})
In [30]: df7=pd.DataFrame({'name':['Mary','Joseph'], 'drink':['wine', 'beer']})
In [31]: pd.merge(df6,df7)
Out[31]:
name food drink
0 Mary bread wine
In [32]: pd.merge(df6,df7, how='outer')
Out[32]:
name food drink
0 Peter fish NaN
1 Paul beans NaN
2 Mary bread wine
3 Joseph NaN beer
列重名:suffixes参数
In [33]: df8=pd.DataFrame({'name':['Bob','Jake','Lisa','Sue'], 'rank':[1,2,3,4]})
In [34]: df9 = pd.DataFrame({'name':['Bob','Jake', 'Lisa', 'Sue'], 'rank':[3,1,4,2]})
In [35]: print(pd.merge(df8,df9, on="name"))
name rank_x rank_y
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2
因为两个都有重复列名,所以默认加_x和_y,我们也可以通过suffixes定义后缀名:
n [36]: print(pd.merge(df8, df9, on='name',suffixes=["_L",'_R']))
name rank_L rank_R
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2
GroupBy:分割、应用和组合
GroupBy可以理解为:先分割再应用函数最后组合,类似SQL
数据透视表
透视表可以理解为多维的GroupBy累计操作
df.pivot_table('column', index='[index_name]', columns='[col_name]') # column表示你要的列,index_name表示作为行的列名,columns表示列的名字
处理时间序列
python日期与时间工具
基本的日期与时间功能在标准库的datetime模块中。
In [1]: from datetime import datetime
In [2]: datetime(year=2023,month=7,day=25)
Out[2]: datetime.datetime(2023, 7, 25, 0, 0)
可以用dateutil模块对各种字符串格式的日期进行正确解析:
In [3]: from dateutil import parser
In [4]: date = parser.parse("25th of July, 2023")
In [5]: date.strftime("%A")
Out[5]: 'Tuesday'
Pandas的日期与时间工具
pandas所有关于日期与时间的处理方法全部都是通过Timestamp对象实现的。
In [7]: date = pd.to_datetime("25th of July, 2023")
In [8]: date
Out[8]: Timestamp('2023-07-25 00:00:00')
也可以直接进行Numpy类型的向量化运算
In [12]: date + pd.to_timedelta(np.arange(12), 'D')
Out[12]:
DatetimeIndex(['2023-07-25', '2023-07-26', '2023-07-27', '2023-07-28',
'2023-07-29', '2023-07-30', '2023-07-31', '2023-08-01',
'2023-08-02', '2023-08-03', '2023-08-04', '2023-08-05'],
dtype='datetime64[ns]', freq=None)
Pandas时间序列:用时间作索引
pandas可以用来处理带时间戳的索引数据
In [13]: index = pd.DatetimeIndex(["2023-08-01","2023-08-02"])
In [14]: data = pd.Series([0,1], index=index)
In [15]: data
Out[15]:
2023-08-01 0
2023-08-02 1
dtype: int64
可以用索引来切片取值。
Pandas时间序列数据结构
最基础的日期/时间对象是Timestamp和DatetimeIndex,最常用的是pd.to_datetime()
解析日期与时间格式,返回timestamp类型,传递一个时间序列就会返回DatetimeIndex类型。
pd.date_range
可以创建有规律的时间序列。
重新取样、迁移窗口
可以用resample()
和asfreq()
方法。resample是以数据累计为基础,asfreq是以数据选择。shift()
进行迁移数据,tshift()
迁移索引。rolling
返回类似groupby结果,移动视图使得许多累计操作成为可能。