Pandas教程(非常详细)(第四部分)

接着Pandas教程(非常详细)(第一部分),继续讲述。

十九、Pandas groupby分组操作详解

在数据分析中,经常会遇到这样的情况:根据某一列(或多列)标签把数据划分为不同的组别,然后再对其进行数据分析。比如,某网站对注册用户的性别或者年龄等进行分组,从而研究出网站用户的画像(特点)。在 Pandas 中,要完成数据的分组操作,需要使用 groupby() 函数,它和 SQL 的GROUP BY操作非常相似。 

在划分出来的组(group)上应用一些统计函数,从而达到数据分析的目的,比如对分组数据进行聚合、转换,或者过滤。这个过程主要包含以下三步:

  • 拆分(Spliting):表示对数据进行分组;
  • 应用(Applying):对分组数据应用聚合函数,进行相应计算;
  • 合并(Combining):最后汇总计算结果。

下面对 groupby() 函数的应用过程进行具体的讲解。

1、创建DataFrame对象

首先我们创建一个 DataFrame 对象,下面数据描述了某班学生,计算机选修课的考试成绩:

import pandas as pd 
import numpy as np 
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'], 
   'score': [82, 98, 91, 87], 
   'option_course': ['C#','Python','Java','C']} 
df = pd.DataFrame(data)
print(df)  

输出结果:   

     Name  score    option_course

0   John     82            C#

1  Helen     98        Python

2   Sona     91          Java

3   Ella        87             C

2、创建groupby分组对象

使用 groupby() 可以沿着任意轴分组。您可以把分组时指定的键(key)作为每组的组名,方法如下所示:

  • df.groupby("key")
  • df.groupby("key",axis=1)
  • df.groupby(["key1","key2"])

通过上述方法对 DataFrame 对象进行分组操作:

import pandas as pd
import numpy as np
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],
   'score': [82, 98, 91, 87],
   'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
print(df)
#生成分组groupby对象
print(df.groupby('score'))

输出结果:

3、查看分组结果

(1) groups查看分组结果

通过调用groups属性查看分组结果:

import pandas as pd
import numpy as np
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],
   'score': [82, 98, 91, 87],
   'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
#查看分组
print(df.groupby('score').groups)

输出结果:

{82: Int64Index([0], dtype='int64'), 87: Int64Index([3], dtype='int64'), 91: Int64Index([2], dtype='int64'), 98: Int64Index([1], dtype='int64')}

(2) 多个列标签分组

当然也可以指定多个列标签进行分组,示例如下:

import pandas as pd
import numpy as np
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],'score': [82, 98, 91, 87],'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
#查看分组
print(df.groupby(['Name','score']).groups)

输出结果:

{('Ella', 87): Int64Index([3], dtype='int64'),

('Helen', 98): Int64Index([1], dtype='int64'),

('John', 82): Int64Index([0], dtype='int64'),

('Sona', 91): Int64Index([2], dtype='int64')}

通过 get_group() 方法可以选择组内的具体数据项:

import pandas as pd
import numpy as np
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],
   'score': [82, 98, 91, 87],
   'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
#根据score来分组
grouped=df.groupby('score')
#根据对应组的数据值,选择一个组
print(grouped.get_group(91))

输出结果:

     Name     score    option_course

2     Sona      91              Java       

4、遍历分组数据

通过以下方法来遍历分组数据,示例如下:

import pandas as pd
import numpy as np
data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],
   'score': [82, 98, 91, 87],
   'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
#查看分组
grouped=df.groupby('score')
for label, option_course in grouped:
#其中key代表分组后字典的键,也就是score
    print(label)
#字典对应的值选修的科目
    print(option_course)

输出结果:

82

    Name    score    option_course

0   John         82            C#

87

     Name    score    option_course

3      Ella        87            C

91

     Name      score     option_course

2       Sona       91          Java

98

     Name       score      option_course

1       Helen     98             Python

如上所示, groupby 对象的组名称与 score 中的的元素值一一对应。

5、应用聚合函数

当您在创建 groupby 对象时,通过 agg() 函数可以对分组对象应用多个聚合函数: 

import pandas as pd 
import numpy as np 
data = {'name': ['John', 'Helen', 'Sona', 'Ella'], 'score': [82, 98, 91, 87],  'option_course': ['C#','Python','Java','C']} 
df = pd.DataFrame(data)
grouped=df.groupby('name')
#应用一个聚合函数求均值
grouped = grouped['score']).agg(np.mean)
print(grouped)

输出结果:

name

Ella         87

Helen     98

John       82

Sona       91

Name: score, dtype: int64

当然,您也可以一次性应有多个聚合函数,示例如下:

import pandas as pd
import numpy as np
data = {'name': ['John', 'Helen', 'Sona', 'Ella'],'score': [82, 98, 91, 87],'option_course': ['C#','Python','Java','C']}
df = pd.DataFrame(data)
grouped=df.groupby('name')
grouped=grouped['score'].agg([np.size,np.mean,np.std])
print(grouped)

输出结果:

            size      mean       std

name

Ella         1          87        NaN

Helen     1          98         NaN

John       1         82          NaN

Sona      1         91          NaN

6、组的转换操作

在组的行或列上可以执行转换操作,最终会返回一个与组大小相同的索引对象。示例如下:

import pandas as pd
import numpy as np
df = pd.DataFrame({'种类':['水果','水果','水果','蔬菜','蔬菜','肉类','肉类'],'产地':['朝鲜','中国','缅甸','中国','菲律宾','韩国','中国'],'水果':['橘子','苹果','哈密瓜','番茄','椰子','鱼肉','牛肉'],'数量':[3,5,5,3,2,15,9],'价格':[2,5,12,3,4,18,20]})
#分组求均值,水果、蔬菜、肉类
#对可执行计算的数值列求均值
print(df.groupby('种类').transform(np.mean))
#transform()直接应用demean,实现去均值操作
demean = lambda arr:arr-arr.mean()
print(df.groupby('种类').transform(demean))
#自定义函数
# 返回分组的前n行数据
def get_rows(df,n):
#从1到n行的所有列
    return df.iloc[:n,:]
#分组后的组名作为行索引
print(df.groupby('种类').apply(get_rows,n=1))

输出结果:

        数量      价格

0 4.333333 6.333333

1 4.333333 6.333333

2 4.333333 6.333333

3 2.500000 3.500000

4 2.500000 3.500000

5 12.000000 19.000000

6 12.000000 19.000000

       数量        价格

0 -1.333333 -4.333333

1 0.666667 -1.333333

2 0.666667 5.666667

3 0.500000 -0.500000

4 -0.500000 0.500000

5 3.000000 -1.000000

6 -3.000000 1.000000

               种类     产地    水果    数量   价格

种类

水果  0   水果     朝鲜      橘子     3        2

肉类  5   肉类     韩国      鱼肉    15      18

蔬菜  3   蔬菜     中国      番茄     3        3

7、组的数据过滤操作

通过 filter() 函数可以实现数据的筛选,该函数根据定义的条件过滤数据并返回一个新的数据集。

下面,筛选出参加比赛超过两次的球队(包含两次):

import pandas as pd
import numpy as np
data = {'Team': ['Riders', 'Riders', 'Devils', 'Devils', 'Kings','kings', 'Kings', 'Kings','Riders', 'Royals', 'Royals', 'Riders'],'Rank': [1, 2, 2, 3, 3,4 ,1 ,1,2 , 4,1,2],'Year': [2014,2015,2014,2015,2014,2015,2016,2017,2016,2014,2015,2017],'Points':[874,789,863,663,741,802,756,788,694,701,812,698]}
df = pd.DataFrame(data)
#定义lambda函数来筛选数据
print (df.groupby('Team').filter(lambda x: len(x) >= 2))

输出结果:

     Team    Rank    Year    Points

0   Riders     1        2014     874

1   Riders     2        2015     789

4   Kings      3        2014     741

6   Kings      1        2016     756

7   Kings      1        2017     788

8   Riders     2       2016     694

11 Riders     2       2017     698

二十、Pandas merge合并操作

Pandas 提供的 merge() 函数能够进行高效的合并操作,这与 SQL 关系型数据库的 MERGE 用法非常相似。从字面意思上不难理解,merge 翻译为“合并”,指的是将两个 DataFrame 数据表按照指定的规则进行连接,最后拼接成一个新的 DataFrame 数据表。

 merge() 函数的法格式如下:

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,left_index=False, right_index=False, sort=True,suffixes=('_x', '_y'), copy=True)

参数说明,如下表所示:

参数名称 说明
left/right 两个不同的 DataFrame 对象。
on 指定用于连接的键(即列标签的名字),该键必须同时存在于左右两个 DataFrame 中,如果没有指定,并且其他参数也未指定, 那么将会以两个 DataFrame 的列名交集做为连接键。
left_on 指定左侧 DataFrame 中作连接键的列名。该参数在左、右列标签名不相同,但表达的含义相同时非常有用。
right_on 指定左侧 DataFrame 中作连接键的列名。
left_index 布尔参数,默认为 False。如果为 True 则使用左侧 DataFrame 的行索引作为连接键,若 DataFrame 具有多层
索引(MultiIndex),则层的数量必须与连接键的数量相等。
right_index 布尔参数,默认为 False。如果为 True 则使用左侧 DataFrame 的行索引作为连接键。
how 要执行的合并类型,从 {'left', 'right', 'outer', 'inner'} 中取值,默认为“inner”内连接。
sort 布尔值参数,默认为True,它会将合并后的数据进行排序;若设置为 False,则按照 how 给定的参数值进行排序。
suffixes 字符串组成的元组。当左右 DataFrame 存在相同列名时,通过该参数可以在相同的列名后附加后缀名,默认为('_x','_y')。
copy 默认为 True,表示对数据进行复制。

注意:Pandas 库的 merge() 支持各种内外连接,与其相似的还有 join() 函数(默认为左连接)

下面创建两个不同的 DataFrame,然后对它们进行合并操作:

import pandas as pd
left = pd.DataFrame({'id':[1,2,3,4],'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({'id':[1,2,3,4],'Name': ['William', 'Albert', 'Tony', 'Allen'],'subject_id':['sub2','sub4','sub3','sub6']})
print (left)
print (right)

输出如下:

       id        Name         subject_id

0     1         Smith            sub1

1     2         Maiki             sub2

2     3         Hunter           sub4

3     4         Hilen             sub6

       id         Name        subject_id

0     1        William           sub2

1     2        Albert             sub4

2     3        Tony               sub3

3     4         Allen              sub6

(1) 在单个键上进行合并操作

通过 on 参数指定一个连接键,然后对上述 DataFrame 进行合并操作:

import pandas as pd
left = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['William', 'Albert', 'Tony', 'Allen'],
'subject_id':['sub2','sub4','sub3','sub6']})
#通过on参数指定合并的键
print(pd.merge(left,right,on='id'))

输出结果:

    id   Name_x   subject_id_x  Name_y subject_id_y

0   1      Smith            sub1         William     sub2

1   2      Maiki             sub2          Albert      sub4

2   3    Hunter            sub4           Tony        sub3

3   4      Hilen             sub6           Allen       sub6

(2) 在多个键上进行合并操作

下面示例,指定多个键来合并上述两个 DataFrame 对象:

import pandas as pd
left = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Bill', 'Lucy', 'Jack', 'Mike'],
'subject_id':['sub2','sub4','sub3','sub6']})
print(pd.merge(left,right,on=['id','subject_id']))

输出结果:

      id    Name_x    subject_id    Name_y

0    4        Hilen          sub6           Mike

使用how参数合并

通过how参数可以确定 DataFrame 中要包含哪些键,如果在左表、右表都不存的键,那么合并后该键对应的值为 NaN。为了便于大家学习,我们将 how 参数和与其等价的 SQL 语句做了总结:

Merge方法 等效 SQL 描述
left LEFT OUTER JOIN 使用左侧对象的key
right RIGHT OUTER JOIN 使用右侧对象的key
outer FULL OUTER JOIN 使用左右两侧所有key的并集
inner INNER JOIN 使用左右两侧key的交集

(1) left join

import pandas as pd
left = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Bill', 'Lucy', 'Jack', 'Mike'],
'subject_id':['sub2','sub4','sub3','sub6']})
#以left侧的subject_id为键
print(pd.merge(left,right,on='subject_id',how="left"))

输出结果:

   id_x    Name_x      subject_id     id_y     Name_y

0    1        Smith             sub1         NaN      NaN

1    2        Maiki              sub2         1.0         Bill

2    3        Hunter            sub4         2.0         Lucy

3    4        Hilen              sub6          4.0        Mike

在你的合并操作中,id_yName_y 是合并后的结果中的两列,它们的值是从右侧DataFrame (right) 中的'id'和'Name'列获得的。以下是如何得到这些值的具体步骤:

  1. 合并基于subject_id列进行,即合并是通过在leftright中找到匹配的subject_id值来实现的。

  2. 对于每一行,在合并时,Pandas会检查subject_id值是否在leftright中都有匹配项。

  3. 如果有匹配项,合并操作会将匹配项从right中的对应行中的'id'和'Name'列的值复制到合并结果的id_yName_y列中。

具体来说,对于你的数据,这是如何得到id_yName_y 的值的示例:

  • 对于第一行合并(subject_id为'sub1'),在right中找不到匹配项,所以id_yName_y都是NaN。

  • 对于第二行合并(subject_id为'sub2'),在right中找到匹配项('sub2'),所以id_y的值为1(来自right中的'id'列),而Name_y的值为'Bill'(来自right中的'Name'列)。

  • 对于第三行合并(subject_id为'sub4'),在right中找到匹配项('sub4'),所以id_y的值为2(来自right中的'id'列),而Name_y的值为'Lucy'(来自right中的'Name'列)。

  • 对于第四行合并(subject_id为'sub6'),在right中找到匹配项('sub6'),所以id_y的值为4(来自right中的'id'列),而Name_y的值为'Mike'(来自right中的'Name'列)。

总之,id_yName_y 的值是从右侧DataFrame (right) 中的'id'和'Name'列根据匹配的subject_id值复制而来的。如果没有匹配项,那么它们的值将是NaN。这是合并操作的一种方式,用于将两个DataFrame的数据关联在一起。

(2) right join

import pandas as pd
left = pd.DataFrame({'id':[1,2,3,4],'Name': ['Smith', 'Maiki', 'Hunter','Hilen'],'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({'id':[1,2,3,4],'Name': ['Bill', 'Lucy', 'Jack', 'Mike'],
'subject_id':['sub2','sub4','sub3','sub6']})
#以right侧的subject_id为键
print(pd.merge(left,right,on='subject_id',how="right"))

输出结果:

    id_x   Name_x    subject_id    id_y     Name_y

0    2.0        Maiki          sub2           1          Bill

1    3.0      Hunter          sub4           2        Lucy

2    NaN     NaN            sub3           3        Jack

3    4.0        Hilen          sub6           4        Mike

(3) outer join(并集)

import pandas as pd
left = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Bill', 'Lucy', 'Jack', 'Mike'],
'subject_id':['sub2','sub4','sub3','sub6']})
#求出两个subject_id的并集,并作为键
print(pd.merge(left,right,on='subject_id',how="outer"))

输出结果:

    id_x    Name_x     subject_id    id_y    Name_y

0     1.0     Smith              sub1       NaN     NaN

1     2.0      Maiki              sub2       1.0        Bill

2     3.0      Hunter            sub4       2.0       Lucy

3     4.0      Hilen              sub6        4.0       Mike

  NaN      NaN              sub3        3.0       Jack

(4) inner join(交集)

import pandas as pd
left = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
'subject_id':['sub1','sub2','sub4','sub6']})
right = pd.DataFrame({
'id':[1,2,3,4],
'Name': ['Bill', 'Lucy', 'Jack', 'Mike'],
'subject_id':['sub2','sub4','sub3','sub6']})
#求出两个subject_id的交集,并将结果作为键
print(pd.merge(left,right,on='subject_id',how="inner"))

输出结果:

   id_x   Name_x    subject_id    id_y    Name_y

0   2        Maiki        sub2           1           Bill

1   3       Hunter       sub4           2          Lucy

2   4        Hilen         sub6           4          Mike

注意:当 a 与 b 进行内连操作时 a.join(b) 不等于 b.join(a)。

二十一、Pandas concat连接操作

Pandas 通过 concat() 函数能够轻松地将 Series 与 DataFrame 对象组合在一起,函数的语法格式如下:

 pd.concat(objs,axis=0,join='outer',join_axes=None,ignore_index=False)

参数说明如下所示:

参数名称 说明
objs 一个序列或者是Series、DataFrame对象。
axis 表示在哪个轴方向上(行或者列)进行连接操作,默认 axis=0 表示行方向。
join 指定连接方式,取值为{"inner","outer"},默认为 outer 表示取并集,inner代表取交集。
ignore_index 布尔值参数,默认为 False,如果为 True,表示不在连接的轴上使用索引。
join_axes 表示索引对象的列表。

1、concat()

concat() 函数用于沿某个特定的轴执行连接操作。下面让我们创建不同的对象,并对其进行连接。

import pandas as pd
a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                   'C': ['C0', 'C1', 'C2', 'C3'],
                  'D': ['D0', 'D1', 'D2', 'D3']},
                    index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                  'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D4', 'D5', 'D6', 'D7']},

#连接a与b
print(pd.concat([a,b]))

输出结果:

    A   B   C  D

0 A0 B0 C0 D0

1 A1 B1 C1 D1

2 A2 B2 C2 D2

3 A3 B3 C3 D3

4 A4 B4 C4 D4

5 A5 B5 C5 D5

6 A6 B6 C6 D6

7 A7 B7 C7 D7

如果想把指定的键与 DataFrame 对象连接,您可以使用 keys 参数来实现。如下所示:

import pandas as pd
a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                   'C': ['C0', 'C1', 'C2', 'C3'],
                  'D': ['D0', 'D1', 'D2', 'D3']},
                    index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                  'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D1', 'D2', 'D5', 'D6']},
index=[2,3,4,5])

#连接a与b,并给a,b连接一个指定的键
print(pd.concat([a,b],keys=['x','y']))

输出结果:     

        A     B    C   D

x 0  A0  B0  C0  D0  

   1  A1  B1  C1  D1  

   2  A2  B2  C2  D2  

   3  A3  B3  C3  D3

y 2  A4  B4  C4  D1  

   3  A5  B5  C5  D2  

   4  A6  B6  C6  D5  

   5  A7  B7  C7  D6

上述示中,可以看出行索引 index 存在重复使用的现象,如果想让输出的行索引遵循依次递增的规则,那么需要将 ignore_index 设置为 True。

import pandas as pd
a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                   'C': ['C0', 'C1', 'C2', 'C3'],
                  'D': ['D0', 'D1', 'D2', 'D3']},
                    index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                  'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D1', 'D2', 'D5', 'D6']},
index=[2,3,4,5])

#连接a与b,设置 ignore_index 等于 True
print(pd.concat([a,b],keys=['x','y'],ignore_index=True))

输出结果:

    A   B   C   D

0 A0 B0 C0 D0

1 A1 B1 C1 D1

2 A2 B2 C2 D2

3 A3 B3 C3 D3

4 A4 B4 C4 D1

5 A5 B5 C5 D2

6 A6 B6 C6 D5

7 A7 B7 C7 D6

注意:此时的索引顺序被改变了,而且键 keys 指定的键也被覆盖了。

如果您想要沿着 axis=1 添加两个对象,那么将会追加新的列。

import pandas as pd
a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                   'C': ['C0', 'C1', 'C2', 'C3'],
                  'D': ['D0', 'D1', 'D2', 'D3']},
                    index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                  'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D1', 'D2', 'D5', 'D6']},
index=[4,5,6,7])

#沿着 axis=1,连接a与b
print(pd.concat([a,b],axis=1))

输出结果:

    A         B     C      D    A      B     C      D

0 A0       B0   C0    D0 NaN NaN NaN NaN

1 A1       B1   C1    D1 NaN NaN NaN NaN

2 A2       B2   C2    D2 NaN NaN NaN NaN

3 A3       B3   C3    D3 NaN NaN NaN NaN

4 NaN NaN NaN NaN    A4   B4   C4    D1

5 NaN NaN NaN NaN    A5   B5   C5    D2

6 NaN NaN NaN NaN    A6   B6   C6    D5

7 NaN NaN NaN NaN    A7   B7   C7    D6

2、append()

如果要连接 Series 和 DataFrame 对象,有一个最方便、快捷的方法,那就是 append() 方法。该方法沿着 axis=0 (行方向)进行操作。

import pandas as pd
a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                   'C': ['C0', 'C1', 'C2', 'C3'],
                  'D': ['D0', 'D1', 'D2', 'D3']},
                    index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                  'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D1', 'D2', 'D5', 'D6']},
index=[4,5,6,7])
#沿着 axis=0,使用 apppend()方法连接a与b
print(a.append(b))

输出结果:

    A   B   C   D

0 A0 B0 C0 D0

1 A1 B1 C1 D1

2 A2 B2 C2 D2

3 A3 B3 C3 D3

4 A4 B4 C4 D1

5 A5 B5 C5 D2

6 A6 B6 C6 D5

7 A7 B7 C7 D6

当然 append() 函数也可接收多个对象,示例如下:

a= pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],'B': ['B0', 'B1', 'B2', 'B3'],'C': ['C0', 'C1', 'C2', 'C3'],'D': ['D0', 'D1', 'D2', 'D3']},index=[0, 1, 2, 3])

b= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],'B': ['B4', 'B5', 'B6', 'B7'],'C': ['C4', 'C5', 'C6', 'C7'],'D': ['D1', 'D2', 'D5', 'D6']},index=[4,5,6,7])
c= pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],'B': ['B8', 'B9', 'B10', 'B7'],'C': ['C9', 'C8', 'C7', 'C6'],'D': ['D8', 'D5', 'D7', 'D6']},index=[8,9,10,11])
print(a.append[b,c,a])

输出结果:

    A   B   C  D

0 A0 B0 C0 D0

1 A1 B1 C1 D1

2 A2 B2 C2 D2

3 A3 B3 C3 D3

4 A4 B4 C4 D1

5 A5 B5 C5 D2

6 A6 B6 C6 D5

7 A7 B7 C7 D6

8 A4 B8 C9 D8

9 A5 B9 C8 D5

10 A6 B10 C7 D7

11 A7 B7 C6 D6

0 A0 B0 C0 D0

1 A1 B1 C1 D1

2 A2 B2 C2 D2

3 A3 B3 C3 D3

二十二、Python Pandas时间序列详解

顾名思义,时间序列(time series),就是由时间构成的序列,它指的是在一定时间内按照时间顺序测量的某个变量的取值序列,比如一天内的温度会随时间而发生变化,或者股票的价格会随着时间不断的波动,这里用到的一系列时间,就可以看做时间序列。时间序列包含三种应用场景,分别是:

  • 特定的时刻(timestamp),也就是时间戳;
  • 固定的日期(period),比如某年某月某日;
  • 时间间隔(interval),每隔一段时间具有规律性;

在处理时间序列的过程中,我们一般会遇到两个问题,第一,如何创建时间序列;第二,如何更改已生成时间序列的频率。 Pandas 为解决上述问题提供了一套简单、易用的方法。

下面用 Python 内置的 datetime 模块来获取当前时间,通过该模块提供的now()方法即可实现。

from datetime import datetime
#数据类型为datetime
print(datetime.now())

输出结果:

2020-12-16 16:36:18.791297

1、创建时间戳  Timestamp()

TimeStamp(时间戳) 是时间序列中的最基本的数据类型,它将数值与时间点完美结合在一起。Pandas 使用下列方法创建时间戳:

import pandas as pd
print (pd.Timestamp('2017-03-01'))

输出结果:

2017-03-01 00:00:00

同样,可以将整型或浮点型表示的时间转换为时间戳。默认的单位是纳秒(时间戳单位),示例如下:

import pandas as pd
print(pd.Timestamp(1587687255,unit='s'))

输出结果:

2022-03-19 14:26:39

2、创建时间范围 date_range()

通过 date_range() 方法可以创建某段连续的时间或者固定间隔的时间时间段。该函数提供了三个参数,分别是:

  • start:开始时间
  • end:结束时间
  • freq:时间频率,默认为 "D"(天)

示例如下:

import pandas as pd
#freq表示时间频率,每30min变化一次
print(pd.date_range("9:00", "18:10", freq="30min").time)

输出结果:

[datetime.time(9, 0) datetime.time(9, 30) datetime.time(10, 0)

datetime.time(10, 30) datetime.time(11, 0) datetime.time(11, 30)

datetime.time(12, 0) datetime.time(12, 30) datetime.time(13, 0)

datetime.time(13, 30) datetime.time(14, 0) datetime.time(14, 30)

datetime.time(15, 0) datetime.time(15, 30) datetime.time(16, 0)

datetime.time(16, 30) datetime.time(17, 0) datetime.time(17, 30)

datetime.time(18, 0)]

3、更改时间频率

import pandas as pd
#修改为按小时
print(pd.date_range("6:10", "11:45", freq="H").time)

输出结果:

[datetime.time(6, 10) datetime.time(7, 10) datetime.time(8, 10)

datetime.time(9, 10) datetime.time(10, 10) datetime.time(11, 10)]

4、转化为时间戳 to_datetime()

您可以使用 to_datetime() 函数将 series 或 list 转换为日期对象,其中 list 会转换为DatetimeIndex。示例如下:

import pandas as pd
print(pd.to_datetime(pd.Series(['Jun 3, 2020','2020-12-10', None])))

输出结果:

0 2020-06-03

1 2020-12-10

2       NaT

dtype: datetime64[ns]

注意:NaT 表示的不是时间 ,它等效于 NaN。

最后再来看一个示例:

import pandas as pd
#传入list,生成Datetimeindex
print(pd.to_datetime(['Jun 3, 2020','2020-12-10', None]))

输出结果:

DatetimeIndex(['2020-06-03', '2020-12-10', 'NaT'], dtype='datetime64[ns]', freq=None)

5、频率和周期转换 Periods()

Time Periods 表示时间跨度,一段时间周期,它被定义在 Pandas Periods 类中,通过该类提供的方法可以实现将频率转换为周期。比如 Periods() 方法,可以将频率 "M"(月)转换为 Period(时间段)。

下面示例,使用 asfreq() 和 start 参数,打印 "01" ,若使用 end 参数,则打印 "31"。示例如下:

import pandas as pd
x = pd.Period('2014', freq='M')
#start参数
x.asfreq('D', 'start')
#end参数
x.asfreq('D', 'end')

输出结果:

Period('2014-01-01', 'D')

Period('2014-01-31', 'D')

对于常用的时间序列频率,Pandas 为其规定了一些字符串别名,我们将这些别名称为“offset(偏移量)”。如下表所示:

别名 描述 别名 描述
B 工作日频率 BQS 工作季度开始频率
D 日历日频率 A 年终频率
W 每周频率 BA 工作年度结束频率
M 月末频率 BAS 工作年度开始频率
SM 半月结束频率 BH 营业时间频率
BM 工作月结束频率 H 小时频率
MS 月开始频率 T,min 每分钟频率
SMS 半月开始频率 S 每秒钟频率
BMS 工作月开始频率 L,ms 毫秒
Q 季末频率 U,us 微妙
BQ 工作季度结束频率 N 纳秒
QS 季度开始频率

6、时间周期计算

周期计算,指的是对时间周期进行算术运算,所有的操作将在“频率”的基础上执行。

import pandas as pd
#S表示秒
x = pd.Period('2014', freq='S')
x

输出结果:

Period('2014-01-01 00:00:00', 'S')

执行计算示例:

import pandas as pd
x = pd.Period('2014', freq='S')
#加1s的时间
print(x+1)

输出结果:

Period('2014-01-01 00:00:01', 'S')

再看一组完整的示例:

#定义时期period,默认freq="Y"年份
p1=pd.Period('2020')
p2=pd.Period('2019')
#使用f''格式化输出
print(f'p1={p1}年')
print(f'p2={p2}年')
print(f'p1和p2间隔{p1-p2}年')
#f''表示字符串格式化输出
print(f'五年前是{p1-5}年')

输出结果:

p1=2020年

p2=2019年

p1和p2间隔

五年前是2015年

7、创建时间周期 period_range()

我们可以使用 period_range() 方法来创建时间周期范围。示例如下:

import pandas as pd
#Y表示年
p = pd.period_range('2016','2018', freq='Y')
p

输出结果:

PeriodIndex(['2016', '2017', '2018'], dtype='period[A-DEC]', freq='A-DEC')

8、时间序列转换

如果想要把字符串日期转换为 Period,首先需要将字符串转换为日期格式,然后再将日期转换为 Period。示例如下:

# 创建时间序列
index=pd.date_range("2020-03-17","2020-03-30",freq="1.5H")
#随机选取4个互不相同的数
loc=np.random.choice(np.arange(len(index)),size=4,replace=False)
loc.sort()
ts_index=index[loc]
ts_index
pd_index=ts_index.to_period('D')
pd_index

输出结果:

DatetimeIndex(['2020-03-17 12:00:00', '2020-03-22 04:30:00',

                          '2020-03-27 03:00:00', '2020-03-30 00:00:00'],

                          dtype='datetime64[ns]', freq=None)

PeriodIndex(['2020-03-17', '2020-03-19', '2020-03-19', '2020-03-27'], dtype='period[D]', freq='D')

使用 to_timestamp() 能够将 Period 时期转换为时间戳(timestamp),示例如下:

import pandas as pd
p1=pd.Period("2020-2-3")
p1.to_timestamp()

输出结果:

Timestamp('2020-02-03 00:00:00')

9、创建日期范围 date_range()

Pandas 提供了用来创建日期序列的函数 date_range(),该函数的默认频率为 "D", 也就是“天”。日期序列只包含年、月、日,不包含时、分、秒。

下面是一组简单的示例,如下所示:

import pandas as pd
print(pd.date_range('12/15/2020', periods=10))

输出结果:

DatetimeIndex(['2020-12-15', '2020-12-16', '2020-12-17', '2020-12-18',

                         '2020-12-19', '2020-12-20', '2020-12-21', '2020-12-22',

                         '2020-12-23', '2020-12-24'],

                         dtype='datetime64[ns]', freq='D')

当我们使用 date_range() 来创建日期范围时,该函数包含结束的日期,用数学术语来说就是区间左闭右闭,即包含起始值,也包含结束值。示例如下:

import pandas as pd
#建议使用Python的datetime模块创建时间
start = pd.datetime(2019, 1, 1)
end = pd.datetime(2019, 1, 5)
print pd.date_range(start,end)

输出结果:

DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04','2019-01-05'] ,dtype='datetime64[ns]', freq='D')

10、更改日频率

使用下列方法可以修改频率,比如按“天”为按“月”,示例如下:

import pandas as pd
print(pd.date_range('12/15/2011', periods=5,freq='M'))

输出结果:

DatetimeIndex(['2020-12-31', '2021-01-31', '2021-02-28', '2021-03-31',               

                                                '2021-04-30'],dtype='datetime64[ns]', freq='M')

11、工作日时间 bdate_range()

bdate_range() 表示创建工作日的日期范围,它与 date_range() 不同,它不包括周六、周日。

import pandas as pd
print(pd.date_range('11/25/2020', periods=8))

输出结果:

DatetimeIndex(['2020-11-25', '2020-11-26', '2020-11-27', '2020-11-28','2020-11-29', '2020-11-30', '2020-12-01', '2020-12-02'],dtype='datetime64[ns]', freq='D')

上述方法中,date_range() 默认频率是日历日,而 bdate_range() 的默认频率是工作日。

二十三、Pandas日期时间格式化

当进行数据分析时,我们会遇到很多带有日期、时间格式的数据集,在处理这些数据集时,可能会遇到日期格式不统一的问题,此时就需要对日期时间做统一的格式化处理。比如“Wednesday, June 6, 2020”可以写成“6/6/20”,或者写成“06-06-2020。

1、日期格式化符号

在对时间进行格式化处理时,它们都有固定的表示格式,比如小时的格式化符号为%H ,分钟简写为%M ,秒简写为%S下表对常用的日期格式化符号做了总结:

日期格式化符号

符号 说明
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地英文缩写星期名称
%A 本地英文完整星期名称
%b 本地缩写英文的月份名称
%B 本地完整英文的月份名称
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%U 一年中的星期数(00-53)星期天为星期的开始
%j 年内的一天(001-366)
%c 本地相应的日期表示和时间表示

2、Python处理 strptime()

Python 内置的 strptime() 方法能够将字符串日期转换为 datetime 类型,下面看一组示例:

from datetime import datetime
#将日期定义为字符串
date_str1 = 'Wednesday, July 18, 2020'
date_str2 = '18/7/20'
date_str3 = '18-07-2020'
#将日期转化为datetime对象
dmy_dt1 = datetime.strptime(date_str1, '%A,%B%d,%Y')
dmy_dt2 = datetime.strptime(date_str2, '%d/%m/%y')
dmy_dt3 = datetime.strptime(date_str3, '%d-%m-%Y')
#处理为相同格式,并打印输出
print(dmy_dt1)
print(dmy_dt2)
print(dmy_dt3)

输出结果:

2020-07-18 00:00:00

2020-07-18 00:00:00

2020-07-18 00:00:00

注意:strftime() 可以将 datetime 类型转换为字符串类型,恰好与 strptime() 相反。

3、Pandas处理 pd.to_datetime() 和 pd.DatetimeIndex()

除了使用 Python 内置的 strptime() 方法外,你还可以使用 Pandas 模块的 pd.to_datetime() 和 pd.DatetimeIndex() 进行转换。

(1) to_datetime()

通过 to_datetime() 直接转换为 datetime 类型

import pandas as pd
import numpy as np
date = ['2012-05-06 11:00:00','2012-05-16 11:00:00']
pd_date=pd.to_datetime(date)
df=pd.Series(np.random.randn(2),index=pd_date)

输出结果:

2012-05-06 11:00:00 0.189865

2012-05-16 11:00:00 1.052456

dtype: float64

(2) DatetimeIndex()

使用 Datetimeindex() 函数设置时间序,示例如下:

date = pd.DatetimeIndex(['1/1/2008', '1/2/2008', '1/3/2008', '1/4/2008', '1/5/2008'])
dt = pd.Series(np.random.randn(5),index = date)
print(dt)

输出结果:

2008-01-01 1.965619

2008-01-02 -2.897374

2008-01-03 0.625929

2008-01-04 1.204926

2008-01-05 1.755680

dtye: float64

二十四、Padans Timedelta时间差

Timedelta 表示时间差(或者时间增量),我们可以使用不同的时间单位来表示它,比如,天、小时、分、秒。时间差的最终的结果可以是正时间差,也可以是负时间差。

本节主要介绍创建 Timedelta (时间差)的方法以及与时间差相关的运算法则。

1、字符串

通过传递字符串可以创建 Timedelta 对象,示例如下:

import pandas as pd
print(pd.Timedelta('5 days 8 hours 6 minutes 59 seconds'))

输出结果:

5 days 08:06:59

2、整数

通过传递整数值和unit参数也可以创建一个 Timedelta 对象。

import pandas as pd
print(pd.Timedelta(19,unit='h'))

输出结果:

0 days 19:00:00

3、数据偏移量

数据偏移量, 比如,周(weeks)、天(days)、小时(hours)、分钟(minutes)、秒(milliseconds)、毫秒、微秒、纳秒都可以使用。

import pandas as pd
print (pd.Timedelta(days=2,hours=6))

输出结果:

2 days 06:00:00

4、to_timedelta()

您可以使用pd.to_timedelta()方法,将具有 timedelta 格式的值 (标量、数组、列表或 Series)转换为 Timedelta 类型。如果输入是 Series,则返回 Series;如果输入是标量,则返回值也为标量,其他情况输出 TimedeltaIndex。示例如下:

import pandas as pd
print(pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan']))
print(pd.to_timedelta(np.arange(5), unit='s'))

输出结果:

TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015', NaT],dtype='timedelta64[ns]', freq=None)

TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02','0 days 00:00:03', '0 days 00:00:04'],dtype='timedelta64[ns]', freq=None)

5、算术操作

通过对datetime64[ns]类型的时间序列或时间戳做算术运算,其运算结果依然是datetime64[ns]数据类型。接下来,我们创建一个带有 Timedelta 与 datetime 的 DataFrame 对象,并对其做一些算术运算。

import pandas as pd
s = pd.Series(pd.date_range('2020-1-1', periods=5, freq='D'))
#推导式用法
td = pd.Series([ pd.Timedelta(days=i) for i in range(5)])
df = pd.DataFrame(dict(A = s, B = td))
print(df)

输出结果:

           A             B

0 2020-01-01 0 days

1 2020-01-02 1 days

2 2020-01-03 2 days

3 2020-01-04 3 days

4 2020-01-05 4 days

6、加法运算

import pandas as pd
s = pd.Series(pd.date_range('20120-1-1', periods=3, freq='D'))
td = pd.Series([ pd.Timedelta(days=i) for i in range(3) ])
df = pd.DataFrame(dict(A = s, B = td))
#加法运算
df['C']=df['A']+df['B']
print(df)

输出结果:

         A               B                 C

0 2020-01-01 0 days 2020-01-01

1 2020-01-02 1 days 2020-01-03

2 2020-01-03 2 days 2020-01-05

7、减法运算

import pandas as pd
s = pd.Series(pd.date_range('2012-1-1', periods=3, freq='D'))
td = pd.Series([ pd.Timedelta(days=i) for i in range(3) ])
df = pd.DataFrame(dict(A = s, B = td))
df['C']=df['A']+df['B']
df['D']=df['C']-df['B']
print(df)

输出结果:

         A                B             C               D

0 2019-01-01 0 days 2019-01-01 2019-01-01

1 2019-01-02 1 days 2019-01-03 2019-01-02

2 2019-01-03 2 days 2019-01-05 2019-01-03

后续内容将在Pandas教程(非常详细)(第五部分),继续讲述。

你可能感兴趣的:(Python语言学习,pandas)