airpredict/空气质量预测/源码/服务端/iapip-svr/test/material-ranking.test.ts

55 lines
2.3 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { computeYpt, buildTierClassifier, annotate } from '../src/common/material-ranking'
const M = (methanal: number|null, tvoc: number|null, benzene: number|null, toluene: number|null, pxylene: number|null) => ({
methanal_be_area: methanal, tvoc_be_area: tvoc, benzene_be_area: benzene, toluene_be_area: toluene, p_xylene_be_area: pxylene
})
describe('computeYpt', () => {
it('按权重求和', () => {
// 1*1.8 + 2*0.25 + 3*1 + 4*0.45 + 5*0.38 = 1.8+0.5+3+1.8+1.9 = 9
expect(computeYpt(M(1, 2, 3, 4, 5))).toBeCloseTo(9, 6)
})
it('任一缺值返回 null', () => {
expect(computeYpt(M(1, null, 3, 4, 5))).toBeNull()
expect(computeYpt(M(1, 2, 3, 4, undefined as never))).toBeNull()
})
})
describe('buildTierClassifier', () => {
it('全库三分位: 6 个材料 -> 2A 2B 2C', () => {
// ypt 分别为 1..6(每个只用 methanal 权重 1.8,但相对顺序即 methanal 顺序)
const all = [1, 2, 3, 4, 5, 6].map(v => M(v, 0, 0, 0, 0))
const classify = buildTierClassifier(all)
const ypts = all.map(computeYpt)
expect(ypts.map(classify)).toEqual(['A', 'A', 'B', 'B', 'C', 'C'])
})
it('缺值材料不参与分位、classify(null)=null', () => {
const all = [M(1,0,0,0,0), M(2,0,0,0,0), M(3,0,0,0,0), M(1,null,0,0,0)]
const classify = buildTierClassifier(all)
expect(classify(null)).toBeNull()
})
it('相同 Ypt 落同档(并列不强拆)', () => {
const all = [M(1,0,0,0,0), M(1,0,0,0,0), M(1,0,0,0,0)]
const classify = buildTierClassifier(all)
const tiers = all.map(m => classify(computeYpt(m)))
expect(new Set(tiers).size).toBe(1) // 全相等 -> 同一档
})
it('小 n=1 -> 该材料为 A', () => {
const all = [M(5,0,0,0,0)]
const classify = buildTierClassifier(all)
expect(classify(computeYpt(all[0]))).toBe('A')
})
})
describe('annotate', () => {
it('挂上 ypt 与 health_tier', () => {
const all = [M(1,0,0,0,0), M(2,0,0,0,0), M(3,0,0,0,0)]
const classify = buildTierClassifier(all)
const out = annotate(all, classify)
expect(out[0].health_tier).toBe('A')
expect(out[0].ypt).toBeCloseTo(1.8, 6)
expect(out[2].health_tier).toBe('C')
})
})