python pandas详解(二)

Pandas 条件数据筛选

在上一期介绍数据选取文章中,我们介绍了使用loc函数进行简单条件筛选的功能。实际上除了loc函数,Pandas 中还有几个可以用来数据筛选的函数或形式,比如query函数、切片([])等。在这些函数中,笔者认为通用性最佳的方式是使用切片([])来做数据筛选。切片筛选的形式如下。

变量名[<条件表达式>]

如何使用切片来做数据筛选呢?下面我们以上一节读取的数据data为例,从不同的维度的筛选条件做介绍。

1条件数据筛选的不同维度

(1) 比较数据值

一种最简单,但又最常见的数据筛选条件就是通过判断一个字段的值是否等于、不等于指定的值。例如当我们想筛选上市地点在北京的A股上市公司数据,可以使用下面的筛选代码。

# 筛选上市地点在北京的A股上市公司数据
data[data['上市地点'] == '北京']

代码中的data['上市地点'] == '北京'就是筛选的条件,筛选结果(部分)如下图所示。

图片

如果想获取data中不是在北京上市的企业,那么筛选条件就是上市地点不等于“北京”,编写代码时只需将上述代码中的等于号==改成不等于号!=即可,这里不再具体演示。

当情况比较多的时候,例如筛选出城市字段是北京市、上海市、广州市、深圳市其中一种的数据时,可以使用的方法不止一种,下面是其中一种方法的代码。

# 筛选出城市字段是北京市、上海市、广州市、深圳市其中一种的数据
data[data['城市'].isin(['北京市', '上海市', '广州市', '深圳市'])]

图片

上面两个案例是根据内容为文本的字段进行筛选,如果需要对一个数字字段做筛选,我们还可以使用 Python 中的其他比较运算符来做筛选,举个例子,当需要筛选员工总数大于等于 10000 的A股上市企业时,可以使用下面的代码。

# 筛选员工总数大于等于 10000 的企业
data[data['员工总数'] >= 10000]

图片

同理,还可以使用大于、小于、小于等于等比较运算符来做数据筛选。这些比较数据值大小的筛选条件中,除了能拿字段和常量做比较,还可以拿字段和字段作比较,例如我们可以筛选出员工总数大于等于所属行政区划代码的数据(当然这个条件没有任何实际意义,这里只是举一个例子),可以使用下面的代码。

# 先将 所属行政区划代码 字段类型转为数字型
data['所属行政区划代码'] = data['所属行政区划代码'].astype(int)
# 筛选出员工总数大于等于所属行政区划代码的数据
data[data['员工总数'] >= data['所属行政区划代码']]

图片

(2) 是否为空值

有些时候,我们需要根据某个字段的值是否为空值来做筛选,比如筛选数据data所属上市标准字段值不为空的数据,可以使用下面的代码。

# 筛选所属上市标准字段值不为空的数据
data[data['所属上市标准'].notna()]

图片

如果需要获取某字段值为空的数据,可以将上述代码中的notna()替换为isna()即可。

Python 中默认的空值是 None,在 Pandas 中,表格数据中默认的空值(或者说缺失值)是 NaN,它是 numpy 库中的常量,意为 Not a Number,由于 Pandas 是以 numpy 库为基础开发的,故在表格数据中保留此设定,将空值设置为 NaN(numpy.nan)。而isna()notna() 函数能够识别的空值也包括 None 和 NaN,但不包括空字符'',即空字符''在 pandas 中不属于空值。

(3) 文本内容筛选

正如介绍 Excel / WPS 时说的那样,这类办公软件可以对某个字段做文本筛选,筛选字段中包含、不包含、开头(不)是、结尾(不)是某关键词的数据,除此之外还可使使用一些通配符来做模糊筛选。这种筛选操作在  Excel 中已经很强大了,但是这些在拥有正则表达式加持的 Pandas 面前,都算是小菜一碟。下面我们来介绍一下 Pandas 如何根据字段中的文本内容进行筛选数据。

首先,最简单的条件就是字段中是否包含(或不包含)某关键词,例如筛选数据data公司中文名称字段含有关键词“证券”的企业,可以使用下面的数据筛选代码。

# 筛选数据中 公司中文名称 字段含有关键词“证券”的企业
data[data['公司中文名称'].str.contains('证券')]
# 筛选后得到 41 条符合条件的数据

图片

如果希望筛选出所有公司中文名称不含关键词“证券”的数据,则可以在上面代码的条件表达式最前面加一个波浪符~,代码如下(不再展示筛选结果)。

# 筛选数据中 公司中文名称 字段不含关键词“证券”的企业
data[~data['公司中文名称'].str.contains('证券')]

除了以上这种最基本的关键词筛选之外,其他的像 Excel 那种根据文本的开头或结尾进行筛选的操作,在 Pandas 都可以通过正则表达式来实现,那么如何在 Pandas 的数据筛选中使用正则表达式呢?很简单,以上述代码为例,只需要将正则表达式传入contains函数,取代关键词即可。例如筛选公司中文名称段中含有“证券”,“银行”或“保险” 的数据时,就可以使用正则表达式进行处理,代码如下。

正则表达式教程已经更新完毕,请到文末查找相关链接

# 筛选公司中文名称字段中含有“证券”,“银行”或“保险” 的数据
data[data['公司中文名称'].str.contains('证券|银行|保险')]

图片

除此之外,我们还可以使用更加复杂的正则表达式进行更加复杂的筛选操作,例如筛选注册地址字段中不含有中文的数据可以使用下面的代码来实现。

# 筛选 注册地址 字段中不含有中文的数据
data[~data['注册地址'].str.contains('[\u4e00-\u9fa5]')]

图片

根据文本内容做筛选时,有一点需要特别注意,那就是进行筛选的字段中,必须所有值都是字符类型,不能是空值(若含有空值,可先填充缺失值为字符型),不能包含数字型和日期型,因为正则表达式无法对这些类型做处理。

(4) 数据值长度

有时候字段中文本的长度也能反映出一些信息,例如注册地址中字符数量越多,说明地址的描述越详细,而Pandas 则可以根据字符的长度做筛选,例如当需要筛选注册地址字段中字符数量小于等于 10 的所有数据时,可以使用下面的代码。

# 筛选注册地址字段中字符数量小于等于 10 的所有数据
data[data['注册地址'].str.len() < 10]

图片

(5) 日期筛选

除了对数值和字符进行筛选,Pandas 也能像 Excel 那样根据日期筛选数据,例如筛选 2023 年新上市的A股企业,就可以使用下面的代码。

# 先将 上市日期 字段转为 python 中的日期类型
data['上市日期'] = data['上市日期'].astype('datetime64[ns]')
# 筛选 2023 年新上市的A股企业
data[data['上市日期'] >= pd.Timestamp(2023, 1, 1)]

图片

(6) 其他

除了以上介绍的数据筛选维度,Pandas 的数据筛选还可以有其他的维度,比如根据索引值、字段名筛选,不过这些知识点的实用性算不上高,这就不过多介绍了。

2复合条件筛选

大家可能已经注意到,上文介绍的数据筛选虽然维度不同,但是所有的筛选案例中都只有一个筛选条件。实际上 Pandas 的数据筛选是可以存在多个筛选条件(两个及两个以上)的。当存在多个条件时,每个条件最好都使用括号括起来,如果其中两个条件时是“或”关系,那么使用逻辑或符号|来连接它们;如果两个条件的关系是“与”关系,那么就要用逻辑与符号&来连接它们。在 Pandas 中,上文中介绍的所有条件形式都可以进行任意的排列组合。下面我们通过几个例子来了解如何进行复合条件筛选。

【例 1】筛选出A股上市公司数据中,所属行政区划位于“江浙沪”,且员工总数大于等于 20000 的数据。

## 【例 1】筛选出A股上市公司数据中,所属行政区划位于“江浙沪”,且员工总数大于等于 20000 的数据。
# 为了方便查看,在代码中加了几个不必要的空格
data[ (data['所属行政区划'].isin(['上海', '江苏省', '浙江省'])) & (data['员工总数'] >= 20000) ]

图片

【例 2】筛选出企业规模为大型,或注册资本大于等于 10 亿的企业数据。

## 【例 2】筛选出企业规模为大型,或注册资本大于等于 10 亿的企业数据。
data[(data['企业规模']=='大型') | (data['注册资本'] > 1000000000)]

图片

以上就是 Pandas 条件数据筛选的内容。

行、列的增加和删除

先读取演示用的数据表。

import pandas as pd     # 导入 pandas 库
data = pd.read_excel('./2、工业互联网专题数据库2000-2020.03企业时间分布统计数据.xlsx') # 读取演示数据
data     # 输出查看演示数据

图片

(一)增加列(字段)

处理表格数据时经常需要添加或者删除字段,在 Pandas 中,添加字段的方式不止一种,下面我们来介绍一下。

(1)在表格尾端添加一列

在 Pandas 基础中,我们曾介绍过表示表格(DataFrame,数据框)中一个字段的代码是表格变量名[字段名],例如上文读取的表格数据变量名为data,那么获取年份这一字段的代码就是data['年份'],得到的是一个一维 Series,实际上除了这种表示方法,使用表格变量名.字段名同样可以表示一个字段,例如data.年份。不过使用后者表示字段时需要注意,如果字段名是字符型,则不可以使用引号引起来,且字段名中不能含有空格符.半角括号等在 Python 中具有特殊意义的字符。知道如何表示一个字段后,添加字段也就轻而易举了,我们直接将新添加字段的值赋给新字段即可,不过只能使用表格变量名[字段名]的方式来添加字段,例如下面是添加一个新字段“备注”,取值为“无”的代码。

# 在数据尾端添加一个新字段“备注”, 该字段的所有值都是‘无’
data['备注'] = '无'     # 不可以使用 data.备注='无'
data   # 输出查看 data

图片

这种添加字段的方式在添加时会为整个字段赋予一个指定值,例如上述代码中的'无',这种方式常用于先增加字段,后续再根据情况填充新字段。如果我们想在创建一个新字段的时候,为每一行赋予指定值,可以直接将包含值的列表赋值给新字段,但是需要注意,列表中元素的个数必须与数据表的行数保持一致,即每一行的值都必须指定才行,代码如下。

# 创建新字段时,为每一行设置指定值
data['备注'] = ['', '', '该年份数据有缺失', '', '', '', '',
              '', '', '该年份数据有缺失', '', '', '', '',
              '', '', '', '', '', '', '该年份数据有缺失']   # 数据表共 21 行,则需要填入 21 个值
data   # 输出查看 data

图片

💡 注意,以上两种方式不仅可以用来添加新字段,也可以修改已有字段的内容。

(2)通过计算得到新字段

除了以上两种方式外,我们还可以根据字段之间的运算得到新字段,例如我们想生成一个新字段“净增加企业数”,这个字段的内容是“企业成立数”与“企业死亡数”相减的结果,那么可以使用下面的代码。

# 通过字段之间的计算得到新字段
data['净增加企业数'] = data['企业成立数'] - data['企业死亡数']
data

图片

使用这种(计算)方式生成新字段时需要注意,参与计算的字段之间必须是可以进行运算的,例如两个字段中的值都是数值型,那么它们之间就可以进行加、减、乘、除、幂等基本运算,但如果两个字段中的值不是可以进行运算的,例如字符型和浮点型就无法进行运算,那么运算操作就无法进行。因此需要特别注意参与计算的两个字段,是否存在缺失值,其内部的值是不是可以两两运算。如果有个别的特殊值导致字段之间不能运算,我们还可以使用函数的方式去做处理,不过那是后续文章才要讲的内容。

(3)指定位置插入字段

前面介绍的几种方式都是在表格的尾端创建新字段,很多时候表格的字段之间是有顺序的,添加新字段也要在指定的位置进行。在 Pandas 中,insert()函数就是用来在指定位置插入字段的,使用方法如下。

表格变量名.insert(插入字段的位置, 插入字段的名称, 插入字段的值)

上述伪代码中,插入字段的位置是指待插入字段所在的顺序(默认从 0 开始计算),例如当我们想在演示数据中企业死亡数之后插入一个字段当年净增加企业数,其值是企业成立数字段与企业死亡数字段的差,那么就可以使用下面的代码。

# 先在指定位置(序号为 3)插入一个空字段
data.insert(3, '当年净增加企业数', '')
# 再将计算结果赋值给该字段
data['当年净增加企业数'] = data['企业成立数'] - data['企业死亡数']
data

图片

如果你的表格中字段数量太多,想要快速获取某个字段的序号来做参照,可以使用下面的代码。

# 获取“备注”字段的位置,注意这个位置是从 0 开始计算的
data.columns.get_loc('备注')   # 得到:5

(二) 删除列

我们提到删除缺失值所在行或列使用的是dropna()函数,而在这里,直接删除行或列的则是drop()函数。两者的区别在于前者是根据表格内数据缺失的情况自动删除数据,而后者是用来删除指定数据行或数据列的。drop()函数的常用参数如下表所示。

图片

例如删除“备注”字段时就可以使用下面的代码。

## 删除“备注”字段,且使删除操作在原数据上生效
# 方法 1
# data.drop(columns='备注', inplace=True) # 或者 data = data.drop(columns='备注'), 但要注意赋值操作和 inplace 参数不可同时使用,否则直接丢失原数据
# 方法 2
data.drop('备注', axis=1, inplace=True) # 或者 data = data.drop('备注', axis=1)
# 方法 3
# data.drop(['备注'], axis=1, inplace=True) # 或者 data = data.drop(['备注'], axis=1)

由于drop()函数默认不会直接修改原数据,所以如果想让删除操作在原数据上生效,就需要主动设置参数inplace=True;或者不设置inplace参数,而是将drop()函数返回的新对象重新赋值给原来的变量名。这段代码中介绍了 6 种删除一个字段的方法,实际操作时使用其中之一即可。如果想要一次性删除表中多个字段,例如删除data中的“当年净增加企业数”和“净增加企业数”两个字段,可以使用下面的代码。

# 删除“当年净增加企业数”和“净增加企业数”两个字段
# 方法 1
# data.drop(['当年净增加企业数', '净增加企业数'], axis=1, inplace=True)  # 或 data = data.drop(['当年净增加企业数', '净增加企业数'], axis=1)
# 方法 2
data.drop(columns=['当年净增加企业数', '净增加企业数'], inplace=True)  # 或 data = data.drop(columns=['当年净增加企业数', '净增加企业数'])、
data

图片

(三) 增加行

Pandas 对数据行的增加并不友好,不像增加数据列那样简单方便,最常见的就是在数据的尾端添加一行数据,下面是添加一行数据的代码。

# 方法 1:使用 append 函数,可能会遇到警告,该函数将会在未来的 pandas 版本中移除
# data = data.append({'年份':2021, '企业成立数':5000, '企业死亡数':2000, '企业存续数':250000}, ignore_index=True) # 添加的一行并非真实统计数据,只做演示用
# 方法 2:使用数据切片(数据选取),注意代码中的 21 是增加数据行的索引值,添加的索引必不能是事先存在于 data 中的
data.loc[21] = {'年份':2021, '企业成立数':5000, '企业死亡数':2000, '企业存续数':250000}
data

图片

除了以上这种增加一行的操作之外,还可以一次性将多行数据添加到一张表中,实际上是通过拼接多个表格实现的,这个我们会在后续的文章中介绍。

(四) 删除行

删除数据行与删除数据列使用的都是drop()函数,这一点我们在上文中介绍drop()函数的参数时已经有提到。删除数据列时,使用的是columns参数,或者在设置参数axis=1的前提下使用labels参数;根据上文中的参数列表,我们可以知道,删除数据行应该使用index参数,或者直接使用labels参数(因为axis参数默认为 0,即默认删除的是数据行)。例如删除索引值为 4,5,6 的数据行可以使用下面的代码。

# 删除索引值为 4,5,6 的数据行,并将删除后的数据赋值给新变量 data_droped
data_droped = data.drop([4,5,6]) # 这里使用的是 labels 参数,与使用 index 参数是一样的效果,因为 axis 的默认值为 0
# 删除后的数据就不再文中展示了

除了直接传入具体的行索引值列表,我们还可以借助数据筛选来获取索引值,并以此达到删除符合条件的数据,例如可以使用下面的代码来删除 2010 年之前的数据。

# 删除 2010 年之前的数据
data_Before2010 = data[data['年份'] < 2010]
# 根据筛选结果的索引值进行删除
data_After2010 = data.drop(data_Before2010.index)  # 也可以直接这样写:data_After2010 = data.drop(data[data['年份'] < 2010].index),这样只需要一句代码就可以了
# 查看删除后的数据值
data_After2010

图片

由于 Pandas 中表格数据类型 DataFrame 的行索引值不会自动变化,所以增加、删除数据行可能会导致行索引变得不连续,如果需要让数据的索引重新变得连续,可以使用reset_index()函数来重置索引,代码如下。

# 重置 data_After2010 的行索引,使其从0 开始且连续
# drop=True 参数表示重置索引后,原来的索引会被删除,如果不设置此参数,原来的索引值会自动添加到数据中,字段名为“index”
# 同 drop() 函数一样,reset_index() 也会返回一个新对象,而不是直接修改原数据,如果希望重置索引操作在原数据上生效
# 可以使用赋值方式,或者在 reset_index() 中设置参数 inplace=True
data_After2010 = data_After2010.reset_index(drop=True) 
data_After2010   # 查看重置索引后的值

图片

表格转置

什么是转置?首先,转置是一个数学名词,是线性代数中矩阵的一种变换。我们常用的表格数据实际上就是一种矩阵,所以也有转置操作,那么转置到底是一种怎么样的数据操作呢?在表格中,转置就是表格沿着数据的左上角与右下角形成的对角线进行翻转,最后转置前数据的第 n 行就会变成转置后数据的第 n 列,如下图所示。

图片

有时候遇到一些需要做行列转换的数据,就可以使用 Pandas 来对数据做转置操作,转置后数据框(DataFrame)的行索引和列索引(即字段名)也会互换位置,下面是对上文中表格数据data_After2010做转置的代码。

# 代码【数据框.T】就可以返回转置后的数据了 
data_转置 = data_After2010.T
data_转置

图片

数据展开与合并

一些时候过多的数据都被存放在一个单元格中,为了让数据更清晰,或者为了匹配数据,需要对数据做“展开”,将一行数据变为多行数据;而有时候为了减少数据量,又要去做相反的处理,将多行数据合并到一行。下面我们以数据变量清单表为例,演示如何进行上述操作,下面先读取演示数据。

# 读取演示数据
DATA = pd.read_excel('./1、工业互联网变量清单.xlsx', usecols=['表名称', '字段名称'])

图片

图片

💡 如果 Excel 表中存在合并的单元格,那么经过 Pandas 读取后,合并区域中只有左上方的小单元格内有值,其余区域都是空值,如上图所示。

(一) 多行合并为一行

我们的目的是将上图中“字段名称”列中的值根据“表名称”列进行合并。也就是说根据第一列对第二列做合并,那么多行合并一行的代码如下。

# 由于【表名称】字段的原始数据是合并的单元格,所以合并前需要做向下填充操作
# 向下填充,就是在一列数据中使用上一个非空值来填充下面的缺失值,操作方式如下
DATA['表名称'] = DATA['表名称'].ffill()

# 然后再进行合并数据行
# 先定义拼接函数,并对字段进行去重
def concat_func(x):
    return pd.Series({'字段名称':'、'.join(x['字段名称'])})
# 分组聚合 + 拼接
DATA_合并 = DATA.groupby(DATA['表名称']).apply(concat_func).reset_index()
DATA_合并    # 合并后数据的顺序会乱

图片

(二) 一行展开为多行

除了能够将多行数据合并为一行,Pandas 还可以将一行数据展开为多行数据,而且更加简单,下面我们来对上图所示数据注意一个反向的展开操作,代码如下。

# 第一步,先使用匿名函数将可展开的字符转为列表
DATA_合并['字段名称'] = DATA_合并['字段名称'].apply(lambda x: x.split('、'))
# 第二步,使用 explode() 函数对【字段名称】列做展开操作
DATA_展开 = DATA_合并.explode('字段名称')
DATA_展开

图片

以上就是本期 pandas 表格行列变换的全部内容。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高亚奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值