28. Pandas - 2

由于文章太长造成了发布文章频繁崩溃,没办法拆分成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

时间序列

⽣成⼀段时间范围

该函数主要⽤于⽣成⼀个固定频率的时间索引,在调⽤构造⽅法时,必须指定startendperiods中的两个参数值,否则报错。

时间序列频率 解释
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

按理来说,我们最后展示数据的时候,在用完age0,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()
image-20230822200739326.png

结尾

那小伙伴们,到今天为止,我们《AI秘籍》系列课程中的「Python部分」也就完全讲完了。就如我一开始向大家承诺的,这部分内容将完全免费。

而我们的《AI秘籍》课程也才刚刚开始,未来很长一段时间内,我们都将继续和这些基础内容频繁打交道。用它们来呈现我们之后要用到的所有课程。包括AI基础,CV,BI,NLP等课程。

不过在结束了一个阶段之后,我需要花点时间休整一下,也是为了好好的备课,找到最好的结构和顺序为大家编写后面的课程。本次课程的最后这两节课我都有些疲劳,为了赶快完成进度,编写过程当中可能有些粗糙或者遗漏,也请大家多包涵。日后,我可能会对这两节课进行更新,将一些细节的讲解补充完整。

免费部分结束了,日后的收费课程,也期望大家能一如即往的支持。

在这里,我也给大家推荐一些比较好的书籍,希望看到小伙伴们能够快速成长起来。

感谢大家,好了。本节课到此结束,下课了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容