提取工具函数
This commit is contained in:
@@ -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
|
||||
)
|
||||
@@ -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}'"
|
||||
+59
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user