SPB Git

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%
3.1 KB · 89 lines swift
Raw Blame History
1//2//  MarginsTests.swift3//  Metrika4//5//  Author:  Simon-Pierre Boucher6//  Contact: contact@spboucher.ai7//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.8//910import Foundation11import Testing12import ZQEngine1314@Suite("margins", .serialized)15struct MarginsTests {16    let fixtures: Fixtures17    let session: ZQSession1819    init() async throws {20        self.fixtures = try Fixtures()21        self.session = try ZQSession(discoverUserCommands: false)22        _ = try await session.execute("use \(fixtures.datasetURL.path)")23        _ = try await session.execute("gen log_rev = ln(revenue)")24    }2526    @Test("OLS marginal effect is the coefficient with its SE")27    func olsMargins() async throws {28        let fit = try await session.execute("reg log_rev price")29        let margins = try await session.execute("margins, dydx(price)")30        #expect(margins.scalars["dydx_price"] == fit.scalars["b_price"])31        #expect(margins.scalars["se_price"] == fit.scalars["se_price"])32    }3334    @Test("logit AME and delta-method SE match R")35    func logitMargins() async throws {36        _ = try await session.execute("logit purchase price")37        let margins = try await session.execute("margins, dydx(price)")38        expectClose(39            try #require(margins.scalars["dydx_price"]),40            fixtures["margins_logit_price"], "logit AME"41        )42        expectClose(43            try #require(margins.scalars["se_price"]),44            fixtures["margins_logit_se"], "logit delta SE"45        )46    }4748    @Test("poisson AME and delta-method SE match R")49    func poissonMargins() async throws {50        _ = try await session.execute("poisson orders price")51        let margins = try await session.execute("margins, dydx(price)")52        expectClose(53            try #require(margins.scalars["dydx_price"]),54            fixtures["margins_pois_price"], "poisson AME"55        )56        expectClose(57            try #require(margins.scalars["se_price"]),58            fixtures["margins_pois_se"], "poisson delta SE"59        )60    }6162    @Test("dydx over factor terms is rejected for now")63    func factorRejected() async throws {64        _ = try await session.execute("reg log_rev price i.region")65        await #expect(throws: ZQEngineError.self) {66            _ = try await session.execute("margins, dydx(region)")67        }68        // Continuous term of the same model still works.69        let margins = try await session.execute("margins, dydx(price)")70        #expect(margins.scalars["dydx_price"] != nil)71    }7273    @Test("margins requires estimation results and a dydx() option")74    func validation() async throws {75        let fresh = try ZQSession(discoverUserCommands: false)76        _ = try await fresh.execute("use \(fixtures.datasetURL.path)")77        await #expect(throws: ZQEngineError.self) {78            _ = try await fresh.execute("margins, dydx(price)")79        }80        _ = try await session.execute("reg log_rev price")81        await #expect(throws: ZQEngineError.self) {82            _ = try await session.execute("margins")83        }84        await #expect(throws: ZQEngineError.self) {85            _ = try await session.execute("margins, dydx(nonexistent)")86        }87    }88}89