Pandas

7/8/2022 PythonDataScience

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)
1
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
1
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)
1
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
1
2
3
4
5
6
7

数据框架的转置

print(df.T)
1
   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
1
2
3
4
5
  • 实际上这里的DataFrame就是一个a[0][0]为空,第一列、第一行为属性的矩阵

打印某一个属性值

print(df['d'])
1
[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
1
2
3
4
5
6
7
8
  • 第一列为索引,第二列为属性d的值

打印索引

print(df.index)
1
DatetimeIndex(['2018-08-19', '2018-08-20', '2018-08-21', '2018-08-22',
               '2018-08-23', '2018-08-24'],
              dtype='datetime64[ns]', freq='D')
1
2
3

打印行属性

print(df.columns)
1
Index(['a', 'b', 'c', 'd'], dtype='object')
1

打印所有数据

print(df.values)
1
[[-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]]
1
2
3
4
5
6

打印数据统计总结

print(df.describe())
1
              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
1
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)
1
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)
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)
1
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
1
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)
1
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
1
2
3
4
5

降序排列行属性

df22 = df2.sort_index(axis=0, ascending=False)
print(df22)
1
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
1
2
3
4
5
  • 这里的排序都是不改变原数据矩阵的

根据特定属性进行排列:使用函数df.sort_values(by, ascending)

  • by=某一属性,将按照这一属性的具体值进行排列
  • ascengding=True表示升序排列
df23 = df2.sort_values(by='C', ascending=False)
print(df23)
1
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
1
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)
1
2
3
4
5
6

检索某一列

print(df['A'])
print(df.A)
1
2

检索多行,含头不含尾,即0,1,2

print(df[0:3])
1

也可以用行属性名来检索,此时含头含尾,即表示检索从2022-07-062022-07-11的数据

print(df['2022-07-06':'2022-07-11'])
1
  • 这里的日期也可以用20220706的形式,即省略-

检索特定行:使用dataframeloc数组

print(df.loc['2022-07-09'])
print(df.loc['2022-07-09':'2022-07-11'])
1
2

检索特定列,同样使用loc数组,第一个入参为行,第二个入参为列,当单独搜索多列时,令第一个入参为:即可

print(df.loc[:, 'A':'C'])
1

当然也可以限定列

print(df.loc['20220707', 'A':'C'])
print(df.loc['20220707':'20220709', 'A':'C'])
1
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]])
1
2
3
4
5
6
7
8
9
10
11

通过值进行筛选,选出A属性大于8的行

print(df[df.A>8])
print(df.loc[df.A>8])
1
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)
1
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)
1
2
3
4
5
6
7

设置多行/多列

df.iloc[[1,2]] = 1
print(df)
df.loc[:, ['B','C']] = 2
print(df)
1
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
1
2
3
4
  • A属性大于4的行的B属性设置为np.nan
  • 四种写法均可

添加行

date = pd.to_datetime('20220712')
df.loc[date] = [24,25,26,27]
print(df)
1
2
3

添加列

df['E'] = pd.Series([1,2,3,4,5,6], index=pd.date_range('20220706',periods=6))
print(df)
1
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)
1
2
3
4
5
6
7
8

上述数据中有两个丢失数据NaN

删除有丢失数据的行/列

df1 = df.dropna()
print(df1)
1
2
  • 无参的dropna()默认删除所有含有NaN的行,并返回没有NaN的矩阵,不改变原矩阵

其实有两个参数:axishow

df2 = df.dropna(axis=1, how='any')
print(df2)
1
2
  • axis=1时表示删除Nan所在列,默认axis=0
  • how='any'表示只要存在Nan即删除,how='all'表示要这一行/列全为Nan时才删除,默认为any

Nan替换为0或其他

df3 = df.fillna(value='nmsl')
print(df3)
1
2

在各元素上判断是否有NanNULL,输出一个布尔矩阵

df4 = df3.isnull()
df5 = df.isnull()
print(df4)
print(df5)
1
2
3
4

在各元素上判断是否有Nan,输出一个布尔矩阵

df6 = df.isna()
print(df6)
1
2

判断各列是否有缺失数据

df7 = df.isna().any()
print(df7)
1
2
A    False
B    False
C     True
D     True
dtype: bool
1
2
3
4
5

判断整张表中是否有缺失数据

flag = np.any(df.isnull())
print(flag)
1
2

导入导出

导入现成数据

import pandas as pd

data = pd.read_csv('student.csv')
print(data)
1
2
3
4
  • 注意这里要把student.csv放在.py的同一目录下,或者用相对、绝对路径去找

前三行和后四行(就像linux的命令)

print(data.head(3))
print(data.tail(4))
1
2

导出数据

data.to_pickle('student.pickle')
print(pd.read_pickle('student.pickle'))
1
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)
1
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)
1
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)
1
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)
1
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)
1
2
3
4
5
6
7
  • 和数据库表的外连接一样(左右外连接)
  • 首先是上下连接
  • 由于df1df2的列属性不尽相同,列属性相同的列直接合并,对于不同的列属性,各自不存在的数据均用NaN填充

内连接

res4 = pd.concat([df3,df4], ignore_index=True, join='inner')
print(res4)
1
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)
1
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

通过列属性key进行合并

res = pd.merge(left, right, on='key')
print(res)
1
2

得到数据集

merge([keyAB0K0A0B01K1A1B12K2A2B23K3A3B3],[keyCD0K0C0D01K1C1D12K2C2D23K3C3D3],on=key)=[keyABCD0K0A0B0C1D11K1A1B1C2D22K2A2B2C3D33K3A3B3C4D4] merge( \begin{bmatrix} & key & A & B\\ 0 & K0 & A0 & B0\\ 1 & K1 & A1 & B1\\ 2 & K2 & A2 & B2\\ 3 & K3 & A3 & B3\\ \end{bmatrix} , \begin{bmatrix} & key & C & D\\ 0 & K0 & C0 & D0\\ 1 & K1 & C1 & D1\\ 2 & K2 & C2 & D2\\ 3 & K3 & C3 & D3\\ \end{bmatrix} ,on='key')= \begin{bmatrix} & key & A & B & C & D\\ 0 & K0 & A0 & B0 & C1 & D1\\ 1 & K1 & A1 & B1 & C2 & D2\\ 2 & K2 & A2 & B2 & C3 & D3\\ 3 & K3 & A3 & B3 & C4 & D4 \end{bmatrix}
两列合并

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 这里默认为内连接,即只保留leftright公共的(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')
1
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)
1
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)
1
2
3
4

可以注意到这里的数据集boys, girls均有列属性age,表示ki班级的男/女平均年龄,在合并时若想把二者分开统计,则令名字相同的列属性添加后缀_boy, _girl,注意这里会把每个相同的列属性均分开,同时对两个数据集按照先后顺序添加后缀

res = pd.merge(boys,girls,on='k',suffixes=['_boy','_girl'],how='inner')
print(res)
1
2
    k  age_boy  age_girl
0  K0        1         4
1  K0        1         5
1
2
3

plot出图

调用库matplotlib.pyplotSeriesDataFrame进行绘图

绘制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()
1
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()
1
2
3
4

绘制data1x-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()
1
2
3
4

Pandas 50

Last Updated: 7/16/2024, 4:06:40 AM
妖风过海
刘森