Pandas
Pandas是基于Numpy构建的库,目的是让Numpy为中心的应用变得更加简单
Pandas For Beginner
Series
创建一个系列
# Series
s = pd.Series([1,3,6,np.nan,44,1], index=[6,5,4,3,2,1])
print(s)
s.index = [1,2,3,4,5,6]
print(s)
2
3
4
5
- 系列即一串带有索引的数据
其输出结果为
6 1.0
5 3.0
4 6.0
3 NaN
2 44.0
1 1.0
dtype: float64
1 1.0
2 3.0
3 6.0
4 NaN
5 44.0
6 1.0
dtype: float64
2
3
4
5
6
7
8
9
10
11
12
13
14
- 默认的索引从
0
开始,步长为1
DataFrame
创建一个数据框架
- 创建连续日期数组
pd.date_range('2018-08-19',periods=6)
,意为从8.19日起往后数6天构成的日期数组 - 返回标准正态分布样本
np.random.randn(6,4)
意为返回6x4
个样本,于numpy.random.rand(d0, d1, …, dn)
不同,其返回的随机样本位于[0, 1)
中
import pandas as pd
import numpy as np
# DataFrame
dates = pd.date_range('2018-08-19',periods=6)
# dates = pd.date_range('2018-08-19','2018-08-24') # 起始、结束 与上述等价
df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=['a','b','c','d'])
print(df)
2
3
4
5
6
7
8
9
a b c d
2018-08-19 -0.147933 0.726108 2.725752 1.373449
2018-08-20 -0.512244 0.218556 0.820553 0.582996
2018-08-21 -0.455171 0.361705 1.196131 -0.577475
2018-08-22 -1.600681 -0.987295 1.361328 0.427573
2018-08-23 -1.683981 -1.075722 -0.360446 0.651535
2018-08-24 -0.652182 -0.516795 -1.100374 1.577188
2
3
4
5
6
7
数据框架的转置
print(df.T)
2018-08-19 2018-08-20 ... 2018-08-23 2018-08-24
a -0.147933 -0.512244 ... -1.683981 -0.652182
b 0.726108 0.218556 ... -1.075722 -0.516795
c 2.725752 0.820553 ... -0.360446 -1.100374
d 1.373449 0.582996 ... 0.651535 1.577188
2
3
4
5
- 实际上这里的
DataFrame
就是一个a[0][0]
为空,第一列、第一行为属性的矩阵
打印某一个属性值
print(df['d'])
[4 rows x 6 columns]
2018-08-19 1.373449
2018-08-20 0.582996
2018-08-21 -0.577475
2018-08-22 0.427573
2018-08-23 0.651535
2018-08-24 1.577188
Freq: D, Name: d, dtype: float64
2
3
4
5
6
7
8
- 第一列为索引,第二列为属性
d
的值
打印索引
print(df.index)
DatetimeIndex(['2018-08-19', '2018-08-20', '2018-08-21', '2018-08-22',
'2018-08-23', '2018-08-24'],
dtype='datetime64[ns]', freq='D')
2
3
打印行属性
print(df.columns)
Index(['a', 'b', 'c', 'd'], dtype='object')
打印所有数据
print(df.values)
[[-0.14793322 0.72610839 2.72575215 1.3734486 ]
[-0.5122437 0.21855648 0.82055337 0.58299647]
[-0.45517147 0.3617052 1.19613096 -0.57747469]
[-1.6006812 -0.98729492 1.36132808 0.42757327]
[-1.68398091 -1.07572233 -0.36044647 0.65153468]
[-0.65218188 -0.51679505 -1.10037376 1.57718844]]
2
3
4
5
6
打印数据统计总结
print(df.describe())
a b c d
count 6.000000 6.000000 6.000000 6.000000
mean -0.842032 -0.212240 0.773824 0.672544
std 0.641976 0.752936 1.351658 0.766898
min -1.683981 -1.075722 -1.100374 -0.577475
25% -1.363556 -0.869670 -0.065197 0.466429
50% -0.582213 -0.149119 1.008342 0.617266
75% -0.469440 0.325918 1.320029 1.192970
max -0.147933 0.726108 2.725752 1.577188
2
3
4
5
6
7
8
9
在创建DataFrame
时不规定行列属性,索引将默认从0
开始
df1 = pd.DataFrame(np.arange(12).reshape((3,4)))
print(df1)
print(df1[1])
print(df1.index)
print(df1.columns)
2
3
4
5
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
0 1
1 5
2 9
Name: 1, dtype: int64
RangeIndex(start=0, stop=3, step=1)
RangeIndex(start=0, stop=4, step=1)
2
3
4
5
6
7
8
9
10
另一种以对象的形式构建DataFrame
df2 = pd.DataFrame({ 'A' : 1.,
'B' : pd.Timestamp('20130102'),
'C' : pd.Series(1,index=list(range(4)),dtype='float32'),
'D' : np.array([3]*4,dtype='int32'),
'E' : pd.Categorical(["test","train","test","train"]),
'F' : 'foo' })
print(df2)
2
3
4
5
6
7
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 3.0 3 train foo
2 1.0 2013-01-02 6.0 3 test foo
3 1.0 2013-01-02 9.0 3 train foo
2
3
4
5
排序:使用函数df.sort_index(axis, ascending)
axis=1
表示排序列属性,axis=0
表示排序行属性,这里的排序的对象其实是索引,通过排序索引更改数据的位置ascending=True
表示升序,ascending=False
表示降序
对列进行降序排列
df21 = df2.sort_index(axis=1, ascending=False) # 降序排列列属性
print(df21)
2
F E D C B A
0 foo test 3 1.0 2013-01-02 1.0
1 foo train 3 3.0 2013-01-02 1.0
2 foo test 3 6.0 2013-01-02 1.0
3 foo train 3 9.0 2013-01-02 1.0
2
3
4
5
降序排列行属性
df22 = df2.sort_index(axis=0, ascending=False)
print(df22)
2
A B C D E F
3 1.0 2013-01-02 9.0 3 train foo
2 1.0 2013-01-02 6.0 3 test foo
1 1.0 2013-01-02 3.0 3 train foo
0 1.0 2013-01-02 1.0 3 test foo
2
3
4
5
- 这里的排序都是不改变原数据矩阵的
根据特定属性进行排列:使用函数df.sort_values(by, ascending)
by=某一属性
,将按照这一属性的具体值进行排列ascengding=True
表示升序排列
df23 = df2.sort_values(by='C', ascending=False)
print(df23)
2
A B C D E F
3 1.0 2013-01-02 9.0 3 train foo
2 1.0 2013-01-02 6.0 3 test foo
1 1.0 2013-01-02 3.0 3 train foo
0 1.0 2013-01-02 1.0 3 test foo
2
3
4
5
选择数据
基于
DataFrame
对其中数据进行筛选排除
import pandas as pd
import numpy as np
dates = pd.date_range('20220706', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)), index=dates, columns=['A','B','C','D'])
print(df)
2
3
4
5
6
检索某一列
print(df['A'])
print(df.A)
2
检索多行,含头不含尾,即0,1,2
行
print(df[0:3])
也可以用行属性名来检索,此时含头含尾,即表示检索从2022-07-06
到2022-07-11
的数据
print(df['2022-07-06':'2022-07-11'])
- 这里的日期也可以用
20220706
的形式,即省略-
检索特定行:使用dataframe
的loc
数组
print(df.loc['2022-07-09'])
print(df.loc['2022-07-09':'2022-07-11'])
2
检索特定列,同样使用loc
数组,第一个入参为行,第二个入参为列,当单独搜索多列时,令第一个入参为:
即可
print(df.loc[:, 'A':'C'])
当然也可以限定列
print(df.loc['20220707', 'A':'C'])
print(df.loc['20220707':'20220709', 'A':'C'])
2
使用iloc
列表进行筛选
# 第三行第一列元素
print(df.iloc[3,1])
# 第3,4行,第1,2列元素
print(df.iloc[3:5, 1:3])
# 第1,3,5行,第1,2列元素
print(df.iloc[[1,3,5], 1:3])
# 第0,1,2行,第0,2列元素
print(df.iloc[:3,[0,2]])
2
3
4
5
6
7
8
9
10
11
通过值进行筛选,选出A
属性大于8
的行
print(df[df.A>8])
print(df.loc[df.A>8])
2
- 这两种写法等价
总结一波
单列 | 单行 | 多列 | 多行 |
---|---|---|---|
df['A'] | df.loc['20220707'] | df.loc[:, 'A':'C'] | df[0,3] |
df.A | df['20220707','20220711'] (含头含尾) | ||
df.loc[:, 'A'] | df.loc['20220707','20220711'] |
loc/iloc
是最全能的,可以搜任意行/列iloc
只能接收整型参数,即严格依靠坐标取值loc
只能接收字符串参数,依靠行、列属性值进行取值- 均用
:
代表全体 ix
似乎被废弃了
设置值
基本设置,就是通过loc/iloc
确定范围,然后直接赋值
import pandas as pd
import numpy as np
dates = pd.date_range('20220706',periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)), index=dates, columns=['A','B','C','D'])
print(df)
df.iloc[0,0] = 23
print(df)
df.loc['20220711','D'] = 0
print(df)
2
3
4
5
6
7
8
9
10
11
设置整行/列数据
df.iloc[0] = 'nmsl'
print(df)
df.iloc[:,1] = 'hahaha'
print(df)
df.loc['20220706'] = 'sad'
df.loc[:, 'A'] = 'hehe'
print(df)
2
3
4
5
6
7
设置多行/多列
df.iloc[[1,2]] = 1
print(df)
df.loc[:, ['B','C']] = 2
print(df)
2
3
4
根据条件设置
df.B[df.A>4] = np.nan
df.B.loc[df.A>4] = np.nan
df['B'][df.A>4] = np.nan
df['B'].loc[df.A>4] = np.nan
2
3
4
- 将
A
属性大于4
的行的B
属性设置为np.nan
- 四种写法均可
添加行
date = pd.to_datetime('20220712')
df.loc[date] = [24,25,26,27]
print(df)
2
3
添加列
df['E'] = pd.Series([1,2,3,4,5,6], index=pd.date_range('20220706',periods=6))
print(df)
2
- 这里的数据个数必须和已有索引保持一致,不然会报错
- 这里的索引最好和已有的数据对齐,没有对齐的索引所对应的数据将变为
NaN
处理丢失数据
丢失数据即指
NaN
import pandas as pd
import numpy as np
dates = pd.date_range('20220706', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)), index=dates, columns=['A','B','C','D'])
df.iloc[2,2] = np.nan
df.iloc[5,3] = np.nan
print(df)
2
3
4
5
6
7
8
上述数据中有两个丢失数据NaN
删除有丢失数据的行/列
df1 = df.dropna()
print(df1)
2
- 无参的
dropna()
默认删除所有含有NaN
的行,并返回没有NaN
的矩阵,不改变原矩阵
其实有两个参数:axis
和how
df2 = df.dropna(axis=1, how='any')
print(df2)
2
axis=1
时表示删除Nan
所在列,默认axis=0
how='any'
表示只要存在Nan
即删除,how='all'
表示要这一行/列全为Nan
时才删除,默认为any
将Nan
替换为0
或其他
df3 = df.fillna(value='nmsl')
print(df3)
2
在各元素上判断是否有Nan
或NULL
,输出一个布尔矩阵
df4 = df3.isnull()
df5 = df.isnull()
print(df4)
print(df5)
2
3
4
在各元素上判断是否有Nan
,输出一个布尔矩阵
df6 = df.isna()
print(df6)
2
判断各列是否有缺失数据
df7 = df.isna().any()
print(df7)
2
A False
B False
C True
D True
dtype: bool
2
3
4
5
判断整张表中是否有缺失数据
flag = np.any(df.isnull())
print(flag)
2
导入导出
导入现成数据
import pandas as pd
data = pd.read_csv('student.csv')
print(data)
2
3
4
- 注意这里要把
student.csv
放在.py
的同一目录下,或者用相对、绝对路径去找
前三行和后四行(就像linux
的命令)
print(data.head(3))
print(data.tail(4))
2
导出数据
data.to_pickle('student.pickle')
print(pd.read_pickle('student.pickle'))
2
- 均在当前目录下完成
合并操作
很像数据库表的合并
Concat
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.zeros((3,4)), columns=['A','B','C','D'])
df2 = pd.DataFrame(np.ones((3,4)), columns=['A','B','C','D'])
df3 = pd.DataFrame(np.ones((3,4))*2, columns=['A','B','C','D'])
print(df1)
print(df2)
print(df3)
2
3
4
5
6
7
8
9
10
- 列索引均为
A,B,C,D
- 行索引均为
0,1,2,3
上下合并
res1 = pd.concat([df1,df2,df3], axis=0)
print(res1)
2
这种合并方式,其行索引会重复,即为0,1,2,3,0,1,2,3,0,1,2,3
在合并时可以手动忽略索引,重新从0
开始建立行索引
res2 = pd.concat([df1,df2,df3], axis=0, ignore_index=True)
print(res2)
2
使用append
函数进行合并
df7 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df8 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df9 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])
s = pd.Series([1,2,3,4], index=['a','b','c','d'])
# 将df2合并到df1下面,以及重置index,并打印出结果
res7 = df7.append(df8,ignore_index=True)
print(res7)
res8 = df7.append([df8,df9], ignore_index=True)
print(res8)
res9 = df7.append(s, ignore_index=True)
print(res9)
2
3
4
5
6
7
8
9
10
11
- 这里的
append
均为上下合并,要求列数相同,不然报错
内外连接
外连接
df3 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df4 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
print(df3)
print(df4)
res3 = pd.concat([df3,df4], ignore_index=True, join='outer')
print(res3)
2
3
4
5
6
7
- 和数据库表的外连接一样(左右外连接)
- 首先是上下连接
- 由于
df1
和df2
的列属性不尽相同,列属性相同的列直接合并,对于不同的列属性,各自不存在的数据均用NaN
填充
内连接
res4 = pd.concat([df3,df4], ignore_index=True, join='inner')
print(res4)
2
- 只保留相同的列,舍弃掉不同的列属性
左右合并
df5 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df6 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
print(df5)
print(df6)
res5 = pd.concat([df5,df6], axis=1)
print(res5)
2
3
4
5
6
7
不同的行、列元素用NaN
填充,注意这里的横向连接即使列属性名字一样,也视作不同的列
Merge
pandas.merge(df1, df2, on='', how='', indicator='', left_index=True/False, right_index=True/False, suffixes=[])
df1,df2
是要合并的两个数据集how=inner/outer
表示合并方式,内连接或外连接left_index/right_index
表示是否用索引进行连接suffixes
用于分开相同的列,如df1,df2
中均有列属性age
,加入参数suffixes['_boy', '_girl']
,其合并的数据集中将出现两个age
属性,即age_boy, age_girl
indicator
会在合并的数据集中新建一列,表明每行的连接方式,有left_only, right_only, both
,分别代表左外连接,右外连接,全连接
import pandas as pd
import numpy as np
import pandas as pd
# 依据一组key合并
# 定义资料集并打印出
left = pd.DataFrame({'key' : ['K0','K1','K2','K3'],
'A' : ['A0','A1','A2','A3'],
'B' : ['B0','B1','B2','B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'C' : ['C0', 'C1', 'C2', 'C3'],
'D' : ['D0', 'D1', 'D2', 'D3']})
print(left)
print(right)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过列属性key
进行合并
res = pd.merge(left, right, on='key')
print(res)
2
得到数据集
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
print(left)
print(right)
res = pd.merge(left, right, on=['key1','key2'])
print(res)
2
3
4
5
6
7
8
9
10
11
12
13
- 这里默认为内连接,即只保留
left
和right
公共的(key1,key2)
组合所在的行
当然可以设置为外连接,左外连接,右外连接
res1 = pd.merge(left, right, on=['key1', 'key2'], how='outer')
res2 = pd.merge(left, right, on=['key1', 'key2'], how='left')
res3 = pd.merge(left, right, on=['key1', 'key2'], how='right')
2
3
根据索引连接
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
'D': ['D0', 'D2', 'D3']},
index=['K0', 'K2', 'K3'])
print(left)
print(right)
res = pd.merge(left, right, left_index=True, right_index=True, how='outer')
print(res)
2
3
4
5
6
7
8
9
10
11
- 令
left_index, right_index
均为True
表示用二者的索引连接,同样有四种连接方式outer, inner, left, right
解决列重叠问题
boys = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'age': [1, 2, 3]})
girls = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'age': [4, 5, 6]})
print(boys)
print(girls)
2
3
4
可以注意到这里的数据集boys, girls
均有列属性age
,表示ki
班级的男/女平均年龄,在合并时若想把二者分开统计,则令名字相同的列属性添加后缀_boy, _girl
,注意这里会把每个相同的列属性均分开,同时对两个数据集按照先后顺序添加后缀
res = pd.merge(boys,girls,on='k',suffixes=['_boy','_girl'],how='inner')
print(res)
2
k age_boy age_girl
0 K0 1 4
1 K0 1 5
2
3
plot出图
调用库matplotlib.pyplot
对Series
和DataFrame
进行绘图
绘制Series
的曲线图,横轴为索引,纵轴为值
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.Series(np.random.randn(1000), index=np.arange(1000))
print(data)
print(data.cumsum())
data.plot()
plt.show()
2
3
4
5
6
7
8
9
10
绘制DataFrame
四个列属性的坐标图,横轴为行索引,纵轴为数据值,每个列属性代表一条曲线
data1 = pd.DataFrame(np.random.randn(1000,4), index=np.arange(1000), columns=list("ABCD"))
data1.cumsum()
data1.plot()
plt.show()
2
3
4
绘制data1
的x-y
坐标图
ax = data1.plot.scatter(x='A',y='B',color='DarkBlue',label='Class1')
# 将之下这个 data 画在上一个 ax 上面
data1.plot.scatter(x='A',y='C',color='LightGreen',label='Class2',ax=ax)
plt.show()
2
3
4