Power Query 系列 (13) - 自定义函数

article/2025/9/24 17:47:37

本篇博客介绍 Power Query 自定义函数的技巧,在 PQ 中计算个税。

以工资类所得应交个税为例,最新的个税起征点为 5000 并按下表的级次进行缴税(假设没有其它扣除项)。

对照税率表,我们先看看手工如何计算。比如 xxx 的薪资为 8900,没有其它扣除,那么应缴纳的个税为:

1:  应纳税所得额 = 8900 - 5000 = 3900
2: 3900 对应级数为 1,应纳税额 = 3900 * 10% - 210 = 180

接下来介绍在 PQ 中如何通过自定义函数来计算。有下面两个表:税率表(TaxRate)he员工薪资表(salaries),要计算张三和李四应交个税金额。假设我们用自定义函数的方式。本文将给出三种方法。

将 Excel 工作表中的两个数据源通过 Ctrl + T 转换成表,加载到 PQ 中。

函数的语法

PQ 函数的语法示例:

(x, y) => x + y

方法1:在高级编辑器中手写代码。新建一个空查询,改名为 GetIncomeTax,进入高级查询,在高级查询中输入下面的代码:

(taxable) =>if taxable <=0 then 0else if taxable <= 3000 then taxable * 0.03else if taxable <= 12000 then taxable * 0.1 - 210else if taxable <= 25000 then taxable * 0.2 - 1410else if taxable <= 35000 then taxable * 0.25 - 2660else if taxable <= 55000 then taxable * 0.3 - 4410else if taxable <= 80000 then taxable *0.35 - 7160else taxable * 0.45 - 15160

点击完成按钮,回到查询编辑器界面,这个函数就完成了。以上代码跟其他编程语言差不多,思路比较直观。可以通过上面代码熟悉 M语言 if then else 的语法。

函数调用

选中 salaries 查询,右键,点击【复制】,将 salaries 查询表复制一个名为 IncomeTaxMethod1 的新查询,添加一个自定义列, 计算应纳税所得额 (用 Taxable 表示):

再增加一个计算列,调用自定义函数 GetIncomeTax 计算个税 :


点击完成按钮回到查询编辑器界面,第一种方法定义函数和调用函数完成。


对应的 M 语言脚本:

let源 = Excel.CurrentWorkbook(){[Name="salaries"]}[Content],ChangedTypes = Table.TransformColumnTypes(源,{{"Name", type text}, {"Salary", Int64.Type}}),AddedTaxableCol = Table.AddColumn(ChangedTypes, "Taxable", each [Salary] - 5000),Result = Table.AddColumn(AddedTaxableCol, "IncomeTax", each GetIncomeTax([Taxable]))
inResult

查询转函数

方法2:方法 2 的个税计算方法来自于我见过的 Excel 中最简洁的计算公式:

=ROUND(MAX((B4-5000)*{3;10;20;25;30;35;45}%-{0;210;1410;2660;4410;7160;15160},0),2)

公式中只有一个参数,但这个公式有点晦涩,先解释一下。这个公式用到了 Excel 的数组。第一个数组是税率, 为方便表述称为 taxrate_array,第二个数组是速算扣除数,为表述方便称为 deduction_array:

taxrate_array: {3;10;20;25;30;35;45}
deduction_array: {0;210;1410;2660;4410;7160;15160}

所以,

(D2-5000)*{3;10;20;25;30;35;45}%-{0;210;1410;2660;4410;7160;15160}

表示是将 (salary-5000)后的值 (taxable)与 taxrate_array 数组的每一个元素进行计算,再减去 deduction_array 数组的对应值,结果组成一个新数组。然后再从这个结果数组中取最大值,四舍五入保留两位小数。用伪代码表述更加清晰:

taxable = salary - 5000
incomeTax = {taxable*0.03-0; taxable*0.1-210; taxable*0.2-1410; ...}
incomeTax = Max({incomeTax})
IncomeTax = Round(IncomeTax, 2)

这个算法是怎么想出来的我们不去管它,主要讲解在 PQ 中用同样算法实现的步骤。

选中 TaxrateTable 查询,右键,点击【引用】菜单,得到一个新的查询,将查询改名为 GetTax。后面基于这个查询编写计算个税的函数。

根据刚才的 Excel 公式,上图中 Level 字段、TaxableFrom 字段和 TaxableTo 字段是无关字段,可以删除。可以用 Table.RemoveColumn 函数删除,也可以使用 Table.SelectColumns 函数保留需要的列。本次使用Table.SelectColumns 函数。在高级编辑器或公式栏中操作都可以:

SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"})


这样选择了相关的两列,查询编辑器界面如下:


函数需要参数,比如计算个税需要应纳税所得额。为了方便,我先用一个特定值,后面再替换。进入高级编辑器,目前的代码如下:

letSource = TaxRatesTable,SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"})
inSelectedCols

比如用刚才的应纳税所得额 3900,将代码变更为:

lettaxable = 3900,Source = TaxRatesTable,SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"})
inSelectedCols

注意下图我的变更是插入了一个变量:

然后增加一个自定义列 Trial (表示试算):


此时查询编辑器界面如下:

对应的 M 语言代码如下:

lettaxable = 3900,Source = TaxRatesTable,SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"}),Trials = Table.AddColumn(SelectedCols, "Trial", each taxable * [Rate] - [Deduction])
inTrials


接下来取出 Trial 列的最大值。我们知道 Table 每列都是 List 类型的数据,所以可以增加一个步骤,命名为 MaxOfTrial,在公式栏输入:

= List.Max(Trials[Trial])

再套用 Number.Round 函数:

= Number.Round(List.Max(Trials[Trial]),2)

此时查询编辑器的界面如下:


对应的 M 语言代码如下:

lettaxable = 3900,Source = TaxRatesTable,SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"}),Trials = Table.AddColumn(SelectedCols, "Trial", each taxable * [Rate] - [Deduction]),MaxOfTrial = Number.Round(List.Max(Trials[Trial]),2)
inMaxOfTrial

接下来是见证奇迹的时刻,在高级编辑器中将代码变更为:

(taxable as number ) as number =>letSource = TaxRatesTable,SelectedCols = Table.SelectColumns(Source, {"Rate", "Deduction"}),Trials = Table.AddColumn(SelectedCols, "Trial", each taxable * [Rate] - [Deduction]),MaxOfTrial = Number.Round(List.Max(Trials[Trial]),2)inMaxOfTrial

回到查询编辑器界面,PQ 将查询变成了函数。经过本步骤,第二种方法定义函数完成。请体会和掌握如何将查询转换成函数,这种方式可以将编写函数的步骤分解,并且能看到每一个步骤的计算结果,从而降低了手写代码的难度。


函数的调用比较简单,略去不提。

个税计算方法 3: 对于应纳税所得额来说,需要在 TaxRatesTable 中找到对应行,从而确定税率和速算扣除数。仍以 3900 的应纳税所得额为例,对应下图的级次确定税率和速算扣除数:


在 PQ 中,从另外一个 Table 中找出一行,使用构造结构化列的方法。这种方法具有普遍意义,能够处理两个表没有相等值,从而不能用合并查询的场景。

选中 salaries 查询表,右键菜单【引用】,新建一个名为 IncomeTaxMethod3 的查询。添加一个自定义列。这一列每个单元格都包含完整的 TaxRatesTable 查询表数据。


回到查询编辑器界面,我们看到,TaxInfo 每一个单元格都包含 TaxRateTable 的所有数据,我们需要对 sub-table 的数据进行筛选。

这种操作技巧在本系列第 11 篇有详细介绍,本篇就不再赘述。在高级编辑器中,将代码变更。变更前代码:

letSource = salaries,AddedTaxData = Table.AddColumn(Source, "TaxInfo", each TaxRatesTable)
inAddedTaxData

变更后代码后:

    Source = salaries,AddedTaxData = Table.AddColumn(Source, "TaxInfo", each Table.SelectRows(TaxRatesTable, (row)=>row[TaxableFrom]<=[Salary] - 5000))
inAddedTaxData

通过 Table.SelectRows 将税率级次高的数据排除掉。比如 8900 - 5000 = 3900,则只需先下面两行:


接下来调用 Table.Last 函数获取最后一行,得到一个 record 类型的数据:

letSource = salaries,AddedTaxData = Table.AddColumn(Source, "TaxInfo", each Table.Last(Table.SelectRows(TaxRatesTable, (row)=>row[TaxableFrom]<=[Salary] - 5000)))
inAddedTaxData

这时 TaxInfo 变成了 record,对 TaxInfo 列进行展开操作,保留 Rate 和 Deduction:


查询编辑器的界面如下:

最后添加一个计算列,计算出各自的个税:


这种结构化列的方法,同样可以先做出查询,然后转换成函数。操作过程类似,就不重复说明了。给出获取 TaxRateTable 相应行的函数代码:

(taxable as number) =>letSource = Table.SelectColumns(TaxRatesTable, {"TaxableFrom", "Rate", "Deduction"}),SelectedRow = Table.Last(Table.SelectRows(Source,  (row)=>row[TaxableFrom] <= taxable   )) inSelectedRow

示例数据已经放到 github - Income Tax Calculation 上,方便大家学习。


http://chatgpt.dhexx.cn/article/psMvEuU2.shtml

相关文章

MySQL—自定义函数

函数 本文是在 navicat 中演示如何创建 自定义函数 &#xff0c;建议先去看有关的书籍了解一下。我之前分享过一篇 《MySQL 8》的资料 可以参考一下。因为是 navicat 是客户端工具&#xff0c;所以他会给我们省去一些操作&#xff0c;比如 &#xff1b; 的转义&#xff1b; 参…

【MySQL 第十天 自定义函数|创建和使用自定义函数|循环判断语句的使用】

【MySQL 第十天 自定义函数|创建和使用自定义函数|循环判断语句的使用】 【1】mysql自定义函数的介绍【2】mysql创建和使用自定义函数【3】mysql删除自定义函数【4】mysql流控的使用【4.1】mysql流程控制语句【4.2】mysql IF语句【4.3】mysql CASE语句【4.4】mysql WHILE循环语…

SQL Function 自定义函数

目录 产生背景&#xff08;已经有了存储过程&#xff0c;为什么还要使用自定义函数&#xff09; 发展历史 构成 使用方法 适用范围 注意事项 疑问 内容 产生背景&#xff08;已经有了存储过程&#xff0c;为什么还要使用自定义函数&#xff09; 与存储过程的区别&…

【精品】MySQL 自定义函数

语法 CREATE FUNCTION <函数名>(参数列表) RETURNS <返回值数据类型> BEGINRETURN(<SQL语句>); END示例一&#xff1a;生成随机的电话号码 CREATE FUNCTION generatePhone() RETURNS char(11) CHARSET utf8 DETERMINISTIC BEGINDECLARE head VARCHAR(100)…

sql:mysql:自定义函数

一、基本语法 delimiter 自定义符号  -- 如果函数体只有一条语句, begin和end可以省略, 同时delimiter也可以省略create function 函数名(形参列表) returns 返回类型  -- 注意是retrunsbegin函数体    -- 函数内定义的变量如&#xff1a;set x 1; 变量x为全局变量&am…

mysql之自定义函数

本文内容&#xff1a; 什么是函数函数的创建函数的调用函数的查看函数的修改函数的删除 首发日期&#xff1a;2018-04-18 什么是函数&#xff1a; 函数存储着一系列sql语句&#xff0c;调用函数就是一次性执行这些语句。所以函数可以降低语句重复。【但注意的是函数注重返回值&…

SQL Server 自定义函数(Function)

SQL Server 自定义函数&#xff08;Function&#xff09;——参数默认值 sql server 自定义函数分为三种类型&#xff1a;标量函数&#xff08;Scalar Function&#xff09;、内嵌表值函数&#xff08;Inline Function&#xff09;、多声明表值函数&#xff08;Multi-Statemen…

mysql自定义函数实现

mysql自定义函数实现 环境准备 查看mysql版本 select version();查看mysql存储引擎 show variables like %storage_engine%;创建测试表 CREATE TABLE dsp_user_media_tag (id int(11) NOT NULL AUTO_INCREMENT,imei varchar(50) COLLATE utf8_bin DEFAULT NULL,tagname va…

MySQL自定义函数用法详解

MySQL自定义函数用法详解-复合结构自定义变量/流程控制 自定义函数 (user-defined function UDF)就是用一个象ABS() 或 CONCAT()这样的固有&#xff08;内建&#xff09;函数一样作用的新函数去扩展MySQL。 所以UDF是对MySQL功能的一个扩展 创建和删除自定义函数语法: 创建UDF:…

SQL server 自定义函数FUNCTION的使用

前言&#xff1a; 在SQL server中不仅可以可以使用系统自带的函数&#xff08;时间函数、聚合函数、字符串函数等等&#xff09;&#xff0c;还可以根据需要自定义函数。 一、定义&#xff1a; 用户自定义函数的类型&#xff1a; 1、标量值函数&#xff08;返回一个标量值&a…

功能测试用例设计方法分享

测试用例可以用来衡量一个项目测试质量&#xff0c;因此在平时的测试流程中&#xff0c;编写测试用例就是测试过程中很重要的一步&#xff0c;每一个测试工程师都需要并且非常熟练的编写测试用例&#xff0c;能在编写测试用例中尽可能的覆盖任何异常的测试点&#xff1b;如何能…

判定表测试用例方法——实例

测试用例设计方法——判定表法 判定表法表示的是有多个输入&#xff0c;和多个输出&#xff0c;而且输入与输入之间有相互的组合关系、输入和输出之间有相互的制约和依赖关系, 判定表由四个组成部分 判定表基本概念  条件桩&#xff1a;输入条件, 列出了系统的所有输入&…

测试用例编写方法

测试用例编写方法 等价类划分法 一、等价类划分法的定义 把程序的输入域和输出域划分成若干部分&#xff0c;然后从各个部分中选取若干代表性数据作为测试用例。这些数据在测试中的作用等价于其所属部分的其他值。 二、等价类划分法的术语 等价类&#xff1a;输入域的各个…

软件测试用例设计方法(一)

目录 软件测试用例设计之等价类划分法一、等价类划分法的定义二、等价类划分法的术语三、等价类划分原则四、实例演示&#xff08;三角形问题和档案管理系统问题&#xff09; 软件测试用例之边界值分析法一、边界值分析法定义二、等价类划分法和边界值分析法的区别三、内部边界…

测试用例方法-判定表法

判定表法案例 若用户欠费或关机&#xff0c;则不允许主被叫 等价类划分法和边界值分析法都是着重考虑单个输入的输入条件&#xff0c;但是没有考虑输入条件的各种组合&#xff0c;输入条件与输出条件之间的相互制约关系。所以要使用判定表法才能解决上述案例编写测试用例的过…

编写测试用例方法之错误推测法

今天我们再来介绍另外一个编写测试用例的方法&#xff1a;错误推测法。话不多说&#xff0c;开始整干货&#xff0c;老规矩&#xff0c;首先全图镇楼。 错误推测法其实它不同于等价类划分法或者边界值分析法&#xff0c;它是对有效等价类和边界值分析法的一个补充。因为错误推测…

【测试】编写测试用例的常用方法

文章目录 1&#xff09;等价类划分法1.1 什么是等价类有效等价类&#xff1a;无效等价类&#xff1a; 1.2 划分标准1.3 划分方法1、确立等价类2、转化为测试用例 1.4 实例&#xff1a;三角形问题1.4.1 分析对输入条件的要求&#xff08;显性和隐性&#xff09;1.4.2 列出等价类…

编写测试用例的方法

编写测试用例的方法 一、等价类划分法&#xff08;重点&#xff09;1、应用场景&#xff1a;多用于输入框。2、概念&#xff1a;3、等价类&#xff1a;4、示例&#xff1a; 二、边界值法&#xff08;重点&#xff09;三、场景法&#xff08;重点&#xff09;1、概念&#xff1a…

编写测试用例的七种方法

1 测试用例的概念 测试用例是为了实施测试而向被测试系统提供的一组集合&#xff0c;这组集合包括&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素 2 常见编写测试用例的七种方法 基于需求的设计方法 等价类 边界值 因果图 场景设计法 错误猜测法 3 基于需…

【路径规划】(4) 蚁群算法,附python完整代码

大家好&#xff0c;今天和各位分享一下蚁群算法&#xff0c;并基于 tkinter 完成一个旅行商问题。完整代码可以从我的 GitHub 中获得&#xff1a; https://github.com/LiSir-HIT/Mathematical-Programming/tree/main/Path%20Planning 1. 算法介绍 蚁群算法是由 Mr.Dorigo 博士…