化于无形的 lambda 语法

简介: 在数据计算中,循环处理集合成员是常见任务。Python、SQL 和 SPL 都提供了简化方式:Python 使用 lambda 表达式,SQL 将 lambda 隐于 CASE WHEN 中,SPL 则通过 ~、# 等符号将 lambda 化于无形,语法更简洁统一,适应面更广,尤其支持多维集合与有序集合操作。三种语言各具特点,SPL 在灵活性和简洁性上表现尤为突出。

针对数据集合的每个成员进行计算是很常见的任务,用循环语句当然能实现,但比较麻烦,算个简单的求和都要写很多句代码。

编程语言经常把这些运算封装成函数,比如 Python 的 sum 函数,求订单价格总和是这样写的:

total_price = orders['price'].sum()

SQL 也可以写成:

select sum(price) total_price from orders

SPL 当然也没问题:

total_price = orders.sum(price)

看起来都很简洁。

任务当然不会总是这么简单,看一个更复杂的例子:对员工计算标签列,薪酬在 5000 以上的经理标签为 yes,其他员工为 no。

这个计算可以用一个不太复杂的表达式来描述,但不能在循环外部事先计算出结果,而要在循环中针对每个集合成员计算。这时候,可以把这个表达式定义成函数,再把这个函数作为参数传递给循环计算的函数,Python 可以这么写:

def calc_flag(row):
return 'yes' if row['position'] == 'manager' and row['salary'] > 5000 else 'no'

employee['flag']=employee.apply(calc_flag, axis=1)

这段代码先定义了一个函数 calc_flag,对传入的记录 row 计算表达式。

apply 函数以 calc_flag 为参数,在循环中将集合的当前成员(记录)传递给 calc_flag 计算,并用结果组成新序列。

显然,每次都预先定义成一个函数实在是麻烦,特别是对这么一个简单表达式就能搞定的任务。于是业界发明了 lambda 语法,可以在参数中定义函数,代码简洁很多:

employee['flag'] = employee.apply(lambda row: 'yes' if row['position'] == 'manager' and row['salary'] > 5000 else 'no', axis=1)

apply 函数的参数中用 lambda 关键字定义了一个匿名函数,传入参数是记录 row。在循环过程中,apply 函数将每个成员(记录)传给 lambda 函数,计算得到新的序列。

Python 的这种写法是显式的 lambda 语法,有 lambda 关键字,要定义参数,还要写函数体。

SQL 又是如何处理这种问题的呢?

SELECT *,
    CASE WHEN position = 'manager' AND salary > 5000 THEN 'yes' ELSE 'no' END AS flag
FROM employee;

没有 lambda,似乎更简单了。

其实,CASE WHEN 表达式还是相当于定义了一个函数。这还是把表达式定义的函数当成循环运算的参数,本质上仍是 lambda 语法。只是 SQL 更简洁,已经看不出 lambda 语法的形式了。

SQL 专业面向结构化数据计算,仅支持二维数据表这一种集合,lambda 函数传入参数只能是记录。SQL 就不需要像 Python 那样显式定义一个 row 参数,而可以直接访问字段,这会更便捷。大多数情况下,lambda 函数中可以直接使用字段名,只有存在同名字段时才需要冠以表名(或表别名)以示区分。这样,表名和记录参数都省了,SQL 就把 lambda 函数写成了简单表达式,将 lambda 语法化于无形了。

esProc SPL 继承了 SQL 这些优点,同样把 lambda 化于无形:

employee.derive(if(position == "manager" && salary > 5000, "yes","no"))

数据集合并不只有数据表这一种。SQL 不支持其他形式的集合,处理起来会麻烦了。对于单值成员组成的集合,SQL 还可以用只有一个字段的数据表来对付。比如求一组数值的平方和,SQL 这样写:

create table numbers as
    select value as n
    from (select 4.3 as value union all select 2.3 union all select 6.5 union all select 44.1) t;
select sum(n*n) from numbers;

这组数值还要额外起一个字段名和表名,有点啰嗦了。

Python 支持单值组成的集合,可以用 lambda 语法写出这样的运算:

numbers=pd.Series([4.3,2.3,6.5,44.1])
result=numbers.apply(lambda x: x * x).sum()

SPL 也有单值集合:

numbers=[4.3,2.3,6.5,44.1]
numbers.sum(~*~)

这里,sum 函数对集合循环计算的时候,将当前成员 ~ 传递给 lambda 函数求平方,再由 sum 函数求和。~ 就相当于前面 Python 代码中的 x。

在循环中,lambda 函数几乎总是用到集合的当前成员,SPL 把这个参数固化为 ~ 符号,这样就省去了参数的定义,从而把 lambda 写成简单表达式,继续保持将 lambda 化于无形的优点。

不过,对于这种相对简单的情况,Python 更提倡对位集合运算,可以避免使用 lambda 语法:

numbers=pd.Series([4.3,2.3,6.5,44.1])
result = (numbers * numbers).sum()

这显得更简洁。

前面那个员工标签的例子也可以写出来:

employee['flag'] = np.where((employee['position'] == 'manager') & (employee['salary'] > 5000), 'yes', 'no')

当表达式较复杂的时候,看着就不如 lamdba 语法简洁了。而且这种写法只适用于针对数组做过优化的运算 (比如加减乘除和这里的 if),大部分数学函数都没有做过这种优化,碰到也只能写成 lambda 语法。

说句题外话,这种写法要用 where 函数,逻辑运算符是 &,而前面 lambda 语法中是用 if 函数和 and,Python 语法经常会表现出这种不一致。lambda 语法也有这种不一致问题,在 apply 函数中能用,但 sort_values 中就不能用,这些都会加大学习难度。

SPL 也支持对位集合运算的书写形式:

(numbers ** numbers).sum()

看起来和 Python 差不多,但不如化于无形的 lambda 语法简单了。

SPL 还支持集合的集合。实际上,只要是集合,SPL 就都可以使用化于无形的 lambda 语法。比如求员工超过 10 个的部门有哪些员工:

employee.group(department).select(~.len()>10)

group 函数按部门分组后得到一个大集合,其成员是同一部门员工组成的子集合。表达式 ~.len()>10 是一个 lambda 函数,其中的 ~ 是集合的当前成员,也就是分组子集。

select 函数对大集合循环计算时,将当前成员(子集)传递给 lambda 函数,判断子集长度是否大于 10,再由 select 函数保留或舍弃这个子集。这是很自然的解题思路。

Python 一定程度也可以表示集合的集合,可以写出类似代码:

result=employee.groupby('department').filter(lambda x: len(x) >10)

对于集合的集合,就不能再使用对位运算了,采用 lambda 语法是 Python 最简单的写法,换其它方法,思路和代码都会变得更复杂。

SQL 不能描述集合的集合,对于这个问题要换种思路去实现(麻烦很多),lamdba 语法对这个问题已经无能为力了。

SPL 不仅仅是针对结构化数据计算的,但由于结构化数据过于常见,SPL 和 SQL 一样专门做了语法简化。再看一下前面计算员工标签列的例子,其实 SPL 引用字段的完整写法应该是 ~.position、~.salary,而 SPL 也提供了直接访问字段的便捷机制,就能把 lambda 函数写的和 SQL 一样简洁:

if(position == "manager" && salary > 5000, "yes","no")

Python 没做这种简化,只能写成下面这样,会导致这种最常见的情况写起来比较啰嗦。

lambda row: 'yes' if row['position'] == 'manager' and row['salary'] > 5000 else 'no'

除了当前成员之外,针对有序集合的循环计算经常还会用到成员的序号。比如:要取出一组数值中,第偶数个成员。简单思路是循环计算这个集合,如果成员的序号能被 2 整除就保留,否则就舍弃。

Python 只能给 lambda 函数传入当前成员这一个参数,必须改造这个集合,给每个成员附加上序号,才能实现这个思路:

result = filter(lambda x: x[0] % 2 == 1, enumerate(number))
even_index_members = [x[1] for x in result]

enumerate 函数将 number 的每个成员都变成数组,数组的第 0 个成员是序号,第 1 个成员是原来的数值。这样,lambda 函数才能用 x[0] 取得成员序号。过滤后还要把原来的数值拆出来,这个过程很绕,代码也繁琐。

SQL 基于无序集合,成员序号没有意义,这个问题又得绕路实现。

SPL 用 #表示当前成员的序号,用上述简单思路写出的代码非常简洁:

number.select(# % 2 ==0)

和 ~ 一样,也是 SPL 循环函数中 lambda 函数的传入参数。

小结一下:

SQL 把二维数据表运算的 lambda 语法化于无形,用于描述常规结构化数据集合运算还是比较方便简捷的,但不能支持结构化数据以外的集合。Python 支持各种形式的集合,但 lambda 函数代码写起来也有些啰嗦,适应面不全面,语法风格也不太一致。SPL 继承了 SQL 的所有优势,且对各种形式的集合都可以使用化于无形的 lambda 语法,引入了 ~、# 等符号,进一步简化代码,而且适应面广且风格统一,是三者中最强的。
SPL是开源免费的,欢迎前往乾学院了解更多~~

相关文章
|
搜索推荐 数据可视化 数据挖掘
产品服务数据分析与报告
产品服务数据分析与报告
433 3
|
机器学习/深度学习 算法 Python
【Python机器学习】神经网络中常用激活函数、损失函数、优化方法(图文解释 附源码)
【Python机器学习】神经网络中常用激活函数、损失函数、优化方法(图文解释 附源码)
804 0
|
11月前
|
人工智能 JSON 自然语言处理
除了MCP我们还有什么?
本文详细描述 agents.json ,涵盖了其背景、工作原理、与 OpenAPI 的关系等内容。
906 94
除了MCP我们还有什么?
|
10月前
|
人工智能 资源调度 监控
LangChain脚本如何调度及提效?
本文介绍了通过任务调度系统SchedulerX管理LangChain脚本的方法。LangChain是开源的大模型开发框架,支持快速构建AI应用,而SchedulerX可托管AI任务,提供脚本版本管理、定时调度、资源优化等功能。文章重点讲解了脚本管理和调度、Prompt管理、资源利用率提升、限流控制、失败重试、依赖编排及企业级可观测性等内容。同时展望了AI任务调度的未来需求,如模型Failover、Tokens限流等,并提供了相关参考链接。
482 30
LangChain脚本如何调度及提效?
|
自然语言处理 数据可视化 数据挖掘
带你飞上云端,轻松解析数据——gopup库详细解析--包含安装库教程
本文介绍了Python库gopup,它是一个用于轻松爬取互联网数据的工具,简化了数据收集和处理的过程。文章提供了gopup的安装教程,特别强调了安装时需注意setuptools版本,并给出了PyCharm和命令行两种安装方法。gopup库能获取包括指数、百度和谷歌数据等多种类型的数据。文中还展示了如何使用gopup获取微博指数和豆瓣新片榜数据,并通过代码示例呈现数据和图表。此外,文章提醒了使用时的风险和部分接口的失效情况,并提供了库文档链接以供深入学习。gopup库适用于数据可视化和数据分析,是进行文本挖掘和自然语言处理项目的理想工具。
带你飞上云端,轻松解析数据——gopup库详细解析--包含安装库教程
|
10月前
|
人工智能 安全 API
不到100行代码,实现一个简易通用智能LLM Agent
本文将分享如何使用不到 100 行的 Python 代码,实现一个具备通用智能潜力的简易 LLM Agent。你将看到整个实现过程——从核心原理、提示(Prompt)调优、工具接口设计到主循环交互,并获得完整复现代码的详细讲解。
1850 101
不到100行代码,实现一个简易通用智能LLM Agent
|
8月前
|
数据可视化 数据挖掘
ingest和BBKNN进行单细胞整合(2)
ingest和BBKNN进行单细胞整合(2)
ingest和BBKNN进行单细胞整合(2)
|
8月前
|
负载均衡 监控 应用服务中间件
探索Nginx高效请求处理的奥秘
Nginx采用Epoll等I/O多路复用技术,允许一个线程同时跟踪并处理多个Socket的状态,从而高效处理大量并发请求。这种技术能够有效降低系统资源的消耗,提高处理效率,并在大并发量场景下表现优异。
|
9月前
|
开发工具
HarmonyOS NEXT实战:启动页
本教程介绍如何在HarmonyOS中实现启动页广告功能。通过设置LaunchPage作为入口页面,使用Router模块进行页面跳转,并结合setInterval与clearInterval控制定时器,实现5秒倒计时后自动或手动关闭广告页并跳转至主页的功能。适合初学者掌握HarmonyOS基础开发技巧。
296 0
|
9月前
|
存储 SQL Java
数据存储使用文件还是数据库,哪个更合适?
数据库和文件系统各有优劣:数据库读写性能较低、结构 rigid,但具备计算能力和数据一致性保障;文件系统灵活易管理、读写高效,但缺乏计算能力且无法保证一致性。针对仅需高效存储与灵活管理的场景,文件系统更优,但其计算短板可通过开源工具 SPL(Structured Process Language)弥补。SPL 提供独立计算语法及高性能文件格式(如集文件、组表),支持复杂计算与多源混合查询,甚至可替代数据仓库。此外,SPL 易集成、支持热切换,大幅提升开发运维效率,是后数据库时代文件存储的理想补充方案。

热门文章

最新文章