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'))

输出结果:

<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000021DE9A89640>

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描述
leftLEFT OUTER JOIN使用左侧对象的key
rightRIGHT OUTER JOIN使用右侧对象的key
outerFULL OUTER JOIN使用左右两侧所有key的并集
innerINNER 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间隔<YearEnd: month=12>

五年前是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)
%H24小时制小时数(0-23)
%I12小时制小时数(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教程(非常详细)(第五部分),继续讲述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值