php正则匹配sg-nc-wap_Pandas基础操作(上)

文章目录

一、Pandas文件读取

1.pandas数据读取

1、读取纯文本文件

1.1 读取csv,使用默认的标题行、逗号分隔符

1.2 读取txt文件,自己指定分隔符、列名

2、读取excel文件

3、读取sql文件

二、pandas的数据结构DataFrame和Series

DataFrame:二维数据,整个表格,多行多列

1.Series

1.1 仅有数据列表即可生产最简单的Series

1.2 创建一个具有标签索引的Series

1.3 使用python字典创建Series

1.4 根据数据标签索引查询数据

2. DataFrame

2.1 根据多个字典序列创建DataFrame

从DataFrame中查询出Series

3.1 查询一列,结果是一个pd.Series

3.2 查询多列,结果是一个pd.DataFrame

3.3 查询一行,结果是一个pd.Series

3.4 查询多行,结果是一个pd.DataFrame

三.Pandas查询数据的5种方法

Pandas查询数据的几种方法

Pandas使用df.loc查询数据的方法

注意

0. 读取数据

1. 使用单个label值查询数据

2. 使用值列进行表批量查询

3. 使用数值区间进行范围查询

4. 使用条件表达式查询

复杂条件查询,查询一下完美得天气

5. 调用函数查询

四、Pandas怎样新增数据列

0. 读取csv数据到DataFrame

1. 直接赋值的方法

2. df.apply方法

3. df.assign方法

4. 按条件选择分组分别赋值

五、Pandas数据统计函数

0. 读取csv数据

1. 汇总类统计

2. 唯一去重和按值计数

2.1 唯一去重

2.2 按值计数

3. 相关系数和协防差

六、Pandas对缺失值的处理

实例:特殊excel的读取、清洗、处理

步骤1:读取excel的时候,忽略前几个空行

步骤2:检测空值

步骤3:删除掉全是空值的列

步骤4:删除掉全是空值的行

步骤5:将分数列为空的填充为0分

步骤6:将姓名的缺失值填充

步骤7:将清晰好的excel保存

七、Pandas的SettingWithCopyWarning报警

0. 数据读取

1. 复现

2、原因

4. 解决方法2

Pandas不允许先筛选子DataFrame,在进行修改写入

八、Pandas数据排序

0. 读取数据

1. Series的排序

2. DataFrame的排序

2.1 单列排序

2.2 多列排序

九、Pandas字符串处理

0. 读取北京2018年天气数据

1. 获取Series的str属性,使用各种字符串处理函数

4. 使用正则表达式的处理

Series.str默认就开启了正则表达式模式

十、Pandas的axis参数怎么理解?

***按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动\***

1. 单列drop, 就是删除某一列

3. 按照axis=0/index执行mean聚合操作

***按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动\***

3. 按照axis=1/columns执行mean聚合操作

***按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动\***

5. 再次举例, 加深理解

***按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动\***

十一、Pandas的索引index的用途

1. 使用index查询数据

2. 使用index会提升查询性能

实验1:完全随机的查询顺序

实验2:将index排序后的查询

3.使用index能自动对齐数据

s1,s2都具有b,c索引,而a,d为各自独有,无法对齐,所有相加结果为空

4. 使用index更多更强大的数据结构支持

十二、Pandas怎样实现DataFrame的Merge

merge的语法:

1、电影数据集的join实例

电影评分数据集

2、理解merge时数量的对齐关系

2.1 one-to-one 一对一关系的merge

2.2 one-to-many 一对多关系的merge

2.3 many-to-many 多对多关系的merge

3、理解left join、right join、inner join、outer join的区别

3.1 inner join,默认

3.2 left join

3.3 right join

3.4 outer join

4、如果出现非Key的字段重名怎么办

十三、Pandas实现数据的合并concat

使用场景:

一句话说明concat语法:

concat语法:pandas.concat(objs, axis=0, join='outer', ignore_index=False)

append语法:DataFrame.append(other, ignore_index=False)

参考文档:

一、使用Pandas.concat合并数据

1. 默认的concat, 参数为axis=0, join=outer, ignore_index=False

2. 使用ignore_index=True可以忽略原来的索引

3. 使用join=inner过滤掉不匹配的列

4. 使用axis=1相当于添加新列

A:添加一列Series

B:添加多列Series

二、使用DateFrame.append按行合并数据

1. 给一个DataFrame添加另一个DataFrame

2. 忽略原来的索引,另ignore_index=True

3.可以一行一行的给DataFrame添加数据

A:低性能版

B:性能好的版本

十四、Pandas批量拆分Excel与合并Excel

0. 读取源Excel到Pandas

1、将一个大excel等份拆成多个Excel

1. 1 计算拆分后的每个excel的行数

1.2 拆分成多个DataFrame

1.3 将每个DataFrame存入excel

2、合并多个小Excel到一个大Excel

2.1 遍历文件夹,得到要合并的Excel名称列表

2.2 分别读取到DataFrame

2.3 使用pd.concat进行合并

2.4 将合并后的DataFrame输出到Excel

十五、Pandas怎样实现groupby分组统计

1、分组使用聚合函数做数据统计

1.1 单个列groupby,查询所有数据列的统计

1.2 多个列groupby,查询所有数据列的统计

1.3 同时查看多种数据统计

1.4 查看单列的结果数据统计

1.5 不同列使用不同的聚合函数

2、遍历groupby的结果理解执行流程

2.1 遍历单个列聚合的分组

2.2 遍历多个列聚合的分组

3、实例分组探索天气数据

3.1 查看每个月的最高温度

3.2 查看每个月的最高温度、最低温度、平均空气质量指数

十六、Pandas的分层索引MultiIndex

1、Series的分层索引MultiIndex

2、Series有多层索引MultiIndex怎么筛选数据?

3、DataFrame的多层索引MultiIndex

4、DataFrame有多层索引MultiIndex怎样筛选?

十七、Pandas的数据转换函数map、apply、applymap

1. map用于Series值的转换

方法1:Series.map(dict)

方法2:Series.map(function)

2. apply用于Series和DataFrame的转换

Series.apply(function)

DataFrame.apply(function)

3. applymap用于DataFrame所有值的转换

十八、Pandas怎样对每个分组应用apply函数?

知识:Pandas的GroupBy遵从split、apply、combine模式

GroupBy.apply(function)

本次实例演示:

实例1:怎样对数值列按分组的归一化?

演示:用户对电影评分的归一化

实例2:怎么取每个分组的TOP N数据

一、Pandas文件读取

1.pandas数据读取

pandas需要先读取表格类型的数据,然后进行分析

数据类型

说明

pandas读取方法

csv、tsv、txt

用逗号分隔、tab分割的纯文本文件

pd.read_csv

excel

微软xls或者xlsx文件

pd.read_excel

mysql

关系型数据库表

pd.read_sql

In [1]:

import pandas as pd

1、读取纯文本文件

1.1 读取csv,使用默认的标题行、逗号分隔符

In [2]:

fpath = "./pandas-learn-code/datas/ml-latest-small/ratings.csv"

In [3]:

# 使用pd.read_csv读取数据

ratings = pd.read_csv(fpath)

In [4]:

# 查看前几行数据

ratings.head()

Out[4]:

userId

movieId

rating

timestamp

0

1

1

4.0

964982703

1

1

3

4.0

964981247

2

1

6

4.0

964982224

3

1

47

5.0

964983815

4

1

50

5.0

964982931

In [5]:

# 查看数据的形状,返回(行数、列数)

ratings.shape

Out[5]:

(100836, 4)

In [6]:

# 查看列名列表

ratings.columns

Out[6]:

Index(['userId', 'movieId', 'rating', 'timestamp'], dtype='object')

In [7]:

# 查看索引

ratings.index

Out[7]:

RangeIndex(start=0, stop=100836, step=1)

In [9]:

# 查看每列的数据类型

ratings.dtypes

Out[9]:

userId int64

movieId int64

rating float64

timestamp int64

dtype: object

1.2 读取txt文件,自己指定分隔符、列名

In [10]:

fpath = "./pandas-learn-code/datas/crazyant/access_pvuv.txt"

In [11]:

pvuv = pd.read_csv(fpath, sep="\t", header=None, names=["pdate","pv","uv"])

sep代表分隔符

header=none代表没有列名

names代表指定的列明

In [13]:

pvuv.head()

Out[13]:

pdate

pv

uv

0

2019-09-10

139

92

1

2019-09-09

185

153

2

2019-09-08

123

59

3

2019-09-07

65

40

4

2019-09-06

157

98

2、读取excel文件

In [18]:

fpath = "./pandas-learn-code/datas/crazyant/access_pvuv.xlsx"

pvuv = pd.read_excel(fpath)

In [19]:

pvuv

Out[19]:

日期

PV

UV

0

2019-09-10

139

92

1

2019-09-09

185

153

2

2019-09-08

123

59

3

2019-09-07

65

40

4

2019-09-06

157

98

5

2019-09-05

205

151

6

2019-09-04

196

167

7

2019-09-03

216

176

8

2019-09-02

227

148

9

2019-09-01

105

61

3、读取sql文件

In [36]:

import pymysql

conn = pymysql.connect(

host="127.0.0.1",

user="root",

password="123456",

database="test",

charset="utf8"

)

In [41]:

fpath = "./pandas-learn-code/datas/crazyant/test_crazyant_pvuv.sql"

mysql_page = pd.read_sql("select * from crazyant_pvuv", con=conn)

In [42]:

pvuv

Out[42]:

日期

PV

UV

0

2019-09-10

139

92

1

2019-09-09

185

153

2

2019-09-08

123

59

3

2019-09-07

65

40

4

2019-09-06

157

98

5

2019-09-05

205

151

6

2019-09-04

196

167

7

2019-09-03

216

176

8

2019-09-02

227

148

9

2019-09-01

105

61

二、pandas的数据结构DataFrame和Series

DataFrame:二维数据,整个表格,多行多列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tT4RRssV-1597761927694)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200730213558995.png)]

In [1]:

import pandas as pd

import numpy as np

1.Series

Series是一种类似于一维数组的对象,它由一组数据(不同数据类型)以及一组与之相关的数据标签(即索引)组成。

1.1 仅有数据列表即可生产最简单的Series

In [3]:

s1 = pd.Series([1,'a',5.2,7])

In [5]:

# 左侧为索引,右侧是数据

s1.head()

Out[5]:

0 1

1 a

2 5.2

3 7

dtype: object

In [6]:

# 获取索引

s1.index

Out[6]:

RangeIndex(start=0, stop=4, step=1)

In [7]:

# 获取数据

s1.values

Out[7]:

array([1, 'a', 5.2, 7], dtype=object)

1.2 创建一个具有标签索引的Series

In [8]:

s2 = pd.Series([1,'a',5.2,7], index=['a','b','c','d'])

In [9]:

s2

Out[9]:

a 1

b a

c 5.2

d 7

dtype: object

In [10]:

s2.index

Out[10]:

Index(['a', 'b', 'c', 'd'], dtype='object')

1.3 使用python字典创建Series

In [11]:

sdata = {'Ohio':35000, 'Texas':72000, 'Oregon':16000, 'Utah':5000}

In [13]:

s3 = pd.Series(sdata)

In [14]:

# 字典的key成为了Series的索引

s3

Out[14]:

Ohio 35000

Texas 72000

Oregon 16000

Utah 5000

dtype: int64

1.4 根据数据标签索引查询数据

类似python的字典dict

In [15]:

s2

Out[15]:

a 1

b a

c 5.2

d 7

dtype: object

In [20]:

s2['a']

Out[20]:

1

In [21]:

# 查询一个值,返回查询值的数据类型

type(s2['a'])

Out[21]:

int

In [18]:

# 一次查询多个值

s2[['a','b','c']]

Out[18]:

a 1

b a

c 5.2

dtype: object

In [22]:

# 查询多个值,返回的还是Series

type(s2[['a','b','c']])

Out[22]:

pandas.core.series.Series

2. DataFrame

DataFrame是一个表格型的数据结构

每列可以是不同的值类型(数值,字符串,布尔值等)

既有行索引index,也有列索引columns

可以被看做由Series组成的字典

2.1 根据多个字典序列创建DataFrame

In [24]:

data = {

'state':['Ohio','Ohio','Ohio','Nevada','Nevada'],

'year':[2000,2001,2002,2003,2004],

'pop':[1.5,1.7,3.6,2.4,2.9]

}

df = pd.DataFrame(data)

In [25]:

df

Out[25]:

state

year

pop

0

Ohio

2000

1.5

1

Ohio

2001

1.7

2

Ohio

2002

3.6

3

Nevada

2003

2.4

4

Nevada

2004

2.9

In [26]:

df.dtypes

Out[26]:

state object

year int64

pop float64

dtype: object

In [27]:

df.columns

Out[27]:

Index(['state', 'year', 'pop'], dtype='object')

In [28]:

df.index

Out[28]:

RangeIndex(start=0, stop=5, step=1)

从DataFrame中查询出Series

如果只查询一列,一列,返回的是pd.Series

如果查询多行,多列,返回的是pd.DataFrame

In [29]:

df

Out[29]:

state

year

pop

0

Ohio

2000

1.5

1

Ohio

2001

1.7

2

Ohio

2002

3.6

3

Nevada

2003

2.4

4

Nevada

2004

2.9

3.1 查询一列,结果是一个pd.Series

In [30]:

df['year']

Out[30]:

0 2000

1 2001

2 2002

3 2003

4 2004

Name: year, dtype: int64

In [35]:

# 返回的是一个Series

type(df['year'])

Out[35]:

pandas.core.series.Series

3.2 查询多列,结果是一个pd.DataFrame

In [33]:

df[['year', 'pop']]

Out[33]:

year

pop

0

2000

1.5

1

2001

1.7

2

2002

3.6

3

2003

2.4

4

2004

2.9

In [34]:

# 返回的结果是一个DataFrame

type(df[['year','pop']])

Out[34]:

pandas.core.frame.DataFrame

3.3 查询一行,结果是一个pd.Series

In [39]:

df.loc[0]

Out[39]:

state Ohio

year 2000

pop 1.5

Name: 0, dtype: object

In [40]:

type(df.loc[0])

Out[40]:

pandas.core.series.Series

3.4 查询多行,结果是一个pd.DataFrame

In [41]:

# DataFrame中切片会返回结尾的数据

df.loc[0:3]

Out[41]:

state

year

pop

0

Ohio

2000

1.5

1

Ohio

2001

1.7

2

Ohio

2002

3.6

3

Nevada

2003

2.4

In [42]:

type(df.loc[0:3])

Out[42]:

pandas.core.frame.DataFrame

三.Pandas查询数据的5种方法

Pandas查询数据的几种方法

df.loc方法,根据行,列的标签值查询

df.iloc方法,根据行,列的数字位置查询

df.where方法

df.query方法

.loc方法既能查询,又能覆盖写入,推荐使用此方法

Pandas使用df.loc查询数据的方法

使用单个label值查询数据

使用值列表批量查询

使用数值区间进行范围查询

使用条件表达式查询

调用函数查询

注意

以上查询方法,既适用于行,也适用于列

In [3]:

import pandas as pd

0. 读取数据

数据为北京2018年全年天气预报

In [4]:

df = pd.read_csv("./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv")

In [5]:

df.head()

Out[5]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [6]:

# 设定索引为日期,方便按日期筛选

df.set_index('ymd', inplace=True)

In [7]:

df.head()

Out[7]:

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

ymd

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [8]:

# 时间序列见后续课程,本次按字符串处理

df.index

Out[8]:

Index(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05',

'2018-01-06', '2018-01-07', '2018-01-08', '2018-01-09', '2018-01-10',

...

'2018-12-22', '2018-12-23', '2018-12-24', '2018-12-25', '2018-12-26',

'2018-12-27', '2018-12-28', '2018-12-29', '2018-12-30', '2018-12-31'],

dtype='object', name='ymd', length=365)

In [9]:

# 替换掉温度的后缀℃

# df.loc[:]表示筛选出所有的行

df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃","").astype('int32')

df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃","").astype('int32')

In [10]:

# bWendu和yWendu改为int类型

df.dtypes

Out[10]:

bWendu int32

yWendu int32

tianqi object

fengxiang object

fengli object

aqi int64

aqiInfo object

aqiLevel int64

dtype: object

1. 使用单个label值查询数据

行或者列,都可以只传入单个值,实现精确匹配

In [11]:

# 得到单个值

df.loc['2018-01-03','bWendu']

Out[11]:

2

In [12]:

# 得到一个Series

df.loc['2018-01-03',['bWendu', 'yWendu']]

Out[12]:

bWendu 2

yWendu -5

Name: 2018-01-03, dtype: object

2. 使用值列进行表批量查询

In [13]:

# 得到Series

df.loc[['2018-01-03','2018-01-04','2018-01-05'], 'bWendu']

Out[13]:

ymd

2018-01-03 2

2018-01-04 0

2018-01-05 3

Name: bWendu, dtype: int32

In [14]:

# 得到DataFrame

df.loc[['2018-01-03','2018-01-04','2018-01-05'], ['bWendu','yWendu']]

Out[14]:

bWendu

yWendu

ymd

2018-01-03

2

-5

2018-01-04

0

-8

2018-01-05

3

-6

3. 使用数值区间进行范围查询

注意:区间既包含开始,也包含结束

In [15]:

# 行index按区间

df.loc['2018-01-03':'2018-01-05', 'bWendu']

Out[15]:

ymd

2018-01-03 2

2018-01-04 0

2018-01-05 3

Name: bWendu, dtype: int32

In [16]:

# 列index按区间

df.loc['2018-01-03','bWendu':'fengxiang']

Out[16]:

bWendu 2

yWendu -5

tianqi 多云

fengxiang 北风

Name: 2018-01-03, dtype: object

In [17]:

# 行和列都按区间查询

df.loc['2018-01-03':'2018-01-05','bWendu':'fengxiang']

Out[17]:

bWendu

yWendu

tianqi

fengxiang

ymd

2018-01-03

2

-5

多云

北风

2018-01-04

0

-8

东北风

2018-01-05

3

-6

多云~晴

西北风

4. 使用条件表达式查询

bool列表的长度得等于行数或者列数

In [23]:

df.loc[df["yWendu"]

Out[23]:

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

ymd

2018-01-23

-4

-12

西北风

3-4级

31

1

2018-01-24

-4

-11

西南风

1-2级

34

1

2018-01-25

-3

-11

多云

东北风

1-2级

27

1

2018-12-26

-2

-11

晴~多云

东北风

2级

26

1

2018-12-27

-5

-12

多云~晴

西北风

3级

48

1

2018-12-28

-3

-11

西北风

3级

40

1

2018-12-29

-3

-12

西北风

2级

29

1

2018-12-30

-2

-11

晴~多云

东北风

1级

31

1

In [24]:

df["yWendu"]

Out[24]:

ymd

2018-01-01 False

2018-01-02 False

2018-01-03 False

2018-01-04 False

2018-01-05 False

2018-01-06 False

2018-01-07 False

2018-01-08 False

2018-01-09 False

2018-01-10 False

2018-01-11 False

2018-01-12 False

2018-01-13 False

2018-01-14 False

2018-01-15 False

2018-01-16 False

2018-01-17 False

2018-01-18 False

2018-01-19 False

2018-01-20 False

2018-01-21 False

2018-01-22 False

2018-01-23 True

2018-01-24 True

2018-01-25 True

2018-01-26 False

2018-01-27 False

2018-01-28 False

2018-01-29 False

2018-01-30 False

...

2018-12-02 False

2018-12-03 False

2018-12-04 False

2018-12-05 False

2018-12-06 False

2018-12-07 False

2018-12-08 False

2018-12-09 False

2018-12-10 False

2018-12-11 False

2018-12-12 False

2018-12-13 False

2018-12-14 False

2018-12-15 False

2018-12-16 False

2018-12-17 False

2018-12-18 False

2018-12-19 False

2018-12-20 False

2018-12-21 False

2018-12-22 False

2018-12-23 False

2018-12-24 False

2018-12-25 False

2018-12-26 True

2018-12-27 True

2018-12-28 True

2018-12-29 True

2018-12-30 True

2018-12-31 False

Name: yWendu, Length: 365, dtype: bool

复杂条件查询,查询一下完美得天气

注意,组合条件用&符号合并,每个条件判断都得带括号

In [29]:

df.loc[(df["bWendu"]<=30) & (df["yWendu"]>=15) & (df["tianqi"]=="晴") & (df["aqiLevel"]==1),:]

Out[29]:

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

ymd

2018-08-24

30

20

北风

1-2级

40

1

2018-09-07

27

16

西北风

3-4级

22

1

In [30]:

(df["bWendu"]<=30) & (df["yWendu"]>=15) & (df["tianqi"]=="晴") & (df["aqiLevel"]==1)

Out[30]:

ymd

2018-01-01 False

2018-01-02 False

2018-01-03 False

2018-01-04 False

2018-01-05 False

2018-01-06 False

2018-01-07 False

2018-01-08 False

2018-01-09 False

2018-01-10 False

2018-01-11 False

2018-01-12 False

2018-01-13 False

2018-01-14 False

2018-01-15 False

2018-01-16 False

2018-01-17 False

2018-01-18 False

2018-01-19 False

2018-01-20 False

2018-01-21 False

2018-01-22 False

2018-01-23 False

2018-01-24 False

2018-01-25 False

2018-01-26 False

2018-01-27 False

2018-01-28 False

2018-01-29 False

2018-01-30 False

...

2018-12-02 False

2018-12-03 False

2018-12-04 False

2018-12-05 False

2018-12-06 False

2018-12-07 False

2018-12-08 False

2018-12-09 False

2018-12-10 False

2018-12-11 False

2018-12-12 False

2018-12-13 False

2018-12-14 False

2018-12-15 False

2018-12-16 False

2018-12-17 False

2018-12-18 False

2018-12-19 False

2018-12-20 False

2018-12-21 False

2018-12-22 False

2018-12-23 False

2018-12-24 False

2018-12-25 False

2018-12-26 False

2018-12-27 False

2018-12-28 False

2018-12-29 False

2018-12-30 False

2018-12-31 False

Length: 365, dtype: bool

5. 调用函数查询

In [31]:

# 直接写lambda表达式

df.loc[lambda df: (df["bWendu"]<=30) & (df["yWendu"]>=15),:]

Out[31]:

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

ymd

2018-04-28

27

17

西南风

3-4级

125

轻度污染

3

2018-04-29

30

16

多云

南风

3-4级

193

中度污染

4

2018-05-04

27

16

晴~多云

西南风

1-2级

86

2

2018-05-09

29

17

晴~多云

西南风

3-4级

79

2

2018-05-10

26

18

多云

南风

3-4级

118

轻度污染

3

2018-05-11

24

15

阴~多云

东风

1-2级

106

轻度污染

3

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

2018-05-13

30

17

南风

1-2级

68

2

2018-05-16

29

21

多云~小雨

东风

1-2级

142

轻度污染

3

2018-05-17

25

19

小雨~多云

北风

1-2级

70

2

2018-05-18

28

16

多云~晴

南风

1-2级

49

1

2018-05-19

27

16

多云~小雨

南风

1-2级

69

2

2018-05-20

21

16

阴~小雨

东风

1-2级

54

2

2018-05-23

29

15

西南风

3-4级

153

中度污染

4

2018-05-26

30

17

小雨~多云

西南风

3-4级

143

轻度污染

3

2018-05-28

30

16

西北风

4-5级

178

中度污染

4

2018-06-09

23

17

小雨

北风

1-2级

45

1

2018-06-10

27

17

多云

东南风

1-2级

51

2

2018-06-11

29

19

多云

西南风

3-4级

85

2

2018-06-13

28

19

雷阵雨~多云

东北风

1-2级

73

2

2018-06-18

30

21

雷阵雨

西南风

1-2级

112

轻度污染

3

2018-06-22

30

21

雷阵雨~多云

东南风

1-2级

83

2

2018-07-08

30

23

雷阵雨

南风

1-2级

73

2

2018-07-09

30

22

雷阵雨~多云

东南风

1-2级

106

轻度污染

3

2018-07-10

30

22

多云~雷阵雨

南风

1-2级

48

1

2018-07-11

25

22

雷阵雨~大雨

东北风

1-2级

44

1

2018-07-12

27

22

多云

南风

1-2级

46

1

2018-07-13

28

23

雷阵雨

东风

1-2级

60

2

2018-07-17

27

23

中雨~雷阵雨

西风

1-2级

28

1

2018-07-24

28

26

暴雨~雷阵雨

东北风

3-4级

29

1

2018-08-11

30

23

雷阵雨~中雨

东风

1-2级

60

2

2018-08-12

30

24

雷阵雨

东南风

1-2级

74

2

2018-08-14

29

24

中雨~小雨

东北风

1-2级

42

1

2018-08-16

30

21

晴~多云

东北风

1-2级

40

1

2018-08-17

30

22

多云~雷阵雨

东南风

1-2级

69

2

2018-08-18

28

23

小雨~中雨

北风

3-4级

40

1

2018-08-19

26

23

中雨~小雨

东北风

1-2级

37

1

2018-08-22

28

21

雷阵雨~多云

西南风

1-2级

48

1

2018-08-24

30

20

北风

1-2级

40

1

2018-08-27

30

22

多云~雷阵雨

东南风

1-2级

89

2

2018-08-28

29

22

小雨~多云

南风

1-2级

58

2

2018-08-30

29

20

多云

南风

1-2级

47

1

2018-08-31

29

20

多云~阴

东南风

1-2级

48

1

2018-09-01

27

19

阴~小雨

南风

1-2级

50

1

2018-09-02

27

19

小雨~多云

南风

1-2级

55

2

2018-09-03

30

19

北风

3-4级

70

2

2018-09-06

27

18

多云~晴

西北风

4-5级

37

1

2018-09-07

27

16

西北风

3-4级

22

1

2018-09-08

27

15

多云~晴

北风

1-2级

28

1

2018-09-09

28

16

西南风

1-2级

51

2

2018-09-10

28

19

多云

南风

1-2级

65

2

2018-09-11

26

19

多云

南风

1-2级

68

2

2018-09-12

29

19

多云

南风

1-2级

59

2

2018-09-13

29

20

多云~阴

南风

1-2级

107

轻度污染

3

2018-09-14

28

19

小雨~多云

南风

1-2级

128

轻度污染

3

2018-09-15

26

15

多云

北风

3-4级

42

1

2018-09-17

27

17

多云~阴

北风

1-2级

37

1

2018-09-18

25

17

阴~多云

西南风

1-2级

50

1

2018-09-19

26

17

多云

南风

1-2级

52

2

2018-09-20

27

16

多云

西南风

1-2级

63

2

64 rows × 8 columns

In [33]:

# 编写自己的函数,查询9月份,空气质量好的数据

def query_my_data(df):

return df.index.str.startswith("2018-09") & (df["aqiLevel"]==1)

df.loc[query_my_data,:]

Out[33]:

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

ymd

2018-09-01

27

19

阴~小雨

南风

1-2级

50

1

2018-09-04

31

18

西南风

3-4级

24

1

2018-09-05

31

19

晴~多云

西南风

3-4级

34

1

2018-09-06

27

18

多云~晴

西北风

4-5级

37

1

2018-09-07

27

16

西北风

3-4级

22

1

2018-09-08

27

15

多云~晴

北风

1-2级

28

1

2018-09-15

26

15

多云

北风

3-4级

42

1

2018-09-16

25

14

多云~晴

北风

1-2级

29

1

2018-09-17

27

17

多云~阴

北风

1-2级

37

1

2018-09-18

25

17

阴~多云

西南风

1-2级

50

1

2018-09-21

25

14

西北风

3-4级

50

1

2018-09-22

24

13

西北风

3-4级

28

1

2018-09-23

23

12

西北风

4-5级

28

1

2018-09-24

23

11

北风

1-2级

28

1

2018-09-25

24

12

晴~多云

南风

1-2级

44

1

2018-09-29

22

11

北风

3-4级

21

1

2018-09-30

19

13

多云

西北风

4-5级

22

1

四、Pandas怎样新增数据列

In [1]:

import pandas as pd

0. 读取csv数据到DataFrame

In [15]:

df = pd.read_csv("./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv")

In [16]:

df.head()

Out[16]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

1. 直接赋值的方法

实例:清理温度列,变成数字类型

In [31]:

df.loc[:,"bWendu"] = df["bWendu"].str.replace("℃","").astype('int32')

df.loc[:,"yWendu"] = df["yWendu"].str.replace("℃","").astype('int32')

实例:计算温差

In [49]:

del df["bWendnu"]

In [51]:

del df["bWednu"]

In [52]:

# 注意,fpath["bWendu"]其实是一个Series,后面的减法返回的是Series

df.loc[:,"wencha"] = df["bWendu"] - df["yWendu"]

In [53]:

df.head()

Out[53]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

wencha

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

9

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

7

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

7

3

2018-01-04

0

-8

东北风

1-2级

28

1

8

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

9

2. df.apply方法

Apply a function along an axis of the DataFrame. Objects passed to the function are Series objects whose index is either the DataFrame’s index (axis=0) or the DataFrame’s columns (axis=1) 实例:添加一列温度类型:

如果最高温度大于33度就是高温

低于-10度是低温

否则是常温

In [60]:

def get_wendu_type(x):

if x["bWendu"] > 33:

return "高温"

if x["yWendu"] < -10:

return "低温"

return "常温"

# 注意需要设置axis=1

df.loc[:,"wendu_type"] = df.apply(get_wendu_type, axis=1)

In [61]:

# 查看温度类型的计数

df["wendu_type"].value_counts()

Out[61]:

常温 328

高温 29

低温 8

Name: wendu_type, dtype: int64

3. df.assign方法

Assign new columns to a DataFrame.

Returns a new object with all original columns in addtion to new ones.

实例:将温度从摄氏度变成华氏度

In [63]:

# 可以同时添加多个新的列

df.assign(

yWendu_huashi = lambda x: x["yWendu"]*9/5 + 32,

bWendu_huashi = lambda x: x["bWendu"]*9/5 + 32

)

. . .

4. 按条件选择分组分别赋值

按条件选择数据,然后对整个数据赋值新列

实例:高低温差大于10度,则认为温差大

In [65]:

df.loc[:,"wencha_type"] = ""

df.loc[df["bWendu"]-df["yWendu"]>10, "wencha_type"] = "温差大"

df.loc[df["bWendu"]-df["yWendu"]<=10, "wencha_type"]= "温度正常"

In [67]:

df["wencha_type"].value_counts()

Out[67]:

温度正常 187

温差大 178

Name: wencha_type, dtype: int64

五、Pandas数据统计函数

汇总类统计

唯一去重和按值计数

相关系数和协方差

In [2]:

import pandas as pd

0. 读取csv数据

In [5]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

In [6]:

df.head(3)

Out[6]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

In [12]:

df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃","").astype("int32")

In [14]:

df.head(3)

Out[14]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

1. 汇总类统计

In [15]:

# 一次提取所有数字列统计结果

df.describe()

Out[15]:

bWendu

yWendu

aqi

aqiLevel

count

365.000000

365.000000

365.000000

365.000000

mean

18.665753

8.358904

82.183562

2.090411

std

11.858046

11.755053

51.936159

1.029798

min

-5.000000

-12.000000

21.000000

1.000000

25%

8.000000

-3.000000

46.000000

1.000000

50%

21.000000

8.000000

69.000000

2.000000

75%

29.000000

19.000000

104.000000

3.000000

max

38.000000

27.000000

387.000000

6.000000

In [16]:

# 查看单个Series的数据

df["bWendu"].mean()

Out[16]:

18.665753424657535

In [17]:

# 最高温

df["bWendu"].max()

Out[17]:

38

In [18]:

# 最低温

df["bWendu"].min()

Out[18]:

-5

2. 唯一去重和按值计数

2.1 唯一去重

一般不用于数值列,而是枚举、分类列

In [19]:

df["fengxiang"].unique()

Out[19]:

array(['东北风', '北风', '西北风', '西南风', '南风', '东南风', '东风', '西风'], dtype=object)

In [20]:

df["tianqi"].unique()

Out[20]:

array(['晴~多云', '阴~多云', '多云', '阴', '多云~晴', '多云~阴', '晴', '阴~小雪', '小雪~多云',

'小雨~阴', '小雨~雨夹雪', '多云~小雨', '小雨~多云', '大雨~小雨', '小雨', '阴~小雨',

'多云~雷阵雨', '雷阵雨~多云', '阴~雷阵雨', '雷阵雨', '雷阵雨~大雨', '中雨~雷阵雨', '小雨~大雨',

'暴雨~雷阵雨', '雷阵雨~中雨', '小雨~雷阵雨', '雷阵雨~阴', '中雨~小雨', '小雨~中雨', '雾~多云',

'霾'], dtype=object)

In [22]:

df["fengli"].unique()

Out[22]:

array(['1-2级', '4-5级', '3-4级', '2级', '1级', '3级'], dtype=object)

2.2 按值计数

In [24]:

df["fengxiang"].value_counts()

Out[24]:

南风 92

西南风 64

北风 54

西北风 51

东南风 46

东北风 38

东风 14

西风 6

Name: fengxiang, dtype: int64

In [25]:

df["tianqi"].unique()

Out[25]:

array(['晴~多云', '阴~多云', '多云', '阴', '多云~晴', '多云~阴', '晴', '阴~小雪', '小雪~多云',

'小雨~阴', '小雨~雨夹雪', '多云~小雨', '小雨~多云', '大雨~小雨', '小雨', '阴~小雨',

'多云~雷阵雨', '雷阵雨~多云', '阴~雷阵雨', '雷阵雨', '雷阵雨~大雨', '中雨~雷阵雨', '小雨~大雨',

'暴雨~雷阵雨', '雷阵雨~中雨', '小雨~雷阵雨', '雷阵雨~阴', '中雨~小雨', '小雨~中雨', '雾~多云',

'霾'], dtype=object)

In [26]:

df["fengli"].value_counts()

Out[26]:

1-2级 236

3-4级 68

1级 21

4-5级 20

2级 13

3级 7

Name: fengli, dtype: int64

3. 相关系数和协防差

用途:

两只股票,是不是同涨同跌?程度多大?正相关还是负相关?

产品销量的波动,跟哪些因素正相关、负相关,程度有多大?

对于两个变量x, y:

协方差:衡量同向反向程度,如果协方差为正,说明x,y同向变化,协方差越大说明同向程度越高;如果协方差为负,说明x,y反向运动,协方差越小说明反向程度越高。

相关系数:衡量相似度程度,当他们的相关系数为1时,说明两个变量变化时正向相似度越大,当关系数为-1时,说明两个变量变化的反向相似度最大

In [27]:

# 协方差矩阵

df.cov()

Out[27]:

bWendu

yWendu

aqi

aqiLevel

bWendu

140.613247

135.529633

47.462622

0.879204

yWendu

135.529633

138.181274

16.186685

0.264165

aqi

47.462622

16.186685

2697.364564

50.749842

aqiLevel

0.879204

0.264165

50.749842

1.060485

In [28]:

# 相关系数矩阵

df.corr()

Out[28]:

bWendu

yWendu

aqi

aqiLevel

bWendu

1.000000

0.972292

0.077067

0.071999

yWendu

0.972292

1.000000

0.026513

0.021822

aqi

0.077067

0.026513

1.000000

0.948883

aqiLevel

0.071999

0.021822

0.948883

1.000000

In [29]:

# 单独查看空气质量和最高温度的相关系数

df["aqi"].corr(df["bWendu"])

Out[29]:

0.07706705916811067

In [30]:

df["aqi"].corr(df["yWendu"])

Out[30]:

0.026513282672968895

In [31]:

# 空气质量和温差的相关系数

df["aqi"].corr(df["bWendu"]-df["yWendu"])

Out[31]:

0.2165225757638205

虽然单独观察最高温度和最低温度对空气质量的影响不大,但是明显温差对空气质量的影响要大得多,因此,前端数据的挖掘对结果的呈现十分重要。

六、Pandas对缺失值的处理

Pandas使用这些函数处理缺失值:

isnull和notnull:检测是否是空值,可用于df和Series

dropna:丢弃、删除缺失值

axis:删除行还是列,{0 or “index”, 1 or “columns”}, default 0

how:如果等于any则任何值为空都删除,如果等于all则所有值都为空才删除

inplace:如果为True则修改当前df,否则返回新的df

fillna:填充空值

value:用于填充的值,可以是单个值,或者字典(key是列名,value是值)

method:等于ffill使用前一个不为空的值填充forward fill,等于fill使用后一个不为 空的值填充backword fill

axis:按行还是列填充,{0 or “index”, 1 or "columns’}

inplace:如果为True则修改当前df,否则返回新的df

In [1]:

import pandas as pd

实例:特殊excel的读取、清洗、处理

步骤1:读取excel的时候,忽略前几个空行

In [5]:

# skiprows=2, 跳过前两行

studf = pd.read_excel("./pandas-learn-code/datas/student_excel/student_excel.xlsx", skiprows=2)

In [6]:

studf

Out[6]:

Unnamed: 0

姓名

科目

分数

0

NaN

小明

语文

85.0

1

NaN

NaN

数学

80.0

2

NaN

NaN

英语

90.0

3

NaN

NaN

NaN

NaN

4

NaN

小王

语文

85.0

5

NaN

NaN

数学

NaN

6

NaN

NaN

英语

90.0

7

NaN

NaN

NaN

NaN

8

NaN

小刚

语文

85.0

9

NaN

NaN

数学

80.0

10

NaN

NaN

英语

90.0

步骤2:检测空值

In [7]:

studf.isnull()

Out[7]:

Unnamed: 0

姓名

科目

分数

0

True

False

False

False

1

True

True

False

False

2

True

True

False

False

3

True

True

True

True

4

True

False

False

False

5

True

True

False

True

6

True

True

False

False

7

True

True

True

True

8

True

False

False

False

9

True

True

False

False

10

True

True

False

False

In [9]:

studf["分数"].isnull()

Out[9]:

0 False

1 False

2 False

3 True

4 False

5 True

6 False

7 True

8 False

9 False

10 False

Name: 分数, dtype: bool

In [10]:

studf["分数"].notnull()

Out[10]:

0 True

1 True

2 True

3 False

4 True

5 False

6 True

7 False

8 True

9 True

10 True

Name: 分数, dtype: bool

In [12]:

# 筛选没有空分数的所有行

studf.loc[studf["分数"].notnull(), :]

Out[12]:

Unnamed: 0

姓名

科目

分数

0

NaN

小明

语文

85.0

1

NaN

NaN

数学

80.0

2

NaN

NaN

英语

90.0

4

NaN

小王

语文

85.0

6

NaN

NaN

英语

90.0

8

NaN

小刚

语文

85.0

9

NaN

NaN

数学

80.0

10

NaN

NaN

英语

90.0

步骤3:删除掉全是空值的列

In [15]:

studf.dropna(axis="columns", how="all", inplace=True)

In [16]:

studf

Out[16]:

姓名

科目

分数

0

小明

语文

85.0

1

NaN

数学

80.0

2

NaN

英语

90.0

4

小王

语文

85.0

5

NaN

数学

NaN

6

NaN

英语

90.0

8

小刚

语文

85.0

9

NaN

数学

80.0

10

NaN

英语

90.0

步骤4:删除掉全是空值的行

In [13]:

studf.dropna(axis="index", how="all", inplace=True)

In [17]:

studf

Out[17]:

姓名

科目

分数

0

小明

语文

85.0

1

NaN

数学

80.0

2

NaN

英语

90.0

4

小王

语文

85.0

5

NaN

数学

NaN

6

NaN

英语

90.0

8

小刚

语文

85.0

9

NaN

数学

80.0

10

NaN

英语

90.0

步骤5:将分数列为空的填充为0分

In [19]:

studf.fillna({"分数": 0})

. . .

In [20]:

# 等同于

studf.loc[:,"分数"] = studf["分数"].fillna(0)

In [21]:

studf

Out[21]:

姓名

科目

分数

0

小明

语文

85.0

1

NaN

数学

80.0

2

NaN

英语

90.0

4

小王

语文

85.0

5

NaN

数学

0.0

6

NaN

英语

90.0

8

小刚

语文

85.0

9

NaN

数学

80.0

10

NaN

英语

90.0

步骤6:将姓名的缺失值填充

使用前面的有效值填充,用ffill:forward fill

In [22]:

studf.loc[:, "姓名"] = studf['姓名'].fillna(method="ffill")

In [23]:

studf

Out[23]:

姓名

科目

分数

0

小明

语文

85.0

1

小明

数学

80.0

2

小明

英语

90.0

4

小王

语文

85.0

5

小王

数学

0.0

6

小王

英语

90.0

8

小刚

语文

85.0

9

小刚

数学

80.0

10

小刚

英语

90.0

步骤7:将清晰好的excel保存

In [25]:

studf.to_excel(r"D:\WinterIsComing\python\New_Wave\pandas_basic\student_excel.xlsx", index=False)

七、Pandas的SettingWithCopyWarning报警

0. 数据读取

In [1]:

import pandas as pd

In [2]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

In [3]:

df.head()

Out[3]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [5]:

df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃","").astype("int32")

df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃","").astype("int32")

In [7]:

df.head()

Out[7]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

1. 复现

In [10]:

# 筛选出3月份的数据用于分析

condition = df.loc[:, "ymd"].str.startswith("2018-03")

In [11]:

# 设置三月份的温差

# 错误写法

df[condition]["wen_cha"] = df["bWendu"] - df["yWendu"]

D:\Tools\Anaconda3\lib\site-packages\ipykernel_launcher.py:3: SettingWithCopyWarning:

A value is trying to be set on a copy of a slice from a DataFrame.

Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

This is separate from the ipykernel package so we can avoid doing imports until

In [12]:

# 查看修改是否成功

df[condition].head()

# 只筛选了3月的数据,但没有新增温差列

Out[12]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

59

2018-03-01

8

-3

多云

西南风

1-2级

46

1

60

2018-03-02

9

-1

晴~多云

北风

1-2级

95

2

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

62

2018-03-04

7

-2

阴~多云

东南风

1-2级

144

轻度污染

3

63

2018-03-05

8

-3

南风

1-2级

94

2

2、原因

发出警告的代码 df[condition][“wen_cha”] = df[“bWendu”]-df[“yWendu”]

相当于:df.get(condition).set(wen_cha),第一步骤的get发出了报警

*链式操作其实是两个步骤,先get后set,get得到的dataframe可能是view(是DateFrame的子视图,我们对它修改会直接影响原DateFrame)也可能是copy,pandas发出警告*

官网文档: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

核心要诀:pandas的dataframe的修改写操作,只允许在源dataframe上进行,一步到位

## 3. 解决方法1

将get+set的两步操作,改成set的一步操作

In [15]:

df.loc[condition, "wen_cha"] = df["bWendu"] - df["yWendu"]

In [18]:

df.head(2)

Out[18]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

wen_cha

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

NaN

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

NaN

In [19]:

df[condition].head(2)

Out[19]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

wen_cha

59

2018-03-01

8

-3

多云

西南风

1-2级

46

1

11.0

60

2018-03-02

9

-1

晴~多云

北风

1-2级

95

2

10.0

4. 解决方法2

如果需要预筛选数据做后续的处理分析,先使用copy复制DataFrame并进行操作

In [20]:

# 复制一个新的DateFrame df_month3:筛选3月份的数据并复制

df_month3 = df[condition].copy()

In [22]:

df_month3.head()

Out[22]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

wen_cha

59

2018-03-01

8

-3

多云

西南风

1-2级

46

1

11.0

60

2018-03-02

9

-1

晴~多云

北风

1-2级

95

2

10.0

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

10.0

62

2018-03-04

7

-2

阴~多云

东南风

1-2级

144

轻度污染

3

9.0

63

2018-03-05

8

-3

南风

1-2级

94

2

11.0

In [24]:

df_month3["wencha"] = df_month3["bWendu"] - df_month3["yWendu"]

In [25]:

df_month3.head()

Out[25]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

wen_cha

wencha

59

2018-03-01

8

-3

多云

西南风

1-2级

46

1

11.0

11

60

2018-03-02

9

-1

晴~多云

北风

1-2级

95

2

10.0

10

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

10.0

10

62

2018-03-04

7

-2

阴~多云

东南风

1-2级

144

轻度污染

3

9.0

9

63

2018-03-05

8

-3

南风

1-2级

94

2

11.0

11

Pandas不允许先筛选子DataFrame,在进行修改写入

要么使用.loc实现一个步骤直接修改源dataframe

要么先复制一个子DataFrame再一个步骤执行修改

八、Pandas数据排序

Series的排序:

*Series.sort_values(ascending=True, inplace=False)*

参数说明:

ascending:默认为True升序排序,为False降序排序

inplace:是否修改原始Series

DataFrame的排序:

*DataFrame.sort_values(by, ascending=True, inplace=False)*

参数说明:

by:字符串或者List,单列排序或者多列排序

ascending:bool或者List,升序还是降序,如果是list对应by的多列

inplace:是否修改原始DataFrame

In [1]:

import pandas as pd

0. 读取数据

In [2]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

In [4]:

# 替换温度的后缀℃

df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃","").astype("int32")

df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃","").astype("int32")

In [5]:

df.head()

Out[5]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

1. Series的排序

In [7]:

# 默认为升序

df["aqi"].sort_values()

Out[7]:

271 21

281 21

249 22

272 22

301 22

246 24

35 24

33 24

10 24

273 25

282 25

359 26

9 26

111 27

24 27

2 28

264 28

319 28

250 28

266 28

3 28

265 28

205 28

197 28

204 29

258 29

362 29

283 30

308 30

22 31

...

334 163

109 164

108 170

68 171

176 174

70 174

294 176

124 177

286 177

147 178

49 183

131 186

13 187

118 193

336 198

287 198

330 198

306 206

61 214

90 218

316 219

57 220

335 234

85 243

329 245

317 266

71 287

91 287

72 293

86 387

Name: aqi, Length: 365, dtype: int64

In [10]:

# 将排序方式调整为降序

df["aqi"].sort_values(ascending=False)

Out[10]:

86 387

72 293

91 287

71 287

317 266

329 245

85 243

335 234

57 220

316 219

90 218

61 214

306 206

330 198

287 198

336 198

118 193

13 187

131 186

49 183

147 178

286 177

124 177

294 176

70 174

176 174

68 171

108 170

109 164

334 163

...

22 31

308 30

283 30

362 29

258 29

204 29

197 28

205 28

265 28

3 28

266 28

250 28

319 28

264 28

2 28

24 27

111 27

9 26

359 26

282 25

273 25

10 24

33 24

35 24

246 24

301 22

272 22

249 22

281 21

271 21

Name: aqi, Length: 365, dtype: int64

In [12]:

# 对中文也可以排序

df["tianqi"].sort_values()

Out[12]:

225 中雨~小雨

230 中雨~小雨

197 中雨~雷阵雨

196 中雨~雷阵雨

112 多云

108 多云

232 多云

234 多云

241 多云

94 多云

91 多云

88 多云

252 多云

84 多云

364 多云

165 多云

81 多云

79 多云

78 多云

77 多云

257 多云

74 多云

69 多云

67 多云

261 多云

262 多云

268 多云

270 多云

226 多云

253 多云

...

338 阴~多云

111 阴~多云

243 阴~小雨

139 阴~小雨

20 阴~小雪

167 阴~雷阵雨

237 雷阵雨

195 雷阵雨

223 雷阵雨

187 雷阵雨

168 雷阵雨

188 雷阵雨

193 雷阵雨

175 雷阵雨

218 雷阵雨~中雨

216 雷阵雨~中雨

224 雷阵雨~中雨

222 雷阵雨~中雨

189 雷阵雨~多云

163 雷阵雨~多云

180 雷阵雨~多云

183 雷阵雨~多云

194 雷阵雨~多云

172 雷阵雨~多云

233 雷阵雨~多云

191 雷阵雨~大雨

219 雷阵雨~阴

335 雾~多云

353 霾

348 霾

Name: tianqi, Length: 365, dtype: object

2. DataFrame的排序

2.1 单列排序

In [13]:

# 按照空气质量进行排序

df.sort_values(by="aqi")

Out[13]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

271

2018-09-29

22

11

北风

3-4级

21

1

281

2018-10-09

15

4

多云~晴

西北风

4-5级

21

1

249

2018-09-07

27

16

西北风

3-4级

22

1

272

2018-09-30

19

13

多云

西北风

4-5级

22

1

301

2018-10-29

15

3

北风

3-4级

22

1

246

2018-09-04

31

18

西南风

3-4级

24

1

35

2018-02-05

0

-10

北风

3-4级

24

1

33

2018-02-03

0

-9

多云

北风

1-2级

24

1

10

2018-01-11

-1

-10

北风

1-2级

24

1

273

2018-10-01

24

12

北风

4-5级

25

1

282

2018-10-10

17

4

多云~晴

西北风

1-2级

25

1

359

2018-12-26

-2

-11

晴~多云

东北风

2级

26

1

9

2018-01-10

-2

-10

西北风

1-2级

26

1

111

2018-04-22

16

12

阴~多云

东北风

3-4级

27

1

24

2018-01-25

-3

-11

多云

东北风

1-2级

27

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

264

2018-09-22

24

13

西北风

3-4级

28

1

319

2018-11-16

8

-1

晴~多云

北风

1-2级

28

1

250

2018-09-08

27

15

多云~晴

北风

1-2级

28

1

266

2018-09-24

23

11

北风

1-2级

28

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

265

2018-09-23

23

12

西北风

4-5级

28

1

205

2018-07-25

32

25

多云

北风

1-2级

28

1

197

2018-07-17

27

23

中雨~雷阵雨

西风

1-2级

28

1

204

2018-07-24

28

26

暴雨~雷阵雨

东北风

3-4级

29

1

258

2018-09-16

25

14

多云~晴

北风

1-2级

29

1

362

2018-12-29

-3

-12

西北风

2级

29

1

283

2018-10-11

18

5

晴~多云

北风

1-2级

30

1

308

2018-11-05

10

2

多云

西南风

1-2级

30

1

22

2018-01-23

-4

-12

西北风

3-4级

31

1

334

2018-12-01

7

0

多云

东南风

1级

163

中度污染

4

109

2018-04-20

28

14

多云~小雨

南风

4-5级

164

中度污染

4

108

2018-04-19

26

13

多云

东南风

4-5级

170

中度污染

4

68

2018-03-10

14

-2

东南风

1-2级

171

中度污染

4

176

2018-06-26

36

25

西南风

3-4级

174

中度污染

4

70

2018-03-12

15

3

多云~晴

南风

1-2级

174

中度污染

4

294

2018-10-22

19

5

多云~晴

西北风

1-2级

176

中度污染

4

124

2018-05-05

25

13

多云

北风

3-4级

177

中度污染

4

286

2018-10-14

21

10

多云

南风

1-2级

177

中度污染

4

147

2018-05-28

30

16

西北风

4-5级

178

中度污染

4

49

2018-02-19

6

-3

多云

南风

1-2级

183

中度污染

4

131

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

13

2018-01-14

6

-5

晴~多云

西北风

1-2级

187

中度污染

4

118

2018-04-29

30

16

多云

南风

3-4级

193

中度污染

4

336

2018-12-03

8

-3

多云~晴

东北风

3级

198

中度污染

4

287

2018-10-15

17

11

小雨

北风

1-2级

198

中度污染

4

330

2018-11-27

9

-3

晴~多云

西北风

2级

198

中度污染

4

306

2018-11-03

16

6

多云

南风

1-2级

206

重度污染

5

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

90

2018-04-01

25

11

晴~多云

南风

1-2级

218

重度污染

5

316

2018-11-13

13

5

多云

东南风

1-2级

219

重度污染

5

57

2018-02-27

7

0

东风

1-2级

220

重度污染

5

335

2018-12-02

9

2

雾~多云

东北风

1级

234

重度污染

5

85

2018-03-27

27

11

南风

1-2级

243

重度污染

5

329

2018-11-26

10

0

多云

东南风

1级

245

重度污染

5

317

2018-11-14

13

5

多云

南风

1-2级

266

重度污染

5

71

2018-03-13

17

5

晴~多云

南风

1-2级

287

重度污染

5

91

2018-04-02

26

11

多云

北风

1-2级

287

重度污染

5

72

2018-03-14

15

6

多云~阴

东北风

1-2级

293

重度污染

5

86

2018-03-28

25

9

多云~晴

东风

1-2级

387

严重污染

6

365 rows × 9 columns

In [14]:

# 指定降序

df.sort_values(by="aqi", ascending=False)

Out[14]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

86

2018-03-28

25

9

多云~晴

东风

1-2级

387

严重污染

6

72

2018-03-14

15

6

多云~阴

东北风

1-2级

293

重度污染

5

71

2018-03-13

17

5

晴~多云

南风

1-2级

287

重度污染

5

91

2018-04-02

26

11

多云

北风

1-2级

287

重度污染

5

317

2018-11-14

13

5

多云

南风

1-2级

266

重度污染

5

329

2018-11-26

10

0

多云

东南风

1级

245

重度污染

5

85

2018-03-27

27

11

南风

1-2级

243

重度污染

5

335

2018-12-02

9

2

雾~多云

东北风

1级

234

重度污染

5

57

2018-02-27

7

0

东风

1-2级

220

重度污染

5

316

2018-11-13

13

5

多云

东南风

1-2级

219

重度污染

5

90

2018-04-01

25

11

晴~多云

南风

1-2级

218

重度污染

5

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

306

2018-11-03

16

6

多云

南风

1-2级

206

重度污染

5

287

2018-10-15

17

11

小雨

北风

1-2级

198

中度污染

4

336

2018-12-03

8

-3

多云~晴

东北风

3级

198

中度污染

4

330

2018-11-27

9

-3

晴~多云

西北风

2级

198

中度污染

4

118

2018-04-29

30

16

多云

南风

3-4级

193

中度污染

4

13

2018-01-14

6

-5

晴~多云

西北风

1-2级

187

中度污染

4

131

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

49

2018-02-19

6

-3

多云

南风

1-2级

183

中度污染

4

147

2018-05-28

30

16

西北风

4-5级

178

中度污染

4

286

2018-10-14

21

10

多云

南风

1-2级

177

中度污染

4

124

2018-05-05

25

13

多云

北风

3-4级

177

中度污染

4

294

2018-10-22

19

5

多云~晴

西北风

1-2级

176

中度污染

4

70

2018-03-12

15

3

多云~晴

南风

1-2级

174

中度污染

4

176

2018-06-26

36

25

西南风

3-4级

174

中度污染

4

68

2018-03-10

14

-2

东南风

1-2级

171

中度污染

4

108

2018-04-19

26

13

多云

东南风

4-5级

170

中度污染

4

109

2018-04-20

28

14

多云~小雨

南风

4-5级

164

中度污染

4

334

2018-12-01

7

0

多云

东南风

1级

163

中度污染

4

274

2018-10-02

24

11

西北风

1-2级

31

1

308

2018-11-05

10

2

多云

西南风

1-2级

30

1

283

2018-10-11

18

5

晴~多云

北风

1-2级

30

1

362

2018-12-29

-3

-12

西北风

2级

29

1

258

2018-09-16

25

14

多云~晴

北风

1-2级

29

1

204

2018-07-24

28

26

暴雨~雷阵雨

东北风

3-4级

29

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

250

2018-09-08

27

15

多云~晴

北风

1-2级

28

1

205

2018-07-25

32

25

多云

北风

1-2级

28

1

197

2018-07-17

27

23

中雨~雷阵雨

西风

1-2级

28

1

264

2018-09-22

24

13

西北风

3-4级

28

1

266

2018-09-24

23

11

北风

1-2级

28

1

265

2018-09-23

23

12

西北风

4-5级

28

1

319

2018-11-16

8

-1

晴~多云

北风

1-2级

28

1

111

2018-04-22

16

12

阴~多云

东北风

3-4级

27

1

24

2018-01-25

-3

-11

多云

东北风

1-2级

27

1

9

2018-01-10

-2

-10

西北风

1-2级

26

1

359

2018-12-26

-2

-11

晴~多云

东北风

2级

26

1

273

2018-10-01

24

12

北风

4-5级

25

1

282

2018-10-10

17

4

多云~晴

西北风

1-2级

25

1

33

2018-02-03

0

-9

多云

北风

1-2级

24

1

246

2018-09-04

31

18

西南风

3-4级

24

1

10

2018-01-11

-1

-10

北风

1-2级

24

1

35

2018-02-05

0

-10

北风

3-4级

24

1

249

2018-09-07

27

16

西北风

3-4级

22

1

301

2018-10-29

15

3

北风

3-4级

22

1

272

2018-09-30

19

13

多云

西北风

4-5级

22

1

271

2018-09-29

22

11

北风

3-4级

21

1

281

2018-10-09

15

4

多云~晴

西北风

4-5级

21

1

365 rows × 9 columns

2.2 多列排序

In [15]:

# 按空气质量等级、最高温度默认排序,默认升序

df.sort_values(by=["aqiLevel", "bWendu"])

Out[15]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

360

2018-12-27

-5

-12

多云~晴

西北风

3级

48

1

22

2018-01-23

-4

-12

西北风

3-4级

31

1

23

2018-01-24

-4

-11

西南风

1-2级

34

1

340

2018-12-07

-4

-10

西北风

3级

33

1

21

2018-01-22

-3

-10

小雪~多云

东风

1-2级

47

1

24

2018-01-25

-3

-11

多云

东北风

1-2级

27

1

25

2018-01-26

-3

-10

晴~多云

南风

1-2级

39

1

361

2018-12-28

-3

-11

西北风

3级

40

1

362

2018-12-29

-3

-12

西北风

2级

29

1

9

2018-01-10

-2

-10

西北风

1-2级

26

1

339

2018-12-06

-2

-9

西北风

3级

40

1

341

2018-12-08

-2

-10

晴~多云

西北风

2级

37

1

359

2018-12-26

-2

-11

晴~多云

东北风

2级

26

1

363

2018-12-30

-2

-11

晴~多云

东北风

1级

31

1

10

2018-01-11

-1

-10

北风

1-2级

24

1

32

2018-02-02

-1

-9

北风

3-4级

32

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

33

2018-02-03

0

-9

多云

北风

1-2级

24

1

35

2018-02-05

0

-10

北风

3-4级

24

1

8

2018-01-09

1

-8

西北风

3-4级

34

1

34

2018-02-04

1

-8

西南风

1-2级

36

1

40

2018-02-10

1

-9

西北风

3-4级

39

1

345

2018-12-12

1

-8

西南风

1级

50

1

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

5

2018-01-06

2

-5

多云~阴

西南风

1-2级

32

1

7

2018-01-08

2

-6

西北风

4-5级

50

1

14

2018-01-15

2

-5

东南风

1-2级

47

1

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

346

2018-12-13

3

-7

西北风

2级

42

1

330

2018-11-27

9

-3

晴~多云

西北风

2级

198

中度污染

4

56

2018-02-26

12

-1

晴~多云

西南风

1-2级

157

中度污染

4

68

2018-03-10

14

-2

东南风

1-2级

171

中度污染

4

70

2018-03-12

15

3

多云~晴

南风

1-2级

174

中度污染

4

287

2018-10-15

17

11

小雨

北风

1-2级

198

中度污染

4

294

2018-10-22

19

5

多云~晴

西北风

1-2级

176

中度污染

4

286

2018-10-14

21

10

多云

南风

1-2级

177

中度污染

4

84

2018-03-26

25

7

多云

西南风

1-2级

151

中度污染

4

124

2018-05-05

25

13

多云

北风

3-4级

177

中度污染

4

108

2018-04-19

26

13

多云

东南风

4-5级

170

中度污染

4

109

2018-04-20

28

14

多云~小雨

南风

4-5级

164

中度污染

4

131

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

142

2018-05-23

29

15

西南风

3-4级

153

中度污染

4

118

2018-04-29

30

16

多云

南风

3-4级

193

中度污染

4

147

2018-05-28

30

16

西北风

4-5级

178

中度污染

4

133

2018-05-14

34

22

晴~多云

南风

3-4级

158

中度污染

4

176

2018-06-26

36

25

西南风

3-4级

174

中度污染

4

57

2018-02-27

7

0

东风

1-2级

220

重度污染

5

335

2018-12-02

9

2

雾~多云

东北风

1级

234

重度污染

5

329

2018-11-26

10

0

多云

东南风

1级

245

重度污染

5

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

316

2018-11-13

13

5

多云

东南风

1-2级

219

重度污染

5

317

2018-11-14

13

5

多云

南风

1-2级

266

重度污染

5

72

2018-03-14

15

6

多云~阴

东北风

1-2级

293

重度污染

5

306

2018-11-03

16

6

多云

南风

1-2级

206

重度污染

5

71

2018-03-13

17

5

晴~多云

南风

1-2级

287

重度污染

5

90

2018-04-01

25

11

晴~多云

南风

1-2级

218

重度污染

5

91

2018-04-02

26

11

多云

北风

1-2级

287

重度污染

5

85

2018-03-27

27

11

南风

1-2级

243

重度污染

5

86

2018-03-28

25

9

多云~晴

东风

1-2级

387

严重污染

6

365 rows × 9 columns

In [17]:

# 两个字段都是降序

df.sort_values(by=["aqiLevel","bWendu"],ascending=False)

Out[17]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

86

2018-03-28

25

9

多云~晴

东风

1-2级

387

严重污染

6

85

2018-03-27

27

11

南风

1-2级

243

重度污染

5

91

2018-04-02

26

11

多云

北风

1-2级

287

重度污染

5

90

2018-04-01

25

11

晴~多云

南风

1-2级

218

重度污染

5

71

2018-03-13

17

5

晴~多云

南风

1-2级

287

重度污染

5

306

2018-11-03

16

6

多云

南风

1-2级

206

重度污染

5

72

2018-03-14

15

6

多云~阴

东北风

1-2级

293

重度污染

5

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

316

2018-11-13

13

5

多云

东南风

1-2级

219

重度污染

5

317

2018-11-14

13

5

多云

南风

1-2级

266

重度污染

5

329

2018-11-26

10

0

多云

东南风

1级

245

重度污染

5

335

2018-12-02

9

2

雾~多云

东北风

1级

234

重度污染

5

57

2018-02-27

7

0

东风

1-2级

220

重度污染

5

176

2018-06-26

36

25

西南风

3-4级

174

中度污染

4

133

2018-05-14

34

22

晴~多云

南风

3-4级

158

中度污染

4

118

2018-04-29

30

16

多云

南风

3-4级

193

中度污染

4

147

2018-05-28

30

16

西北风

4-5级

178

中度污染

4

142

2018-05-23

29

15

西南风

3-4级

153

中度污染

4

109

2018-04-20

28

14

多云~小雨

南风

4-5级

164

中度污染

4

131

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

108

2018-04-19

26

13

多云

东南风

4-5级

170

中度污染

4

84

2018-03-26

25

7

多云

西南风

1-2级

151

中度污染

4

124

2018-05-05

25

13

多云

北风

3-4级

177

中度污染

4

286

2018-10-14

21

10

多云

南风

1-2级

177

中度污染

4

294

2018-10-22

19

5

多云~晴

西北风

1-2级

176

中度污染

4

287

2018-10-15

17

11

小雨

北风

1-2级

198

中度污染

4

70

2018-03-12

15

3

多云~晴

南风

1-2级

174

中度污染

4

68

2018-03-10

14

-2

东南风

1-2级

171

中度污染

4

56

2018-02-26

12

-1

晴~多云

西南风

1-2级

157

中度污染

4

330

2018-11-27

9

-3

晴~多云

西北风

2级

198

中度污染

4

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

346

2018-12-13

3

-7

西北风

2级

42

1

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

5

2018-01-06

2

-5

多云~阴

西南风

1-2级

32

1

7

2018-01-08

2

-6

西北风

4-5级

50

1

14

2018-01-15

2

-5

东南风

1-2级

47

1

8

2018-01-09

1

-8

西北风

3-4级

34

1

34

2018-02-04

1

-8

西南风

1-2级

36

1

40

2018-02-10

1

-9

西北风

3-4级

39

1

345

2018-12-12

1

-8

西南风

1级

50

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

33

2018-02-03

0

-9

多云

北风

1-2级

24

1

35

2018-02-05

0

-10

北风

3-4级

24

1

10

2018-01-11

-1

-10

北风

1-2级

24

1

32

2018-02-02

-1

-9

北风

3-4级

32

1

9

2018-01-10

-2

-10

西北风

1-2级

26

1

339

2018-12-06

-2

-9

西北风

3级

40

1

341

2018-12-08

-2

-10

晴~多云

西北风

2级

37

1

359

2018-12-26

-2

-11

晴~多云

东北风

2级

26

1

363

2018-12-30

-2

-11

晴~多云

东北风

1级

31

1

21

2018-01-22

-3

-10

小雪~多云

东风

1-2级

47

1

24

2018-01-25

-3

-11

多云

东北风

1-2级

27

1

25

2018-01-26

-3

-10

晴~多云

南风

1-2级

39

1

361

2018-12-28

-3

-11

西北风

3级

40

1

362

2018-12-29

-3

-12

西北风

2级

29

1

22

2018-01-23

-4

-12

西北风

3-4级

31

1

23

2018-01-24

-4

-11

西南风

1-2级

34

1

340

2018-12-07

-4

-10

西北风

3级

33

1

360

2018-12-27

-5

-12

多云~晴

西北风

3级

48

1

365 rows × 9 columns

In [18]:

# 分别指定升序和降序

df.sort_values(by=["aqiLevel", "bWendu"], ascending=[True, False])

Out[18]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

178

2018-06-28

35

24

多云~晴

北风

1-2级

33

1

149

2018-05-30

33

18

西风

1-2级

46

1

206

2018-07-26

33

25

多云~雷阵雨

东北风

1-2级

40

1

158

2018-06-08

32

19

多云~雷阵雨

西南风

1-2级

43

1

205

2018-07-25

32

25

多云

北风

1-2级

28

1

226

2018-08-15

32

24

多云

东北风

3-4级

33

1

231

2018-08-20

32

23

多云~晴

北风

1-2级

41

1

232

2018-08-21

32

22

多云

北风

1-2级

38

1

148

2018-05-29

31

16

多云

西北风

1-2级

41

1

196

2018-07-16

31

24

中雨~雷阵雨

南风

1-2级

43

1

234

2018-08-23

31

21

多云

北风

1-2级

43

1

240

2018-08-29

31

20

晴~多云

北风

3-4级

44

1

246

2018-09-04

31

18

西南风

3-4级

24

1

247

2018-09-05

31

19

晴~多云

西南风

3-4级

34

1

190

2018-07-10

30

22

多云~雷阵雨

南风

1-2级

48

1

220

2018-08-09

30

24

多云

南风

1-2级

49

1

227

2018-08-16

30

21

晴~多云

东北风

1-2级

40

1

235

2018-08-24

30

20

北风

1-2级

40

1

219

2018-08-08

29

24

雷阵雨~阴

东北风

1-2级

45

1

225

2018-08-14

29

24

中雨~小雨

东北风

1-2级

42

1

241

2018-08-30

29

20

多云

南风

1-2级

47

1

242

2018-08-31

29

20

多云~阴

东南风

1-2级

48

1

137

2018-05-18

28

16

多云~晴

南风

1-2级

49

1

204

2018-07-24

28

26

暴雨~雷阵雨

东北风

3-4级

29

1

229

2018-08-18

28

23

小雨~中雨

北风

3-4级

40

1

233

2018-08-22

28

21

雷阵雨~多云

西南风

1-2级

48

1

192

2018-07-12

27

22

多云

南风

1-2级

46

1

197

2018-07-17

27

23

中雨~雷阵雨

西风

1-2级

28

1

243

2018-09-01

27

19

阴~小雨

南风

1-2级

50

1

248

2018-09-06

27

18

多云~晴

西北风

4-5级

37

1

142

2018-05-23

29

15

西南风

3-4级

153

中度污染

4

109

2018-04-20

28

14

多云~小雨

南风

4-5级

164

中度污染

4

131

2018-05-12

28

16

小雨

东南风

3-4级

186

中度污染

4

108

2018-04-19

26

13

多云

东南风

4-5级

170

中度污染

4

84

2018-03-26

25

7

多云

西南风

1-2级

151

中度污染

4

124

2018-05-05

25

13

多云

北风

3-4级

177

中度污染

4

286

2018-10-14

21

10

多云

南风

1-2级

177

中度污染

4

294

2018-10-22

19

5

多云~晴

西北风

1-2级

176

中度污染

4

287

2018-10-15

17

11

小雨

北风

1-2级

198

中度污染

4

70

2018-03-12

15

3

多云~晴

南风

1-2级

174

中度污染

4

68

2018-03-10

14

-2

东南风

1-2级

171

中度污染

4

56

2018-02-26

12

-1

晴~多云

西南风

1-2级

157

中度污染

4

330

2018-11-27

9

-3

晴~多云

西北风

2级

198

中度污染

4

336

2018-12-03

8

-3

多云~晴

东北风

3级

198

中度污染

4

334

2018-12-01

7

0

多云

东南风

1级

163

中度污染

4

13

2018-01-14

6

-5

晴~多云

西北风

1-2级

187

中度污染

4

49

2018-02-19

6

-3

多云

南风

1-2级

183

中度污染

4

85

2018-03-27

27

11

南风

1-2级

243

重度污染

5

91

2018-04-02

26

11

多云

北风

1-2级

287

重度污染

5

90

2018-04-01

25

11

晴~多云

南风

1-2级

218

重度污染

5

71

2018-03-13

17

5

晴~多云

南风

1-2级

287

重度污染

5

306

2018-11-03

16

6

多云

南风

1-2级

206

重度污染

5

72

2018-03-14

15

6

多云~阴

东北风

1-2级

293

重度污染

5

61

2018-03-03

13

3

多云~阴

北风

1-2级

214

重度污染

5

316

2018-11-13

13

5

多云

东南风

1-2级

219

重度污染

5

317

2018-11-14

13

5

多云

南风

1-2级

266

重度污染

5

329

2018-11-26

10

0

多云

东南风

1级

245

重度污染

5

335

2018-12-02

9

2

雾~多云

东北风

1级

234

重度污染

5

57

2018-02-27

7

0

东风

1-2级

220

重度污染

5

86

2018-03-28

25

9

多云~晴

东风

1-2级

387

严重污染

6

365 rows × 9 columns

九、Pandas字符串处理

前面我们已经使用了字符串的处理函数:

df[“bWendu”].str.replace(“℃”, “”).astype(‘int32’)

*Pandas的字符串处理:*

使用方法:先获取Series的str属性,然后在属性上调用函数;

只能在字符串列上使用,不能数字列上使用;

Dataframe上没有str属性和处理方法

Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;

*Series.str字符串方法列表参考文档:*

https://pandas.pydata.org/pandas-docs/stable/reference/series.html#string-handling

*本节演示内容:*

获取Series的str属性,然后使用各种字符串处理函数

使用str的startswith、contains等bool类Series可以做条件查询

需要多次str处理的链式操作

使用正则表达式的处理

0. 读取北京2018年天气数据

In [5]:

import pandas as pd

In [6]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

In [8]:

df.head()

Out[8]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [13]:

df.dtypes

Out[13]:

ymd object

bWendu object

yWendu object

tianqi object

fengxiang object

fengli object

aqi int64

aqiInfo object

aqiLevel int64

dtype: object

1. 获取Series的str属性,使用各种字符串处理函数

In [14]:

df["bWendu"].str

Out[14]:

In [15]:

# 字符串替换函数

df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃","").astype("int32")

In [16]:

df.head()

Out[16]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0

-8℃

东北风

1-2级

28

1

4

2018-01-05

3

-6℃

多云~晴

西北风

1-2级

50

1

In [19]:

# 判断是不是数字

df["yWendu"].str.isnumeric()

Out[19]:

0 False

1 False

2 False

3 False

4 False

5 False

6 False

7 False

8 False

9 False

10 False

11 False

12 False

13 False

14 False

15 False

16 False

17 False

18 False

19 False

20 False

21 False

22 False

23 False

24 False

25 False

26 False

27 False

28 False

29 False

...

335 False

336 False

337 False

338 False

339 False

340 False

341 False

342 False

343 False

344 False

345 False

346 False

347 False

348 False

349 False

350 False

351 False

352 False

353 False

354 False

355 False

356 False

357 False

358 False

359 False

360 False

361 False

362 False

363 False

364 False

Name: yWendu, Length: 365, dtype: bool

In [21]:

# 在数列列上调用str会报错

df["aqi"].str.len()

. . .

## 2. 使用str的startswith, contains等得到的bool的Series可以做条件查询

In [23]:

# 查询三月数据

condition = df["ymd"].str.startswith("2018-03")

In [25]:

condition

. . .

In [27]:

df[condition].head()

Out[27]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

59

2018-03-01

8

-3℃

多云

西南风

1-2级

46

1

60

2018-03-02

9

-1℃

晴~多云

北风

1-2级

95

2

61

2018-03-03

13

3℃

多云~阴

北风

1-2级

214

重度污染

5

62

2018-03-04

7

-2℃

阴~多云

东南风

1-2级

144

轻度污染

3

63

2018-03-05

8

-3℃

南风

1-2级

94

2

## 3. 需要多次str处理的链式操作

怎么提取201803这样的数字月份

1. 先将日期2018-03-31替换成20180331的形式

2. 提取月份字符串201803

In [28]:

df["ymd"].str.replace("-","")

. . .

In [29]:

# 每次调用函数,都返回一个新Series

# 不能直接在Series上调用str方法

df["ymd"].str.replace("-","").slice(0, 6)

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

in 1 # 每次调用函数,都返回一个新Series

----> 2 df["ymd"].str.replace("-","").slice(0, 6)

D:\Tools\Anaconda3\lib\site-packages\pandas\core\generic.py in __getattr__(self, name)

5065 if self._info_axis._can_hold_identifiers_and_holds_name(name):

5066 return self[name]

-> 5067 return object.__getattribute__(self, name)

5068

5069 def __setattr__(self, name, value):

AttributeError: 'Series' object has no attribute 'slice'

In [31]:

# replace后得到的是Series,通过再次.str后才能切片

df["ymd"].str.replace("-","").str.slice(0, 6)

Out[31]:

0 201801

1 201801

2 201801

3 201801

4 201801

5 201801

6 201801

7 201801

8 201801

9 201801

10 201801

11 201801

12 201801

13 201801

14 201801

15 201801

16 201801

17 201801

18 201801

19 201801

20 201801

21 201801

22 201801

23 201801

24 201801

25 201801

26 201801

27 201801

28 201801

29 201801

...

335 201812

336 201812

337 201812

338 201812

339 201812

340 201812

341 201812

342 201812

343 201812

344 201812

345 201812

346 201812

347 201812

348 201812

349 201812

350 201812

351 201812

352 201812

353 201812

354 201812

355 201812

356 201812

357 201812

358 201812

359 201812

360 201812

361 201812

362 201812

363 201812

364 201812

Name: ymd, Length: 365, dtype: object

In [32]:

# slice就是切片语法,可以直接调用

df["ymd"].str.replace("-","").str[0:6]

Out[32]:

0 201801

1 201801

2 201801

3 201801

4 201801

5 201801

6 201801

7 201801

8 201801

9 201801

10 201801

11 201801

12 201801

13 201801

14 201801

15 201801

16 201801

17 201801

18 201801

19 201801

20 201801

21 201801

22 201801

23 201801

24 201801

25 201801

26 201801

27 201801

28 201801

29 201801

...

335 201812

336 201812

337 201812

338 201812

339 201812

340 201812

341 201812

342 201812

343 201812

344 201812

345 201812

346 201812

347 201812

348 201812

349 201812

350 201812

351 201812

352 201812

353 201812

354 201812

355 201812

356 201812

357 201812

358 201812

359 201812

360 201812

361 201812

362 201812

363 201812

364 201812

Name: ymd, Length: 365, dtype: object

In [37]:

df.head()

Out[37]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0

-8℃

东北风

1-2级

28

1

4

2018-01-05

3

-6℃

多云~晴

西北风

1-2级

50

1

4. 使用正则表达式的处理

In [42]:

# 添加新列

def get_nianyueri(x):

year, month, day = x["ymd"].split("-")

return f"{year}年{month}月{day}日"

df["中文日期"] = df.apply(get_nianyueri, axis=1)

In [40]:

df["中文日期"]

. . .

问题:怎么将"2018年12月31日"中的年,月,日三个中文字符去除

In [44]:

# 方法1:链式replace

df["中文日期"].str.replace("年","").str.replace("月","").str.replace("日","")

. . .

Series.str默认就开启了正则表达式模式

In [43]:

# 方法2:正则表达式替换

df["中文日期"].str.replace("[年月日]","")

. . .

十、Pandas的axis参数怎么理解?

axis=0或者"index":

如果是单行操作,就指的是某一行

如果是聚合操作,指的是跨行cross rows

axis=1或者"columns":

如果是单列操作,就指的是某一列

如果是聚合操作,指的是跨列cross columns

*按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动*

In [ ]:

In [2]:

import pandas as pd

import numpy as np

In [7]:

df = pd.DataFrame(

np.arange(12).reshape(3,4),

columns = ["A", "B", "C", "D"]

)

In [8]:

df

Out[8]:

A

B

C

D

0

0

1

2

3

1

4

5

6

7

2

8

9

10

11

1. 单列drop, 就是删除某一列

In [9]:

# 代表的就是删除某列

df.drop("A", axis=1)

Out[9]:

B

C

D

0

1

2

3

1

5

6

7

2

9

10

11

In [10]:

# 代表的就是删除某行

df.drop(1, axis=0)

Out[10]:

A

B

C

D

0

0

1

2

3

2

8

9

10

11

3. 按照axis=0/index执行mean聚合操作

反直觉:输出的不是每行的结果,而是每列的结果

In [11]:

df

Out[11]:

A

B

C

D

0

0

1

2

3

1

4

5

6

7

2

8

9

10

11

In [16]:

# axis=0 or axis=index

df.mean(axis=0)

Out[16]:

A 4.0

B 5.0

C 6.0

D 7.0

dtype: float64

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2VilxhcD-1597761927700)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-axis-index.png)]

*按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动*

3. 按照axis=1/columns执行mean聚合操作

反直觉:输出的不是每行的结果,而是每列的结果

In [21]:

df

Out[21]:

A

B

C

D

0

0

1

2

3

1

4

5

6

7

2

8

9

10

11

In [22]:

# axis=1 or axis = columns

df.mean(axis=1)

Out[22]:

0 1.5

1 5.5

2 9.5

dtype: float64

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XahGzry0-1597761927702)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-axis-columns.png)]

*按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动*

5. 再次举例, 加深理解

In [23]:

def get_sum_value(x):

return x["A"] + x["B"] + x["C"] + x["D"]

df["sum_value"] = df.apply(get_sum_value, axis=1)

In [24]:

df

Out[24]:

A

B

C

D

sum_value

0

0

1

2

3

6

1

4

5

6

7

22

2

8

9

10

11

38

*按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动*

In [27]:

df["A"]

Out[27]:

0 0

1 4

2 8

Name: A, dtype: int32

十一、Pandas的索引index的用途

把数据存储于普通的column列也能用于数据查询,那使用index有什么好处?

index的用途总结:

更方便的数据查询;

使用index可以获得性能提升;

自动的数据对齐功能;

更多更强大的数据结构支持;

In [1]:

import pandas as pd

In [2]:

df = pd.read_csv("./pandas-learn-code/datas/ml-latest-small/ratings.csv")

In [3]:

df.head()

Out[3]:

userId

movieId

rating

timestamp

0

1

1

4.0

964982703

1

1

3

4.0

964981247

2

1

6

4.0

964982224

3

1

47

5.0

964983815

4

1

50

5.0

964982931

In [4]:

df.count()

Out[4]:

userId 100836

movieId 100836

rating 100836

timestamp 100836

dtype: int64

1. 使用index查询数据

In [5]:

# drop==False,让索引列还保持在column

# 下列代码实现了将userId设置成了index,同时保留了userId

df.set_index("userId", inplace=True, drop=False)

In [6]:

df.head()

Out[6]:

userId

movieId

rating

timestamp

userId

1

1

1

4.0

964982703

1

1

3

4.0

964981247

1

1

6

4.0

964982224

1

1

47

5.0

964983815

1

1

50

5.0

964982931

In [7]:

df.index

Out[7]:

Int64Index([ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

...

610, 610, 610, 610, 610, 610, 610, 610, 610, 610],

dtype='int64', name='userId', length=100836)

In [8]:

# 使用index的查询方法:在loc[]中直接写入要查询的参数

# 查询userId为500的用户信息

df.loc[500].head(5)

Out[8]:

userId

movieId

rating

timestamp

userId

500

500

1

4.0

1005527755

500

500

11

1.0

1005528017

500

500

39

1.0

1005527926

500

500

101

1.0

1005527980

500

500

104

4.0

1005528065

In [9]:

# 使用column的condition查询方法

df.loc[df["userId"]==500].head()

Out[9]:

userId

movieId

rating

timestamp

userId

500

500

1

4.0

1005527755

500

500

11

1.0

1005528017

500

500

39

1.0

1005527926

500

500

101

1.0

1005527980

500

500

104

4.0

1005528065

2. 使用index会提升查询性能

如果index是唯一的,Pandas会使用哈希表优化,查询性能为O(1);

如果index不是唯一的,但是有序,Pandas会使用二分查找算法,查询性能为O(logN);

如果index是完全随机的,那么每次查询都要扫描全表,查询性能为O(N);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3xFJcngE-1597761927705)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-index-performance.png)]

实验1:完全随机的查询顺序

In [11]:

# 将数据随机打散

from sklearn.utils import shuffle

df_shuffle = shuffle(df)

In [12]:

df_shuffle.head()

Out[12]:

userId

movieId

rating

timestamp

userId

244

244

1377

4.0

975093513

413

413

3753

5.0

1484439911

280

280

6539

3.5

1348435219

18

18

86332

3.5

1455051197

274

274

3160

2.5

1197275106

In [13]:

# 索引是否是递增的

df_shuffle.index.is_monotonic_increasing

Out[13]:

False

In [14]:

# 索引是否是唯一的

df_shuffle.index.is_unique

Out[14]:

False

In [15]:

# 计时,查看id==500的数据性能

# %timeit将名称执行多次,查看性能

%timeit df_shuffle.loc[500]

366 µs ± 7.09 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

实验2:将index排序后的查询

In [17]:

# 将df_shuffle进行排序

df_sorted = df_shuffle.sort_index()

In [18]:

df_sorted.head()

Out[18]:

userId

movieId

rating

timestamp

userId

1

1

3578

5.0

964980668

1

1

2406

4.0

964982310

1

1

110

4.0

964982176

1

1

2090

5.0

964982838

1

1

2096

4.0

964982838

In [19]:

# 索引是否是递增的

df_sorted.index.is_monotonic_increasing

Out[19]:

True

In [20]:

df_sorted.index.is_unique

Out[20]:

False

In [21]:

%timeit df_sorted.loc[500]

178 µs ± 4.55 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

3.使用index能自动对齐数据

包括Series和DateFrame

In [22]:

s1 = pd.Series([1,2,3], index=list("abc"))

In [23]:

s1

Out[23]:

a 1

b 2

c 3

dtype: int64

In [24]:

s2 = pd.Series([2,3,4], index=list("bcd"))

In [25]:

s2

Out[25]:

b 2

c 3

d 4

dtype: int64

In [26]:

s1 + s2

Out[26]:

a NaN

b 4.0

c 6.0

d NaN

dtype: float64

s1,s2都具有b,c索引,而a,d为各自独有,无法对齐,所有相加结果为空

4. 使用index更多更强大的数据结构支持

*很多强大的索引数据结构*

CategoricalIndex,基于分类数据的Index,提升性能;

MultiIndex,多维索引,用于groupby多维聚合后结果等;

DatetimeIndex,时间类型索引,强大的日期和时间的方法支持;

十二、Pandas怎样实现DataFrame的Merge

Pandas的Merge,相当于Sql的Join,将不同的表按key关联到一个表

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, indicator=False, validate=None)

left,right:要merge的dataframe或者有name的Series

how:join类型,‘left’, ‘right’, ‘outer’, ‘inner’

on:join的key,left和right都需要有这个key

left_on:left的df或者series的key

right_on:right的df或者seires的key

left_index,right_index:使用index而不是普通的column做join

suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是(’*x’, ‘*y’)

文档地址:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html

本次讲解提纲:

电影数据集的join实例

理解merge时一对一、一对多、多对多的数量对齐关系

理解left join、right join、inner join、outer join的区别

如果出现非Key的字段重名怎么办

1、电影数据集的join实例

电影评分数据集

是推荐系统研究的很好的数据集

位于本代码目录:./datas/movielens-1m

包含三个文件:

用户对电影的评分数据 ratings.dat

用户本身的信息数据 users.dat

电影本身的数据 movies.dat

可以关联三个表,得到一个完整的大表

数据集官方地址:https://grouplens.org/datasets/movielens/

In [8]:

import pandas as pd

In [12]:

df_ratings = pd.read_csv(

"./pandas-learn-code/datas/movielens-1m/ratings.dat",

sep = "::",

engine = "python",

names = "UserID::MovieID::Rating::Timestamp".split("::")

)

In [13]:

df_ratings.head()

Out[13]:

UserID

MovieID

Rating

Timestamp

0

1

1193

5

978300760

1

1

661

3

978302109

2

1

914

3

978301968

3

1

3408

4

978300275

4

1

2355

5

978824291

In [14]:

df_users = pd.read_csv(

"./pandas-learn-code/datas/movielens-1m/users.dat",

sep = "::",

engine = "python",

names = "UserID::Gender::Age::Occupation::Zip-code".split("::")

)

In [15]:

df_users.head()

Out[15]:

UserID

Gender

Age

Occupation

Zip-code

0

1

F

1

10

48067

1

2

M

56

16

70072

2

3

M

25

15

55117

3

4

M

45

7

02460

4

5

M

25

20

55455

In [17]:

df_movies = pd.read_csv(

"./pandas-learn-code/datas/movielens-1m/movies.dat",

sep = "::",

engine = "python",

names = "MovieID::Title::Genres".split("::")

)

In [18]:

df_movies.head()

Out[18]:

MovieID

Title

Genres

0

1

Toy Story (1995)

Animation|Children’s|Comedy

1

2

Jumanji (1995)

Adventure|Children’s|Fantasy

2

3

Grumpier Old Men (1995)

Comedy|Romance

3

4

Waiting to Exhale (1995)

Comedy|Drama

4

5

Father of the Bride Part II (1995)

Comedy

In [ ]:

df_

In [21]:

# inner:两边都有某个数据时才会保留

df_ratings_users = pd.merge(

df_ratings, df_users, left_on="UserID", right_on="UserID", how="inner"

)

In [22]:

df_ratings_users.head()

Out[22]:

UserID

MovieID

Rating

Timestamp

Gender

Age

Occupation

Zip-code

0

1

1193

5

978300760

F

1

10

48067

1

1

661

3

978302109

F

1

10

48067

2

1

914

3

978301968

F

1

10

48067

3

1

3408

4

978300275

F

1

10

48067

4

1

2355

5

978824291

F

1

10

48067

In [25]:

df_ratings_users_movies = pd.merge(

df_ratings_users, df_movies, left_on="MovieID", right_on="MovieID", how="inner"

)

In [26]:

df_ratings_users_movies.head()

Out[26]:

UserID

MovieID

Rating

Timestamp

Gender

Age

Occupation

Zip-code

Title

Genres

0

1

1193

5

978300760

F

1

10

48067

One Flew Over the Cuckoo’s Nest (1975)

Drama

1

2

1193

5

978298413

M

56

16

70072

One Flew Over the Cuckoo’s Nest (1975)

Drama

2

12

1193

4

978220179

M

25

12

32793

One Flew Over the Cuckoo’s Nest (1975)

Drama

3

15

1193

4

978199279

M

25

7

22903

One Flew Over the Cuckoo’s Nest (1975)

Drama

4

17

1193

5

978158471

M

50

1

95350

One Flew Over the Cuckoo’s Nest (1975)

Drama

2、理解merge时数量的对齐关系

以下关系要正确理解:

one-to-one:一对一关系,关联的key都是唯一的

比如(学号,姓名) merge (学号,年龄)

结果条数为:1*1

one-to-many:一对多关系,左边唯一key,右边不唯一key

比如(学号,姓名) merge (学号,[语文成绩、数学成绩、英语成绩])

结果条数为:1*N

many-to-many:多对多关系,左边右边都不是唯一的

比如(学号,[语文成绩、数学成绩、英语成绩]) merge (学号,[篮球、足球、乒乓球])

结果条数为:M*N

2.1 one-to-one 一对一关系的merge

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gL3djpVk-1597761927707)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-merge-one-to-one.png)]

In [31]:

left = pd.DataFrame({"sno":[11, 12, 13, 14],

"name":["name_a","name_b","name_c","name_d"]

})

left

Out[31]:

sno

name

0

11

name_a

1

12

name_b

2

13

name_c

3

14

name_d

In [28]:

right = pd.DataFrame({"sno":[11, 12, 13, 14],

"age":["21","22","23","24"]

})

right

Out[28]:

sno

age

0

11

21

1

12

22

2

13

23

3

14

24

In [30]:

# 一对一关系,结果中有4条

pd.merge(left, right, on="sno")

Out[30]:

sno

name

age

0

11

name_a

21

1

12

name_b

22

2

13

name_c

23

3

14

name_d

24

2.2 one-to-many 一对多关系的merge

注意:数据会被复制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xToCx8V-1597761927707)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-merge-one-to-many.png)]

In [32]:

left = pd.DataFrame({"sno":[11, 12, 13, 14],

"name":["name_a","name_b","name_c","name_d"]

})

left

Out[32]:

sno

name

0

11

name_a

1

12

name_b

2

13

name_c

3

14

name_d

In [33]:

right = pd.DataFrame({"sno":[11, 11, 11, 12, 12, 13],

"grade":["语文88","数学90","英语75","语文66", "数学55", "英语29"]

})

right

Out[33]:

sno

grade

0

11

语文88

1

11

数学90

2

11

英语75

3

12

语文66

4

12

数学55

5

13

英语29

In [35]:

# 数目以多的一边为准

pd.merge(left, right, on="sno")

Out[35]:

sno

name

grade

0

11

name_a

语文88

1

11

name_a

数学90

2

11

name_a

英语75

3

12

name_b

语文66

4

12

name_b

数学55

5

13

name_c

英语29

2.3 many-to-many 多对多关系的merge

注意:结果数量会出现乘法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9grLyta-1597761927708)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-merge-many-to-many.png)]

In [36]:

left = pd.DataFrame({"sno":[11, 11, 12, 12, 12],

"爱好":["篮球","羽毛球","乒乓球","篮球", "足球"]

})

left

Out[36]:

sno

爱好

0

11

篮球

1

11

羽毛球

2

12

乒乓球

3

12

篮球

4

12

足球

In [37]:

right = pd.DataFrame({"sno":[11, 11, 11, 12, 12, 13],

"grade":["语文88","数学90","英语75","语文66", "数学55", "英语29"]

})

right

Out[37]:

sno

grade

0

11

语文88

1

11

数学90

2

11

英语75

3

12

语文66

4

12

数学55

5

13

英语29

In [38]:

pd.merge(left, right, on="sno")

Out[38]:

sno

爱好

grade

0

11

篮球

语文88

1

11

篮球

数学90

2

11

篮球

英语75

3

11

羽毛球

语文88

4

11

羽毛球

数学90

5

11

羽毛球

英语75

6

12

乒乓球

语文66

7

12

乒乓球

数学55

8

12

篮球

语文66

9

12

篮球

数学55

10

12

足球

语文66

11

12

足球

数学55

3、理解left join、right join、inner join、outer join的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-45nVqPR9-1597761927708)(http://localhost:8888/notebooks/pandas-learn-code/other_files/pandas-leftjoin-rightjoin-outerjoin.png)]

In [52]:

left = pd.DataFrame({"key":["K0", "K1", "K2", "K3"],

"A":["A0","A1","A2","A3"],

"B":["B0","B1","B2","B3"]})

right = pd.DataFrame({"key":["K0", "K1", "K4", "K5"],

"C":["C0","C1","C2","C3"],

"D":["D0","D1","D2","D3"]})

In [53]:

left

Out[53]:

key

A

B

0

K0

A0

B0

1

K1

A1

B1

2

K2

A2

B2

3

K3

A3

B3

In [54]:

right

Out[54]:

key

C

D

0

K0

C0

D0

1

K1

C1

D1

2

K4

C2

D2

3

K5

C3

D3

3.1 inner join,默认

左边和右边的key都有,才会出现在结果里

In [55]:

pd.merge(left, right, how="inner")

Out[55]:

key

A

B

C

D

0

K0

A0

B0

C0

D0

1

K1

A1

B1

C1

D1

3.2 left join

左边的都会出现在结果里,右边的如果无法匹配则为Null

In [56]:

pd.merge(left, right, how="left")

Out[56]:

key

A

B

C

D

0

K0

A0

B0

C0

D0

1

K1

A1

B1

C1

D1

2

K2

A2

B2

NaN

NaN

3

K3

A3

B3

NaN

NaN

3.3 right join

右边的都会出现在结果里,左边的如果无法匹配则为Null

In [57]:

pd.merge(left, right, how="right")

Out[57]:

key

A

B

C

D

0

K0

A0

B0

C0

D0

1

K1

A1

B1

C1

D1

2

K4

NaN

NaN

C2

D2

3

K5

NaN

NaN

C3

D3

3.4 outer join

左边、右边的都会出现在结果里,如果无法匹配则为Null

In [58]:

pd.merge(left, right, how="outer")

Out[58]:

key

A

B

C

D

0

K0

A0

B0

C0

D0

1

K1

A1

B1

C1

D1

2

K2

A2

B2

NaN

NaN

3

K3

A3

B3

NaN

NaN

4

K4

NaN

NaN

C2

D2

5

K5

NaN

NaN

C3

D3

4、如果出现非Key的字段重名怎么办

In [61]:

left = pd.DataFrame({"key":["K0", "K1", "K2", "K3"],

"A":["A0","A1","A2","A3"],

"B":["B0","B1","B2","B3"]})

right = pd.DataFrame({"key":["K0", "K1", "K4", "K5"],

"A":["A10","A11","A12","A13"],

"D":["D0","D1","D4","D5"]})

In [60]:

left

Out[60]:

key

A

B

0

K0

A0

B0

1

K1

A1

B1

2

K2

A2

B2

3

K3

A3

B3

In [62]:

right

Out[62]:

key

A

D

0

K0

A10

D0

1

K1

A11

D1

2

K4

A12

D4

3

K5

A13

D5

In [64]:

pd.merge(left, right, on="key")

Out[64]:

key

A_x

B

A_y

D

0

K0

A0

B0

A10

D0

1

K1

A1

B1

A11

D1

In [65]:

# 两个元素的后缀,如果列有重名,自动添加后缀,默认是('x', 'y')

pd.merge(left, right, on="key", suffixes=('_left', '_right'))

Out[65]:

key

A_left

B

A_right

D

0

K0

A0

B0

A10

D0

1

K1

A1

B1

A11

D1

十三、Pandas实现数据的合并concat

使用场景:

批量合并相同格式的Excel、给DataFrame添加行、给DataFrame添加列

一句话说明concat语法:

使用某种合并方式(inner/outer)

沿着某个轴向(axis=0/1)

把多个Pandas对象(DataFrame/Series)合并成一个。

concat语法:pandas.concat(objs, axis=0, join=‘outer’, ignore_index=False)

objs:一个列表,内容可以是DataFrame或者Series,可以混合

axis:默认是0代表按行合并,如果等于1代表按列合并

join:合并的时候索引的对齐方式,默认是outer join,也可以是inner join

ignore_index:是否忽略掉原来的数据索引

append语法:DataFrame.append(other, ignore_index=False)

append只有按行合并,没有按列合并,相当于concat按行的简写形式

other:单个dataframe、series、dict,或者列表

ignore_index:是否忽略掉原来的数据索引

参考文档:

pandas.concat的api文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html

pandas.concat的教程:https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html

pandas.append的api文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html

In [1]:

import pandas as pd

import warnings

warnings.filterwarnings("ignore")

一、使用Pandas.concat合并数据

In [2]:

df1 = pd.DataFrame({"A":["A0","A1","A2","A3"],

"B":["B0","B1","B2","B3"],

"C":["C0","C1","C2","C3"],

"D":["D0","D1","D2","D3"],

"E":["E0","E1","E2","E3"]

})

df1

Out[2]:

A

B

C

D

E

0

A0

B0

C0

D0

E0

1

A1

B1

C1

D1

E1

2

A2

B2

C2

D2

E2

3

A3

B3

C3

D3

E3

In [3]:

df2 = pd.DataFrame({"A":["A4","A5","A6","A7"],

"B":["B4","B5","B6","B7"],

"C":["C4","C5","C6","C7"],

"D":["D4","D5","D6","D7"],

"F":["F4","F5","F6","F7"]

})

df2

Out[3]:

A

B

C

D

F

0

A4

B4

C4

D4

F4

1

A5

B5

C5

D5

F5

2

A6

B6

C6

D6

F6

3

A7

B7

C7

D7

F7

1. 默认的concat, 参数为axis=0, join=outer, ignore_index=False

In [4]:

pd.concat([df1, df2])

Out[4]:

A

B

C

D

E

F

0

A0

B0

C0

D0

E0

NaN

1

A1

B1

C1

D1

E1

NaN

2

A2

B2

C2

D2

E2

NaN

3

A3

B3

C3

D3

E3

NaN

0

A4

B4

C4

D4

NaN

F4

1

A5

B5

C5

D5

NaN

F5

2

A6

B6

C6

D6

NaN

F6

3

A7

B7

C7

D7

NaN

F7

2. 使用ignore_index=True可以忽略原来的索引

In [5]:

pd.concat([df1, df2], ignore_index=True)

Out[5]:

A

B

C

D

E

F

0

A0

B0

C0

D0

E0

NaN

1

A1

B1

C1

D1

E1

NaN

2

A2

B2

C2

D2

E2

NaN

3

A3

B3

C3

D3

E3

NaN

4

A4

B4

C4

D4

NaN

F4

5

A5

B5

C5

D5

NaN

F5

6

A6

B6

C6

D6

NaN

F6

7

A7

B7

C7

D7

NaN

F7

3. 使用join=inner过滤掉不匹配的列

In [6]:

pd.concat([df1, df2], ignore_index=True, join="inner")

Out[6]:

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

4. 使用axis=1相当于添加新列

In [7]:

df1

Out[7]:

A

B

C

D

E

0

A0

B0

C0

D0

E0

1

A1

B1

C1

D1

E1

2

A2

B2

C2

D2

E2

3

A3

B3

C3

D3

E3

A:添加一列Series

In [9]:

s1 = pd.Series(list(range(4)), name="F")

pd.concat([df1, s1], axis=1)

Out[9]:

A

B

C

D

E

F

0

A0

B0

C0

D0

E0

0

1

A1

B1

C1

D1

E1

1

2

A2

B2

C2

D2

E2

2

3

A3

B3

C3

D3

E3

3

B:添加多列Series

In [10]:

s2 = df1.apply(lambda x:x["A"] + "_GG", axis=1)

In [11]:

s2

Out[11]:

0 A0_GG

1 A1_GG

2 A2_GG

3 A3_GG

dtype: object

In [12]:

s2.name="G"

In [13]:

# 列表可以只有Series

pd.concat([s1,s2], axis=1)

Out[13]:

F

G

0

0

A0_GG

1

1

A1_GG

2

2

A2_GG

3

3

A3_GG

In [14]:

# 列表是可以混合顺序的

pd.concat([s1, df1, s2], axis=1)

Out[14]:

F

A

B

C

D

E

G

0

0

A0

B0

C0

D0

E0

A0_GG

1

1

A1

B1

C1

D1

E1

A1_GG

2

2

A2

B2

C2

D2

E2

A2_GG

3

3

A3

B3

C3

D3

E3

A3_GG

二、使用DateFrame.append按行合并数据

In [15]:

df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list("AB"))

df1

Out[15]:

A

B

0

1

2

1

3

4

In [16]:

df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list("AB"))

df2

Out[16]:

A

B

0

5

6

1

7

8

1. 给一个DataFrame添加另一个DataFrame

In [18]:

df1.append(df2)

Out[18]:

A

B

0

1

2

1

3

4

0

5

6

1

7

8

2. 忽略原来的索引,另ignore_index=True

In [19]:

df1.append(df2, ignore_index=True)

Out[19]:

A

B

0

1

2

1

3

4

2

5

6

3

7

8

3.可以一行一行的给DataFrame添加数据

In [21]:

# 创建一个空的df

df = pd.DataFrame(columns=["A"])

df

Out[21]:

A

A:低性能版

In [22]:

for i in range(5):

# 注意:这里每次都在复制

df = df.append({"a":i}, ignore_index=True)

df

Out[22]:

A

a

0

NaN

0.0

1

NaN

1.0

2

NaN

2.0

3

NaN

3.0

4

NaN

4.0

B:性能好的版本

In [23]:

# 第一个

pd.concat(

[pd.DataFrame([i], columns=["A"]) for i in range(5)],

ignore_index=True

)

Out[23]:

A

0

0

1

1

2

2

3

3

4

4

In [27]:

ss = pd.DataFrame( i for i in range(5))

ss

Out[27]:

0

0

0

1

1

2

2

3

3

4

4

十四、Pandas批量拆分Excel与合并Excel

实例演示:

将一个大Excel等份拆成多个Excel

将多个小Excel合并成一个大Excel并标记来源

In [51]:

work_dir = "D:/WinterIsComing/python/New_Wave/pandas_basic/15.excel_split_merge"

# 用来放置拆分后的小文件

splits_dir = f"{work_dir}/splits"

In [52]:

import os

if not os.path.exists(splits_dir):

os.mkdir(splits_dir)

0. 读取源Excel到Pandas

In [54]:

import pandas as pd

In [53]:

df_source = pd.read_excel(r"D:/WinterIsComing/python/New_Wave/pandas_basic/15.excel_split_merge/crazyant_blog_articles_source.xlsx")

In [55]:

df_source.head()

Out[55]:

id

title

tags

0

2585

Tensorflow怎样接收变长列表特征

python,tensorflow,特征工程

1

2583

Pandas实现数据的合并concat

pandas,python,数据分析

2

2574

Pandas的Index索引有什么用途?

pandas,python,数据分析

3

2564

机器学习常用数据集大全

python,机器学习

4

2561

一个数据科学家的修炼路径

数据分析

In [56]:

df_source.index

Out[56]:

RangeIndex(start=0, stop=258, step=1)

In [57]:

# 258行,3列

df_source.shape

Out[57]:

(258, 3)

In [58]:

# 通过df_source.shape得到元组(258, 3)

# 通过df_source.shape[0]得到行数

total_row_count = df_source.shape[0]

total_row_count

Out[58]:

258

1、将一个大excel等份拆成多个Excel

使用df.iloc方法,将一个大的DataFrame,拆分成多个小DataFrame

将使用DataFrame.to_excel保存每个小Excel

1. 1 计算拆分后的每个excel的行数

In [59]:

# 将一个大Excel,拆分给这几个人

user_names = ["A", "B", "C", "D", "E", "F"]

In [60]:

# 每个人的任务数目

split_size = total_row_count // len(user_names)

# 此处的作用在于如果有余数,可以将未分配的行数,分配给前面几人,保证所有的行都分配出去

if total_row_count % len(user_names) != 0:

split_size += 1

split_size

Out[60]:

43

1.2 拆分成多个DataFrame

In [64]:

df_subs = []

for idx, user_name in enumerate(user_names):

# iloc的开始索引

begin = idx*split_size

# iloc的结束索引

end = begin + split_size

# 实现df按照iloc拆分

df_sub = df_source.iloc[begin:end]

# 将每个子df存入列表

df_subs.append((idx, user_name, df_sub))

df_subs[0][2].head(5)

Out[64]:

id

title

tags

0

2585

Tensorflow怎样接收变长列表特征

python,tensorflow,特征工程

1

2583

Pandas实现数据的合并concat

pandas,python,数据分析

2

2574

Pandas的Index索引有什么用途?

pandas,python,数据分析

3

2564

机器学习常用数据集大全

python,机器学习

4

2561

一个数据科学家的修炼路径

数据分析

In [65]:

df_subs[1][2].head(5)

Out[65]:

id

title

tags

43

2120

Zookeeper并不保证读取的是最新数据

zookeeper

44

2089

Mybatis源码解读-初始化过程详解

mybatis

45

2076

怎样借助Python爬虫给宝宝起个好名字

python,爬虫

46

2022

Mybatis源码解读-设计模式总结

mybatis,设计模式

47

2012

打工者心态、主人公意识、个人公司品牌

程序人生

1.3 将每个DataFrame存入excel

In [63]:

for idx, user_name, df_sub in df_subs:

file_name = f"{splits_dir}/spike_pandas_{idx}_{user_name}.xlsx"

df_sub.to_excel(file_name, index=False)

2、合并多个小Excel到一个大Excel

遍历文件夹,得到要合并的Excel文件列表

分别读取到DataFrame,给每个df添加一列用于标记来源

使用pd.concat进行df批量合并

将合并后的DataFrame输出到excel

2.1 遍历文件夹,得到要合并的Excel名称列表

In [66]:

import os

excel_names = []

# listdir返回指定目录下的所有文件和文件夹名称

for excel_name in os.listdir(splits_dir):

excel_names.append(excel_name)

excel_names

Out[66]:

['spike_pandas_0_A.xlsx',

'spike_pandas_1_B.xlsx',

'spike_pandas_2_C.xlsx',

'spike_pandas_3_D.xlsx',

'spike_pandas_4_E.xlsx',

'spike_pandas_5_F.xlsx']

2.2 分别读取到DataFrame

In [70]:

df_list = []

for excel_name in excel_names:

# 读取每个excel到df

excel_path = f"{splits_dir}/{excel_name}"

df_split = pd.read_excel(excel_path)

# 得到username,通过字符串切片

username = excel_name.replace("spike_pandas_", "").replace(".xlsx", "")[2:]

# print(username)

# 给df_split添加一列username

df_split["username"] = username

df_list.append(df_split)

2.3 使用pd.concat进行合并

In [71]:

df_merged = pd.concat(df_list)

In [72]:

df_merged.shape

Out[72]:

(258, 4)

In [74]:

df_merged.head()

Out[74]:

id

title

tags

username

0

2585

Tensorflow怎样接收变长列表特征

python,tensorflow,特征工程

A

1

2583

Pandas实现数据的合并concat

pandas,python,数据分析

A

2

2574

Pandas的Index索引有什么用途?

pandas,python,数据分析

A

3

2564

机器学习常用数据集大全

python,机器学习

A

4

2561

一个数据科学家的修炼路径

数据分析

A

In [76]:

df_merged["username"].value_counts()

Out[76]:

B 43

F 43

D 43

E 43

A 43

C 43

Name: username, dtype: int64

2.4 将合并后的DataFrame输出到Excel

In [77]:

df_merged.to_excel(f"{work_dir}/spike_pandas_merged.xlsx", index=False)

十五、Pandas怎样实现groupby分组统计

类似SQL:

select city,max(temperature) from city_weather group by city;

groupby:先对数据分组,然后在每个分组上应用聚合函数、转换函数

本次演示:

一、分组使用聚合函数做数据统计

二、遍历groupby的结果理解执行流程

三、实例分组探索天气数据

In [1]:

import pandas as pd

import numpy as np

# 加上这一句,能在jupyter notebook展示matplo图表

%matplotlib inline

In [4]:

df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],

'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],

'C': np.random.randn(8),

'D': np.random.randn(8)})

df

Out[4]:

A

B

C

D

0

foo

one

-0.102369

0.042233

1

bar

one

1.552845

-0.623522

2

foo

two

0.770077

0.205682

3

bar

three

-1.989910

-0.617111

4

foo

two

1.230455

-0.422428

5

bar

two

-0.697516

-0.964579

6

foo

one

-0.939646

-0.414017

7

foo

three

0.763570

0.451086

1、分组使用聚合函数做数据统计

1.1 单个列groupby,查询所有数据列的统计

In [5]:

df.groupby("A").sum()

Out[5]:

C

D

A

bar

-1.134582

-2.205211

foo

1.722086

-0.137444

我们看到:

groupby中的’A’变成了数据的索引列

因为要统计sum,但B列不是数字,所以被自动忽略掉

1.2 多个列groupby,查询所有数据列的统计

In [6]:

# 以A,B为索引,查询C,D的平均值

df.groupby(["A", "B"]).mean()

Out[6]:

C

D

A

B

bar

one

1.552845

-0.623522

three

-1.989910

-0.617111

two

-0.697516

-0.964579

foo

one

-0.521008

-0.185892

three

0.763570

0.451086

two

1.000266

-0.108373

In [7]:

# 取消A.B作为索引

df.groupby(["A", "B"], as_index=False).mean()

Out[7]:

A

B

C

D

0

bar

one

1.552845

-0.623522

1

bar

three

-1.989910

-0.617111

2

bar

two

-0.697516

-0.964579

3

foo

one

-0.521008

-0.185892

4

foo

three

0.763570

0.451086

5

foo

two

1.000266

-0.108373

1.3 同时查看多种数据统计

In [8]:

df.groupby("A").agg([np.sum, np.mean, np.std])

Out[8]:

C

D

sum

mean

std

sum

mean

std

A

bar

-1.134582

-0.378194

1.792834

-2.205211

-0.735070

0.198786

foo

1.722086

0.344417

0.864635

-0.137444

-0.027489

0.385242

我们看到:列变成了多级索引

1.4 查看单列的结果数据统计

In [10]:

# 预过滤,性能更好

df.groupby("A")["C"].agg([np.sum, np.mean, np.std])

Out[10]:

sum

mean

std

A

bar

-1.134582

-0.378194

1.792834

foo

1.722086

0.344417

0.864635

In [9]:

# 方法2

df.groupby("A").agg([np.sum, np.mean, np.std])["C"]

Out[9]:

sum

mean

std

A

bar

-1.134582

-0.378194

1.792834

foo

1.722086

0.344417

0.864635

1.5 不同列使用不同的聚合函数

In [12]:

# 以字典的形式对不同的列使用不同的聚合函数

df.groupby("A").agg({"C":np.sum, "D":np.mean})

Out[12]:

C

D

A

bar

-1.134582

-0.735070

foo

1.722086

-0.027489

2、遍历groupby的结果理解执行流程

for循环可以直接遍历每个group

2.1 遍历单个列聚合的分组

In [13]:

g = df.groupby("A")

g

Out[13]:

In [16]:

df

Out[16]:

A

B

C

D

0

foo

one

-0.102369

0.042233

1

bar

one

1.552845

-0.623522

2

foo

two

0.770077

0.205682

3

bar

three

-1.989910

-0.617111

4

foo

two

1.230455

-0.422428

5

bar

two

-0.697516

-0.964579

6

foo

one

-0.939646

-0.414017

7

foo

three

0.763570

0.451086

In [15]:

for name,group in g:

print(name)

print(group)

print()

# name:bar and foo

# group:是两个DataFrame

bar

A B C D

1 bar one 1.552845 -0.623522

3 bar three -1.989910 -0.617111

5 bar two -0.697516 -0.964579

foo

A B C D

0 foo one -0.102369 0.042233

2 foo two 0.770077 0.205682

4 foo two 1.230455 -0.422428

6 foo one -0.939646 -0.414017

7 foo three 0.763570 0.451086

*可以获取单个分组的数据*

In [17]:

g.get_group("bar")

Out[17]:

A

B

C

D

1

bar

one

1.552845

-0.623522

3

bar

three

-1.989910

-0.617111

5

bar

two

-0.697516

-0.964579

2.2 遍历多个列聚合的分组

In [20]:

g = df.groupby(["A", "B"])

g

Out[20]:

In [21]:

for name, group in g:

print(name)

print(group)

print()

# 分组的名称变成了元组

('bar', 'one')

A B C D

1 bar one 1.552845 -0.623522

('bar', 'three')

A B C D

3 bar three -1.98991 -0.617111

('bar', 'two')

A B C D

5 bar two -0.697516 -0.964579

('foo', 'one')

A B C D

0 foo one -0.102369 0.042233

6 foo one -0.939646 -0.414017

('foo', 'three')

A B C D

7 foo three 0.76357 0.451086

('foo', 'two')

A B C D

2 foo two 0.770077 0.205682

4 foo two 1.230455 -0.422428

可以看到,name是一个2个元素的tuple,代表不同的列

In [22]:

g.get_group(("foo", "one"))

Out[22]:

A

B

C

D

0

foo

one

-0.102369

0.042233

6

foo

one

-0.939646

-0.414017

*可以直接查询group后的某几列,生成Series或者子DataFrame*

In [24]:

# 获得一个SeriesGroupBy

g["C"]

Out[24]:

In [25]:

for name, group in g["C"]:

print(name)

print(group)

print(type(group))

print()

('bar', 'one')

1 1.552845

Name: C, dtype: float64

('bar', 'three')

3 -1.98991

Name: C, dtype: float64

('bar', 'two')

5 -0.697516

Name: C, dtype: float64

('foo', 'one')

0 -0.102369

6 -0.939646

Name: C, dtype: float64

('foo', 'three')

7 0.76357

Name: C, dtype: float64

('foo', 'two')

2 0.770077

4 1.230455

Name: C, dtype: float64

其实所有的聚合统计,都是在dataframe和series上进行的;

3、实例分组探索天气数据

In [27]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

df.head()

Out[27]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [28]:

# 替换掉温度的后缀℃

df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃","").astype("int32")

df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃","").astype("int32")

df.head()

Out[28]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

3

2018-01-04

0

-8

东北风

1-2级

28

1

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

In [29]:

# 新增一列为月份

df["month"] = df["ymd"].str[:7]

df.head()

Out[29]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

month

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

2018-01

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2018-01

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

2018-01

3

2018-01-04

0

-8

东北风

1-2级

28

1

2018-01

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

2018-01

3.1 查看每个月的最高温度

In [31]:

data = df.groupby("month")["bWendu"].max()

data

Out[31]:

month

2018-01 7

2018-02 12

2018-03 27

2018-04 30

2018-05 35

2018-06 38

2018-07 37

2018-08 36

2018-09 31

2018-10 25

2018-11 18

2018-12 10

Name: bWendu, dtype: int32

In [32]:

type(data)

Out[32]:

pandas.core.series.Series

In [34]:

data.plot()

Out[34]:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ssxk6ssS-1597761927710)(data:image/png;base64,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)]

3.2 查看每个月的最高温度、最低温度、平均空气质量指数

In [35]:

df.head()

Out[35]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

month

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

2018-01

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2018-01

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

2018-01

3

2018-01-04

0

-8

东北风

1-2级

28

1

2018-01

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

2018-01

In [38]:

group_data = df.groupby("month").agg({"bWendu":np.max, "yWendu":np.min, "aqi":np.mean})

group_data

Out[38]:

bWendu

yWendu

aqi

month

2018-01

7

-12

60.677419

2018-02

12

-10

78.857143

2018-03

27

-4

130.322581

2018-04

30

1

102.866667

2018-05

35

10

99.064516

2018-06

38

17

82.300000

2018-07

37

22

72.677419

2018-08

36

20

59.516129

2018-09

31

11

50.433333

2018-10

25

1

67.096774

2018-11

18

-4

105.100000

2018-12

10

-12

77.354839

In [39]:

group_data.plot()

Out[39]:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lwmi8hwM-1597761927711)(data:image/png;base64,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)]

十六、Pandas的分层索引MultiIndex

为什么要学习分层索引MultiIndex?

分层索引:在一个轴向上拥有多个索引层级,可以表达更高维度数据的形式;

可以更方便的进行数据筛选,如果有序则性能更好;

groupby等操作的结果,如果是多KEY,结果是分层索引,需要会使用

一般不需要自己创建分层索引(MultiIndex有构造函数但一般不用)

演示数据:百度、阿里巴巴、爱奇艺、京东四家公司的10天股票数据

数据来自:英为财经

https://cn.investing.com/

本次演示提纲:

一、Series的分层索引MultiIndex

二、Series有多层索引怎样筛选数据?

三、DataFrame的多层索引MultiIndex

四、DataFrame有多层索引怎样筛选数据?

In [7]:

import pandas as pd

%matplotlib inline

In [8]:

fpath = "./pandas-learn-code/datas/stocks/互联网公司股票.xlsx"

stocks = pd.read_excel(fpath)

In [10]:

stocks.shape

Out[10]:

(12, 8)

In [5]:

stocks.head()

Out[5]:

日期

公司

收盘

开盘

交易量

涨跌幅

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

In [12]:

stocks["公司"].unique()

Out[12]:

array(['BIDU', 'BABA', 'IQ', 'JD'], dtype=object)

In [14]:

# 按公司分组查询收盘价的平均值

stocks.groupby("公司")["收盘"].mean()

Out[14]:

公司

BABA 166.80

BIDU 102.98

IQ 15.90

JD 28.35

Name: 收盘, dtype: float64

1、Series的分层索引MultiIndex

In [16]:

# ser是Series,有两列索引

ser = stocks.groupby(["公司", "日期"])["收盘"].mean()

ser

Out[16]:

公司 日期

BABA 2019-10-01 165.15

2019-10-02 165.77

2019-10-03 169.48

BIDU 2019-10-01 102.00

2019-10-02 102.62

2019-10-03 104.32

IQ 2019-10-01 15.92

2019-10-02 15.72

2019-10-03 16.06

JD 2019-10-01 28.19

2019-10-02 28.06

2019-10-03 28.80

Name: 收盘, dtype: float64

多维索引中,空白的意思是:使用上面的值

In [20]:

ser.index

Out[20]:

MultiIndex(levels=[['BABA', 'BIDU', 'IQ', 'JD'], ['2019-10-01', '2019-10-02', '2019-10-03']],

codes=[[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]],

names=['公司', '日期'])

In [21]:

# unstack把二级索引变成列

# 公司继续作为索引,但日期变为columns

ser.unstack()

Out[21]:

日期

2019-10-01

2019-10-02

2019-10-03

公司

BABA

165.15

165.77

169.48

BIDU

102.00

102.62

104.32

IQ

15.92

15.72

16.06

JD

28.19

28.06

28.80

In [22]:

ser

Out[22]:

公司 日期

BABA 2019-10-01 165.15

2019-10-02 165.77

2019-10-03 169.48

BIDU 2019-10-01 102.00

2019-10-02 102.62

2019-10-03 104.32

IQ 2019-10-01 15.92

2019-10-02 15.72

2019-10-03 16.06

JD 2019-10-01 28.19

2019-10-02 28.06

2019-10-03 28.80

Name: 收盘, dtype: float64

In [24]:

# 将两层索引(公司,日期)都变成了columns

ser.reset_index()

Out[24]:

公司

日期

收盘

0

BABA

2019-10-01

165.15

1

BABA

2019-10-02

165.77

2

BABA

2019-10-03

169.48

3

BIDU

2019-10-01

102.00

4

BIDU

2019-10-02

102.62

5

BIDU

2019-10-03

104.32

6

IQ

2019-10-01

15.92

7

IQ

2019-10-02

15.72

8

IQ

2019-10-03

16.06

9

JD

2019-10-01

28.19

10

JD

2019-10-02

28.06

11

JD

2019-10-03

28.80

2、Series有多层索引MultiIndex怎么筛选数据?

In [25]:

ser

Out[25]:

公司 日期

BABA 2019-10-01 165.15

2019-10-02 165.77

2019-10-03 169.48

BIDU 2019-10-01 102.00

2019-10-02 102.62

2019-10-03 104.32

IQ 2019-10-01 15.92

2019-10-02 15.72

2019-10-03 16.06

JD 2019-10-01 28.19

2019-10-02 28.06

2019-10-03 28.80

Name: 收盘, dtype: float64

In [27]:

ser.loc["BIDU"]

Out[27]:

日期

2019-10-01 102.00

2019-10-02 102.62

2019-10-03 104.32

Name: 收盘, dtype: float64

In [ ]:

# 多层索引,可以用元组的形式筛选

In [28]:

ser.loc[("BIDU","2019-10-02")]

Out[28]:

102.62

In [29]:

ser.loc[:, "2019-10-02"]

Out[29]:

公司

BABA 165.77

BIDU 102.62

IQ 15.72

JD 28.06

Name: 收盘, dtype: float64

3、DataFrame的多层索引MultiIndex

In [30]:

stocks.head()

Out[30]:

日期

公司

收盘

开盘

交易量

涨跌幅

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

In [40]:

stocks.set_index(["公司", "日期"], inplace=True)

. . .

In [41]:

stocks.head()

Out[41]:

收盘

开盘

交易量

涨跌幅

公司

日期

BIDU

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

2019-10-01

102.00

102.80

103.26

101.00

1.78

-0.01

BABA

2019-10-03

169.48

166.65

170.18

165.00

10.39

0.02

2019-10-02

165.77

162.82

166.88

161.90

11.60

0.00

In [42]:

stocks.index

Out[42]:

MultiIndex(levels=[['BABA', 'BIDU', 'IQ', 'JD'], ['2019-10-01', '2019-10-02', '2019-10-03']],

codes=[[1, 1, 1, 0, 0, 0, 2, 2, 2, 3, 3, 3], [2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0]],

names=['公司', '日期'])

In [43]:

stocks.sort_index(inplace=True)

In [44]:

stocks

Out[44]:

收盘

开盘

交易量

涨跌幅

公司

日期

BABA

2019-10-01

165.15

168.01

168.23

163.64

14.19

-0.01

2019-10-02

165.77

162.82

166.88

161.90

11.60

0.00

2019-10-03

169.48

166.65

170.18

165.00

10.39

0.02

BIDU

2019-10-01

102.00

102.80

103.26

101.00

1.78

-0.01

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

IQ

2019-10-01

15.92

16.14

16.22

15.50

11.65

-0.01

2019-10-02

15.72

15.85

15.87

15.12

8.10

-0.01

2019-10-03

16.06

15.71

16.38

15.32

10.08

0.02

JD

2019-10-01

28.19

28.22

28.57

27.97

10.64

0.00

2019-10-02

28.06

28.00

28.22

27.53

9.53

0.00

2019-10-03

28.80

28.11

28.97

27.82

8.77

0.03

4、DataFrame有多层索引MultiIndex怎样筛选?

【*重要知识*】在选择数据时:

元组(key1,key2)代表筛选多层索引,其中key1是索引第一级,key2是第二级,比如key1=JD, key2=2019-10-02

列表[key1,key2]代表同一层的多个KEY,其中key1和key2是并列的同级索引,比如key1=JD, key2=BIDU

In [45]:

stocks.loc["BIDU"]

Out[45]:

收盘

开盘

交易量

涨跌幅

日期

2019-10-01

102.00

102.80

103.26

101.00

1.78

-0.01

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

In [46]:

# BIDU, 2019-10-02当天所有的相关数据

stocks.loc[("BIDU", "2019-10-02"), :]

Out[46]:

收盘 102.62

开盘 100.85

高 103.24

低 99.50

交易量 2.69

涨跌幅 0.01

Name: (BIDU, 2019-10-02), dtype: float64

In [48]:

# 逻辑关系为BIDU的2019-10-02的开盘数据

stocks.loc[("BIDU", "2019-10-02"), "开盘"]

Out[48]:

100.85

In [50]:

# 并列筛选,BIDU和JD为同级关系

stocks.loc[["BIDU", "JD"], :]

Out[50]:

收盘

开盘

交易量

涨跌幅

公司

日期

BIDU

2019-10-01

102.00

102.80

103.26

101.00

1.78

-0.01

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

JD

2019-10-01

28.19

28.22

28.57

27.97

10.64

0.00

2019-10-02

28.06

28.00

28.22

27.53

9.53

0.00

2019-10-03

28.80

28.11

28.97

27.82

8.77

0.03

In [51]:

stocks.loc[(["BIDU", "JD"], "2019-10-03"), :]

Out[51]:

收盘

开盘

交易量

涨跌幅

公司

日期

BIDU

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

JD

2019-10-03

28.80

28.11

28.97

27.82

8.77

0.03

In [52]:

stocks.loc[(["BIDU", "JD"], "2019-10-03"), "收盘"]

Out[52]:

公司 日期

BIDU 2019-10-03 104.32

JD 2019-10-03 28.80

Name: 收盘, dtype: float64

In [54]:

stocks.loc[("BIDU",["2019-10-02", "2019-10-03"]), "收盘"]

Out[54]:

公司 日期

BIDU 2019-10-02 102.62

2019-10-03 104.32

Name: 收盘, dtype: float64

In [55]:

# slice(None)代表筛选这一索引的所有内容

stocks.loc[(slice(None), ["2019-10-02", "2019-10-03"]),:]

Out[55]:

收盘

开盘

交易量

涨跌幅

公司

日期

BABA

2019-10-02

165.77

162.82

166.88

161.90

11.60

0.00

2019-10-03

169.48

166.65

170.18

165.00

10.39

0.02

BIDU

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

IQ

2019-10-02

15.72

15.85

15.87

15.12

8.10

-0.01

2019-10-03

16.06

15.71

16.38

15.32

10.08

0.02

JD

2019-10-02

28.06

28.00

28.22

27.53

9.53

0.00

2019-10-03

28.80

28.11

28.97

27.82

8.77

0.03

In [56]:

# 将多层索引恢复成列

stocks.reset_index()

Out[56]:

公司

日期

收盘

开盘

交易量

涨跌幅

0

BABA

2019-10-01

165.15

168.01

168.23

163.64

14.19

-0.01

1

BABA

2019-10-02

165.77

162.82

166.88

161.90

11.60

0.00

2

BABA

2019-10-03

169.48

166.65

170.18

165.00

10.39

0.02

3

BIDU

2019-10-01

102.00

102.80

103.26

101.00

1.78

-0.01

4

BIDU

2019-10-02

102.62

100.85

103.24

99.50

2.69

0.01

5

BIDU

2019-10-03

104.32

102.35

104.73

101.15

2.24

0.02

6

IQ

2019-10-01

15.92

16.14

16.22

15.50

11.65

-0.01

7

IQ

2019-10-02

15.72

15.85

15.87

15.12

8.10

-0.01

8

IQ

2019-10-03

16.06

15.71

16.38

15.32

10.08

0.02

9

JD

2019-10-01

28.19

28.22

28.57

27.97

10.64

0.00

10

JD

2019-10-02

28.06

28.00

28.22

27.53

9.53

0.00

11

JD

2019-10-03

28.80

28.11

28.97

27.82

8.77

0.03

十七、Pandas的数据转换函数map、apply、applymap

数据转换函数对比:map、apply、applymap:

map:只用于Series,实现每个值->值的映射;

apply:用于Series实现每个值的处理,用于Dataframe实现某个轴的Series的处理;

applymap:只能用于DataFrame,用于处理该DataFrame的每个元素;

1. map用于Series值的转换

实例:将股票代码英文转换成中文名字

Series.map(dict) or Series.map(function)均可

In [2]:

import pandas as pd

stocks = pd.read_excel(r"D:\WinterIsComing\python\New_Wave\pandas_basic\pandas-learn-code\datas\stocks\互联网公司股票.xlsx")

In [3]:

stocks.head()

Out[3]:

日期

公司

收盘

开盘

交易量

涨跌幅

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

In [7]:

stocks["公司"].unique()

Out[7]:

array(['BIDU', 'BABA', 'IQ', 'JD'], dtype=object)

In [8]:

# 公司股票代码到中文的映射,注意这里是小写

dict_company_names={

"bidu":"百度",

"baba":"阿里巴巴",

"iq":"爱奇艺",

"jd":"京东"

}

方法1:Series.map(dict)

In [9]:

stocks["中文公司1"]=stocks["公司"].str.lower().map(dict_company_names)

In [10]:

stocks

Out[10]:

日期

公司

收盘

开盘

交易量

涨跌幅

中文公司1

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

百度

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

百度

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

百度

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

阿里巴巴

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

阿里巴巴

5

2019-10-01

BABA

165.15

168.01

168.23

163.64

14.19

-0.01

阿里巴巴

6

2019-10-03

IQ

16.06

15.71

16.38

15.32

10.08

0.02

爱奇艺

7

2019-10-02

IQ

15.72

15.85

15.87

15.12

8.10

-0.01

爱奇艺

8

2019-10-01

IQ

15.92

16.14

16.22

15.50

11.65

-0.01

爱奇艺

9

2019-10-03

JD

28.80

28.11

28.97

27.82

8.77

0.03

京东

10

2019-10-02

JD

28.06

28.00

28.22

27.53

9.53

0.00

京东

11

2019-10-01

JD

28.19

28.22

28.57

27.97

10.64

0.00

京东

方法2:Series.map(function)

function的参数是Series的每个元素的值

In [13]:

# lambda x中的x代表Series的每个值(即stocks["公司"]中的每个值)

stocks["公司中文2"]=stocks["公司"].map(lambda x : dict_company_names[x.lower()])

In [12]:

stocks.head()

Out[12]:

日期

公司

收盘

开盘

交易量

涨跌幅

中文公司1

公司中文2

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

百度

百度

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

百度

百度

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

百度

百度

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

阿里巴巴

阿里巴巴

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

阿里巴巴

阿里巴巴

2. apply用于Series和DataFrame的转换

Series.apply(function), 函数的参数是每个值

DataFrame.apply(function), 函数的参数是Series

Series.apply(function)

function的参数是Series的每个值

In [14]:

stocks["中文公司3"]=stocks["公司"].apply(lambda x : dict_company_names[x.lower()])

In [16]:

stocks.head()

Out[16]:

日期

公司

收盘

开盘

交易量

涨跌幅

中文公司1

公司中文2

中文公司3

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

百度

百度

百度

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

百度

百度

百度

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

百度

百度

百度

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

阿里巴巴

阿里巴巴

阿里巴巴

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

阿里巴巴

阿里巴巴

阿里巴巴

DataFrame.apply(function)

function的参数是对应轴的Series

In [18]:

stocks["中文公司4"]=stocks.apply(lambda x: dict_company_names[x["公司"].lower()], axis=1)

In [19]:

stocks["公司"]

Out[19]:

0 BIDU

1 BIDU

2 BIDU

3 BABA

4 BABA

5 BABA

6 IQ

7 IQ

8 IQ

9 JD

10 JD

11 JD

Name: 公司, dtype: object

注意这个代码:

1、apply是在stocks这个DataFrame上调用;

2、lambda x的x是一个Series,因为指定了axis=1所以Seires的key是列名,可以用x[‘公司’]获取

In [20]:

stocks.head()

Out[20]:

日期

公司

收盘

开盘

交易量

涨跌幅

中文公司1

公司中文2

中文公司3

中文公司4

0

2019-10-03

BIDU

104.32

102.35

104.73

101.15

2.24

0.02

百度

百度

百度

百度

1

2019-10-02

BIDU

102.62

100.85

103.24

99.50

2.69

0.01

百度

百度

百度

百度

2

2019-10-01

BIDU

102.00

102.80

103.26

101.00

1.78

-0.01

百度

百度

百度

百度

3

2019-10-03

BABA

169.48

166.65

170.18

165.00

10.39

0.02

阿里巴巴

阿里巴巴

阿里巴巴

阿里巴巴

4

2019-10-02

BABA

165.77

162.82

166.88

161.90

11.60

0.00

阿里巴巴

阿里巴巴

阿里巴巴

阿里巴巴

3. applymap用于DataFrame所有值的转换

In [21]:

sub_df = stocks[["收盘","开盘","高","低","交易量"]]

In [22]:

sub_df

Out[22]:

收盘

开盘

交易量

0

104.32

102.35

104.73

101.15

2.24

1

102.62

100.85

103.24

99.50

2.69

2

102.00

102.80

103.26

101.00

1.78

3

169.48

166.65

170.18

165.00

10.39

4

165.77

162.82

166.88

161.90

11.60

5

165.15

168.01

168.23

163.64

14.19

6

16.06

15.71

16.38

15.32

10.08

7

15.72

15.85

15.87

15.12

8.10

8

15.92

16.14

16.22

15.50

11.65

9

28.80

28.11

28.97

27.82

8.77

10

28.06

28.00

28.22

27.53

9.53

11

28.19

28.22

28.57

27.97

10.64

In [23]:

# 将这些数字取整数,应用于所有元素(即表格中所有的值)

sub_df.applymap(lambda x: int(x))

Out[23]:

收盘

开盘

交易量

0

104

102

104

101

2

1

102

100

103

99

2

2

102

102

103

101

1

3

169

166

170

165

10

4

165

162

166

161

11

5

165

168

168

163

14

6

16

15

16

15

10

7

15

15

15

15

8

8

15

16

16

15

11

9

28

28

28

27

8

10

28

28

28

27

9

11

28

28

28

27

10

In [25]:

# 直接修改原df的这几列

stocks.loc[:, ["收盘","开盘","高","低","交易量"]] = sub_df.applymap(lambda x: int(x))

In [26]:

stocks.head()

Out[26]:

日期

公司

收盘

开盘

交易量

涨跌幅

中文公司1

公司中文2

中文公司3

中文公司4

0

2019-10-03

BIDU

104

102

104

101

2

0.02

百度

百度

百度

百度

1

2019-10-02

BIDU

102

100

103

99

2

0.01

百度

百度

百度

百度

2

2019-10-01

BIDU

102

102

103

101

1

-0.01

百度

百度

百度

百度

3

2019-10-03

BABA

169

166

170

165

10

0.02

阿里巴巴

阿里巴巴

阿里巴巴

阿里巴巴

4

2019-10-02

BABA

165

162

166

161

11

0.00

阿里巴巴

阿里巴巴

阿里巴巴

阿里巴巴

十八、Pandas怎样对每个分组应用apply函数?

知识:Pandas的GroupBy遵从split、apply、combine模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUn7YSnp-1597761927713)(http://localhost:8891/notebooks/pandas-learn-code/other_files/pandas-split-apply-combine.png)]

这里的split指的是pandas的groupby,我们自己实现apply函数,apply返回的结果由pandas进行combine得到结果

GroupBy.apply(function)

function的第一个参数是dataframe

function的返回结果,可是dataframe、series、单个值,甚至和输入dataframe完全没关系

本次实例演示:

怎样对数值列按分组的归一化?

怎样取每个分组的TOPN数据?

实例1:怎样对数值列按分组的归一化?

将不同范围的数值列进行归一化,映射到[0,1]区间:

更容易做数据横向对比,比如价格字段是几百到几千,增幅字段是0到100

机器学习模型学的更快性能更好

归一化的公式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuwTEdIl-1597761927714)(http://localhost:8891/notebooks/pandas-learn-code/other_files/Normalization-Formula.jpg)]

演示:用户对电影评分的归一化

每个用户的评分不同,有的乐观派评分高,有的悲观派评分低,按用户做归一化

In [1]:

import pandas as pd

In [7]:

ratings = pd.read_csv(

"./pandas-learn-code/datas/movielens-1m/ratings.dat",

sep="::",

engine="python",

names="UserID::MovieID::Rating::Timestamp".split("::")

)

In [8]:

ratings.head()

Out[8]:

UserID

MovieID

Rating

Timestamp

0

1

1193

5

978300760

1

1

661

3

978302109

2

1

914

3

978301968

3

1

3408

4

978300275

4

1

2355

5

978824291

In [10]:

# 实现按照用户ID分组,然后对ratings进行归一化

def ratings_norm(df):

# 实际参数是每个用户分组的df(按照UserID分组的DataFrame)

max_value = df["Rating"].max()

min_value = df["Rating"].min()

df["Rating_norm"] = df["Rating"].apply(lambda x:(x - min_value)/(max_value - min_value))

return df

# 按照用户分组,apply一个函数,给该DataFrame新增了一列,实现了Rating列的归一化

ratings = ratings.groupby("UserID").apply(ratings_norm)

In [12]:

ratings["Rating"]

. . .

In [16]:

type(ratings)

Out[16]:

pandas.core.frame.DataFrame

In [17]:

ratings[ratings["UserID"]==1].head()

Out[17]:

UserID

MovieID

Rating

Timestamp

Rating_norm

0

1

1193

5

978300760

1.0

1

1

661

3

978302109

0.0

2

1

914

3

978301968

0.0

3

1

3408

4

978300275

0.5

4

1

2355

5

978824291

1.0

实例2:怎么取每个分组的TOP N数据

获取2018年每个月温度最高的2天数据

In [18]:

fpath = "./pandas-learn-code/datas/beijing_tianqi/beijing_tianqi_2018.csv"

df = pd.read_csv(fpath)

In [19]:

df.head()

Out[19]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

0

2018-01-01

3℃

-6℃

晴~多云

东北风

1-2级

59

2

1

2018-01-02

2℃

-5℃

阴~多云

东北风

1-2级

49

1

2

2018-01-03

2℃

-5℃

多云

北风

1-2级

28

1

3

2018-01-04

0℃

-8℃

东北风

1-2级

28

1

4

2018-01-05

3℃

-6℃

多云~晴

西北风

1-2级

50

1

In [21]:

# 替换掉温度后的℃

df.loc[:, "bWendu"]=df["bWendu"].str.replace("℃","").astype("int32")

df.loc[:, "yWendu"]=df["yWendu"].str.replace("℃","").astype("int32")

In [22]:

# 新增一列为月份

df["month"] = df["ymd"].str[0:7]

df.head()

Out[22]:

ymd

bWendu

yWendu

tianqi

fengxiang

fengli

aqi

aqiInfo

aqiLevel

month

0

2018-01-01

3

-6

晴~多云

东北风

1-2级

59

2

2018-01

1

2018-01-02

2

-5

阴~多云

东北风

1-2级

49

1

2018-01

2

2018-01-03

2

-5

多云

北风

1-2级

28

1

2018-01

3

2018-01-04

0

-8

东北风

1-2级

28

1

2018-01

4

2018-01-05

3

-6

多云~晴

西北风

1-2级

50

1

2018-01

In [24]:

def getWenduTopN(df, topn):

# 这里的df,是每个月份分组group的df

return df.sort_values(by="bWendu")[["ymd", "bWendu"]][-topn:]

df.groupby("month").apply(getWenduTopN, topn=2).head()

Out[24]:

ymd

bWendu

month

2018-01

13

2018-01-14

6

18

2018-01-19

7

2018-02

53

2018-02-23

10

56

2018-02-26

12

2018-03

86

2018-03-28

25

In [25]:

df[["ymd","bWendu"]]

. . .

我们看到,groupby的apply函数返回的DataFrame,其实和原来的DataFrame其实可以完全不一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值