chronaxis.results.readable_format
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""将已计算的时间、状态和质量格式化为可读文字,不改变计算结果。23输入为精简结果的标量,输出为CSV单元格或清单说明。整数纳秒用整数运算4转换为十进制秒,不经过浮点,不补造日期。此模块无文件和输入读取副作用。5"""67# 系统词汇集中维护;业务名称、表达式和普通结果数值不翻译或舍入。8STATES = {9 'complete': '完整', 'partial': '部分有效', 'indeterminate': '无法确定',10 'closed': '正常结束', 'interrupted': '数据中断',11 'truncated': '观测结束,活动尚未确认结束', 'timeout': '超时结束',12 'point': '瞬时事件', 'empty': '空窗口', 'matched': '已响应',13 'no_response': '时限内未响应', 'true': '满足条件', 'unknown': '条件未知',14}15REASONS = {16 'ok': '', 'partial_coverage': '仅统计有效部分', 'partial_input': '输入仅部分有效',17 'insufficient_coverage': '有效数据不足', 'no_observation': '没有所需观测数据',18 'zero_length_window': '统计范围长度为零', 'calculation_failed': '计算失败',19 'empty_selection': '条件已知,未选中数据', 'unknown_selection': '无法确定选中哪些数据',20 'no_samples': '没有可用样本', 'no_valid_selected_data': '选中范围内没有有效数据',21 'insufficient_samples': '有效样本数不足以使用所选自由度',22 'no_evaluations': '没有参与计算的报文', 'contributed': '已计入',23 'missing_input': '缺少计算输入', 'condition_false': '不满足条件,已跳过',24 'evaluation_error': '计算失败,已跳过', 'nonfinite_input': '输入不是有限数,已跳过',25 'observed': '实际观测', 'estimated': '估计值', 'missing': '数据缺失',26 'missing_right': '缺少右侧样本', 'sufficient': '满足覆盖率要求',27}28QUALITY = ((1, '数据缺失'), (2, '数据过期'), (4, '解码失败'), (8, '非有限值'),29 (16, '数值超出允许范围'), (32, '估计值'), (64, '数值溢出'),30 (128, '精度损失'), (256, '计算定义域错误'))31OUTCOMES = {32 'no_observation': '没有所需观测数据', 'insufficient_data': '数据不足,无法确定是否发生事件',33 'events_found': '已找到满足要求的事件', 'duration_not_met': '条件曾成立,但持续时间不满足要求',34 'condition_not_met': '观测范围内条件未成立', 'no_event': '没有产生事件',35}363738def seconds(value):39 """精确转换纳秒,不产生第二套时间值。4041 参数: value为整数纳秒或None。返回: 最多9位小数的秒文本;None为空字符串。42 异常: TypeError为非整数输入;不接受bool或浮点纳秒。43 """44 if value is None:45 return ''46 if type(value) is not int:47 raise TypeError('可读时间需要整数纳秒')48 # 负时间使用绝对值拆分,避免Python负整数除法改变小数部分。49 whole, fraction = divmod(abs(value), 1_000_000_000)50 text = str(whole) + (('.' + f'{fraction:09d}'.rstrip('0')) if fraction else '')51 return ('-' if value < 0 else '') + text525354def quality_text(value):55 """翻译已设置质量位,0不声称数据绝对正确。5657 参数: value为质量整数。返回: 中文原因,以分号分隔;0为空。58 异常: 类型错误传播;未知位明确保留提示,不能静默丢失。59 """60 parts = [text for mask, text in QUALITY if int(value) & mask]61 remainder = int(value) & ~sum(mask for mask, _ in QUALITY)62 if remainder:63 parts.append(f'未识别质量标记{remainder}')64 return ';'.join(parts)656667def explanation(row):68 """合并一项结果的必要原因和质量,不把普通ok当作诊断。6970 参数: row为已校验平坦字段。返回: 中文说明。异常: 缺失字段错误传播。71 """72 reason = row.get('reason', '')73 parts = [REASONS.get(reason, reason), quality_text(row['quality'])]74 if row.get('coverage_status') == 'indeterminate':75 parts.append('未达到要求的覆盖率')76 return ';'.join(dict.fromkeys(part for part in parts if part))777879def outcome_messages(outcome):80 """解释同次分析的轻量概况,不额外寻找未知区间。8182 参数: outcome为AnalysisOutcome或None。返回: 每规则一条说明的列表。83 异常: 无;有未知与已有完整事件可以同时出现。84 """85 if outcome is None:86 return []87 messages = []88 for item in outcome.rules:89 text = f'{item.rule_id}:{OUTCOMES[item.status]}({item.event_count}个)'90 if item.has_unknown:91 text += ';其他观测中存在条件未知,不据此否定已输出区间的计算状态'92 if item.unobserved_signals:93 text += ';未观测到:' + '、'.join(item.unobserved_signals)94 messages.append(text)95 return messages96