由于文章太长造成了发布文章频繁崩溃,没办法拆分成2部分。请先查看27. Pandas - 1
数据合并
我们平时会与拿到的数据可能存储在不同的数据表中,这需要我们对数据进行合并,然后再进行操作。
使⽤join
合并,着重关注的是⾏的合并。
简单合并(默认是left左连接,以左侧df3
为基础)
df3=pd.DataFrame({'Red':[1,3,5],'Green':[5,0,3]},index=list('abc'))
df4=pd.DataFrame({'Blue':[1,9,8],'Yellow':[6,6,7]},index=list('cde'))
print(df3)
print(df4)
df3.join(df4,how='left')
---
Red Green
a 1 5
b 3 0
c 5 3
Blue Yellow
c 1 6
d 9 6
e 8 7
Red Green Blue Yellow
a 1 5 NaN NaN
b 3 0 NaN NaN
c 5 3 1.0 6.0
右链接
df3.join(df4,how='outer')
---
Red Green Blue Yellow
a 1.0 5.0 NaN NaN
b 3.0 0.0 NaN NaN
c 5.0 3.0 1.0 6.0
d NaN NaN 9.0 6.0
e NaN NaN 8.0 7.0
合并多个DataFrame对象
df5=pd.DataFrame({'Brown':[3,4,5],'White':[1,1,2]},index=list('aed'))
df3.join([df4,df5])
---
Red Green Blue Yellow Brown White
a 1.0 5.0 NaN NaN 3.0 1.0
b 3.0 0.0 NaN NaN NaN NaN
c 5.0 3.0 1.0 6.0 NaN NaN
使⽤merge
,着重关注的是列的合并。
我们先来构建两组数据:
df1=pd.DataFrame({'名字':list('ABCDE'),'性别':['男','⼥','男','男','⼥'],'职称': ['副教授','讲师','助教','教授','助教']},index=range(1001,1006))
df1.columns.name='学院⽼师'
df1.index.name='编号'
df1
---
学院⽼师 名字 性别 职称
编号
1001 A 男 副教授
1002 B ⼥ 讲师
1003 C 男 助教
1004 D 男 教授
1005 E ⼥ 助教
df2=pd.DataFrame({'名字':list('ABDAX'),'课程':['C++','计算机导论','汇编','数据结构','马克思原理'],'职称':['副教授','讲师','教授','副教授','讲师']},index= [1001,1002,1004,1001,3001])
df2.columns.name='课程'
df2.index.name='编号'
print(df2)
---
课程 名字 课程 职称
编号
1001 A C++ 副教授
1002 B 计算机导论 讲师
1004 D 汇编 教授
1001 A 数据结构 副教授
3001 X 马克思原理 讲师
默认下是根据左右对象中出现同名的列作为连接的键,且连接⽅式是how=’inner’
print(pd.merge(df1,df2)) # 返回匹配的
---
名字 性别 职称 课程
0 A 男 副教授 C++
1 A 男 副教授 数据结构
2 B ⼥ 讲师 计算机导论
3 D 男 教授 汇编
指定列名合并
pd.merge(df1,df2,on='名字',suffixes=['_1','_2']) # 返回匹配的
---
名字 性别 职称_1 课程 职称_2
0 A 男 副教授 C++ 副教授
1 A 男 副教授 数据结构 副教授
2 B ⼥ 讲师 计算机导论 讲师
3 D 男 教授 汇编 教授
连接⽅式,根据左侧为准
pd.merge(df1,df2,how='left')
---
名字 性别 职称 课程
0 A 男 副教授 C++
1 A 男 副教授 数据结构
2 B ⼥ 讲师 计算机导论
3 C 男 助教 NaN
4 D 男 教授 汇编
5 E ⼥ 助教 NaN
根据右侧为准
pd.merge(df1,df2,how='right')
---
名字 性别 职称 课程
0 A 男 副教授 C++
1 B ⼥ 讲师 计算机导论
2 D 男 教授 汇编
3 A 男 副教授 数据结构
4 X NaN 讲师 马克思原理
所有的数据
pd.merge(df1,df2,how='outer')
---
名字 性别 职称 课程
0 A 男 副教授 C++
1 A 男 副教授 数据结构
2 B ⼥ 讲师 计算机导论
3 C 男 助教 NaN
4 D 男 教授 汇编
5 E ⼥ 助教 NaN
6 X NaN 讲师 马克思原理
根据多个键进⾏连接
pd.merge(df1,df2,*on*=['职称','名字'])
---
名字 性别 职称 课程
0 A 男 副教授 C++
1 A 男 副教授 数据结构
2 B ⼥ 讲师 计算机导论
3 D 男 教授 汇编
除此之外,我们还有一种轴向连接的方式:Concat
Series对象的连接
s1=pd.Series([1,2],index=list('ab'))
s2=pd.Series([3,4,5],index=list('bde'))
print(s1)
print(s2)
pd.concat([s1,s2])
---
a 1
b 2
dtype: int64
b 3
d 4
e 5
dtype: int64
a 1
b 2
b 3
d 4
e 5
dtype: int64
横向连接
pd.concat([s1,s2],*axis*=1)
---
0 1
a 1.0 NaN
b 2.0 3.0
d NaN 4.0
e NaN 5.0
⽤内连接求交集(连接⽅式,共有’inner
’,’left
’,right
’,’outer
’)
pd.concat([s1,s2],axis=1,join='inner')
---
0 1
b 2 3
创建层次化索引
pd.concat([s1,s2],keys=['A','B'])
---
A a 1
b 2
B b 3
d 4
e 5
dtype: int64
当纵向连接时keys为列名
pd.concat([s1,s2],keys=['A','D'],axis=1)
----
A D
a 1.0 NaN
b 2.0 3.0
d NaN 4.0
e NaN 5.0
DataFrame对象的连接
df3=pd.DataFrame({'Red':[1,3,5],'Green':[5,0,3]},index=list('abd'))
df4=pd.DataFrame({'Blue':[1,9],'Yellow':[6,6]},index=list('ce'))
pd.concat([df3,df4])
---
Red Green Blue Yellow
a 1.0 5.0 NaN NaN
b 3.0 0.0 NaN NaN
d 5.0 3.0 NaN NaN
c NaN NaN 1.0 6.0
e NaN NaN 9.0 6.0
⽤字典的⽅式连接同样可以创建层次化列索引
pd.concat({'A':df3,'B':df4},axis=1)
---
A B
Red Green Blue Yellow
a 1.0 5.0 NaN NaN
b 3.0 0.0 NaN NaN
d 5.0 3.0 NaN NaN
c NaN NaN 1.0 6.0
e NaN NaN 9.0 6.0
多层索引(拓展)
创建多层索引
s = Series(np.random.randint(0,150,size=6),index=list('abcdef'))
print(s)
---
a 40
b 122
c 95
d 40
e 35
f 27
dtype: int64
s = Series(np.random.randint(0,150,size=6),
index=[['a','a','b','b','c','c'],['期中','期末','期中','期末','期中','期末']])
print(s)
---
a 期中 132
期末 145
b 期中 33
期末 149
c 期中 10
期末 145
dtype: int64
DataFrame也可以创建多层索引
# DataFrame创建多层索引
df1 = df(np.random.randint(0,150,size=(6,4)),
columns = ['zs','ls','ww','zl'],
index =[['python','python','math','math','En','En'],['期中','期末','期中','期末','期中','期末']])
print(df1)
---
zs ls ww zl
python 期中 123 3 98 95
期末 9 36 15 126
math 期中 86 86 73 115
期末 3 130 52 89
En 期中 75 21 84 98
期末 56 46 111 147
特定结构
class1=['python','python','math','math','En','En']
class2=['期中','期末','期中','期末','期中','期末']
m_index2=pd.MultiIndex.from_arrays([class1,class2])
df2=df(np.random.randint(0,150,(6,4)),index=m_index2)
print(df2)
---
0 1 2 3
python 期中 94 36 6 19
期末 24 41 108 120
math 期中 79 69 144 32
期末 138 100 42 38
En 期中 110 90 123 75
期末 69 59 72 109
class1=['期中','期中','期中','期末','期末','期末']
class2=['python','math','En','python','math','En']
m_index2=pd.MultiIndex.from_arrays([class1,class2])
df2=df(np.random.randint(0,150,(6,4)),index=m_index2)
print(df2)
---
0 1 2 3
期中 python 96 15 135 5
math 66 78 143 93
En 70 27 120 63
期末 python 147 77 92 97
math 121 81 137 102
En 18 12 134 113
product构造
class1=['python','math','En']
class2=['期中','期末']
m_index2=pd.MultiIndex.from_product([class1,class2])
df2=df(np.random.randint(0,150,(6,4)),index=m_index2)
print(df2)
---
0 1 2 3
python 期中 12 72 115 59
期末 36 51 94 111
math 期中 44 14 9 61
期末 115 121 65 93
En 期中 29 23 16 70
期末 30 73 77 53
多层索引对象的索引
让我们先来看看Series的操作
s = Series(np.random.randint(0,150,size=6),
index=[['a','a','b','b','c','c'],['期中','期末','期中','期末','期中','期末']])
print(s)
---
a 期中 31
期末 4
b 期中 101
期末 95
c 期中 54
期末 126
dtype: int64
取⼀个第⼀级索引
print(s['a'])
---
期中 31
期末 4
dtype: int64
取多个第⼀级索引
print(s[['a','b']])
---
a 期中 31
期末 4
b 期中 101
期末 95
dtype: int64
根据索引获取值
print(s['a','期末'])
---
4
loc⽅法取值
print(s.loc['a'])
print(s.loc[['a','b']])
print(s.loc['a','期末'])
---
期中 31
期末 4
dtype: int64
a 期中 31
期末 4
b 期中 101
期末 95
dtype: int64
4
iloc⽅法取值(iloc计算的事最内层索引)
print(s.iloc[1])
print(s.iloc[1:4])
---
4
a 期末 4
b 期中 101
期末 95
dtype: int64
然后再让我们来看看DataFrame的操作
# dataframe
class1=['python','math','En']
class2=['期中','期末']
m_index2=pd.MultiIndex.from_product([class1,class2])
df2=df(np.random.randint(0,150,(6,4)),index=m_index2)
print(df2)
---
0 1 2 3
python 期中 88 69 82 28
期末 110 60 130 133
math 期中 64 103 24 49
期末 23 41 10 61
En 期中 124 139 65 115
期末 114 13 117 79
获取列
print(df2[0])
---
python 期中 88
期末 110
math 期中 64
期末 23
En 期中 124
期末 114
Name: 0, dtype: int64
⼀级索引
print(df2.loc['python'])
---
0 1 2 3
期中 88 69 82 28
期末 110 60 130 133
多个⼀级索引
print(df2.loc[['python','math']])
---
0 1 2 3
python 期中 88 69 82 28
期末 110 60 130 133
math 期中 64 103 24 49
期末 23 41 10 61
取⼀⾏
print(df2.loc['python','期末'])
---
0 110
1 60
2 130
3 133
Name: (python, 期末), dtype: int64
取⼀值
print(df2.loc['python','期末'][0])
---
110
iloc是只取最内层的索引的
print(df2.iloc[0])
---
0 88
1 69
2 82
3 28
Name: (python, 期中), dtype: int64
时间序列
⽣成⼀段时间范围
该函数主要⽤于⽣成⼀个固定频率的时间索引,在调⽤构造⽅法时,必须指定start
、end
、periods
中的两个参数值,否则报错。
时间序列频率 | 解释 |
---|---|
D | ⽇历⽇的每天 |
B | ⼯作⽇的每天 |
H | 每⼩时 |
T或min | 每分钟 |
S | 每秒 |
L或ms | 每毫秒 |
U | 每微秒 |
M | ⽇历⽇的⽉底⽇期 |
BM | ⼯作⽇的⽉底⽇期 |
MS | ⽇历⽇的⽉初⽇期 |
BMS | ⼯作⽇的⽉初⽇期 |
date = pd.date_range(start='20190501',end='20190530')
print(date)
---
DatetimeIndex(['2023-05-01', '2023-05-02', '2023-05-03', '2023-05-04',
'2023-05-05', '2023-05-06', '2023-05-07', '2023-05-08',
'2023-05-09', '2023-05-10', '2023-05-11', '2023-05-12',
'2023-05-13', '2023-05-14', '2023-05-15', '2023-05-16',
'2023-05-17', '2023-05-18', '2023-05-19', '2023-05-20',
'2023-05-21', '2023-05-22', '2023-05-23', '2023-05-24',
'2023-05-25', '2023-05-26', '2023-05-27', '2023-05-28',
'2023-05-29', '2023-05-30'],
dtype='datetime64[ns]', freq='D')
req
:⽇期偏移量,取值为string, 默认为'D',
periods
:固定时期,取值为整数或None
freq
: 时间序列频率
date = pd.date_range(start='20230501',periods=10,freq='10D')
print(date)
---
DatetimeIndex(['2023-05-01', '2023-05-11', '2023-05-21', '2023-05-31',
'2023-06-10', '2023-06-20', '2023-06-30', '2023-07-10',
'2023-07-20', '2023-07-30'],
dtype='datetime64[ns]', freq='10D')
根据closed
参数选择是否包含开始和结束时间closed=None
,left包含开始时间,不包含结束时间, right与之相反。
data_time =pd.date_range(start='2023-08-09',end='2023-08-14',closed='left')
print(data_time)
---
DatetimeIndex(['2023-08-09', '2023-08-10', '2023-08-11', '2023-08-12',
'2023-08-13'],
dtype='datetime64[ns]', freq='D')
时间序列在dataFrame中的作⽤
可以将时间作为索引
index = pd.date_range(start='20230801',periods=10)
df = pd.Series(np.random.randint(0,10,size = 10),index=index)
print(df)
---
2023-08-01 7
2023-08-02 2
2023-08-03 5
2023-08-04 5
2023-08-05 2
2023-08-06 0
2023-08-07 2
2023-08-08 3
2023-08-09 6
2023-08-10 5
Freq: D, dtype: int64
truncate
这个函数将before指定⽇期之前的值全部过滤出去,after指定⽇期之前的值全部过滤出去.
after = df.truncate(after='2023-08-8')
print(after)
---
2023-08-01 7
2023-08-02 2
2023-08-03 5
2023-08-04 5
2023-08-05 2
2023-08-06 0
2023-08-07 2
2023-08-08 3
Freq: D, dtype: int64
long_ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2021',periods=1000))
print(long_ts)
---
2021-01-01 -0.482811
...
2023-09-27 -0.108047
Freq: D, Length: 1000, dtype: float64
根据年份获取
result = long_ts['2022']
print(result)
---
2022-01-01 -0.600007
...
2022-12-31 0.097874
Freq: D, Length: 365, dtype: float64
年份和⽇期获取
result = long_ts['2023-07']
print(result)
---
2023-07-01 -1.797582
...
2023-07-31 0.687787
Freq: D, dtype: float64
使⽤切⽚
# 使⽤切⽚
result = long_ts['2023-05-01':'2023-05-06']
print(result)
---
2023-05-01 -2.338218
2023-05-02 -2.130780
2023-05-03 0.582920
2023-05-04 -0.182540
2023-05-05 0.127363
2023-05-06 -0.032844
Freq: D, dtype: float64
通过between_time()
返回位于指定时间段的数据集
index=pd.date_range("2023-03-17","2023-03-30",freq="2H")
ts = pd.Series(np.random.randn(157),index=index)
print(ts.between_time("7:00","17:00"))
---
2023-03-17 08:00:00 -0.532254
...
2023-03-29 16:00:00 0.437697
Length: 65, dtype: float64
这些操作也都适⽤于dataframe
index=pd.date_range('1/1/2023',periods=100)
df = pd.DataFrame(np.random.randn(100,4),index=index)
print(df.loc['2023-04'])
---
0 1 2 3
2023-04-01 -0.220090 0.335770 -0.086181 -0.046045
2023-04-02 -1.046423 -0.347116 0.367099 -0.979354
2023-04-03 -0.720944 -1.478932 0.220948 0.801831
2023-04-04 1.359946 -1.239004 0.309747 -0.047959
2023-04-05 -0.256502 2.224782 0.494740 -1.322490
2023-04-06 1.488119 0.244942 0.614101 -0.156201
2023-04-07 -1.815019 -1.935966 0.239024 -1.388502
2023-04-08 1.106623 1.148805 2.120405 -0.799290
2023-04-09 -1.902216 0.625965 -0.102506 -0.430550
2023-04-10 -0.876382 -2.034205 -0.060846 2.442651
移位⽇期
ts = pd.Series(np.random.randn(10),index=pd.date_range('1/1/2023',periods=10))
print(ts)
---
2023-01-01 -0.976958
2023-01-02 -0.487439
2023-01-03 0.143104
2023-01-04 -0.964236
2023-01-05 0.758326
2023-01-06 -1.650818
2023-01-07 0.709231
2023-01-08 0.198714
2023-01-09 -1.043443
2023-01-10 0.220834
Freq: D, dtype: float64
移动数据,索引不变,默认由NaN
填充
periods
: 移动的位数 负数是向上移动
fill_value
: 移动后填充数据
freq
: ⽇期偏移量
ts.shift(periods=2,fill_value=100, freq='D')
---
2023-01-03 -0.976958
2023-01-04 -0.487439
2023-01-05 0.143104
2023-01-06 -0.964236
2023-01-07 0.758326
2023-01-08 -1.650818
2023-01-09 0.709231
2023-01-10 0.198714
2023-01-11 -1.043443
2023-01-12 0.220834
Freq: D, dtype: float64
通过tshift()
将索引移动指定的时间:
ts.tshift(2)
---
2023-01-03 -0.976958
2023-01-04 -0.487439
2023-01-05 0.143104
2023-01-06 -0.964236
2023-01-07 0.758326
2023-01-08 -1.650818
2023-01-09 0.709231
2023-01-10 0.198714
2023-01-11 -1.043443
2023-01-12 0.220834
Freq: D, dtype: float64
将时间戳转化成时间根式
pd.to_datetime(1688570740000,unit='ms')
---
Timestamp('2023-07-05 15:25:40')
utc是协调世界时,时区是以UTC的偏移量的形式表示的,但是注意设置utc=True
,是让pandas对象具有时区性质,对于⼀列进⾏转换的,会造成转换错误。
unit='ms'
设置粒度是到毫秒级别的。
时区名字
import pytz
print(pytz.common_timezones)
---
['Africa/Abidjan', ..., 'US/Pacific', 'UTC']
pd.to_datetime(1688570740000,unit='ms').tz_localize('UTC').tz_convert('Asia/Shanghai')
---
Timestamp('2023-07-05 23:25:40+0800', tz='Asia/Shanghai')
一个处理的例子:
df = pd.DataFrame([1688570740000, 1688570800000, 1688570860000],columns = ['time_stamp'])
pd.to_datetime(df['time_stamp'],unit='ms').dt.tz_localize('UTC').dt.tz_convert ('Asia/Shanghai')
---
0 2023-07-05 23:25:40+08:00
1 2023-07-05 23:26:40+08:00
2 2023-07-05 23:27:40+08:00
Name: time_stamp, dtype: datetime64[ns, Asia/Shanghai]
先赋予标准时区,再转换到东⼋区。
处理中⽂
pd.to_datetime('2023年7⽉5⽇',format='%Y年%m⽉%d⽇')
---
Timestamp('2023-07-05 00:00:00')
分组聚合
df=pd.DataFrame({
'name':['BOSS','Lilei','Lilei','Han','BOSS','BOSS','Han','BOSS'], 'Year':[2016,2016,2016,2016,2017,2017,2017,2017],
'Salary':[999999,20000,25000,3000,9999999,999999,3500,999999],
'Bonus':[100000,20000,20000,5000,200000,300000,3000,400000]
})
df
---
name Year Salary Bonus
0 BOSS 2016 999999 100000
1 Lilei 2016 20000 20000
2 Lilei 2016 25000 20000
3 Han 2016 3000 5000
4 BOSS 2017 9999999 200000
5 BOSS 2017 999999 300000
6 Han 2017 3500 3000
7 BOSS 2017 999999 400000
根据name这⼀列进⾏分组
group_by_name=df.groupby('name')
print(type(group_by_name))
---
<class 'pandas.core.groupby.generic.DataFrameGroupBy'>
查看分组
print(group_by_name.groups) # 分组后的数量
print(group_by_name.count())
---
{'BOSS': [0, 4, 5, 7], 'Han': [3, 6], 'Lilei': [1, 2]}
Year Salary Bonus
name
BOSS 4 4 4
Han 2 2 2
Lilei 2 2 2
查看分组的情况
for name,group in group_by_name:
print(name)
---
BOSS
Han
Lilei
组的名字
print(group) # 组具体内容
---
name Year Salary Bonus
1 Lilei 2016 20000 20000
2 Lilei 2016 25000 20000
可以选择分组
print(group_by_name.get_group('BOSS'))
---
name Year Salary Bonus
0 BOSS 2016 999999 100000
4 BOSS 2017 9999999 200000
5 BOSS 2017 999999 300000
7 BOSS 2017 999999 400000
按照某⼀列进⾏分组, 将name这⼀列作为分组的键,对year进⾏分组
group_by_name=df['Year'].groupby(df['name'])
print(group_by_name.count())
---
name
BOSS 4
Han 2
Lilei 2
Name: Year, dtype: int64
按照多列进⾏分组
group_by_name_year=df.groupby(['name','Year'])
for name,group in group_by_name_year:
print(name) # 组的名字
print(group) # 组具体内容
---
('BOSS', 2016)
name Year Salary Bonus
0 BOSS 2016 999999 100000
('BOSS', 2017)
name Year Salary Bonus
4 BOSS 2017 9999999 200000
5 BOSS 2017 999999 300000
7 BOSS 2017 999999 400000
('Han', 2016)
name Year Salary Bonus
3 Han 2016 3000 5000
('Han', 2017)
name Year Salary Bonus
6 Han 2017 3500 3000
('Lilei', 2016)
name Year Salary Bonus
1 Lilei 2016 20000 20000
2 Lilei 2016 25000 20000
可以选择分组
print(group_by_name_year.get_group(('BOSS',2016)))
---
name Year Salary Bonus
0 BOSS 2016 999999 100000
将某列数据按数据值分成不同范围段进⾏分组(groupby)运算
df = pd.DataFrame({'Age': np.random.randint(20, 70, 100),
'Sex': np.random.choice(['M', 'F'], 100),
})
age_groups = pd.cut(df['Age'], bins=[19,40,65,100])
print(df.groupby(age_groups).count())
---
Age Sex
Age
(19, 40] 35 35
(40, 65] 54 54
(65, 100] 11 11
按‘Age’分组范围和性别(sex)进⾏制作交叉表
pd.crosstab(age_groups, df['Sex'])
---
Sex F M
Age
(19, 40] 18 22
(40, 65] 25 27
(65, 100] 3 5
聚合
我们先来看聚合函数的表格
聚合函数 | 解释 |
---|---|
mean | 计算分组平均值 |
count | 分组中⾮NA值的数量 |
sum | ⾮NA值的和 |
median | ⾮NA值的算术中位数 |
std | 标准差 |
var | ⽅差 |
min | ⾮NA值的最⼩值 |
max | ⾮NA值的最⼤值 |
prod | ⾮NA值的积 |
first | 第⼀个⾮NA值 |
last | 最后⼀个⾮NA值 |
mad | 平均绝对偏差 |
mode | 模 |
abs | 绝对值 |
sem | 平均值的标准误差 |
skew | 样品偏斜度(三阶矩) |
kurt | 样品峰度(四阶矩) |
quantile | 样本分位数(百分位上的值) |
cumsum | 累积总和 |
cumprod | 累积乘积 |
cummax | 累积最⼤值 |
cummin | 累积最⼩值 |
df1=pd.DataFrame({'Data1':np.random.randint(0,10,5),
'Data2':np.random.randint(10,20,5),
'key1':list('aabba'),
'key2':list('xyyxy')})
print(df1)
---
Data1 Data2 key1 key2
0 4 17 a x
1 4 13 a y
2 0 12 b y
3 5 16 b x
4 8 10 a y
按key1分组,进⾏聚合计算
⚠️ 注意:当分组后进⾏数值计算时,不是数值类的列(即麻烦列)会被清除
print(df1.groupby('key1').sum())
---
Data1 Data2
key1
a 16 40
b 5 28
只算data1
print(df1['Data1'].groupby(df1['key1']).sum())
print(df1.groupby('key1')['Data1'].sum())
---
key1
a 16
b 5
Name: Data1, dtype: int64
key1
a 16
b 5
Name: Data1, dtype: int64
使⽤agg()
函数做聚合运算
print(df1.groupby('key1').agg('sum'))
---
Data1 Data2
key1
a 16 40
b 5 28
可以同时做多个聚合运算
print(df1.groupby('key1').agg(['sum','mean','std']))
---
Data1 Data2
sum mean std sum mean std
key1
a 16 5.333333 2.309401 40 13.333333 3.511885
b 5 2.500000 3.535534 28 14.000000 2.828427
可⾃定义函数,传⼊agg⽅法中 grouped.agg(func)
def peak_range(df):
"""
返回数值范围
"""
return df.max() - df.min()
print(df1.groupby('key1').agg(peak_range))
---
Data1 Data2
key1
a 4 7
b 5 4
同时应⽤多个聚合函数
print(df1.groupby('key1').agg(['mean', 'std', 'count', peak_range])) # 默认列名为函数名
---
Data1 Data2 \
mean std count peak_range mean std count
key1
a 5.333333 2.309401 3 4 13.333333 3.511885 3
b 2.500000 3.535534 2 5 14.000000 2.828427 2
peak_range
key1
a 7
b 4
通过元组提供新的列名
print(df1.groupby('key1').agg(['mean', 'std', 'count', ('range', peak_range)]))
---
Data1 Data2
mean std count range mean std count range
key1
a 5.333333 2.309401 3 4 13.333333 3.511885 3 7
b 2.500000 3.535534 2 5 14.000000 2.828427 2 4
给每列作⽤不同的聚合函数
dict_mapping = {
'Data1':['mean','max'],
'Data2':'sum'
}
df1.groupby('key1').agg(dict_mapping)
---
Data1 Data2
mean max sum
key1
a 5.333333 8 40
b 2.500000 5 28
拓展apply()
函数
apply函数是pandas⾥⾯所有函数中⾃由度最⾼的函数
df1=pd.DataFrame({'sex':list('FFMFMMF'),'smoker':list('YNYYNYY'),'age': [21,30,17,37,40,18,26],'weight':[120,100,132,140,94,89,123]})
print(df1)
---
sex smoker age weight
0 F Y 21 120
1 F N 30 100
2 M Y 17 132
3 F Y 37 140
4 M N 40 94
5 M Y 18 89
6 F Y 26 123
抽烟的年龄⼤于等18的
def bin_age(age):
if age >=18:
return 1
else:
return 0
print(df1['age'].apply(bin_age))
---
0 1
1 1
2 0
3 1
4 1
5 1
6 1
Name: age, dtype: int64
df1['age'] = df1['age'].apply(bin_age)
print(df1)
---
sex smoker age weight
0 F Y 1 120
1 F N 1 100
2 M Y 0 132
3 F Y 1 140
4 M N 1 94
5 M Y 1 89
6 F Y 1 123
取出抽烟和不抽烟的体重前⼆
def top(smoker,col,n=5):
return smoker.sort_values(by=col)[-n:]
df1.groupby('smoker').apply(top,col='weight',n=2)
---
sex smoker age weight
smoker
N 4 M N 1 94
1 F N 1 100
Y 2 M Y 0 132
3 F Y 1 140
按理来说,我们最后展示数据的时候,在用完age
上0,1
作为判断之后,要恢复成原本的年龄的数据。不过...就这样吧。因为马上,我们要做一个完整的分组案例,从一个csv
文件获取数据,然后分组,最后进行数据可视化展示:
分组案例
我们先来读取数据,案例中使用到的数据会上传到我的Github
仓库中。
data = pd.read_csv('./data/movie_metadata.csv')
print('数据的形状:', data.shape)
print(data.head())
---
数据的形状: (5043, 28)
movie_title language country \
0 Avatar English USA
1 Pirates of the Caribbean: At World's End English USA
2 Spectre English UK
3 The Dark Knight Rises English USA
4 Star Wars: Episode VII - The Force Awakens ... NaN NaN
content_rating title_year color duration genres \
0 PG-13 2009-02 Color 178.0 Action|Adventure|Fantasy|Sci-Fi
1 PG-13 2007-09 Color 169.0 Action|Adventure|Fantasy
2 PG-13 2015-11 Color 148.0 Action|Adventure|Thriller
3 PG-13 2012-08 Color 164.0 Action|Thriller
4 NaN NaN NaN NaN Documentary
plot_keywords budget ... \
0 avatar|future|marine|native|paraplegic 237000000.0 ...
1 goddess|marriage ceremony|marriage proposal|pi... 300000000.0 ...
2 bomb|espionage|sequel|spy|terrorist 245000000.0 ...
3 deception|imprisonment|lawlessness|police offi... 250000000.0 ...
4 NaN NaN ...
actor_2_facebook_likes actor_3_name actor_3_facebook_likes \
0 936.0 Wes Studi 855.0
1 5000.0 Jack Davenport 1000.0
...
然后让我们来处理缺失值:
data = data.dropna(how='any')
print(data.head())
---
movie_title language country content_rating \
0 Avatar English USA PG-13
1 Pirates of the Caribbean: At World's End English USA PG-13
2 Spectre English UK PG-13
3 The Dark Knight Rises English USA PG-13
5 John Carter English USA PG-13
title_year color duration genres \
0 2009-02 Color 178.0 Action|Adventure|Fantasy|Sci-Fi
1 2007-09 Color 169.0 Action|Adventure|Fantasy
2 2015-11 Color 148.0 Action|Adventure|Thriller
3 2012-08 Color 164.0 Action|Thriller
5 2012-07 Color 132.0 Action|Adventure|Sci-Fi
plot_keywords budget ... \
0 avatar|future|marine|native|paraplegic 237000000.0 ...
1 goddess|marriage ceremony|marriage proposal|pi... 300000000.0 ...
2 bomb|espionage|sequel|spy|terrorist 245000000.0 ...
3 deception|imprisonment|lawlessness|police offi... 250000000.0 ...
5 alien|american civil war|male nipple|mars|prin... 263700000.0 ...
actor_2_facebook_likes actor_3_name actor_3_facebook_likes \
0 936.0 Wes Studi 855.0
1 5000.0 Jack Davenport 1000.0
2 393.0 Stephanie Sigman 161.0
...
接着,我们来查看一下票房收入统计
导演vs票房总收⼊
group_director = data.groupby(*by*='director_name')['gross'].sum()
ascending升降序排列,True升序
result = group_director.sort_values()
print(type(result))
print(result)
---
<class 'pandas.core.series.Series'>
director_name
Ekachai Uekrongtham 1.620000e+02
Frank Whaley 7.030000e+02
Ricki Stern 1.111000e+03
Alex Craig Mann 1.332000e+03
Paul Bunnell 2.436000e+03
...
Sam Raimi 2.049549e+09
Tim Burton 2.071275e+09
Michael Bay 2.231243e+09
Peter Jackson 2.592969e+09
Steven Spielberg 4.114233e+09
Name: gross, Length: 1660, dtype: float64
电影产量年份趋势
from matplotlib import pyplot as plt
import random
from matplotlib import font_manager
movie_years = data.groupby('title_year')['movie_title']
print(movie_years.count().index.tolist())
print(movie_years.count().values)
---
['1927-02', ..., '2016-12']
[ 1 ... 6]
最后,我们利用之前学过的matplotlib
进行数据可视化展示:
x = movie_years.count().index.tolist()
y = movie_years.count().values
plt.figure(figsize=(20,8),dpi=80)
plt.plot(x,y)
plt.show()
结尾
那小伙伴们,到今天为止,我们《AI秘籍》系列课程中的「Python部分」也就完全讲完了。就如我一开始向大家承诺的,这部分内容将完全免费。
而我们的《AI秘籍》课程也才刚刚开始,未来很长一段时间内,我们都将继续和这些基础内容频繁打交道。用它们来呈现我们之后要用到的所有课程。包括AI基础,CV,BI,NLP等课程。
不过在结束了一个阶段之后,我需要花点时间休整一下,也是为了好好的备课,找到最好的结构和顺序为大家编写后面的课程。本次课程的最后这两节课我都有些疲劳,为了赶快完成进度,编写过程当中可能有些粗糙或者遗漏,也请大家多包涵。日后,我可能会对这两节课进行更新,将一些细节的讲解补充完整。
免费部分结束了,日后的收费课程,也期望大家能一如即往的支持。
在这里,我也给大家推荐一些比较好的书籍,希望看到小伙伴们能够快速成长起来。
感谢大家,好了。本节课到此结束,下课了。