EP037 List.Zip:多列表拉链配对压缩(纵向多维数组转置与元素批量比对)

文 / 老汪(汽车零部件财务与商业智能实战派)

“老汪,我们车间有 5 道主要工序。每个班组导出的实际单件加工工时是一行数据,厂部工程部定的标准IE标杆工时是一组基准值。我想快速比对每个班组在‘哪几道工序上达到了全厂标杆水平’,难道要在高级编辑器里一条一条写 if A<=标杆A and B<=标杆B... 吗?有没有批量拉链对齐比对的神奇搞法?”
在汽车零部件制造现场,标准工时(ST)、实际工时与定额计件单价的对比核算是成本会计与精益生产BP的日常重头戏。如果用传统思维硬拼,代码冗长且极难扩展。今天老汪就带大家拆解 M 语言中极具极客美感的一把高阶“拉链刀” —— List.Zip!


一、 为什么老汪说“理解了拉链(Zip),就推开了高阶函数式编程的大门”?

“Zip”在英文里就是拉链的意思。大家脑补一下拉衣服拉链的过程: 左边有一排牙齿,右边有一排牙齿。拉链头一拉过去,左边的第 0 个牙齿精准咬合右边的第 0 个牙齿,第 1 个咬合第 1 个……整整齐齐严丝合缝配对在一起。

在 Power Query 中,List.Zip 干的就是这件神奇的事情!

1. 函数基本语法拆解

List.Zip(lists as list) as list
  • 参数(lists):一个由多个列表组成的“大列表”(List of Lists),形如 { 列表1, 列表2, ... };
  • 返回值(list):纵向压缩配对后的二维列表。

2. 白话工作原理极简演示

假设有两个长度相等的平行列表:

零件列表 = { "控制臂", "制动盘", "副车架" }
工时列表 = { 18.5, 12.0, 35.0 }

我们把它们放进一个大列表中,传给 List.Zip:

List.Zip({ 零件列表, 工时列表 })

瞬间返回由一组组对应元素咬合而成的新列表:

{
    { "控制臂", 18.5 },
    { "制动盘", 12.0 },
    { "副车架", 35.0 }
}

看到这个结构没有?这正是二维数据表的一行一行记录的原型!只要外层套上 Table.FromRows,瞬间就能转变成一张标准的二维表格!

💡 老汪敲黑板:列表长度不一致怎么办?(不齐长补空机制)

如果列表 1 有 3 个元素,列表 2 只有 2 个元素,List.Zip 绝不报红字崩溃,而是非常体贴地用 null 自动补齐短板:

List.Zip({ {"A", "B", "C"}, {1, 2} })
// ➔ 返回 { {"A", 1}, {"B", 2}, {"C", null} }

二、 走进车间:汽车零部件底盘产线工段精益工时标杆比对

拿老汪经手的一家知名汽车轻量化底盘零件制造企业(Tier 1)为例。厂里冲焊、压铸和机加车间共有 8 个主力生产工段。

工程技术部(IE)测定并发布了该控制臂产品 5 道工序的全厂精益标杆工时: – 下料基准:4.2 分钟 – CNC机加基准:18.5 分钟 – 激光焊接基准:6.8 分钟 – 尺寸质检基准:3.2 分钟 – 防锈包装基准:2.5 分钟

月末成本部从 MES 系统导出的各工段《工段工序工时台账》如下(包含 8 列大宽表,文末可免费下载配套 Excel 随堂课件):

产线工段编号 责任带班班长 配套车型客户 型材精密下料(分) CNC加工中心(分) 机器人激光焊(分) 三坐标尺寸质检(分) 钝化防锈包装(分)
LINE-01-A 张建国(精益先锋) 比亚迪海豹 4.2 18.5 6.8 3.2 2.5
LINE-01-B 李明华 特斯拉Model Y 4.8 19.2 7.5 3.5 2.8
LINE-02-A 王志刚 吉利极氪001 4.0 18.0 7.2 3.1 2.4
LINE-02-B 赵德利 蔚来ES6总成 4.5 21.0 8.0 3.8 3.0
LINE-03-A 孙强 理想L7底盘 4.1 18.2 6.5 3.0 2.6
LINE-03-B 刘波 小鹏G6三电 5.0 22.5 8.5 4.0 3.2
LINE-04-A 陈明 小米SU7总成 4.2 18.5 6.7 3.1 2.5
LINE-04-B 周福海 广汽埃安Hyper 4.6 20.0 7.8 3.4 2.9

📱 移动端排版体验:上方表格已配置横向平滑滚动容器,在手机端手指左右滑动即可舒展查看完整 8 列数据,单元格字距舒展绝不挤爆!

我们要算清楚:每个工段在 5 道关键工序中,究竟有几道工序的耗时达到或超越了全厂 IE 标杆水平(即 实际工时 <= 标杆工时)?


三、 白话拆解:List.Zip 拉链对齐批量比对四步法

[工段实际工时列表] ───┐
                      ├─ List.Zip ─→ { {4.2, 4.2}, {18.5, 18.5}, {6.8, 6.8}... }
[全厂IE标杆工时列表] ──┘                     │
                                      (List.Select 批量比对)
                                             ↓
                                      { 实际 <= 标杆 的优胜组 }
                                             ↓
                                      List.Count ➔ 达标项数 (如 5 项)
  1. 第一步(取实际):用 Record.SelectFields 与 Record.ToList 将当前行的 5 个工序工时提取为数值列表;
  2. 第二步(拉拉链):调用 List.Zip({实际工时列表, 全厂标杆工时列表}),把对应工序的两组数据自动配成一个个小对子:{实际耗时, 标杆耗时};
  3. 第三步(批量断):用 List.Select(配对组, each _{0} <= _{1}) 瞬间抓出所有达标的工序(_{0} 为实际,_{1} 为标杆);
  4. 第四步(数个数):套上 List.Count,立马得出该班组达到了几个标杆,彻底告别写 5 遍 if 判断的笨办法!

四、 高级编辑器手把手实操代码

在随堂 Excel 课件中,老汪已将 Q001 动态相对路径标准置入【配置】表。进入 Power Query 高级编辑器,贴入老汪的标准自适应代码:

let
    // 💡 老汪敲黑板(免改路径直接刷新秘籍):换电脑解压后直接点【全部刷新】永不报红字!
    当前路径 = try 
        let 
            raw = Text.From(Excel.CurrentWorkbook(){[Name="当前路径"]}[Content]{0}[Column1]),
            clean = Text.Trim(raw)
        in 
            if Text.EndsWith(clean, "\") then clean else clean & "\"
    otherwise 
        "./",

    // 1. 读取原始业务数据源
    数据源路径 = 当前路径 & "原始业务数据表.xlsx",
    源 = Excel.Workbook(File.Contents(数据源路径), null, true),
    工段原始表 = 源{[Item="工段工序工时台账", Kind="Sheet"]}[Data],
    提升标题 = Table.PromoteHeaders(工段原始表, [PromoteAllScalars=true]),
    更改类型 = Table.TransformColumnTypes(提升标题, {
        {"产线工段编号", type text},
        {"责任带班班长", type text},
        {"配套车型客户", type text},
        {"型材精密下料(分)", type number},
        {"CNC加工中心(分)", type number},
        {"机器人激光焊(分)", type number},
        {"三坐标尺寸质检(分)", type number},
        {"钝化防锈包装(分)", type number}
    }),

    // 2. 统一定义需要比对的工序字段与标杆基准
    工序字段列表 = {
        "型材精密下料(分)",
        "CNC加工中心(分)",
        "机器人激光焊(分)",
        "三坐标尺寸质检(分)",
        "钝化防锈包装(分)"
    },

    // 5 道工序的标准基准耗时(分钟)
    全厂标杆工时列表 = { 4.2, 18.5, 6.8, 3.2, 2.5 },

    // 3. 核心大招:List.Zip 拉链配对 + 批量条件筛选
    添加达标工序数 = Table.AddColumn(更改类型, "达到/优于标杆工序数(项)", each 
        let 
            当前工时记录 = Record.SelectFields(_, 工序字段列表),
            当前工时列表 = Record.ToList(当前工时记录),
            // ★核心拉链配对:瞬间组合为 {{实际1, 标杆1}, {实际2, 标杆2}...}
            拉链配对列表 = List.Zip({当前工时列表, 全厂标杆工时列表}),
            // 筛选出 实际耗时 <= 标杆耗时 的优胜工序对
            达标组 = List.Select(拉链配对列表, each _{0} <= _{1}),
            达标数量 = List.Count(达标组)
        in 
            达标数量, Int64.Type),

    // 4. 计算工段单件总耗时与标杆达成率
    添加实际总工时 = Table.AddColumn(添加达标工序数, "实际单件总工时(分)", each 
        List.Sum(Record.ToList(Record.SelectFields(_, 工序字段列表))), type number),

    添加达成率 = Table.AddColumn(添加实际总工时, "标杆工序达成率", each 
        Text.From(Number.Round([#"达到/优于标杆工序数(项)"] / List.Count(全厂标杆工时列表) * 100, 1)) & "%", type text),

    // 5. 评定精益星级与激励
    添加评级对策 = Table.AddColumn(添加达成率, "精益班组评级与激励", each 
        if [#"达到/优于标杆工序数(项)"] = 5 then "🏆 五星全能标杆班组(发放最高精益奖)"
        else if [#"达到/优于标杆工序数(项)"] >= 3 then "⭐ 四星先锋班组(达标工序符合预期)"
        else "⚠️ 效率滞后工段(列入下周IE改善辅导名单)", type text),

    // 整理最终呈现报表
    整理输出字段 = Table.SelectColumns(添加评级对策, {
        "产线工段编号", "责任带班班长", "配套车型客户", 
        "实际单件总工时(分)", "达到/优于标杆工序数(项)", "标杆工序达成率", "精益班组评级与激励"
    })
in
    整理输出字段

五、 最终清洗效果与车间管理看板

运行上述 M 脚本后,我们在 Excel 中得到的标准化分析看板如下(支持移动端自由横向滑动):

产线工段编号 责任带班班长 配套车型客户 实际单件总工时(分) 达到/优于标杆工序数(项) 标杆工序达成率 精益班组评级与激励
LINE-01-A 张建国(精益先锋) 比亚迪海豹 35.2 5 100.0% 🏆 五星全能标杆班组(发放最高精益奖)
LINE-01-B 李明华 特斯拉Model Y 37.8 0 0.0% ⚠️ 效率滞后工段(列入下周IE改善辅导名单)
LINE-02-A 王志刚 吉利极氪001 34.7 4 80.0% ⭐ 四星先锋班组(达标工序符合预期)
LINE-02-B 赵德利 蔚来ES6总成 40.3 0 0.0% ⚠️ 效率滞后工段(列入下周IE改善辅导名单)
LINE-03-A 孙强 理想L7底盘 34.4 4 80.0% ⭐ 四星先锋班组(达标工序符合预期)
LINE-03-B 刘波 小鹏G6三电 43.2 0 0.0% ⚠️ 效率滞后工段(列入下周IE改善辅导名单)
LINE-04-A 陈明 小米SU7总成 35.0 5 100.0% 🏆 五星全能标杆班组(发放最高精益奖)
LINE-04-B 周福海 广汽埃安Hyper 38.7 0 0.0% ⚠️ 效率滞后工段(列入下周IE改善辅导名单)

看!通过 List.Zip,全厂 8 个工段的标杆达标情况一秒算出。张建国班组和陈明班组全工序达标,稳稳拿下五星最高精益激励;而李明华、刘波工段工时偏长,IE 改善工程师直接进驻辅导!


六、 老汪从业多年总结的 2 个避坑指南

  1. 务必牢记外层需要传“列表的列表” { {A}, {B} }: – 很多初学者写 List.Zip(列表A, 列表B),结果直接报错。牢记 List.Zip 只有一个参数,多个列表必须用大括号包起来成为一个母列表:List.Zip({列表A, 列表B})!
  2. 多列构造二维表的神仙组合:Table.FromRows + List.Zip: – 如果你有 3 个独立提取出的列表:工号列表、姓名列表、计件工资列表,怎么一瞬间把它们拼成一张数据表? – 答案是: powerquery Table.FromRows(List.Zip({工号列表, 姓名列表, 计件工资列表}), {"工号", "姓名", "实发计件工资"}) – 这一招在解析多层嵌套 JSON、API 接口或者复杂分段汇总时,堪称降维打击利器!

大家赶紧在文末免费下载随堂实操课件包,打开 PQ 高级编辑器跟老汪同步动手操练起来!

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容