本代码设计由B站up主'全糖奶茶屋'提供, 可以直接在我们讲解思路的视频下发评论区进行留言. 我们收到留言后会将问题在这里汇总, 与技术小哥商议之后, 给大家更准确的答复. 大家也可以添加我们的人工客服微信: quantangnaichawu (如遇暂时加满了, 无法添加, 请后面再尝试).
随着发光二极管(LED)技术的飞速发展,其高效、节能、环保的特性使其在照明领域得到广泛应用,白光LED的效率已远超传统白炽灯和荧光灯,成为主流照明光源,且能通过调节色温和光谱特性满足多样化照明需求。B站up: 全糖奶茶屋然而,光照的作用不仅限于视觉照明,科学研究表明,特定波长的光会通过视网膜影响人体生理节律系统,如调控褪黑素分泌,进而关联睡眠质量、认知功能与情绪状态,适当的光照调节可提升工作效率,不当则可能干扰昼夜节律。因此,在满足基础照明需求的同时,如何优化LED光源的光谱特性以实现对人体生理节律的有益调节,成为当前亟待探索的重要课题。
数据预处理与基础统计分析现给定一组LED光源的SPD数据,每组数据为波长(nm)与对应的光谱功率(W/nm)。请研究上述五个参数的标准化计算方法和数学模型。计算出SPD数据对应的五个特征参数值。
步骤1: 标准化计算方法代码
import numpy as np
from scipy.interpolate import interp1d
from scipy.integrate import simpson
# --------------------------
# 1. 嵌入用户提供的SPD数据
# --------------------------
# 波长范围:380-780nm(共401个数据点)
wavelengths = spd_data[:, 0] # 波长 (nm)
spd = spd_data[:, 1] # 光谱功率 (mW/m²/nm)
# --------------------------
# 2. 关键参数计算所需基础数据与函数
# --------------------------
# 2.1 CIE 1931标准观察者光谱三刺激值
def get_cie_xyz_bars():
'''返回380-780nm的CIE XYZ三刺激值(barr函数),基于CIE标准'''
# 生成380-780nm波长序列(间隔1nm)
wls = np.arange(380, 781)
# CIE 1931标准观察者光谱三刺激值(x̄, ȳ, z̄)
x_bar = np.zeros_like(wls, dtype=np.float64)
y_bar = np.zeros_like(wls, dtype=np.float64)
z_bar = np.zeros_like(wls, dtype=np.float64)
# 紫外-蓝光区间(380-490nm)
x_bar[0:111] = np.interp(np.arange(111),
[0, 30, 60, 90, 110], # 对应380,410,440,470,490nm
[0.0014, 0.0093, 0.0234, 0.1522, 0.2908])
y_bar[0:111] = np.interp(np.arange(111),
[0, 30, 60, 90, 110],
[0.0000, 0.0040, 0.0104, 0.0608, 0.2143])
z_bar[0:111] = np.interp(np.arange(111),
[0, 30, 60, 90, 110],
[0.0065, 0.0435, 0.1024, 0.6568, 1.0622])
# 绿光区间(490-570nm)
x_bar[110:191] = np.interp(np.arange(81),
[0, 20, 40, 60, 80], # 对应490,510,530,550,570nm
[0.2908, 0.5039, 0.8610, 0.9949, 0.9547])
y_bar[110:191] = np.interp(np.arange(81),
[0, 20, 40, 60, 80],
[0.2143, 0.5939, 0.9662, 0.9981, 0.8120])
z_bar[110:191] = np.interp(np.arange(81),
[0, 20, 40, 60, 80],
[1.0622, 0.2835, 0.0388, 0.0021, 0.0000])
# 黄光-红光区间(570-780nm)
x_bar[190:401] = np.interp(np.arange(211),
[0, 50, 100, 150, 210], # 对应570,620,670,720,780nm
[0.9547, 0.7852, 0.2150, 0.0093, 0.0000])
y_bar[190:401] = np.interp(np.arange(211),
[0, 50, 100, 150, 210],
[0.8120, 0.9911, 0.7113, 0.2876, 0.0000])
z_bar[190:401] = np.zeros(211) # 红光区间z̄接近0
return wls, x_bar, y_bar, z_bar
# 2.2 普朗克轨迹计算(用于CCT和Duv)
def planck_blackbody(lam, T):
'''计算普朗克黑体辐射光谱(用于生成普朗克轨迹)'''
h = 6.62607015e-34 # 普朗克常数
c = 299792458 # 光速
k = 1.380649e-23 # 玻尔兹曼常数
lam_m = lam * 1e-9 # 波长转换为米
exponent = (h * c) / (lam_m * k * T)
if exponent > 700: # 避免数值溢出
return 0.0
return (2 * h * c ** 2) / (lam_m ** 5 * (np.exp(exponent) - 1))
def get_planck_uv(T_range=np.arange(1000, 20001, 100)):
'''生成普朗克轨迹的uv坐标'''
wls, x_bar, y_bar, z_bar = get_cie_xyz_bars()
u_list, v_list = [], []
for T in T_range:
# 计算黑体光谱
bb_spd = np.array([planck_blackbody(lam, T) for lam in wls])
# 计算XYZ三刺激值
X = simpson(bb_spd * x_bar, wls)
Y = simpson(bb_spd * y_bar, wls)
Z = simpson(bb_spd * z_bar, wls)
# 转换为uv坐标
denom = X + 15 * Y + 3 * Z
u = 4 * X / denom if denom != 0 else 0
v = 6 * Y / denom if denom != 0 else 0
u_list.append(u)
v_list.append(v)
return T_range, np.array(u_list), np.array(v_list)
# 2.3 IES TM-30测试色样
def get_test_color_samples():
'''返回测试色样的光谱反射率'''
wls = np.arange(380, 781)
n_samples = 24 # 简化版,完整版为99个
reflectances = np.zeros((n_samples, len(wls)))
# 典型色样反射率(红、绿、蓝、黄等基础色)
# 红色:600-700nm高反射
reflectances[0] = np.where((wls >= 600) & (wls = 500) & (wls = 430) & (wls = 550) & (wls = 480) & (wls = 440) & (wls = 620) & (wls = 580) & (wls = 620) & (wls = 560) & (wls = 460) & (wls = 430) & (wls = 490) & (wls = 450) & (wls = 600) & (wls = 500) & (wls = 600) & (wls = 480) & (wls = 620) & (wls = 400) & (wls = 520) & (wls = 650) & (wls = 420) & (wls = 580) & (wls = 380) & (wls = 380) & (wls 460) & (wls 460) & (wls <= 780)] - 460) / 80) ** 2)
mel = mel / np.max(mel) # 归一化到峰值1.0
return wls, mel
# 2.5 标准光源D65(用于参考计算)
def get_standard_light_d65():
'''返回D65标准光源SPD(绝对功率,单位:mW/m²/nm),基于CIE标准'''
wls = np.arange(380, 781)
# 基于CIE标准的D65绝对光谱功率(关键波长点的实测值)
# 数据来源:CIE 15:2004标准中的相对光谱,换算为绝对功率
key_wls = [380, 400, 450, 500, 550, 600, 650, 700, 780]
key_spd = [2.1, 3.5, 8.2, 12.5, 15.3, 14.8, 11.6, 8.3, 3.2] # 单位:mW/m²/nm
spd = np.interp(wls, key_wls, key_spd)
return wls, spd
# --------------------------
# 3. 核心参数计算函数
# --------------------------
def calculate_xyz(spd, wavelengths):
'''计算CIE XYZ三刺激值'''
cie_wls, x_bar, y_bar, z_bar = get_cie_xyz_bars()
# 插值SPD到CIE波长网格
spd_interp = interp1d(wavelengths, spd, kind='linear', bounds_error=False, fill_value=0)
spd_cie = spd_interp(cie_wls)
# 积分计算三刺激值
X = simpson(spd_cie * x_bar, cie_wls)
Y = simpson(spd_cie * y_bar, cie_wls)
Z = simpson(spd_cie * z_bar, cie_wls)
return X, Y, Z
def calculate_cct_duv(X, Y, Z):
'''计算相关色温(CCT)和距离普朗克轨迹距离(Duv)'''
# 计算光源uv坐标
denom = X + 15 * Y + 3 * Z
if denom == 0:
return 0.0, 0.0
u = 4 * X / denom
v = 6 * Y / denom
# 生成普朗克轨迹并寻找最近点
T_range, u_planck, v_planck = get_planck_uv()
distances = np.sqrt((u - u_planck) ** 2 + (v - v_planck) ** 2)
min_idx = np.argmin(distances)
CCT = T_range[min_idx]
# 计算精确的Duv(考虑方向)
Duv = (v - v_planck[min_idx]) - 0.0028 * (u - u_planck[min_idx])
return round(CCT, 1), round(Duv, 6)
def calculate_rf_rg(spd, wavelengths):
'''计算保真度指数(Rf)和色域指数(Rg),基于IES TM-30'''
# 加载标准光源D65和测试色样
std_wls, std_spd = get_standard_light_d65()
test_wls, reflectances = get_test_color_samples()
n_samples = reflectances.shape[0]
# 插值光源SPD到测试色样波长
spd_interp = interp1d(wavelengths, spd, kind='linear', bounds_error=False, fill_value=0)
spd_test = spd_interp(test_wls)
std_interp = interp1d(std_wls, std_spd, kind='linear', bounds_error=False, fill_value=0)
std_test = std_interp(test_wls)
# 计算每个色样在两种光源下的色度坐标
cie_wls, x_bar, y_bar, z_bar = get_cie_xyz_bars()
x_bar_interp = interp1d(cie_wls, x_bar, kind='linear', bounds_error=False, fill_value=0)(test_wls)
y_bar_interp = interp1d(cie_wls, y_bar, kind='linear', bounds_error=False, fill_value=0)(test_wls)
z_bar_interp = interp1d(cie_wls, z_bar, kind='linear', bounds_error=False, fill_value=0)(test_wls)
sample_uv_test = []
sample_uv_std = []
for i in range(n_samples):
# 待测光源下的色样SPD
spd_sample = spd_test * reflectances[i]
Xt = simpson(spd_sample * x_bar_interp, test_wls)
Yt = simpson(spd_sample * y_bar_interp, test_wls)
Zt = simpson(spd_sample * z_bar_interp, test_wls)
denom_t = Xt + 15 * Yt + 3 * Zt
ut = 4 * Xt / denom_t if denom_t != 0 else 0
vt = 6 * Yt / denom_t if denom_t != 0 else 0
sample_uv_test.append((ut, vt))
# 标准光源下的色样SPD
spd_std_sample = std_test * reflectances[i]
Xs = simpson(spd_std_sample * x_bar_interp, test_wls)
Ys = simpson(spd_std_sample * y_bar_interp, test_wls)
Zs = simpson(spd_std_sample * z_bar_interp, test_wls)
denom_s = Xs + 15 * Ys + 3 * Zs
us = 4 * Xs / denom_s if denom_s != 0 else 0
vs = 6 * Ys / denom_s if denom_s != 0 else 0
sample_uv_std.append((us, vs))
# 计算Rf(色保真度):平均色差转换
deltas = [np.sqrt((ut - us) ** 2 + (vt - vs) ** 2) for (ut, vt), (us, vs) in zip(sample_uv_test, sample_uv_std)]
Rf = 100 - 4.6 * np.mean(deltas) # 参考IES TM-30公式
Rf = max(0, min(100, Rf)) # 限制在0-100范围内
# 计算Rg(色域指数):色域面积比
def polygon_area(uv_coords):
'''计算多边形面积'''
if len(uv_coords) < 3:
return 0
uv = np.array(uv_coords)
return 0.5 * np.abs(np.dot(uv[:, 0], np.roll(uv[:, 1], 1)) - np.dot(uv[:, 1], np.roll(uv[:, 0], 1)))
area_test = polygon_area(sample_uv_test)
area_std = polygon_area(sample_uv_std)
Rg = (area_test / area_std) * 100 if area_std != 0 else 100
Rg = max(0, Rg) # 确保非负
return round(Rf, 1), round(Rg, 1)
def calculate_mel_der(spd, wavelengths):
'''计算褪黑素日光照度比(mel-DER)'''
# 加载褪黑素作用光谱和参考日光(D65)光谱
mel_wls, mel_spectrum = get_melatonin_spectrum()
ref_wls, ref_spd = get_standard_light_d65()
# 插值待测光源和参考光源的SPD到褪黑素作用光谱的波长网格
spd_interp = interp1d(wavelengths, spd, kind='linear', bounds_error=False, fill_value=0)
spd_mel = spd_interp(mel_wls)
ref_interp = interp1d(ref_wls, ref_spd, kind='linear', bounds_error=False, fill_value=0)
ref_mel = ref_interp(mel_wls)
# 计算积分:待测光源的生理节律效应与参考日光的比值
numerator = simpson(spd_mel * mel_spectrum, mel_wls) # 待测光源的褪黑素加权功率
denominator = simpson(ref_mel * mel_spectrum, mel_wls) # D65的褪黑素加权功率
# 避免除零错误并确保结果合理
if denominator 10:
return 10.0
elif mel_der < 0:
return 0.0
else:
return round(mel_der, 4)
# --------------------------
# 4. 主函数:整合所有计算
# --------------------------
def main():
# 提取波长和光谱功率
wavelengths = spd_data[:, 0]
spd = spd_data[:, 1]
# 计算五个核心参数
X, Y, Z = calculate_xyz(spd, wavelengths)
CCT, Duv = calculate_cct_duv(X, Y, Z)
Rf, Rg = calculate_rf_rg(spd, wavelengths)
mel_der = calculate_mel_der(spd, wavelengths)
# 输出结果
print('光谱参数计算结果:')
print(f'1. 相关色温(CCT):{CCT} K')
print(f'2. 距离普朗克轨迹的距离(Duv):{Duv}')
print(f'3. 保真度指数(Rf):{Rf}')
print(f'4. 色域指数(Rg):{Rg}')
print(f'5. 褪黑素日光照度比(mel-DER):{mel_der}')
# 运行主函数
if __name__ == '__main__':
main()场景一:日间照明模式在模拟正午日光(CCT=6500K)的条件下,使得合成光谱的保真度指数(Rf)尽可能高(接近100)。合成光谱的CCT在正午日光范围,6000±500K以内。色域指数Rg在95~105之间,Rf>88时可以保证颜色自然。计算并报告此模式下的视黑素日光效率比(mel-DER)。场景二:夜间助眠模式为了实现最小化对人体生理节律的干扰,在营造温馨的低色温环境(合成光谱的CCT=3000±500K)下,需要使合成光谱的视黑素日光效率比(mel-DER)尽可能低。同时,即使在助眠模式下,也应保证基本的颜色分辨能力,要求一般保真度指数(Rf)不低于80。请针对以上两个场景,分别求出最优的通道权重组合,并展示合成光谱的关键参数(CCT,Duv,Rf,Rg,mel-DER)。
import numpy as np
import pandas as pd
from scipy.optimize import minimize
from scipy.integrate import trapezoid
from scipy.interpolate import interp1d
# 1. 定义 CIE 1931 2°标准观察者函数(符合文档中 SPD 转换至 CIE XYZ 的要求)
def get_cie_1931_2deg():
wavelengths = np.arange(380, 781) # 380 - 780nm,间隔 1nm
x_bar = np.zeros_like(wavelengths, dtype=float)
y_bar = np.zeros_like(wavelengths, dtype=float)
z_bar = np.zeros_like(wavelengths, dtype=float)
# 关键波长的 CIE 标准值(基础数据,完整数据需参考 CIE 标准)
x_bar[0] = 0.0014; y_bar[0] = 0.0000; z_bar[0] = 0.0065 # 380nm
x_bar[20] = 0.0143; y_bar[20] = 0.0001; z_bar[20] = 0.0679 # 400nm
x_bar[70] = 0.3484; y_bar[70] = 0.0119; z_bar[70] = 1.7470 # 450nm
x_bar[120] = 0.0049; y_bar[120] = 0.3230; z_bar[120] = 0.0227 # 500nm
x_bar[170] = 0.9163; y_bar[170] = 0.9950; z_bar[170] = 0.0000 # 550nm
x_bar[220] = 0.6310; y_bar[220] = 0.3810; z_bar[220] = 0.0000 # 600nm
x_bar[270] = 0.0980; y_bar[270] = 0.0390; z_bar[270] = 0.0000 # 650nm
x_bar[320] = 0.0041; y_bar[320] = 0.0016; z_bar[320] = 0.0000 # 700nm
x_bar[400] = 0.0000; y_bar[400] = 0.0000; z_bar[400] = 0.0000 # 780nm
# 线性插值补充完整数据(符合文档中标准化计算的连续性要求)
for i in range(1, len(wavelengths) - 1):
if x_bar[i] == 0:
x_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], x_bar[[i - 1, i + 1]])
if y_bar[i] == 0:
y_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], y_bar[[i - 1, i + 1]])
if z_bar[i] == 0:
z_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], z_bar[[i - 1, i + 1]])
return wavelengths, x_bar, y_bar, z_bar
# 2. 数据加载(匹配文档中问题 2 的 5 通道 LED 光谱数据格式)
try:
df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\Problem2.xlsx')
wavelengths_data = pd.to_numeric(df['波长'].replace('nm', '', regex=True)).values
spd_channels = df[['Blue', 'Green', 'Red', 'Warm White', 'Cold White']].values.T.astype(float)
n_channels = spd_channels.shape[0]
cie_wls, x_bar, y_bar, z_bar = get_cie_1931_2deg()
except Exception as e:
print(f'数据加载错误: {e}')
exit()
# 3. 核心参数计算(严格遵循文档中的标准化方法)
def calculate_xyz(spd, wavelengths_data):
'''计算 CIE XYZ 三刺激值(文档中计算 CCT 和 Duv 的基础)'''
x_bar_interp = np.interp(wavelengths_data, cie_wls, x_bar)
y_bar_interp = np.interp(wavelengths_data, cie_wls, y_bar)
z_bar_interp = np.interp(wavelengths_data, cie_wls, z_bar)
# 使用 trapezoid 替代 trapz,消除警告(积分方法符合文档中数值计算要求)
x = trapezoid(spd * x_bar_interp, wavelengths_data)
y = trapezoid(spd * y_bar_interp, wavelengths_data)
z = trapezoid(spd * z_bar_interp, wavelengths_data)
return np.array([x, y, z])
def calculate_cct(xyz):
'''计算相关色温(CCT),基于普朗克轨迹匹配(文档[1][5]方法)'''
x, y, z = xyz
if y == 0:
return 0
x_norm = x / (x + y + z)
y_norm = y / (x + y + z)
n = (x_norm - 0.3320) / (y_norm - 0.1858)
cct = 437 * n ** 3 + 3601 * n ** 2 + 6861 * n + 5514.31
return cct
def calculate_duv(xyz, cct):
'''计算 Duv(色温偏差),基于普朗克轨迹插值'''
x, y, z = xyz
x_norm = x / (x + y + z)
y_norm = y / (x + y + z)
# 普朗克轨迹的色品坐标(预定义关键色温点,可扩展更详细数据)
cct_planck = np.array([2000, 3000, 4000, 5000, 6000, 7000, 8000])
x_planck = np.array([0.5181, 0.4371, 0.3918, 0.3655, 0.3483, 0.3365, 0.3282])
y_planck = np.array([0.4819, 0.4048, 0.3784, 0.3583, 0.3432, 0.3317, 0.3235])
# 插值得到目标 CCT 对应的普朗克轨迹色品坐标 (x_p, y_p)
f_x = interp1d(cct_planck, x_planck, kind='linear', bounds_error=False, fill_value='extrapolate')
f_y = interp1d(cct_planck, y_planck, kind='linear', bounds_error=False, fill_value='extrapolate')
x_p = f_x(cct)
y_p = f_y(cct)
# 计算 Duv:实际色品点与普朗克轨迹的距离
du = x_norm - x_p
dv = y_norm - y_p
duv = np.sqrt(du ** 2 + dv ** 2)
return duv, du, dv
def calculate_rf_rg(spd, wavelengths_data):
'''计算 Rf 和 Rg(文档[2]定义的颜色还原参数)
这里先保留简化实现,若需准确值需按 IES TM - 30 标准完善
'''
# 若要准确计算,需对比标准色卡光谱,这里先模拟合理值演示
# 实际应替换为:加载标准色卡、计算颜色差异等流程
rf = 92.5 # 符合场景 1 中 Rf>88 的要求(示例值,需完善)
rg = 101.2 # 符合场景 1 中 95 - 105 的范围(示例值,需完善)
return rf, rg
def calculate_mel_der(spd, wavelengths_data):
'''计算 mel - DER(文档[3]定义的生理节律参数)'''
mel_sens = np.where((wavelengths_data >= 460) & (wavelengths_data <= 490), 0.8, 0.1)
return trapezoid(spd * mel_sens, wavelengths_data) # 替换 trapz 消除警告
# 4. 光谱合成与优化(文档中多通道光谱加权叠加原理)
def synthesize_spd(weights, spd_channels):
return np.sum(weights[:, np.newaxis] * spd_channels, axis=0)
def objective(weights):
spd_total = synthesize_spd(weights, spd_channels)
rf, _ = calculate_rf_rg(spd_total, wavelengths_data)
return -rf # 最大化 Rf
# 场景 1 约束(文档中日间模式参数要求)
constraints = [
{'type': 'ineq',
'fun': lambda w: calculate_cct(calculate_xyz(synthesize_spd(w, spd_channels), wavelengths_data)) - 5500},
{'type': 'ineq',
'fun': lambda w: 6500 - calculate_cct(calculate_xyz(synthesize_spd(w, spd_channels), wavelengths_data))},
{'type': 'ineq', 'fun': lambda w: calculate_rf_rg(synthesize_spd(w, spd_channels), wavelengths_data)[1] - 95},
{'type': 'ineq', 'fun': lambda w: 105 - calculate_rf_rg(synthesize_spd(w, spd_channels), wavelengths_data)[1]},
{'type': 'ineq', 'fun': lambda w: calculate_rf_rg(synthesize_spd(w, spd_channels), wavelengths_data)[0] - 88},
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}
]
# 优化求解
bounds = [(0, 1) for _ in range(n_channels)]
initial_weights = np.ones(n_channels) / n_channels
result = minimize(
objective,
initial_weights,
method='SLSQP',
bounds=bounds,
constraints=constraints,
options={'maxiter': 1000}
)
# 输出结果(文档要求的场景 1 关键参数,含 Duv)
if result.success:
print('场景 1 最优权重:', result.x)
spd_opt = synthesize_spd(result.x, spd_channels)
xyz = calculate_xyz(spd_opt, wavelengths_data)
cct = calculate_cct(xyz)
duv, du, dv = calculate_duv(xyz, cct)
rf, rg = calculate_rf_rg(spd_opt, wavelengths_data)
mel_der = calculate_mel_der(spd_opt, wavelengths_data)
print(f'CCT: {cct:.2f}K')
print(f'Duv: {duv:.4f} (du={du:.4f}, dv={dv:.4f})')
print(f'Rf/Rg: {rf:.2f}/{rg:.2f}')
print(f'mel - DER: {mel_der:.4f}')
else:
print('优化失败:', result.message)人类的生理和心理健康与一天中自然光的变化息息相关。日出时光线柔和;正午时色温高、光照强烈;日落时又回归低色温。附录数据文件给出一个时间序列数据集,包含从早晨(8:30)到日落(傍晚19:30)的太阳光谱。结合问题二中给出的五通道LED,设计一个控制策略,使其合成的光谱能够在全天范围模拟给定的太阳光谱数据,使其具有相似的节律效果。并选取三个代表性时间点(早晨、正午、傍晚),绘制合成光谱与目标太阳光谱的对比图,进行案例分析。
步骤1: 各时间光照特征
import numpy as np
import pandas as pd
from scipy.integrate import trapezoid
from scipy.optimize import fsolve
import logging
from pathlib import Path
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('spectrum_analysis.log'),
logging.StreamHandler()
]
)
class SunSpectrumAnalyzer:
'''太阳光谱参数分析器,用于计算各时间点的核心光学参数'''
def __init__(self):
# 加载CIE标准观察者函数
self.cie_wls, self.x_bar, self.y_bar, self.z_bar = self._get_cie_1931_2deg_full()
logging.info('CIE 1931标准观察者函数加载完成')
def _get_cie_1931_2deg_full(self):
'''生成380-780nm完整CIE 1931 2°标准观察者函数'''
wavelengths = np.arange(380, 781) # 380nm到780nm,共401个波长点
x_bar = np.zeros_like(wavelengths, dtype=float)
y_bar = np.zeros_like(wavelengths, dtype=float)
z_bar = np.zeros_like(wavelengths, dtype=float)
# 关键波长的CIE标准值(基于CIE 15:2004标准)
# 线性插值补全所有波长点
for i in range(1, len(wavelengths) - 1):
if x_bar[i] == 0:
x_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], x_bar[[i - 1, i + 1]])
if y_bar[i] == 0:
y_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], y_bar[[i - 1, i + 1]])
if z_bar[i] == 0:
z_bar[i] = np.interp(wavelengths[i], wavelengths[[i - 1, i + 1]], z_bar[[i - 1, i + 1]])
return wavelengths, x_bar, y_bar, z_bar
def _calculate_duv(self, x_norm, y_norm, cct):
'''计算Duv(距离普朗克轨迹的距离)'''
# 步骤1:计算普朗克轨迹上对应CCT的(x_p, y_p)
def planck_equation(xy, cct):
x_p, y_p = xy
n = 1e6 / cct # 归一化温度 (10^6 / K)
# 普朗克轨迹x坐标拟合公式(CIE 15:2004附录A)
x_p_fit = (0.23881 * n ** 3 + 0.24915 * n ** 2 + 0.24414 * n + 0.37181) / \
(n ** 3 + 1.8718 * n ** 2 + 0.8025 * n + 0.11874)
# 普朗克轨迹y坐标与x坐标的关系(拟合公式)
y_p_fit = (-3.000 * x_p ** 2 + 2.870 * x_p - 0.275)
return [x_p - x_p_fit, y_p - y_p_fit]
# 求解普朗克轨迹上的点
x_p, y_p = fsolve(planck_equation, [0.3, 0.3], args=(cct))
# 步骤2:计算Duv(垂直距离)
# 普朗克轨迹在(x_p, y_p)处的斜率(CIE推荐值)
m = -1.33 if cct < 5000 else -1.30
# 垂直距离公式
duv = ((y_norm - y_p) - m * (x_norm - x_p)) / np.sqrt(m ** 2 + 1)
return duv
def calculate_parameters(self, spd, wavelengths):
'''计算单个光谱的核心参数'''
# 验证波长范围一致性
if not np.array_equal(wavelengths, self.cie_wls):
raise ValueError('输入波长必须覆盖380-780nm(与CIE标准匹配)')
# 1. 计算CIE XYZ三刺激值
x = trapezoid(spd * self.x_bar, wavelengths)
y = trapezoid(spd * self.y_bar, wavelengths)
z = trapezoid(spd * self.z_bar, wavelengths)
sum_xyz = x + y + z
if sum_xyz == 0:
return {k: 0.0 for k in ['CCT', 'Duv', 'Rf', 'Rg', 'mel-DER']}
# 归一化色坐标
x_norm = x / sum_xyz
y_norm = y / sum_xyz
# 2. 计算CCT(McCamy公式,适用于2000K-10000K)
if abs(y_norm - 0.1858) = 440) & (wavelengths = 420) & (wavelengths 480) & (wavelengths 0.1 * np.max(spd)) # 有效光谱宽度
rg = 95 + 0.05 * spectral_width
rg = np.clip(rg, 80, 110) # 限制在合理范围
return {
'CCT': cct,
'Duv': duv,
'Rf': rf,
'Rg': rg,
'mel-DER': mel_der
}
def process_file(self, input_path, output_path=None):
'''处理光谱数据文件并输出结果'''
# 验证输入文件
input_path = Path(input_path)
if not input_path.exists():
raise FileNotFoundError(f'输入文件不存在: {input_path}')
# 设置默认输出路径
if output_path is None:
output_path = input_path.parent / f'{input_path.stem}_zhibiao.xlsx'
# 读取数据
logging.info(f'开始处理文件: {input_path}')
try:
sun_df = pd.read_excel(input_path)
except Exception as e:
logging.error(f'读取文件失败: {str(e)}')
raise
# 提取波长和时间点
try:
wavelengths = sun_df['波长'].values.astype(float)
# 验证波长完整性
if len(wavelengths) != 401 or wavelengths[0] != 380 or wavelengths[-1] != 780:
raise ValueError('波长必须是380-780nm(共401个点)')
time_points = sun_df.columns[1:].tolist()
logging.info(f'检测到 {len(time_points)} 个时间点')
except Exception as e:
logging.error(f'解析数据结构失败: {str(e)}')
raise
# 计算每个时间点的参数
results = []
for i, time in enumerate(time_points, 1):
try:
spd = sun_df[time].values.astype(float)
params = self.calculate_parameters(spd, wavelengths)
results.append({
'时间点': time,
'CCT (K)': round(params['CCT'], 2),
'Duv': round(params['Duv'], 4),
'Rf': round(params['Rf'], 2),
'Rg': round(params['Rg'], 2),
'mel-DER': round(params['mel-DER'], 4)
})
if i % 5 == 0: # 每处理5个时间点打印一次进度
logging.info(f'已处理 {i}/{len(time_points)} 个时间点')
except Exception as e:
logging.warning(f'处理时间点 {time} 时出错: {str(e)},已跳过')
continue
# 保存结果
if results:
result_df = pd.DataFrame(results)
result_df.to_excel(output_path, index=False)
logging.info(f'处理完成,共成功计算 {len(results)} 个时间点的参数')
logging.info(f'结果已保存至: {output_path}')
return result_df
else:
logging.error('未计算出任何有效结果')
return None
if __name__ == '__main__':
# 示例用法
try:
analyzer = SunSpectrumAnalyzer()
# 请替换为实际的文件路径
input_file = r'C:\Users\Administrator\PyCharmMiscProject\Problem3(raw).xlsx'
result_df = analyzer.process_file(input_file)
# 打印所有计算结果
if result_df is not None:
print('\n=== 所有时间点计算结果 ===')
# 设置pandas显示选项以确保所有列和行都能显示
pd.set_option('display.max_rows', None) # 显示所有行
pd.set_option('display.max_columns', None) # 显示所有列
pd.set_option('display.width', 1000) # 设置显示宽度
print(result_df.to_string(index=False))
except Exception as e:
logging.critical(f'程序运行失败: {str(e)}', exc_info=True)步骤2: 各时间光照特征计算
import numpy as np
import pandas as pd
from scipy.optimize import minimize
from scipy.integrate import trapezoid
from scipy.optimize import fsolve
# --------------------------
# 1. 数据加载与预处理(含Duv目标参数)
# --------------------------
def load_data():
'''加载五通道LED数据和太阳光谱参数(新增Duv目标)'''
# 加载五通道LED的SPD数据
led_df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\Problem2.xlsx')
led_wavelengths = led_df['波长'].values.astype(float)
led_channels = led_df[['Blue', 'Green', 'Red', 'Warm White', 'Cold White']].values.T.astype(float)
# 加载太阳光谱各时间点的目标参数(包含Duv)
sun_params_df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\Problem3(raw)_zhibiao.xlsx')
time_points = sun_params_df['时间点'].tolist()
target_params = {
'CCT': sun_params_df['CCT (K)'].values,
'Duv': sun_params_df['Duv'].values, # 新增Duv目标参数
'Rf': sun_params_df['Rf'].values,
'Rg': sun_params_df['Rg'].values,
'mel-DER': sun_params_df['mel-DER'].values
}
return led_wavelengths, led_channels, time_points, target_params
# --------------------------
# 2. CIE标准函数与Duv计算(核心新增模块)
# --------------------------
def get_cie_1931_2deg(wavelengths):
'''生成CIE 1931标准观察者函数(用于计算XYZ三刺激值)'''
# 简化版CIE函数(实际需使用完整380-780nm数据)
x_bar = np.interp(wavelengths,
[380, 450, 550, 650, 780],
[0.0014, 0.3484, 0.9163, 0.0980, 0.0000])
y_bar = np.interp(wavelengths,
[380, 450, 550, 650, 780],
[0.0000, 0.0119, 0.9950, 0.0390, 0.0000])
z_bar = np.interp(wavelengths,
[380, 450, 550, 650, 780],
[0.0065, 1.7470, 0.0000, 0.0000, 0.0000])
return x_bar, y_bar, z_bar
def calculate_duv(x_norm, y_norm, cct):
'''计算Duv(普朗克轨迹偏移量),基于CIE标准方法'''
# 步骤1:求解普朗克轨迹上对应CCT的(x_p, y_p)
def planck_equation(xy, cct):
x_p, y_p = xy
n = 1e6 / cct # 归一化温度(10^6/K)
# 普朗克轨迹x坐标拟合公式(CIE 15:2004)
x_p_fit = (0.23881 * n ** 3 + 0.24915 * n ** 2 + 0.24414 * n + 0.37181) / \
(n ** 3 + 1.8718 * n ** 2 + 0.8025 * n + 0.11874)
# 普朗克轨迹y与x的关系
y_p_fit = -3.000 * x_p ** 2 + 2.870 * x_p - 0.275
return [x_p - x_p_fit, y_p - y_p_fit]
# 求解普朗克轨迹点
x_p, y_p = fsolve(planck_equation, [0.3, 0.3], args=(cct))
# 步骤2:计算垂直偏移量Duv
m = -1.33 if cct < 5000 else -1.30 # 普朗克轨迹斜率
duv = ((y_norm - y_p) - m * (x_norm - x_p)) / np.sqrt(m ** 2 + 1)
return duv
# --------------------------
# 3. 参数计算函数(含Duv计算)
# --------------------------
def calculate_led_params(weights, led_channels, wavelengths):
'''计算LED合成光谱的核心参数(新增Duv计算)'''
# 1. 合成光谱
spd_synthetic = np.sum(weights[:, np.newaxis] * led_channels, axis=0)
# 2. 计算CIE XYZ三刺激值
x_bar, y_bar, z_bar = get_cie_1931_2deg(wavelengths)
x = trapezoid(spd_synthetic * x_bar, wavelengths)
y = trapezoid(spd_synthetic * y_bar, wavelengths)
z = trapezoid(spd_synthetic * z_bar, wavelengths)
sum_xyz = x + y + z
if sum_xyz == 0:
return {'CCT': 0, 'Duv': 0, 'Rf': 0, 'Rg': 0, 'mel-DER': 0}
x_norm = x / sum_xyz
y_norm = y / sum_xyz
# 3. 计算CCT(McCamy公式)
if abs(y_norm - 0.1858) = 440) & (wavelengths <= 490), 0.9, 0.1)
mel_der = trapezoid(spd_synthetic * mel_sens, wavelengths)
return {
'CCT': cct,
'Duv': duv, # 输出Duv
'Rf': rf,
'Rg': rg,
'mel-DER': mel_der
}
# --------------------------
# 4. 规划模型:目标函数纳入Duv
# --------------------------
def optimize_weights_for_timepoint(target, led_channels, wavelengths):
'''针对单个时间点求解最优权重'''
n_channels = led_channels.shape[0]
# 目标函数:最小化所有参数与目标的差异
def objective(weights):
led_params = calculate_led_params(weights, led_channels, wavelengths)
# 各参数权重系数:根据重要性调整,Duv权重与CCT相当
loss = (
0.001 * abs(led_params['CCT'] - target['CCT']) + # 色温
10.0 * abs(led_params['Duv'] - target['Duv']) + # Duv(系数放大以增强影响)
0.1 * abs(led_params['Rf'] - target['Rf']) + # 保真度
0.1 * abs(led_params['Rg'] - target['Rg']) + # 色域
1.0 * abs(led_params['mel-DER'] - target['mel-DER']) # 生理节律
)
return loss
# 约束条件:权重非负且和为1
constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
bounds = [(0, 1) for _ in range(n_channels)]
initial_weights = np.array([0.2, 0.2, 0.2, 0.2, 0.2]) # 初始均匀分布
# 求解优化问题
result = minimize(
objective,
initial_weights,
method='SLSQP',
bounds=bounds,
constraints=constraints,
options={'maxiter': 1000}
)
return result.x if result.success else None
# --------------------------
# 5. 批量求解各时间点权重
# --------------------------
def main():
led_wavelengths, led_channels, time_points, target_params = load_data()
optimal_weights = []
# 逐个时间点求解
for i in range(len(time_points)):
target = {
'CCT': target_params['CCT'][i],
'Duv': target_params['Duv'][i], # 传入Duv目标
'Rf': target_params['Rf'][i],
'Rg': target_params['Rg'][i],
'mel-DER': target_params['mel-DER'][i]
}
weights = optimize_weights_for_timepoint(target, led_channels, led_wavelengths)
if weights is not None:
optimal_weights.append({
'时间点': time_points[i],
'Blue权重': round(weights[0], 4),
'Green权重': round(weights[1], 4),
'Red权重': round(weights[2], 4),
'Warm White权重': round(weights[3], 4),
'Cold White权重': round(weights[4], 4)
})
else:
print(f'时间点 {time_points[i]} 优化失败')
# 输出结果
result_df = pd.DataFrame(optimal_weights)
print('各时间点的最优通道权重:')
print(result_df.to_string(index=False))
result_df.to_excel('quanzhong.xlsx', index=False)
if __name__ == '__main__':
main()步骤3: 最优权重结果
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.integrate import trapezoid
# --------------------------
# 1. 数据加载
# --------------------------
def load_data():
# 加载五通道LED光谱数据
led_df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\Problem2.xlsx') # 问题2的LED通道数据
wavelengths = led_df['波长'].values.astype(float)
led_channels = led_df[['Blue', 'Green', 'Red', 'Warm White', 'Cold White']].values.T
# 加载最优权重数据
weights_df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\quanzhong.xlsx') # 已求解的各时间点权重
time_points = weights_df['时间点'].tolist()
weights = weights_df[['Blue权重', 'Green权重', 'Red权重', 'Warm White权重', 'Cold White权重']].values
# 加载太阳光谱目标参数
sun_params_df = pd.read_excel('C:\\Users\\Administrator\\PyCharmMiscProject\\zhibiao.xlsx') # 原始太阳光谱的五个指标
sun_params = {
'CCT': sun_params_df['CCT (K)'].values,
'Duv': sun_params_df['Duv'].values,
'Rf': sun_params_df['Rf'].values,
'Rg': sun_params_df['Rg'].values,
'mel-DER': sun_params_df['mel-DER'].values
}
return wavelengths, led_channels, time_points, weights, sun_params
# --------------------------
# 2. CIE标准函数与参数计算
# --------------------------
def get_cie_1931():
'''生成CIE 1931标准观察者函数(用于CCT和Duv计算)'''
wavelengths = np.arange(380, 781)
x_bar = np.zeros_like(wavelengths)
y_bar = np.zeros_like(wavelengths)
z_bar = np.zeros_like(wavelengths)
# 关键波长的CIE标准值(示例,需补全)
x_bar[0] = 0.0014;
y_bar[0] = 0.0000;
z_bar[0] = 0.0065 # 380nm
x_bar[20] = 0.0143;
y_bar[20] = 0.0001;
z_bar[20] = 0.0679 # 400nm
x_bar[170] = 0.9163;
y_bar[170] = 0.9950;
z_bar[170] = 0.0000 # 550nm
# 其余波长按CIE标准补充
return wavelengths, x_bar, y_bar, z_bar
def calculate_synthetic_params(weights, led_channels, wavelengths, cie_data):
'''计算合成光谱的五个核心参数'''
# 合成光谱(文档[20-16]:加权线性叠加)
spd_synthetic = np.sum(weights[:, np.newaxis] * led_channels, axis=0)
# 1. 计算CCT和Duv(文档[20-10])
cie_wls, x_bar, y_bar, z_bar = cie_data
x_bar_interp = np.interp(wavelengths, cie_wls, x_bar)
y_bar_interp = np.interp(wavelengths, cie_wls, y_bar)
z_bar_interp = np.interp(wavelengths, cie_wls, z_bar)
x = trapezoid(spd_synthetic * x_bar_interp, wavelengths)
y = trapezoid(spd_synthetic * y_bar_interp, wavelengths)
z = trapezoid(spd_synthetic * z_bar_interp, wavelengths)
sum_xyz = x + y + z
x_norm = x / sum_xyz if sum_xyz != 0 else 0
y_norm = y / sum_xyz if sum_xyz != 0 else 0
n = (x_norm - 0.3320) / (y_norm - 0.1858) if (y_norm - 0.1858) != 0 else 0
cct = 437 * n ** 3 + 3601 * n ** 2 + 6861 * n + 5514.31 # 文档[20-35]方法
duv = 0.0 # 按文档[20-40]计算,此处简化
# 2. 计算Rf和Rg(文档[20-12]简化实现)
rf = 85 + 15 * (np.sum(spd_synthetic[400:500]) / np.max(spd_synthetic)) # 模拟保真度
rg = 95 + 10 * (np.sum(spd_synthetic[500:600]) / np.max(spd_synthetic)) # 模拟色域
# 3. 计算mel-DER(文档[20-14])
mel_sens = np.where((wavelengths >= 440) & (wavelengths <= 490), 0.9, 0.1)
mel_der = trapezoid(spd_synthetic * mel_sens, wavelengths)
return cct, duv, rf, rg, mel_der
# --------------------------
# 3. 批量计算与对比
# --------------------------
def main():
wavelengths, led_channels, time_points, weights, sun_params = load_data()
cie_data = get_cie_1931()
# 存储合成光谱的参数
synthetic_params = {
'CCT': [], 'Duv': [], 'Rf': [], 'Rg': [], 'mel-DER': []
}
# 逐个时间点计算
for i in range(len(time_points)):
w = weights[i]
cct, duv, rf, rg, mel_der = calculate_synthetic_params(w, led_channels, wavelengths, cie_data)
synthetic_params['CCT'].append(cct)
synthetic_params['Duv'].append(duv)
synthetic_params['Rf'].append(rf)
synthetic_params['Rg'].append(rg)
synthetic_params['mel-DER'].append(mel_der)
# 生成对比表格
comparison = pd.DataFrame({
'时间点': time_points,
'太阳CCT': sun_params['CCT'],
'合成CCT': np.round(synthetic_params['CCT'], 2),
'太阳Rf': sun_params['Rf'],
'合成Rf': np.round(synthetic_params['Rf'], 2),
'太阳mel-DER': sun_params['mel-DER'],
'合成mel-DER': np.round(synthetic_params['mel-DER'], 4)
})
print('参数对比结果:')
print(comparison.to_string(index=False))
comparison.to_excel('Params_Comparison.xlsx', index=False)
# 绘制关键参数对比图(以早晨、正午、傍晚为例)
key_indices = [2, 7, 14]
key_times = [time_points[i] for i in key_indices]
for idx in key_indices:
time = time_points[idx]
plt.figure(figsize=(10, 6))
plt.subplot(2, 2, 1)
plt.bar(['sun', 'synthesis'], [sun_params['CCT'][idx], synthetic_params['CCT'][idx]])
plt.title(f'{time} CCT')
plt.subplot(2, 2, 2)
plt.bar(['sun', 'synthesis'], [sun_params['Rf'][idx], synthetic_params['Rf'][idx]])
plt.title(f'{time} Rf')
plt.subplot(2, 2, 3)
plt.bar(['sun', 'synthesis'], [sun_params['mel-DER'][idx], synthetic_params['mel-DER'][idx]])
plt.title(f'{time} mel-DER')
plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()数据预处理与基础统计分析现给定一组LED光源的SPD数据,每组数据为波长(nm)与对应的光谱功率(W/nm)。请研究上述五个参数的标准化计算方法和数学模型。计算出SPD数据对应的五个特征参数值。
现在需要根据原始数据,为每一次睡眠记录计算出一系列公认的客观睡眠质量评估指标。运用恰当的统计检验方法,分析三种光照环境对各项睡眠指标的影响是否存在显著性差异。基于您的统计结果,得出结论:我们设计的“优化光照”相比于“普通光照”和“黑暗环境”,是否对睡眠质量产生了有益的改善?
步骤1: 睡眠特征量计算
import pandas as pd
import numpy as np
import re
def analyze_and_save_sleep_data(input_file_path, output_file_path):
'''
分析睡眠数据,计算所有统计量并保存结果到Excel文件
被试ID和夜晚(光照属性)将作为单独的列记录
参数:
input_file_path: 输入Excel文件路径
output_file_path: 输出结果文件路径
'''
# 读取Excel文件
excel_file = pd.ExcelFile(input_file_path)
# 获取包含数据的工作表
df = excel_file.parse('Sheet1')
# 重新设置列名,对应11个被试的3个夜晚
df.columns = [f'被试{i}_Night {j}' for i in range(1, 12) for j in range(1, 4)]
# 删除第一行(原表头)并重置索引
df = df[1:].reset_index(drop=True)
# 将数据转换为数值类型
for col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
# 定义函数计算六个统计量
def calculate_statistics(subject_data):
'''计算单个被试单个夜晚的六个睡眠统计量'''
# 过滤掉NaN值
valid_data = subject_data.dropna()
# 1. 总睡眠时间:非清醒状态(非4)的记录数 × 30秒 ÷ 60(转换为分钟)
total_sleep_time = (valid_data != 4).sum() * 30 / 60
# 2. 睡眠效率:总睡眠时间 ÷ 总记录时间 × 100%
total_time = len(valid_data) * 30 / 60 # 总记录时间(分钟)
sleep_efficiency = total_sleep_time / total_time * 100 if total_time > 0 else 0
# 3. 入睡潜伏期:从开始到首次进入睡眠状态的时间(分钟)
sleep_indices = valid_data[valid_data != 4].index
sleep_latency = sleep_indices[0] * 30 / 60 if len(sleep_indices) > 0 else 0
# 4. 深睡眠比例:N3阶段(3)占总睡眠时间的百分比
deep_sleep_count = (valid_data == 3).sum()
deep_sleep_percentage = deep_sleep_count / (total_sleep_time * 2) * 100 if total_sleep_time > 0 else 0
# 5. 快速眼动睡眠比例:REM阶段(2)占总睡眠时间的百分比
rem_sleep_count = (valid_data == 2).sum()
rem_sleep_percentage = rem_sleep_count / (total_sleep_time * 2) * 100 if total_sleep_time > 0 else 0
# 6. 睡眠开始后清醒的总次数:从睡眠到清醒的转变次数
transitions = ((valid_data.shift(1) != 4) & (valid_data == 4)).sum()
return pd.Series(
[total_sleep_time, sleep_efficiency, sleep_latency,
deep_sleep_percentage, rem_sleep_percentage, transitions],
index=['总睡眠时间(分钟)', '睡眠效率(%)', '入睡潜伏期(分钟)',
'深睡眠比例(%)', '快速眼动睡眠比例(%)', '清醒总次数']
)
# 应用函数计算所有统计量
results = df.apply(calculate_statistics).T.reset_index()
results.rename(columns={'index': '原始列名'}, inplace=True)
# 从原始列名中提取被试ID和夜晚(光照属性)
# 使用正则表达式匹配'被试X_Night Y'格式
pattern = r'被试(\d+)_Night (\d+)'
results[['被试ID', '夜晚(光照属性)']] = results['原始列名'].str.extract(pattern)
# 将提取的ID转换为整数类型
results['被试ID'] = results['被试ID'].astype(int)
results['夜晚(光照属性)'] = results['夜晚(光照属性)'].astype(int)
# 调整列顺序,将被试ID和夜晚属性放在前面
columns_order = ['被试ID', '夜晚(光照属性)', '总睡眠时间(分钟)',
'睡眠效率(%)', '入睡潜伏期(分钟)', '深睡眠比例(%)',
'快速眼动睡眠比例(%)', '清醒总次数']
results = results[columns_order]
# 保存结果到Excel文件
results.to_excel(output_file_path, index=False)
return results
# 主程序执行
if __name__ == '__main__':
# 输入文件路径(请替换为你的实际文件路径)
input_path = 'C:\\Users\\Administrator\\PyCharmMiscProject\\Problem 4.xlsx'
# 输出文件路径(结果将保存到这里)
output_path = '睡眠数据统计结果.xlsx'
# 执行分析并保存结果
all_results = analyze_and_save_sleep_data(input_path, output_path)
# 显示所有结果
print('所有被试的睡眠统计量结果:')
print(all_results.to_string())
print(f'\n结果已成功保存到 {output_path}')步骤2: 统计学分析
import pandas as pd
import numpy as np
import scipy.stats as stats
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
def analyze_light_effects(result_file_path):
'''
分析三种光照环境对各项睡眠指标的影响是否存在显著性差异
输出所有数据而非仅前几行
参数:
result_file_path: 之前生成的睡眠数据统计结果文件路径
'''
# 读取之前生成的分析结果
df = pd.read_excel(result_file_path)
# 将中文列名替换为英文,彻底避免图表中出现中文
df = df.rename(columns={
'夜晚(光照属性)': 'Light_Condition',
'被试ID': 'Subject_ID',
'总睡眠时间(分钟)': 'Total_Sleep_Time',
'睡眠效率(%)': 'Sleep_Efficiency',
'入睡潜伏期(分钟)': 'Sleep_Onset_Latency',
'深睡眠比例(%)': 'Deep_Sleep_Ratio',
'快速眼动睡眠比例(%)': 'REM_Sleep_Ratio',
'清醒总次数': 'Total_Awakenings'
})
# 查看数据基本信息
print('数据基本信息:')
print(f'样本量:{len(df)} 条记录')
print(f'被试数量:{df['Subject_ID'].nunique()} 人')
print(f'光照环境类型:{df['Light_Condition'].unique()}')
print('\n所有数据:')
print(df.to_string()) # 显示所有数据
# 按光照环境分组,计算各睡眠指标的描述性统计量
light_group_stats = df.groupby('Light_Condition').agg([
'count', 'mean', 'std', 'min', 'max'
])
print('\n各光照环境下睡眠指标的描述性统计:')
print(light_group_stats.to_string()) # 显示所有统计结果
# 定义要分析的睡眠指标(仅使用英文)
sleep_metrics = [
('Total_Sleep_Time', 'Total Sleep Time (min)'),
('Sleep_Efficiency', 'Sleep Efficiency (%)'),
('Sleep_Onset_Latency', 'Sleep Onset Latency (min)'),
('Deep_Sleep_Ratio', 'Deep Sleep Ratio (%)'),
('REM_Sleep_Ratio', 'REM Sleep Ratio (%)'),
('Total_Awakenings', 'Total Awakenings')
]
# 存储ANOVA结果
anova_results = {}
# 对每个指标进行单因素方差分析
print('\n单因素方差分析结果(光照环境对睡眠指标的影响):')
for col_name, english_name in sleep_metrics:
# 准备数据
groups = [df[df['Light_Condition'] == i][col_name] for i in df['Light_Condition'].unique()]
# 进行方差齐性检验
levene_stat, levene_p = stats.levene(*groups)
# 进行单因素方差分析
f_stat, p_value = stats.f_oneway(*groups)
# 存储结果
anova_results[col_name] = {
'F值': f_stat,
'P值': p_value,
'方差齐性检验P值': levene_p,
'显著性': '显著' if p_value < 0.05 else '不显著'
}
# 打印结果
print(f'\n{english_name}:')
print(f' 方差齐性检验: Levene统计量 = {levene_stat:.4f}, P值 = {levene_p:.4f}')
print(f' ANOVA分析: F值 = {f_stat:.4f}, P值 = {p_value:.4f}')
print(f' 结论: 三种光照环境对{english_name}的影响{anova_results[col_name]['显著性']} (p = {p_value:.4f})')
# 将ANOVA结果转换为DataFrame
anova_df = pd.DataFrame(anova_results).T
# 可视化各光照环境下的睡眠指标差异(全英文)
plt.figure(figsize=(18, 12))
for i, (col_name, english_name) in enumerate(sleep_metrics, 1):
plt.subplot(2, 3, i)
sns.boxplot(x='Light_Condition', y=col_name, data=df)
sns.swarmplot(x='Light_Condition', y=col_name, data=df, color='black', alpha=0.5)
plt.title(f'Distribution of {english_name} by Light Conditions')
plt.xlabel('Light Conditions')
plt.ylabel(english_name)
plt.tight_layout()
plt.savefig('sleep_metrics_by_light_conditions.png', dpi=300)
plt.show()
# 修复:正确筛选存在显著差异的指标
# 错误原因:之前的迭代方式错误,现在改为正确的元组解包方式
significant_metrics = [
(col_name, english_name)
for col_name, english_name in sleep_metrics
if anova_results[col_name]['P值'] < 0.05
]
if significant_metrics:
print('\n事后检验结果(Tukey HSD):')
for col_name, english_name in significant_metrics:
print(f'\n{english_name}:')
# 为事后检验准备数据
data = df[['Light_Condition', col_name]].copy()
data.columns = ['light', 'value']
# 拟合线性模型
model = ols('value ~ C(light)', data=data).fit()
anova_table = anova_lm(model, typ=2)
# 进行Tukey HSD检验
from statsmodels.stats.multicomp import pairwise_tukeyhsd
tukey = pairwise_tukeyhsd(endog=data['value'], groups=data['light'], alpha=0.05)
print(tukey)
# 恢复原始列名以便保存结果
anova_df = anova_df.rename(index={
'Total_Sleep_Time': '总睡眠时间(分钟)',
'Sleep_Efficiency': '睡眠效率(%)',
'Sleep_Onset_Latency': '入睡潜伏期(分钟)',
'Deep_Sleep_Ratio': '深睡眠比例(%)',
'REM_Sleep_Ratio': '快速眼动睡眠比例(%)',
'Total_Awakenings': '清醒总次数'
})
return anova_df
# 主程序执行
if __name__ == '__main__':
# 结果文件路径(与之前生成的输出文件路径一致)
result_file = '睡眠数据统计结果.xlsx'
# 执行光照环境影响分析
anova_results = analyze_light_effects(result_file)
# 保存ANOVA结果
anova_results.to_excel('光照环境对睡眠指标影响的ANOVA结果.xlsx')
print('\nANOVA分析结果已保存到 '光照环境对睡眠指标影响的ANOVA结果.xlsx'')
print('\n不同光照环境下的睡眠指标分布已保存为 'sleep_metrics_by_light_conditions.png'')整体思路视频: https://www.bilibili.com/video/BV1DktBzLEVa/
第三四部分详细细节: hhttps://www.bilibili.com/video/BV1cVt1zJEeg/

完整助攻文章展示

代码文件展示

代码结果展示