数据处理与统计分析篇-day08-apply()自定义函数与分组操作

一. 自定义函数

概述

  1. 当Pandas自带的API不能满足需求, 例如: 我们需要遍历的对Series中的每一条数据/DataFrame中的一列或一行数据做相同的自定义处理, 就可以使用Apply自定义函数

  2. apply函数可以接收一个自定义函数, 可以将Series对象的逐个值或DataFrame的行/列数据传递给自定义函数处理

  3. apply函数类似于编写一个for循环, 遍历行/列的每一个元素,但比使用for循环效率高很多

导包:

import numpy as np
import pandas as pd
import os
​
os.chdir(r'D:\CodeProject\03data_processing_analysis\teacher_project')  # 改变当前的工作目录.  change current work directory

apply()操作Series对象

apply()函数操作Series对象, 是把Series的逐个值进行传入并操作的.

# 1. 定义1个df对象.
df = pd.DataFrame({'a': [10, 20, 30], 'b': [20, 30, 40]})
df
​
# 2. 定义1个函数, 用于求 值 的 平方.  2 => 4,   5 => 25
def my_fun1(x):print('看看我执行了嘛!')return x ** 2
​
​
# 扩展: 定义函数, 计算x的e次方.
def my_fun2(x, e):return x ** e
​
# 3. 把上述的函数, 作用于 df对象的 a列值(Series对象)
df['a'].apply(my_fun1)  # 细节: 这里写的是函数名, 即: 函数对象.  如果写: 函数名() 则表示是在调用函数.
​
df.a.apply(my_fun2, e=3)  # 细节: 传参数时, 使用 关键字参数 写法进行传参.

apply()操作DF对象

df的apply(func, axis=)函数, 默认是传入整列值的, 而不是逐个值进行传入的.

源码解释axis参数:0 or index: apply function to each colum1 or columns: apply function to each row

解释:

axis = 0 按列传递数据 传入一列数据(Series)

axis = 1 按行传递数据 传入一列数据(Series)

df
df.apply(func1)     # 默认axis=0, 代表列, 即: 整列值传入
​
# 计算平均值,验证默认是传入整列值
def func3(x, y, z):return (x + y + z) / 3
​
# 报错,apply函数默认将df对象的整列作为参数传入
df.apply(func3)
​
def func4(x):print(x)print(type(x))df.apply(func4)
​
def func5(x):return x.mean()
​
df.apply(func5)             # 默认: axis=0(列)
df.apply(func5, axis=0)     # 效果同上
df.apply(func5, axis=1)     # 行 传入

函数向量化

def my_fun6(x, y):# 判断, 如果x的值是20, 就返回NaNif x == 20:         # 报错: x是向量, 20是标量, 向量和标量无法直接计算. return np.NAN# for i in x:#     if i == 20:         # 手动遍历, 就不报错了, 但是结果不是我们要的.#         return np.NAN# x代表第1列数据, y代表第2列数据return (x + y) / 2

在处理向量和标量时, 无法将向量直接和标量进行比较, 虽然手动用for循环遍历不会报错, 但是结果不对.

此时需要使用np.vectorize()函数, 将自定义函数向量化. 即: 如果遇到了向量, 则会逐个进行遍历, 获取标量并操作.

函数向量化的写法类似于装饰器的写法

# 定义函数, 接收df对象的两列数据, 计算每行的平均值@np.vectorize
@np.vectorize
def func6(x, y):# 判断, 如果x的值是20, 就返回NaNif x == 2:return np.NAN# x 第一列, y 第二列return (x + y) / 2
​
func6(df.a, df.b)
​
# 使用np.vectorize()函数, 将自定义函数进行向量化
func6 = np.vectorize(func6)
func6(df.a, df.b)

apply()结合lambda表达式

如果需求比较简单, 没有必要重新定义1个新的函数, 可以直接传入Lambda表达式.

# 1. 定义数据集.
df = pd.DataFrame({'a': [10, 20, 30], 'b': [20, 30, 40]})
df
#%%
# 2. 需求: 每个值 => 该值的平方.
def my_fun1(x):return x ** 2
​
df.apply(my_fun1)
#%%
# 3. 上述的需求可以用 Lambda表达式来完成.
df.apply(lambda x : x ** 2)
df.apply(lambda x : x.mean())
df.apply(lambda x : x.mean(), axis=0)   # 效果同上.
​
df.apply(lambda x : x.mean(), axis=1)   # 统计每行的平均值

apply()函数案例

加载数据

# 1. 加载数据集, 获取df对象.
train = pd.read_csv('data/titanic_train.csv')
train.head()
#%%
# 2. 查看数据集的 常用统计值.
train.info()
train.describe()
train
train.shape         # (891, 12)
len(train)          # 891 行数
train.size          # 891 * 12 = 10,772
​
len(train.Age)      # 891
train.Age.size      # 891

需求1:计算每列null总数, 缺失值占比, 非缺失值占比

# 1. 定义函数 count_missing(), 计算每列的缺失值总数
def count_missing(col):           # col => 每列数据, Series对象return col.isnull().sum()
​
# 2. 定义函数 prop_missing(), 计算每列的缺失值占比.
def prop_missing(col):# 缺失值占比 = 缺失值数量 / 该列总长度# return count_missing(col) / len(col)return count_missing(col) / col.size
​
# 3. 定义函数 prop_not_missing(), 计算每列的非缺失值占比.
def prop_not_missing(col):# 非缺失值占比 = 1 - 缺失值占比return 1 - prop_missing(col)
​
# 4. 调用上述的函数, 获取结果.
train.apply(count_missing)      # 获取每列的缺失值总数
train.apply(prop_missing)       # 获取每列的缺失值占比 
train.apply(prop_not_missing)   # 获取每列的非缺失值占比 

需求2: 计算泰坦尼克号数据中, 各年龄段总人数

# 方式1: 直接算每个年龄出现了多少次, 即: 每个年龄的总人数, 但是达不到我们要的效果.
train.Age.value_counts()
​
# 方式2:解题思路: 把年龄变成年龄段的值, 然后再进行统计.
# 1. 定义函数, 接收年龄, 将其转成年龄段. 
def cut_age(age):if 0 <= age < 18:return '未成年'elif 18 <= age < 40:return '青年'elif 40 <= age < 60:return '壮年'elif 60 <= age < 80:return '老年'else:return '未知'# 2. 把上述的函数, 作用于Age列, 得到新的列, 计算结果即可.
train.Age.apply(cut_age)
train.Age.apply(cut_age).value_counts()

需求3: 统计VIP 和 非VIP的客户总数

# VIP规则, 乘客船舱等级为1, 或者 名字中带有: 'Master', 'Sir', 'Dr'
def is_vip(rows):if rows.Pclass == 1 and ('Master' in rows.Name or 'Sir' in rows.Name or 'Dr' in rows.Name):return 'vip'else:return 'not_vip'
​
train.apply(is_vip, axis=1).value_counts()

二. 分组操作

分组 + 聚合

概述

  1. 在SQL中我们经常使用 GROUP BY 将某个字段,按不同的取值进行分组,

  2. 在pandas中也有groupby函数, 分组之后,每组都会有至少1条数据, 将这些数据进一步处理,

  3. 返回单个值的过程就是聚合,比如分组之后计算算术平均值, 或者分组之后计算频数,都属于聚

代码演示

导入数据
# 1. 读取数据, 获取df对象
df = pd.read_csv('data/gapminder.tsv', sep='\t')
df.head()
单变量
# 统计每年平均寿命
# 写法1
df.groupby('year')['lifeExp'].mean()
# 写法2
df.groupby('year').lifeExp.mean()
​
# 上述都是一步到位, 直接计算结果, 我们也可以手动计算. 
# 1. 我们先看看一共有多少个年
df.year.unique()  # 12个年份, 底层算 12 次即可, 这里我们就用 1952年举例.
​
# 2. 获取1952年所有的数据, 计算平均寿命
df[df['year'] == 1952].lifeExp.mean()
df[df.year == 1952].lifeExp.mean()  # 效果同上.
​
​
# 统计各大洲平均寿命
# 写法1
df.groupby('continent')['lifeExp'].mean()
​
# 分组之后, 也可以用 describe()同时计算多个统计量.
df.groupby('continent')['lifeExp'].describe()
​
# 写法2
df.groupby('continent')['lifeExp'].mean()
df.groupby('continent')['lifeExp'].agg('mean')  # 这里的mean是: pandas的函数
# df.groupby('continent')['lifeExp'].agg(np.mean)  # 这里的mean是: Numpy的函数
​
df.groupby('continent')['lifeExp'].aggregate('mean')  # 效果同上.
多变量agg
# 需求: 统计各个大洲 平均寿命, 人口的中位数, 最大GDP
df.groupby('continent').agg({'lifeExp': 'mean', 'pop': 'median', 'gdpPercap': 'max'})
df.groupby('continent').aggregate({'lifeExp': 'mean', 'pop': 'median', 'gdpPercap': 'max'})  # 效果同上
​
# 语法糖, 如果聚合函数一样, 则可以简写成如下操作, 例如: 各个大洲平均寿命, 平均人口, 平均GDP
df.groupby('continent').agg({'lifeExp': 'mean', 'pop': 'mean', 'gdpPercap': 'mean'})
df.groupby('continent')[['lifeExp', 'pop', 'gdpPercap']].mean()
自定义函数聚合运算
# 需求: 计算各个大洲的平均寿命
# 方式1: 使用Pandas的mean()函数.
df.groupby('continent').lifeExp.mean()
df.groupby('continent').lifeExp.agg('mean')
​
# 方式2: 使用自定义函数, 计算平均值.
# 1. 定义函数, 计算某列的平均值.
def my_mean(col):# 某列平均值 = 该列元素和 / 该列元素个数# return col.sum() / len(col)return col.sum() / col.size
​
​
# 2. 调用函数.
df.groupby('continent').lifeExp.apply(my_mean)
df.groupby('continent').lifeExp.agg(my_mean)

分组 + 转换

概述

  1. transform 需要把DataFrame中的值传递给一个函数, 而后由该函数"转换"数据。

  2. 即: aggregate(聚合) 返回单个聚合值,但transform 不会减少数据量。

  3. 分组转换跟SQL中的窗口函数中的聚合函数作用一样。可以把每一条数据和这个数据所属的组的一个聚合值在放在一起, 可以根据需求进行相应计算。

代码演示

计算x的z-score分数

计算x的 z-score分数, 也叫: 标准分数, 公式为: (x - x_mean) / x_std

# 1. 查看数据源
df
#%%
# 2. 定义函数, 计算某列的 z-score分数.
def my_zscore(col):return (col - col.mean()) / col.std()  # (列值 - 平均值) / 标准差
​
​
# 3. 调用上述的格式.
df.groupby('year').lifeExp.apply(my_zscore)  # 1704条
​
#%%
# 4. 查看原始df的数据集总数.
df  # 结论: 分组 + 转换处理后, 数据集总数不变.
分组填充
# 需求: 读取文件(小票信息), 获取df对象. 其中有1列 total_bill 表示总消费. 随机抽取4个缺失值, 然后进行填充. 
# 填充方式: 每个组的平均值. 即: 如果是Male => 就用 Male列的平均值填充, 如果是Female => Female列的平均值填充.
# 1. 读取文件, 获取DataFrame对象
df = pd.read_csv('data/tips.csv')
df
#%%
# 2. 抽样方式, 从上述的df对象中, 随机抽取10条数据. 
# tips_10 = df.sample(10)     # 这里的10表示随机抽取 10 条数据.
# random_state: 随机种子, 只要种子一样, 每次抽取的数值都是一样的. 
tips_10 = df.sample(10, random_state=21)
tips_10
#%%
# 3. 随机的从上述的10条数据中, 抽取4行数据, 设置他们的 total_bill(消费总金额) 为 NaN
# 写法1: 每次固定 这四条数据 的 total_bill为 空值.
# tips_10.loc[[173, 240, 243, 175], 'total_bill'] = np.NaN
​
# 写法2: 每次随机4条数据, 设置它们的 total_bill为 空值.
# np.random.permutation()解释: 随机打乱索引值, 并返回打乱后的索引值.
# np.random.permutation()[索引数] 打乱索引顺序, 返回固定索引数
tips_10.loc[np.random.permutation(tips_10.index)[:4], 'total_bill'] = np.NaN
tips_10
#%%
# 4. 分别计算 Male 和 Female 的平均消费金额, 用于填充对应组的 缺失值.
# 思路1: 直接用 整体的 总消费金额的 平均值 填充.
tips_10.fillna(tips_10.total_bill.mean())
#%%
# 思路2: 自定义函数, 计算每组的平均消费金额, 进行填充
def my_mean(col):# return col.sum() / col.size     # 某列总金额 / 某列元素个数,  这种写法会导致: 本组所有的数据都会被新值覆盖.return col.fillna(col.mean())     # 用该列的平均值, 来填充该列的缺失值, 其它不变.
​
# 调用上述函数, 实现: 分组填充, 即: 给我N条, 处理后, 还是返回N条数据.
# tips_10.groupby('sex').total_bill.apply(my_mean)      # n => 1  聚合的效果.
tips_10.groupby('sex').total_bill.transform(my_mean)    # n => n  类似于: MySQL的窗口函数的效果.
​
# df.groupby('sex').total_bill.transform(my_mean)    # n => n  类似于: MySQL的窗口函数的效果.

分组 + 过滤

概述

  1. 使用groupby方法还可以过滤数据

  2. 调用filter 方法,传入一个返回布尔值的函数,返回False的数据会被过滤掉

代码演示

# 1. 查看源数据
df
#%%
# 2. 查看用餐人数情况.
tmp_df = df.groupby('size', as_index=False).total_bill.count()
tmp_df.columns = ['size', 'count']
tmp_df
​
df.size     # 这样写, 会把 size当做 属性, 而不是 size列.
df['size'].value_counts()
#%%
# 3. 我们发现, 在所有的 消费记录中, 就餐人数 在 1, 5, 6个人的消费次数相对较少, 我们可以过滤掉这部分的数据
tmp_df = df.groupby('size').filter(lambda x : x['size'].count() > 30)
tmp_df
#%%
# 4. 验证上述筛选后的数据, size列只有 2, 3, 4 这三种就餐人数的情况.
tmp_df['size'].value_counts()
#%%
# 5. 上述代码的合并版, 一行搞定.
df.groupby('size').filter(lambda x : x['size'].count() > 30)['size'].value_counts()
​
# 另外一种筛选的方式, 可以基于: query()函数 + 筛选条件, 找出要的合法的数据. 
df.query('size == 2 or size == 3 or size == 4')
df.query('size in [2, 3, 4]')

DataFrameGroupby对象

概述

调用了groupby方法之后, 就会返回一个DataFrameGroupby对象

代码演示

# 1. 从小费数据中, 随机的获取10条数据.
tips_10 = pd.read_csv('data/tips.csv').sample(10, random_state=21)
tips_10
#%%
# 2. 演示 根据性别分组, 获取: 分组对象.
grouped = tips_10.groupby('sex')      # DataFrameGroupBy 对象
grouped
#%%
# 3. 遍历上述的分组对象, 看看每个分组都是啥(即: 每个分组的数据)
for sex_group in grouped:print(sex_group)        # sex_group: 就是具体的每个分组的数据. 
#%%
# 4. 获取指定的某个分组的数据.
grouped.get_group('Male')
grouped.get_group('Female')
#%%
# 5. 需求: 使用groupby() 按 性别 和 用餐时间分组, 计算小费数据的平均值. 
df.groupby(['sex', 'time']).tip.mean()
#%%
# 6. 分组对象不能使用 0 索引获取数据
grouped
# grouped[0]      # 分组对象不能使用 0 索引获取数据, 要获取数据, 可以通过  grouped.get_group() 函数实现
grouped.get_group(('Male'))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1542773.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

K8s 之微服务的定义及详细资源调用案例

什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f; 需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。借助Service&#xff0c;应用可以实现服务发现和负载均衡。service默认只支持4层负载均衡能力&…

OpenCV特征检测(10)检测图像中直线的函数HoughLinesP()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在二值图像中使用概率霍夫变换查找线段。 该函数实现了用于直线检测的概率霍夫变换算法&#xff0c;该算法在文献 181中有所描述。 HoughLines…

JavaEE: 深入探索TCP网络编程的奇妙世界(五)

文章目录 TCP核心机制TCP核心机制六: 拥塞控制为什么要有拥塞控制?动态调整的拥塞控制拥塞控制中,窗口大小具体的变化过程 TCP核心机制七: 延时应答TCP核心机制八: 捎带应答 TCP核心机制 前一篇文章 JavaEE: 深入探索TCP网络编程的奇妙世界(四) 书接上文~ TCP核心机制六: 拥…

Parallels Desktop 20 for Mac 推出:完美兼容 macOS Sequoia 与 Win11 24H2

Parallels Desktop 20 for Mac 近日正式发布&#xff0c;这一新版本不仅全面支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;还在企业版中引入了一个全新的管理门户。新版本针对 Windows、macOS 和 Linux 虚拟机进行了多项改进&#xff0c;其中最引人注目的当属 Parallels …

Python 入门(一、使用 VSCode 开发 Python 环境搭建)

Python 入门第一课 &#xff0c;环境搭建...... by 矜辰所致前言 现在不会 Python &#xff0c;好像不那么合适&#xff0c;咱先不求精通&#xff0c;但也不能不会&#xff0c;话不多说&#xff0c;开干&#xff01; 这是 Python 入门第一课&#xff0c;当然是做好准备工作&a…

计算机毕业设计 校园失物招领网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

嵌入式单片机STM32开发板详细制作过程--01

大家好,今天主要给大家分享一下,单片机开发板的制作过程,原理图的制作与PCB设计,以及电子元器件采购与焊接。 第一:单片机开发板成品展示 板子正面都有各个芯片的丝印与标号,方便焊接元器件的时候,可以参考。(焊接完成之后,成品图如下) 第二:开发板原理图制作 在制…

MATLAB中多张fig图合并为一个图

将下列两个图和为一个图 打开查看-----绘图浏览器 点击第一幅图中曲线右键复制&#xff0c;到第二幅图中粘贴即可完成

布草洗涤-酒店分楼层统计报表--———未来之窗行业应用跨平台架构

一、大酒店分层管理 1. 精准管理库存 - 能够清晰了解每个楼层布草的具体数量和状况&#xff0c;实现对布草库存的精细化管理&#xff0c;避免出现某些楼层布草短缺或过剩的情况。 2. 优化资源分配 - 依据各楼层的使用频率和需求差异&#xff0c;合理调配布草资源&…

排序--归并排序

1.什么是归并排序&#xff1f; 归并排序将待排序的数组分成两部分&#xff0c;对每部分递归地应用归并排序&#xff0c;然后将两个有序的子数组合并成一个有序的数组。这个过程一直重复&#xff0c;直到数组完全有序。归并排序的过程可以用一棵完全二叉树来形象地表示&#xf…

frpc内网穿透

官网地址&#xff1a;frp官网 本次用到的Liunx包&#xff1a; https://github.com/fatedier/frp/releases/download/v0.60.0/frp_0.60.0_linux_amd64.tar.gz下载&#xff1a; wget https://github.com/fatedier/frp/releases/download/v0.60.0/frp_0.60.0_linux_amd64.tar.g…

申论笔记杉树林

同义词尽量用文章中的词进行拼凑不一定要有前置词分条 单一题 同义词给分不一定需要前置词分条 1、2、3、尽量抄文章中的词&#xff0c;通顺即可&#xff0c;不一定要成句子不要过分关注形式 题干&#xff1a; 条理清晰&#xff1a;要求分条&#xff0c;尽量有提示词…

脱离枯燥的CRUD,灵活使用Mybatis,根据mybatis动态的xml片段和接口规范动态生成代理类,轻松应付简单业务场景。

需求 需求是这样的&#xff0c;我们有一个数据服务平台的产品&#xff0c;用户先将数据源信息保存到平台上&#xff0c;一个数据源可以提供多个接口服务&#xff0c;而每个接口服务在数据库中存一个具有mybatis语法的sql片段。这样的话&#xff0c;对于一些简单的业务只需要编…

c++进阶学习-----继承

1.继承的概念及定义 1.1继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。 继承呈现了面向对象 程序设计的…

006——队列

队列&#xff1a; 一种受限的线性表&#xff08;线性逻辑结构&#xff09;&#xff0c;只允许在一段进行添加操作&#xff0c;在另一端只允许进行删除操作&#xff0c;中间位置不可操作&#xff0c;入队的一端被称为队尾&#xff0c;出队的一端被称为队头&#xff0c;在而我们…

iOS 中 KVC 与 KVO 底层原理

KVC 本质&#xff1a; [object setValue: forKey:];即使没有在.h 文件中有property 的属性声明&#xff0c;setValue:forKey依然会按照上图流程执行代码 KVC 如果成功改变了成员变量&#xff0c;是一定可以被 KVO 监听到成员变量的前后改变的 KVO runtime会生成中间类&…

Leetcode 378. 有序矩阵中第 K 小的元素

1.题目基本信息 1.1.题目描述 给你一个 n x n 矩阵 matrix &#xff0c;其中每行和每列元素均按升序排序&#xff0c;找到矩阵中第 k 小的元素。 请注意&#xff0c;它是 排序后 的第 k 小元素&#xff0c;而不是第 k 个 不同 的元素。 你必须找到一个内存复杂度优于 O(n^2…

GPT1-GPT3论文理解

GPT&#xff11;&#xff0d;GPT&#xff13;论文理解 视频参考&#xff1a;https://www.bilibili.com/video/BV1AF411b7xQ/?spm_id_from333.788&vd_sourcecdb0bc0dda1dccea0b8dc91485ef3e74 1 历史 2017.6 Transformer 2018.6 GPT 2018.10 BERT 2019.2 GPT-2 2020…

ER论文阅读-Decoupled Multimodal Distilling for Emotion Recognition

基本介绍&#xff1a;CVPR, 2023, CCF-A 原文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Li_Decoupled_Multimodal_Distilling_for_Emotion_Recognition_CVPR_2023_paper.pdf Abstract 多模态情感识别&#xff08;MER&#xff09;旨在通过语言、…

闯关leetcode——67. Add Binary

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/add-binary/description/ 内容 Given two binary strings a and b, return their sum as a binary string. Example 1: Input: a “11”, b “1” Output: “100” Example 2: Input: a “101…