From 1c48f91be5dc093bd78222884d601dcaf0933903 Mon Sep 17 00:00:00 2001 From: SOR <3272194354@qq.com> Date: Sun, 21 Jun 2026 11:05:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E5=8F=96=E5=B7=A5=E5=85=B7=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bridge resistance measurement.py | 73 +++---------------------- Hall effect.py | 73 +++---------------------- Newton's rings.py | 30 +---------- Speed of sound.py | 18 +------ Surface tension coefficient.py | 93 ++++---------------------------- Viscosity coefficient.py | 56 +++---------------- Young's modulus.py | 69 ++++-------------------- degree.py | 60 +++------------------ lenth.py | 21 +------- utils/__init__.py | 16 ++++++ utils/formatting.py | 76 ++++++++++++++++++++++++++ utils/io.py | 59 ++++++++++++++++++++ utils/stats.py | 59 ++++++++++++++++++++ 13 files changed, 259 insertions(+), 444 deletions(-) create mode 100644 utils/__init__.py create mode 100644 utils/formatting.py create mode 100644 utils/io.py create mode 100644 utils/stats.py diff --git a/Bridge resistance measurement.py b/Bridge resistance measurement.py index 309ff16..823e100 100644 --- a/Bridge resistance measurement.py +++ b/Bridge resistance measurement.py @@ -1,5 +1,11 @@ import math from decimal import Decimal, ROUND_HALF_EVEN +from utils import ( + scientific_round, + calculate_stats, + input_decimal, + input_int +) """ Wheatstone Bridge Resistance Measurement Calculator @@ -56,71 +62,6 @@ Wheatstone Bridge Resistance Measurement Calculator Rx 末位与不确定度对齐 """ -def scientific_round(value, uncertainty): - """ - 按照"四舍六入五凑偶"修约: - 1. 不确定度 u 保留一位有效数字 - 2. 平均值末位与 u 对齐 - """ - if uncertainty <= 0: - return str(value), "0" - - u_float = float(uncertainty) - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final - -def calculate_stats(data_list, delta_inst): - """ - 计算一组数据的平均值、A类、B类及合成不确定度 - u(X) = sqrt( u_A^2 + u_B^2 ) - u_A = s / sqrt(n) - u_B = Δ_inst / sqrt(3) - """ - n = len(data_list) - mean = sum(data_list) / Decimal(n) - - if n > 1: - variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1) - s = Decimal(str(math.sqrt(float(variance)))) - u_a = s / Decimal(str(math.sqrt(n))) - else: - u_a = Decimal("0") - - u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3))) - u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2)))) - - return mean, u_a, u_b, u_combined - -def input_decimal(prompt, default=None): - """输入一个 Decimal 值,支持默认值""" - while True: - try: - suffix = f" (默认 {default})" if default else "" - val = input(f"{prompt}{suffix}: ").strip() - if not val and default is not None: - return Decimal(str(default)) - if not val: - continue - return Decimal(val) - except Exception: - print("输入无效,请输入数字。") - -def input_int(prompt, default=None): - """输入一个整数,支持默认值""" - while True: - try: - suffix = f" (默认 {default})" if default else "" - val = input(f"{prompt}{suffix}: ").strip() - if not val and default is not None: - return default - return int(val) - except Exception: - print("输入无效,请输入整数。") def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements): """ @@ -174,7 +115,7 @@ def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements): delta_inst = R0_mean / S # --- 不确定度分析 --- - R0_mean_final, u_a_R0, u_b_R0, u_R0 = calculate_stats(R0_data, str(delta_inst)) + R0_mean_final, u_R0, u_a_R0, u_b_R0, _ = calculate_stats(R0_data, str(delta_inst)) # Rx = C * R0 Rx_mean = C * R0_mean_final diff --git a/Hall effect.py b/Hall effect.py index fffaa01..91675b2 100644 --- a/Hall effect.py +++ b/Hall effect.py @@ -83,68 +83,11 @@ Hall Effect Experiment Data Processor # 通用工具函数 # ═══════════════════════════════════════════════════════ -def scientific_round(value, uncertainty): - """ - 按照"四舍六入五凑偶"修约: - 1. 不确定度 u 保留一位有效数字 - 2. 结果末位与 u 对齐 - """ - if uncertainty <= 0: - return str(value), "0" - - d_val = Decimal(str(value)) - d_unc = Decimal(str(uncertainty)) - - first_digit_pos = math.floor(math.log10(float(d_unc))) - prec = Decimal('1e' + str(first_digit_pos)) - - u_final = d_unc.quantize(prec, rounding=ROUND_HALF_EVEN) - val_final = d_val.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final - - -def linear_regression(x_list, y_list): - """ - 最小二乘法线性回归: y = k·x + b - 返回: 斜率 k, 截距 b, 相关系数 r, 斜率不确定度 u_k - u_k = |k| · √[(1/r² − 1) / (n − 2)] - """ - n = len(x_list) - x_mean = sum(x_list) / n - y_mean = sum(y_list) / n - - Lxx = sum((xi - x_mean) ** 2 for xi in x_list) - Lyy = sum((yi - y_mean) ** 2 for yi in y_list) - Lxy = sum((x_list[i] - x_mean) * (y_list[i] - y_mean) for i in range(n)) - - k = Lxy / Lxx if Lxx != 0 else 0.0 - b = y_mean - k * x_mean - - denom = math.sqrt(Lxx * Lyy) if Lxx * Lyy > 0 else 1.0 - r = Lxy / denom - - if abs(r) < 1.0 and n > 2: - u_k = abs(k) * math.sqrt((1.0 / r ** 2 - 1.0) / (n - 2)) - else: - u_k = 0.0 - - return k, b, r, u_k - - -def input_float(prompt, default=None): - """输入一个浮点数值,支持默认值""" - while True: - try: - suffix = f" (默认 {default})" if default is not None else "" - val = input(f"{prompt}{suffix}: ").strip() - if not val and default is not None: - return float(default) - if not val: - continue - return float(val) - except Exception: - print(" 输入无效,请输入数字。") +from utils import ( + scientific_round, + linear_regression, + input_float +) def print_regression(label, k, b, r, u_k, x_unit, y_unit): @@ -259,7 +202,7 @@ def main(): print(f"{'─' * 46}") # 线性回归: VH(mV) vs Is(mA) —— 使用绝对值 VH - slope_1, intercept_1, r_1, u_slope_1 = linear_regression(Is_1, VH_abs_1) + slope_1, intercept_1, r_1, u_slope_1 = map(float, linear_regression(Is_1, VH_abs_1)) print_regression("VH-Is", slope_1, intercept_1, r_1, u_slope_1, "mA", "mV") # 注: 斜率单位 mV/mA = V/A @@ -321,7 +264,7 @@ def main(): print(f"{'─' * 46}") # 线性回归: VH(mV) vs Im(mA) - slope_2, intercept_2, r_2, u_slope_2 = linear_regression(Im_2, VH_abs_2) + slope_2, intercept_2, r_2, u_slope_2 = map(float, linear_regression(Im_2, VH_abs_2)) print_regression("VH-Im", slope_2, intercept_2, r_2, u_slope_2, "mA", "mV") input("\n 按回车继续...") @@ -355,7 +298,7 @@ def main(): print(f"{'─' * 36}") # 线性回归: Vσ(mV) vs Is(mA) - slope_3, intercept_3, r_3, u_slope_3 = linear_regression(Is_3, Vsigma) + slope_3, intercept_3, r_3, u_slope_3 = map(float, linear_regression(Is_3, Vsigma)) print_regression("Vσ-Is", slope_3, intercept_3, r_3, u_slope_3, "mA", "mV") print(f" 注: 斜率 = Vσ/Is, 单位 mV/mA = V/A = Ω (样品电阻)") diff --git a/Newton's rings.py b/Newton's rings.py index 4db7654..6a2bd6b 100644 --- a/Newton's rings.py +++ b/Newton's rings.py @@ -8,35 +8,7 @@ Newton's Rings Curvature Radius Calculator (Successive Difference Method) 参考 lenth.py 的高精度与不确定度修约逻辑。 """ -def get_decimal_places(data_strings): - """获取输入数据中小数点后的保留位数,用于自适应展示""" - max_places = 0 - for s in data_strings: - if '.' in s: - places = len(s.split('.')[1]) - max_places = max(max_places, places) - return max_places - -def scientific_round(value, uncertainty): - """ - 按照“四舍六入五凑偶”修约: - 1. 不确定度 u 保留一位有效数字 - 2. 平均值末位与 u 对齐 - """ - if uncertainty == 0: - return str(value), "0" - - u_float = float(uncertainty) - # 找到第一位非零数字的位置 - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - # 不确定度修约 - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - # 平均值修约,保持与不确定度末位对齐 - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final +from utils import get_decimal_places, scientific_round def main(): # --- 1. 参数与常量初始化 --- diff --git a/Speed of sound.py b/Speed of sound.py index 6fc46b3..e76ad35 100644 --- a/Speed of sound.py +++ b/Speed of sound.py @@ -33,23 +33,7 @@ Speed of Sound Calculator (Successive Difference Method) - 声速 v 的不确定度 (忽略 f 的极小不确定度): u(v) = f * u(λ) """ -def scientific_round(value, uncertainty): - """ - 按照“四舍六入五凑偶”修约: - 1. 不确定度 u 保留一位有效数字 - 2. 平均值末位与 u 对齐 - """ - if uncertainty == 0: - return str(value), "0" - - u_float = float(uncertainty) - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final +from utils import scientific_round def calculate_method(method_name, f, k, delta_inst): print(f"\n========================================") diff --git a/Surface tension coefficient.py b/Surface tension coefficient.py index 21b998b..71a3502 100644 --- a/Surface tension coefficient.py +++ b/Surface tension coefficient.py @@ -44,87 +44,12 @@ Surface Tension Coefficient Calculator (Pull-off Method) u_B,X = Δ_inst / sqrt(3) """ -def scientific_round(value, uncertainty): - """ - 按照“四舍六入五凑偶”修约: - 1. 不确定度 u 保留一位有效数字 - 2. 平均值末位与 u 对齐 - """ - if uncertainty <= 0: - return str(value), "0" - - u_float = float(uncertainty) - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final - -def calculate_stats(data_list, delta_inst): - """计算一组数据的平均值、A类、B类及合成不确定度""" - n = len(data_list) - mean = sum(data_list) / Decimal(n) - - if n > 1: - variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1) - s = Decimal(str(math.sqrt(float(variance)))) - u_a = s / Decimal(str(math.sqrt(n))) - else: - u_a = Decimal("0") - - u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3))) - u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2)))) - - return mean, u_combined - -def linear_regression(x_list, y_list): - """ - 对 (x, y) 列表进行线性回归,返回斜率 k, 截距 b, 及斜率的标准不确定度 u_k - """ - n = len(x_list) - x_mean = sum(x_list) / Decimal(n) - y_mean = sum(y_list) / Decimal(n) - - L_xx = sum((x - x_mean)**2 for x in x_list) - L_yy = sum((y - y_mean)**2 for y in y_list) - L_xy = sum((x_list[i] - x_mean) * (y_list[i] - y_mean) for i in range(n)) - - # 斜率 - k = L_xy / L_xx - # 截距 - b = y_mean - k * x_mean - - # 相关系数 r - r = L_xy / Decimal(str(math.sqrt(float(L_xx * L_yy)))) - - # 斜率的标准不确定度 u_k - # u_k = k * sqrt( (1/r^2 - 1) / (n - 2) ) - r_float = float(r) - if abs(r_float) < 1.0: - u_k_float = float(k) * math.sqrt( (1.0 / (r_float**2) - 1.0) / (n - 2) ) - else: - u_k_float = 0.0 - u_k = Decimal(str(abs(u_k_float))) - - return k, b, r, u_k - -def input_data_group(name, count, unit=""): - """输入一组重复测量的数值""" - prompt_unit = f" (单位: {unit})" if unit else "" - print(f"\n请输入 {name} 的 {count} 次测量值{prompt_unit}:") - data = [] - for i in range(count): - while True: - try: - val = input(f"第 {i+1} 次 = ").strip() - if not val: continue - data.append(Decimal(val)) - break - except Exception: - print("输入无效,请输入数字。") - return data +from utils import ( + scientific_round, + calculate_stats, + linear_regression, + input_data_group +) def main(): print("========================================") @@ -196,15 +121,15 @@ def main(): # 输入拉脱电压 U_pull (6次) U_pull_vals = input_data_group("拉脱时的电压差/峰值电压 U_pull", 6, "同标定电压单位") - U_pull_mean, U_pull_u = calculate_stats(U_pull_vals, delta_U) + U_pull_mean, U_pull_u = calculate_stats(U_pull_vals, delta_U)[:2] # 输入内径 d1 (6次) d1_vals = input_data_group("铁环内径 d1", 6, "mm") - d1_mean, d1_u = calculate_stats(d1_vals, delta_d) + d1_mean, d1_u = calculate_stats(d1_vals, delta_d)[:2] # 输入外径 d2 (6次) d2_vals = input_data_group("铁环外径 d2", 6, "mm") - d2_mean, d2_u = calculate_stats(d2_vals, delta_d) + d2_mean, d2_u = calculate_stats(d2_vals, delta_d)[:2] # --- 3. 表面张力系数计算 --- # F = U / K diff --git a/Viscosity coefficient.py b/Viscosity coefficient.py index e4a6e63..5b36fd1 100644 --- a/Viscosity coefficient.py +++ b/Viscosity coefficient.py @@ -38,55 +38,11 @@ Viscosity Coefficient Calculator (Stokes' Law) u_B,X = Δ_inst / sqrt(3) """ -def scientific_round(value, uncertainty): - """ - 按照“四舍六入五凑偶”修约: - 1. 不确定度 u 保留一位有效数字 - 2. 平均值末位与 u 对齐 - """ - if uncertainty <= 0: - return str(value), "0" - - u_float = float(uncertainty) - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final - -def calculate_stats(data_list, delta_inst): - """计算一组数据的平均值、A类、B类及合成不确定度""" - n = len(data_list) - mean = sum(data_list) / Decimal(n) - - if n > 1: - variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1) - s = Decimal(str(math.sqrt(float(variance)))) - u_a = s / Decimal(str(math.sqrt(n))) - else: - u_a = Decimal("0") - - u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3))) - u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2)))) - - return mean, u_combined - -def input_data_group(name, count, unit=""): - """输入一组重复测量的数值""" - print(f"\n请输入 {name} 的 {count} 次测量值" + (f" (单位: {unit})" if unit else "") + ":") - data = [] - for i in range(count): - while True: - try: - val = input(f"第 {i+1} 次 = ").strip() - if not val: continue - data.append(Decimal(val)) - break - except Exception: - print("输入无效,请输入数字。") - return data +from utils import ( + scientific_round, + calculate_stats, + input_data_group +) def main(): print("========================================") @@ -129,7 +85,7 @@ def main(): # 输入5个时间数据 t_vals = input_data_group("小球下落时间 t", 5, "s") - t_mean, t_u = calculate_stats(t_vals, delta_t) + t_mean, t_u = calculate_stats(t_vals, delta_t)[:2] # --- 3. 粘滞系数计算 --- # 统一转化为国际标准单位 (m, kg, s) diff --git a/Young's modulus.py b/Young's modulus.py index 383e49a..22945c5 100644 --- a/Young's modulus.py +++ b/Young's modulus.py @@ -20,62 +20,11 @@ Young's Modulus Calculator (Optical Lever & Successive Difference Method) u_B,X = Δ_inst / sqrt(3) """ -def scientific_round(value, uncertainty): - """ - 按照“四舍六入五凑偶”修约: - 1. 不确定度 u 保留一位有效数字(若首位是1或2,有时保留两位,此处统一保留一位) - 2. 平均值末位与 u 对齐 - """ - if uncertainty <= 0: - return str(value), "0" - - u_float = float(uncertainty) - # 找到第一位非零数字的位置 - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - # 不确定度修约 - u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN) - # 平均值修约,保持与不确定度末位对齐 - val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN) - - return val_final, u_final - -def calculate_stats(data_list, delta_inst): - """计算一组数据的平均值、A类、B类及合成不确定度""" - n = len(data_list) - mean = sum(data_list) / Decimal(n) - - # A类不确定度 - if n > 1: - variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1) - s = Decimal(str(math.sqrt(float(variance)))) - u_a = s / Decimal(str(math.sqrt(n))) - else: - u_a = Decimal("0") - - # B类不确定度 - u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3))) - - # 合成 - u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2)))) - - return mean, u_combined - -def input_data_group(name, count, unit="mm"): - """输入一组重复测量的数值""" - print(f"\n请输入 {name} 的 {count} 次测量值 (单位: {unit}):") - data = [] - for i in range(count): - while True: - try: - val = input(f"{name}_{i+1} = ").strip() - if not val: continue - data.append(Decimal(val)) - break - except Exception: - print("输入无效,请输入数字。") - return data +from utils import ( + scientific_round, + calculate_stats, + input_data_group +) def main(): # --- 1. 常量设置 --- @@ -102,10 +51,10 @@ def main(): delta_d = Decimal(input("d 的仪器误差 (mm, 默认 0.005): ").strip() or "0.005") delta_b = Decimal(input("b 的仪器误差 (mm, 默认 0.02): ").strip() or "0.02") - L_mean, L_u = calculate_stats(L_vals, delta_L) - D_mean, D_u = calculate_stats(D_vals, delta_D) - d_mean, d_u = calculate_stats(d_vals, delta_d) - b_mean, b_u = calculate_stats(b_vals, delta_b) + L_mean, L_u = calculate_stats(L_vals, delta_L)[:2] + D_mean, D_u = calculate_stats(D_vals, delta_D)[:2] + d_mean, d_u = calculate_stats(d_vals, delta_d)[:2] + b_mean, b_u = calculate_stats(b_vals, delta_b)[:2] # --- 3. 刻度值输入 (0-7个砝码, 加重+减重) --- print("\n--- 输入刻度尺读数 n (单位: mm) ---") diff --git a/degree.py b/degree.py index 44ccb23..7add66f 100644 --- a/degree.py +++ b/degree.py @@ -1,55 +1,11 @@ import math from decimal import Decimal, ROUND_HALF_EVEN -def parse_angle_to_minutes(s): - """ - 将输入解析为内部统一单位:分钟(Decimal)。 - 支持格式: - 1. 'D.M' -> D度M分 (例如 '120.15' -> 120*60 + 15 = 7215分) - 2. 'D' (纯数字无点) -> D度 (例如 '120' -> 120*60 = 7200分) - 3. 'D.M.S' -> D度M分S秒 (例如 '120.15.30' -> 7215.5分) - """ - parts = s.split('.') - if len(parts) == 1: - # 视为度 - return Decimal(parts[0]) * 60 - elif len(parts) == 2: - # 度.分 - return Decimal(parts[0]) * 60 + Decimal(parts[1]) - else: - # 度.分.秒 或更多 - res = Decimal(parts[0]) * 60 + Decimal(parts[1]) - if len(parts) >= 3: - res += Decimal(parts[2]) / 60 - return res - -def format_minutes_as_angle(minutes, prec=None): - """ - 将总分钟数转换为 D° M' 格式,支持保持对齐精度。 - """ - neg = minutes < 0 - minutes = abs(minutes) - - d = int(minutes // 60) - m = minutes % 60 - - # 进位处理:如果 m 由于位宽限制进位到 60 - if m >= 60: - d += 1 - m -= 60 - - if prec is not None and prec < 1: - # 计算小数位数 - places = abs(prec.as_tuple().exponent) - m_str = format(m, f'.{places}f') - else: - # 整数或自动小数位 - if m == int(m): - m_str = str(int(m)) - else: - m_str = format(m, 'f').rstrip('0').rstrip('.') - - return f"{'-' if neg else ''}{d}°{m_str}'" +from utils import ( + parse_angle_to_minutes, + format_minutes_as_angle, + scientific_round +) def calc_uncertainty(data_strs, res_str="1"): # 转换为分钟进行高精度计算 @@ -89,15 +45,11 @@ def format_final_result(mean_minutes, u_minutes): if u_float == 0: return f"θ = {format_minutes_as_angle(mean_minutes)} ± 0'" - # 找到第一位非零数字的位置 first_digit_pos = math.floor(math.log10(u_float)) prec = Decimal('1e' + str(first_digit_pos)) - # 修约 - u_final = u_minutes.quantize(prec, rounding=ROUND_HALF_EVEN) - mean_final_val = mean_minutes.quantize(prec, rounding=ROUND_HALF_EVEN) + mean_final_val, u_final = scientific_round(mean_minutes, u_minutes) - # 转换为角度字符串输出 mean_str = format_minutes_as_angle(mean_final_val, prec) u_str = f"{u_final}'" diff --git a/lenth.py b/lenth.py index 21c9496..afaa478 100644 --- a/lenth.py +++ b/lenth.py @@ -1,14 +1,7 @@ import math from decimal import Decimal, ROUND_HALF_EVEN -def get_decimal_places(data_strings): - """获取输入数据中小数点后的位数""" - max_places = 0 - for s in data_strings: - if '.' in s: - places = len(s.split('.')[1]) - max_places = max(max_places, places) - return max_places +from utils import get_decimal_places, scientific_round def calc_uncertainty(data_strs, res_str="0.05"): # 转换为 Decimal 进行高精度计算 @@ -44,17 +37,7 @@ def format_final_result(mean, u): 1. 不确定度 u 保留一位有效数字 2. 平均值末位与 u 对齐 """ - u_float = float(u) - # 找到第一位非零数字的位置 - first_digit_pos = math.floor(math.log10(u_float)) - prec = Decimal('1e' + str(first_digit_pos)) - - # 修改:不确定度 u 的修约改为 ROUND_HALF_EVEN - u_final = u.quantize(prec, rounding=ROUND_HALF_EVEN) - - # 修改:平均值修约保持一致 - mean_final = mean.quantize(prec, rounding=ROUND_HALF_EVEN) - + mean_final, u_final = scientific_round(mean, u) return f"f = {mean_final} ± {u_final}" if __name__ == "__main__": diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..c577f60 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,16 @@ +from utils.formatting import ( + get_decimal_places, + scientific_round, + parse_angle_to_minutes, + format_minutes_as_angle +) +from utils.stats import ( + calculate_stats, + linear_regression +) +from utils.io import ( + input_float, + input_decimal, + input_int, + input_data_group +) diff --git a/utils/formatting.py b/utils/formatting.py new file mode 100644 index 0000000..5d6d3c4 --- /dev/null +++ b/utils/formatting.py @@ -0,0 +1,76 @@ +import math +from decimal import Decimal, ROUND_HALF_EVEN + +def get_decimal_places(data_strings): + """获取输入数据中小数点后的位数,用于自适应展示""" + max_places = 0 + for s in data_strings: + if '.' in s: + places = len(s.split('.')[1]) + max_places = max(max_places, places) + return max_places + +def scientific_round(value, uncertainty): + """ + 按照“四舍六入五凑偶”修约: + 1. 不确定度 u 保留一位有效数字 + 2. 平均值/结果末位与 u 对齐 + """ + if uncertainty <= 0: + return str(value), "0" + + d_val = Decimal(str(value)) + d_unc = Decimal(str(uncertainty)) + + u_float = float(d_unc) + first_digit_pos = math.floor(math.log10(u_float)) + prec = Decimal('1e' + str(first_digit_pos)) + + u_final = d_unc.quantize(prec, rounding=ROUND_HALF_EVEN) + val_final = d_val.quantize(prec, rounding=ROUND_HALF_EVEN) + + return val_final, u_final + +def parse_angle_to_minutes(s): + """ + 将输入解析为内部统一单位:分钟(Decimal)。 + 支持格式: + 1. 'D.M' -> D度M分 (例如 '120.15' -> 120*60 + 15 = 7215分) + 2. 'D' (纯数字无点) -> D度 (例如 '120' -> 120*60 = 7200分) + 3. 'D.M.S' -> D度M分S秒 (例如 '120.15.30' -> 7215.5分) + """ + parts = s.split('.') + if len(parts) == 1: + return Decimal(parts[0]) * 60 + elif len(parts) == 2: + return Decimal(parts[0]) * 60 + Decimal(parts[1]) + else: + res = Decimal(parts[0]) * 60 + Decimal(parts[1]) + if len(parts) >= 3: + res += Decimal(parts[2]) / 60 + return res + +def format_minutes_as_angle(minutes, prec=None): + """ + 将总分钟数转换为 D° M' 格式,支持保持对齐精度。 + """ + neg = minutes < 0 + minutes = abs(minutes) + + d = int(minutes // 60) + m = minutes % 60 + + if m >= 60: + d += 1 + m -= 60 + + if prec is not None and prec < 1: + places = abs(prec.as_tuple().exponent) + m_str = format(m, f'.{places}f') + else: + if m == int(m): + m_str = str(int(m)) + else: + m_str = format(m, 'f').rstrip('0').rstrip('.') + + return f"{'-' if neg else ''}{d}°{m_str}'" diff --git a/utils/io.py b/utils/io.py new file mode 100644 index 0000000..03046d5 --- /dev/null +++ b/utils/io.py @@ -0,0 +1,59 @@ +from decimal import Decimal + +def input_float(prompt, default=None): + """输入一个浮点数值,支持默认值""" + while True: + try: + suffix = f" (默认 {default})" if default is not None else "" + val = input(f"{prompt}{suffix}: ").strip() + if not val and default is not None: + return float(default) + if not val: + continue + return float(val) + except Exception: + print(" 输入无效,请输入数字。") + +def input_decimal(prompt, default=None): + """输入一个 Decimal 值,支持默认值""" + while True: + try: + suffix = f" (默认 {default})" if default is not None else "" + val = input(f"{prompt}{suffix}: ").strip() + if not val and default is not None: + return Decimal(str(default)) + if not val: + continue + return Decimal(val) + except Exception: + print(" 输入无效,请输入数字。") + +def input_int(prompt, default=None): + """输入一个整数,支持默认值""" + while True: + try: + suffix = f" (默认 {default})" if default is not None else "" + val = input(f"{prompt}{suffix}: ").strip() + if not val and default is not None: + return default + if not val: + continue + return int(val) + except Exception: + print(" 输入无效,请输入整数。") + +def input_data_group(name, count, unit=""): + """输入一组重复测量的数值""" + prompt_unit = f" (单位: {unit})" if unit else "" + print(f"\n请输入 {name} 的 {count} 次测量值{prompt_unit}:") + data = [] + for i in range(count): + while True: + try: + val = input(f"第 {i+1} 次 = ").strip() + if not val: continue + data.append(Decimal(val)) + break + except Exception: + print("输入无效,请输入数字。") + return data diff --git a/utils/stats.py b/utils/stats.py new file mode 100644 index 0000000..7a26ec1 --- /dev/null +++ b/utils/stats.py @@ -0,0 +1,59 @@ +import math +from decimal import Decimal + +def calculate_stats(data_list, delta_inst): + """ + 计算一组数据的平均值、A类、B类及合成不确定度。 + 支持输入 list of Decimal 或 list of float/str/int。 + + 返回: (mean, u_combined, u_a, u_b, s) + """ + n = len(data_list) + decimal_data = [Decimal(str(x)) for x in data_list] + mean = sum(decimal_data) / Decimal(n) + + if n > 1: + variance = sum((x - mean)**2 for x in decimal_data) / Decimal(n - 1) + s = Decimal(str(math.sqrt(float(variance)))) + u_a = s / Decimal(str(math.sqrt(n))) + else: + s = Decimal("0") + u_a = Decimal("0") + + u_b = Decimal(str(delta_inst)) / Decimal(str(math.sqrt(3))) + u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2)))) + + return mean, u_combined, u_a, u_b, s + +def linear_regression(x_list, y_list): + """ + 最小二乘法线性回归: y = k·x + b + 返回: (k, b, r, u_k) + u_k = |k| · √[(1/r² − 1) / (n − 2)] + 支持 input numbers as Decimal or float. + """ + n = len(x_list) + x_dec = [Decimal(str(x)) for x in x_list] + y_dec = [Decimal(str(y)) for y in y_list] + + x_mean = sum(x_dec) / Decimal(n) + y_mean = sum(y_dec) / Decimal(n) + + L_xx = sum((x - x_mean)**2 for x in x_dec) + L_yy = sum((y - y_mean)**2 for y in y_dec) + L_xy = sum((x_dec[i] - x_mean) * (y_dec[i] - y_mean) for i in range(n)) + + k = L_xy / L_xx if L_xx != 0 else Decimal("0") + b = y_mean - k * x_mean + + denom = Decimal(str(math.sqrt(float(L_xx * L_yy)))) if L_xx * L_yy > 0 else Decimal("1") + r = L_xy / denom + + r_float = float(r) + if abs(r_float) < 1.0 and n > 2: + u_k_float = float(abs(k)) * math.sqrt((1.0 / (r_float**2) - 1.0) / (n - 2)) + else: + u_k_float = 0.0 + u_k = Decimal(str(u_k_float)) + + return k, b, r, u_k