CLI数据分析【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址https://gitcode.com/gh_mirrors/mi/miller点击查看免费下载导读在 Miller 的put/filterDSL 中map映射是与流式记录处理模型绑定最深的数据类型当前记录$*、全部 out-of-stream 变量的集合*本身就是 map而 map 的插入顺序保持、字符串键归一化、auto-create 与 auto-deepen等特性正是 Miller 能以最少按键完成按字段聚合、跨记录累积、构造嵌套结果这类高频任务的根基。本文将围绕 Miller 官方文档 Maps 参考页 展开完整覆盖 map 字面量、键语义、自动创建/加深、循环遍历、CSV 中 map 字段的扁平化处理并结合 pkg/mlrval/mlrmap.go 与 pkg/bifs/collections.go 的源码实现讲清这些行为背后的设计取舍。读完你可以在mlr put中熟练构造与操纵 map写出与stats1等内置动词等价的聚合脚本。Miller 的完整数据类型清单见 Data types 页本文专门聚焦 map 一种类型。一、map 的形态从字面量到$*与*1.1 Map 字面量与多数编程语言一样Miller 的 map 字面量用花括号书写键是字符串值可以是任意 Miller 数据类型——包括嵌套的 map 或数组。一个特殊之处是字面量里允许写整数键但它会被存成字符串。mlr -n put end { x {a: 1, b: {x: 2, y: [3,4,5]}, 99: true}; dump x; print x[99]; print x[99]; } 输出{ a: 1, b: { x: 2, y: [3, 4, 5] }, 99: true } true true注意这里x[99]与x[99]都打印出true整数索引会在读写时被自动转换为字符串这是 Miller 刻意为之的键语义详见下文字符串键整数的双向转换一节。与数组、参数列表一致map 字面量支持尾随逗号mlr -n put end { x { a : 1, b : 2, c : 3, }; print x; } 输出{ a: 1, b: 2, c: 3 }1.2 当前记录$*就是一个 map在put/filter中$*代表当前流式记录的完整字段集合它就是一个单层map键为字段名、值为字段值。你可以像操作普通 map 一样用方括号索引它mlr --csv --from example.csv head -n 2 then put -q dump $*; print Color is, $*[color]; 输出每条记录一个 map{ color: yellow, shape: triangle, flag: true, k: 1, index: 11, quantity: 43.6498, rate: 9.8870 } Color is yellow { color: red, shape: square, flag: true, k: 2, index: 15, quantity: 79.2778, rate: 0.0130 } Color is red$*的灵活性还体现在整体赋值你可以用一条 map 字面量整体替换当前记录例如交换字段、修改字段并丢弃其余字段mlr --opprint put $* { a: $i, i: $a, y: $y * 10, } data/small输出a i y 1 pan 7.26802 2 eks 5.22151 3 wye 3.3831800000000003 4 eks 1.34188 5 wye 8.636239999999999更完整的字段读写含$[[3]]位置索引、$[计算出的字段名]、unset、NF动态重算等见 DSL variables 页。1.3 out-of-stream 变量集合*也是一个 map所有 out-of-stream 变量以开头的跨记录持久变量的集合*同样是一个 map键是变量名值可以是任意类型包括嵌套 map。下面的例子先用begin块初始化last_rates为空 map逐条记录累积last_rates[$shape] $rate最后在end块整体 dump*mlr --csv --from example.csv put -q begin { last_rates {}; } last_rates[$shape] $rate; last_color $color; end { dump *; } 输出{ last_rates: { triangle: 5.8240, square: 8.2430, circle: 8.3350 }, last_color: purple }由此可以看出 Miller 的三类map 载体流式记录单层、out-of-stream 变量与局部变量可以是多层嵌套例如sum[$x][$y]、以及map 字面量。map 字面量不能出现在赋值表达式的左侧不可对字面量写入。语法上它们看起来像 JSON但 Miller 允许整数键而 JSON 只允许字符串键3而非3。此外多个内置函数以 map 为参数或返回 map例如mapsum、mapdiff、mapexcept、mapselect、mapcount、haskey、get_keys、get_values等详见 pkg/bifs/collections.go。二、核心语义一插入顺序保持Miller map严格保持键的插入顺序这一点与多数编程语言中哈希表无序的直觉不同。例如先写m[y]7再写m[x]3之后无论用哪种循环遍历m得到的键顺序都是y、x。这一语义并非文档空谈而是有明确的源码级实现依据。Miller 的每条记录与 map 值都由 pkg/mlrval/mlrmap.go 中的Mlrmap承载它内部维护一个双向链表Head/Tail指针加每个MlrmapEntry的Prev/Next新增键直接挂到链表尾部因此遍历天然保持插入序见 mlrmap.go。通用的泛型版本见 pkg/lib/ordered_map.go 中的lib.OrderedMap[V]。值得注意的实现细节来自 mlrmap.go 头部设计注释记录在读字符串、写字符串的场景下被访问次数很少因此默认实现可以不预先计算哈希对窄记录字段数少采用线性扫描即可实测对低列宽 CSV/DKVP 有约 10%~25% 的性能收益。但字段数很多时线性扫描代价显著因此Mlrmap采用惰性哈希默认hashRecordstrue字段数达到阈值mlrmapHashThreshold 12时首次查找会构建 key→entry 索引加速对应 mlrmap.go。用户可用mlr --no-hash-records/mlr --hash-records显式切换。换句话说插入顺序是 Miller map 的稳定契约哈希与否只影响查找速度、不影响遍历顺序这也让按输入顺序输出聚合结果成为可能。三、核心语义二字符串键以及整数键的双向转换Miller 的所有 map 键都是字符串。当用整数对 map 进行索引无论是赋值等号的右侧读取还是左侧写入时整数会被自动转成字符串m[3]与m[3]完全等价。这样设计的理由在文档中写得很明白当你在 operating on all records 这类场景下做records[NR] $*时以记录号NR整数作为键必须开箱即用。这也解释了前文x[99]与x[99]结果一致的现象。由此产生一个易混淆点auto-create 的结果是 map 而不是数组即使键全是整数见下一节。如果你想要按下标自动延伸的数组行为必须显式初始化[]。四、核心语义三auto-create自动创建为 map对尚未赋值的局部变量或 out-of-stream 变量做索引赋值会触发auto-create该变量被自动创建为一个 map 变量无需先写begin { last_rates {} }mlr --csv --from example.csv put -q # 下面这行可以写但不必写 # begin { last_rates {} } last_rates[$shape] $rate; end { dump last_rates; } 输出{ triangle: 5.8240, square: 8.2430, circle: 8.3350 }但请记住上面的关键提示auto-create 创建的是 map而非数组哪怕键全是整数。想要自动延伸的数组需要显式begin { my_array [] }。下面的对照实验清晰展示了二者差异——my_array初始化为数组用整数NR索引得到 JSON 数组my_map未初始化auto-create 成 map整数键被字符串化mlr --csv --from example.csv head -n 4 then put -q begin { my_array []; } my_array[NR] $quantity; my_map[NR] $rate; end { dump } 输出{ my_array: [43.6498, 79.2778, 13.8103, 77.5542], my_map: { 1: 9.8870, 2: 0.0130, 3: 2.9010, 4: 7.4670 } }数组与 map 的完整对比一维索引与切片、length/depth等集合函数见 Arrays 页。五、核心语义四auto-deepen自动加深嵌套与 auto-create 对称Miller map 支持auto-deepen你可以直接写m[a][b][c]3无需先手工创建m[a]{}与m[a][b]{}。文档明确指出这是为了以最少的按键完成数据聚合——典型场景是按键分组求和。下面的例子用两层索引quantity_sum[$color][$shape] $rate统计颜色 × 形状的累加和完全不需要任何初始化代码最后用emit把嵌套 map 摊平成记录输出mlr --icsv --opprint --from example.csv put -q quantity_sum[$color][$shape] $rate; end { emit quantity_sum, color, shape; } 输出color shape quantity_sum yellow triangle 9.8870 yellow circle 12.572000000000001 red square 17.011 red circle 2.9010 purple triangle 14.415 purple square 8.2430注意浮点累加产生的12.572000000000001是 IEEE 754 双精度浮点的正常舍入结果若需固定输出位数可配合--ofmt或format_values等处理。多层级联索引的完整语义x_count[$a][$b] 1、emit (x_count, x_sum), a, b等见 DSL variables 页的Indexed out-of-stream variables一节——那里还给出了与mlr stats1 -a count,sum -f x -g a输出逐字段一致的对照示例说明这种 map 聚合写法与内置动词在功能上的等价性。六、遍历single-variable 与 key-value 两种 for 循环Miller 的 DSL 为 map以及$*、*、数组提供两类循环单变量 for 循环single-variable for-loops遍历集合中的每个值键值 for 循环key-value for-loops同时取出键和值例如for (k, v in $*) { ... }、for ((k1, k2), v in *) { ... }。其中在*上做两层键值循环时第一层键k1, k2用圆括号成组绑定。完整语法、作用域规则与示例见 Control structures单变量形式见 single-variable for-loops。需要特别留意的一点见 DSL variablesfor 循环中绑定的k、v是隐式局部变量循环结束后即失效会遮蔽外层同名变量。七、map 值字段与 CSVflatten/unflattenCSV/TSV 这类表格格式没有嵌套结构因此 map 值字段在写回表格格式时需要被扁平化flatten读回 JSON/YAML 时再反扁平化unflatten。Miller 默认用.作为 flatten 分隔符可用--flattensep调整把嵌套键b{x: 2, y: 3}展开为b.x2, b.y3这样的平铺列。完整机制与示例含数组的扁平化、--flattensep、--jvflatsep、--no-auto-unflatten警告等见 Flatten/unflatten 页本页不再展开。这里只需记住只要输入是 JSON/YAML 这类支持嵌套的格式、输出是 CSV/TSV/DKVP 等平铺格式或反向map 字段就会自动经历 flatten/unflatten 转换。八、源码视角map 的底层实现与集合类内建函数8.1Mlrmap插入有序的双向链表 惰性哈希作为全仓库 map 的基础数据结构pkg/mlrval/mlrmap.go 的设计要点如下有序性Mlrmap用Head/Tail与MlrmapEntry.Prev/Next构成双向链表Put新键追加到尾部因此插入顺序保持是结构性的、必然成立查找策略窄记录线性扫描宽记录字段数 ≥mlrmapHashThreshold 12在首次查找时惰性构建keysToEntries哈希索引兼顾低列宽性能与高列宽可扩展性见 mlrmap.go可开关mlr --hash-records/mlr --no-hash-records可全局切换哈希与否见 mlrmap.go。配套的泛型实现lib.OrderedMap[V]在 pkg/lib/ordered_map.go同样以链表 keysToEntries组合提供有序键值存储供非记录场景复用。8.2 集合类内建函数参考文档 收录了操作 map 的内建函数其实现集中在 pkg/bifs/collections.gohaskey(map, key)判断键是否存在键必须是 string 或 int见 collections.go 附近实现mapselect(map, key1, key2, ...)/mapexcept(map, key1, key2, ...)按键筛选/剔除key 可为 string、int 或数组mapsum(map1, map2, ...)/mapdiff(map1, map2, ...)合并多个 map / 求差集mapcount(map)、get_keys(map)、get_values(map)等。它们对非 map 或非法键类型会返回类型错误例如FromNotMapError这也与 Data types 页所述错误error类型用于无法计算的结果一致。九、小结与速查特性行为典型写法字面量花括号 字符串键值可为任意类型{a: 1, b: [2,3]}尾随逗号支持{a:1, b:2,}键类型一律字符串整数自动转换m[3]≡m[3]插入顺序严格保持for (k, v in m)按写入序输出auto-create未初始化即索引 → 自动成 map非数组m[$k] $vauto-deepen多层索引免初始化m[$a][$b] $x当前记录$*是 map可整体读取/赋值$* {a: $i, ...}OOSV 集合*是 map可整体 dump/复制dump *; w v表格格式map 值字段经 flatten/unflatten 处理见 flatten-unflattenMiller map 的四大语义——插入有序、字符串键、auto-create、auto-deepen——共同构成了一套面向流式记录聚合的少按键设计聚合代码无需任何初始化样板输出顺序可预期整数/字符串键混用无摩擦。想深入阅读语言层面类型声明map、var、函数传参等可继续查看 DSL variables 与 Miller programming language结合本文的源码路径即可完整掌握 map 在 Miller 中的一切行为。赞分享CLI数据分析【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址https://gitcode.com/gh_mirrors/mi/miller点击查看免费下载相关推荐Miller DSL 可索引左值Indexable Lvalues完全指南从 $x[1] 到 *[a] 的索引赋值与自动加深机制Miller DSL 可索引左值Indexable Lvalues完全指南从 $x 1 到 a 的索引赋值与自动加深机制 本文以 MilleraCLI数据分析用 Miller 的 DSL 写程序筛法求素数、Mandelbrot 集与 Julia 集的完整实战用 Miller 的 DSL 写程序筛法求素数、Mandelbrot 集与 Julia 集的完整实战 导读 Miller 通常被当作流式数据处理工具使用类似CLI数据分析CVAT 质量报告过滤性能优化target 参数语义与源码实现深度解析CVAT 质量报告过滤性能优化 target 参数语义与源码实现深度解析 导读 本文围绕 CVATComputer Vision Annotation ToCLI数据分析上一篇【亲测免费】 Mocker 使用指南下一篇SSH-Badkeys 项目使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考