spb/metrika Public
Stata-class statistics, GPU-accelerated by Apple Silicon. Native Swift — no Electron, no Python runtime, no compromises.
Swift 92.4%
HTML 3.3%
R 3%
Shell 1.3%
1//2// ZScorePlugin.swift3// Metrika4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7// Copyright © 2026 Simon-Pierre Boucher. All rights reserved.8//910import ZQData11import ZQParser1213/// Reference native plugin (CLAUDE.md §4): `zscore varname` generates14/// `z_varname`, the standardized version of a numeric variable. Declares15/// `mutates: true` and returns the updated dataset via16/// `ZQResult.replacementFrame` — the pattern SPM-compiled-in user17/// plugins follow.18public struct ZScorePlugin: ZQCommandPlugin {19 public static let verb = "zscore"20 public static let syntax = ZQSyntaxSpec(21 acceptsVarlist: true,22 acceptsCondition: false,23 acceptsRange: false,24 acceptsWeight: false,25 options: [],26 mutates: true27 )2829 public init() {}3031 public func execute(_ ctx: ZQContext) async throws -> ZQResult {32 let names = ctx.command.varlist.flatMap(\.referencedNames)33 guard names.count == 1, let name = names.first else {34 throw ZQDataError("zscore: syntax is 'zscore varname'")35 }36 let (values, missing) = try ctx.frame.requireNumeric(name)3738 var count = 039 var sum = 0.040 for i in 0..<values.count where !missing[i] {41 sum += values[i]42 count += 143 }44 guard count > 1 else { throw ZQDataError("zscore: insufficient observations") }45 let mean = sum / Double(count)46 var squared = 0.047 for i in 0..<values.count where !missing[i] {48 squared += (values[i] - mean) * (values[i] - mean)49 }50 let sd = (squared / Double(count - 1)).squareRoot()51 guard sd > 0 else { throw ZQDataError("zscore: '\(name)' is constant") }5253 var scores = [Double](repeating: .nan, count: values.count)54 for i in 0..<values.count where !missing[i] {55 scores[i] = (values[i] - mean) / sd56 }5758 var frame = ctx.frame59 try frame.addColumn(ZQColumn(60 name: "z_\(name)",61 data: .float64(values: scores, missing: missing)62 ))63 return ZQResult(64 text: "(variable z_\(name) generated: mean \(mean), sd \(sd))",65 scalars: ["mean": mean, "sd": sd],66 replacementFrame: frame67 )68 }69}70