当前位置: 首页 > news >正文

模型预估值分布

模型预估返回结果的ctr \ cvr 分布图绘制

1、解析预估结果
import json
import sys
import os
from typing import Dict, List, Anydef parse_response_file(file_path: str) -> Dict[str, List[float]]:"""解析响应文件,提取ctr和cvr数据"""ctr_list = []cvr_list = []try:with open(file_path, 'r', encoding='utf-8') as f:content = f.read().strip()# 尝试解析JSONdata = json.loads(content)# 查找PredictResMap字段predict_res_map = Noneif isinstance(data, dict):# 如果是字典格式,直接查找PredictResMappredict_res_map = data.get('PredictResMap')else:# 如果是字符串格式,尝试解析字符串内容if 'PredictResMap' in content:# 提取PredictResMap部分start_idx = content.find('"PredictResMap":') + len('"PredictResMap":')end_idx = content.find('}', start_idx) + 1predict_res_str = content[start_idx:end_idx].strip()if predict_res_str.startswith('"') and predict_res_str.endswith('"'):predict_res_str = predict_res_str[1:-1]  # 去除引号predict_res_map = json.loads(predict_res_str)if not predict_res_map:print(f"错误: 在文件 {file_path} 中未找到 PredictResMap")return {"ctr": [], "cvr": []}# 解析PredictResMapif isinstance(predict_res_map, str):predict_res_map = json.loads(predict_res_map)# 提取ctr和cvr数据if 'ctr' in predict_res_map:for item in predict_res_map['ctr']:if 'ctr' in item:ctr_list.append(item['ctr'])if 'cvr' in item:cvr_list.append(item['cvr'])print(f"成功解析 {file_path}: 找到 {len(ctr_list)} 个CTR样本, {len(cvr_list)} 个CVR样本")except Exception as e:print(f"解析文件 {file_path} 时出错: {e}")return {"ctr": [], "cvr": []}return {"ctr": ctr_list,"cvr": cvr_list}def calculate_stats(data: List[float]) -> Dict[str, Any]:"""计算数据的统计信息"""if not data:return {}import numpy as nparr = np.array(data)# 移除无效值arr = arr[~np.isnan(arr)]arr = arr[~np.isinf(arr)]if len(arr) == 0:return {}return {"mean": float(np.mean(arr)),"median": float(np.median(arr)),"std": float(np.std(arr, ddof=1)),"var": float(np.var(arr)),"max": float(np.max(arr)),"min": float(np.min(arr)),"num": len(arr),"zero_num": int(np.sum(arr == 0))}def create_distribution_data(data: List[float], q_mul: float = 1000, n_bins: int = 1000) -> List[List[float]]:"""创建分布数据"""if not data:return []import numpy as nparr = np.array(data)# 移除无效值arr = arr[~np.isnan(arr)]arr = arr[~np.isinf(arr)]if len(arr) == 0:return []# 缩放数据arr_scaled = arr * q_mul# 创建分布distribution = []total_count = len(arr_scaled)for i in range(n_bins):# 计算在 [i, i+1) 范围内的数据点if i == 0:# 对于第一个桶,特别处理0值count = np.sum(arr_scaled == 0)else:count = np.sum((arr_scaled >= i) & (arr_scaled < i + 1))frequency = count / total_countbin_center = i / n_binsdistribution.append([bin_center, frequency])return distributiondef generate_ctr_q_json(base_file: str, new_file: str, task_id: str, output_file: str = "ctr_q_json.data"):"""生成ctr_q_json.data格式的数据"""print("开始解析响应文件...")# 解析基础文件和新文件base_data = parse_response_file(base_file)new_data = parse_response_file(new_file)if not base_data['ctr'] or not new_data['ctr']:print("错误: 未能从文件中提取到有效数据")returnresultdir = "result/html_result/" + task_idif not os.path.exists(resultdir):os.makedirs(resultdir)# 构建输出数据结构output_data = {"dis_res": {"queue": [{}, {}],"name": [os.path.basename(new_file), os.path.basename(base_file)],"q_dis": {"ctr": {},"cvr": {}},"path": resultdir}}output_file = resultdir + "/" + output_file# 处理CTR数据print("处理CTR数据...")ctr_distributions = ["click_0"]  # 可以扩展多个分布维度for dist_name in ctr_distributions:# base.json的CTR数据base_ctr_dist = create_distribution_data(base_data['ctr'])base_ctr_stats = calculate_stats(base_data['ctr'])# new.json的CTR数据new_ctr_dist = create_distribution_data(new_data['ctr'])new_ctr_stats = calculate_stats(new_data['ctr'])output_data["dis_res"]["q_dis"]["ctr"][dist_name] = {"series": [{"name": os.path.basename(new_file),"data": new_ctr_dist},{"name": os.path.basename(base_file),"data": base_ctr_dist}],"d_name": dist_name,"param": [new_ctr_stats, base_ctr_stats],"name": [os.path.basename(new_file), os.path.basename(base_file)]}# 处理CVR数据print("处理CVR数据...")cvr_distributions = ["click_0"]  # 可以扩展多个分布维度for dist_name in cvr_distributions:# base.json的CVR数据base_cvr_dist = create_distribution_data(base_data['cvr'], q_mul=1000, n_bins=1000)base_cvr_stats = calculate_stats(base_data['cvr'])# new.json的CVR数据new_cvr_dist = create_distribution_data(new_data['cvr'], q_mul=1000, n_bins=1000)new_cvr_stats = calculate_stats(new_data['cvr'])output_data["dis_res"]["q_dis"]["cvr"][dist_name] = {"series": [{"name": os.path.basename(new_file),"data": new_cvr_dist},{"name": os.path.basename(base_file),"data": base_cvr_dist}],"d_name": dist_name,"param": [new_cvr_stats, base_cvr_stats],"name": [os.path.basename(new_file), os.path.basename(base_file)]}# 保存到文件try:with open(output_file, 'w', encoding='utf-8') as f:json.dump(output_data, f, indent=2, ensure_ascii=False)cmd1 = 'cp -r Q_Distribution.html %s/' % (resultdir)os.system(cmd1)print(f"成功生成文件: {output_file}")print(f"CTR样本数: new={len(new_data['ctr'])}, base={len(base_data['ctr'])}")print(f"CVR样本数: new={len(new_data['cvr'])}, base={len(base_data['cvr'])}")except Exception as e:print(f"保存文件时出错: {e}")def main():"""主函数"""if len(sys.argv) != 3:print("使用方法: python get_q.py <new.response> <base.response>")print("示例: python get_q.py new.response base.response")sys.exit(1)new_file = sys.argv[1]base_file = sys.argv[2]task_id = "test"# 检查文件是否存在if not os.path.exists(new_file):print(f"错误: 文件 {new_file} 不存在")sys.exit(1)if not os.path.exists(base_file):print(f"错误: 文件 {base_file} 不存在")sys.exit(1)# 生成ctr_q_json.data文件generate_ctr_q_json(base_file, new_file, task_id)if __name__ == "__main__":main()
2、根据解析的结果绘制分布图
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>广告指标分布对比分析</title><script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><style>* { margin: 0; padding: 0; box-sizing: border-box; }body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; }.container { max-width: 1400px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; }.header { background: linear-gradient(135deg, #2c3e50, #34495e); color: white; padding: 30px; text-align: center; }.control-panel { background: #f8f9fa; padding: 20px; border-bottom: 2px solid #e9ecef; }.chart-container { height: 400px; padding: 20px; }.loading { text-align: center; padding: 50px; font-size: 1.2em; color: #666; }.error { text-align: center; padding: 50px; color: #e74c3c; background: #ffeaea; border-radius: 10px; margin: 20px; }.file-info { background: #e8f4fd; padding: 15px; border-radius: 8px; margin-bottom: 20px; }.stats-panel { margin-top: 20px; }.debug-info { background: #fff3cd; padding: 10px; border-radius: 5px; margin: 10px 0; font-family: monospace; font-size: 12px; }.control-group { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }.control-item { display: flex; align-items: center; gap: 10px; }select, button, input { padding: 8px 16px; border: 1px solid #ddd; border-radius: 5px; background: white; font-size: 14px; }/* Tab样式 */.tabs-container { margin-bottom: 20px; }.tabs-header { display: flex; background: #f8f9fa; border-bottom: 2px solid #e9ecef; }.tab { padding: 15px 30px; cursor: pointer; font-weight: 600; border-bottom: 3px solid transparent; transition: all 0.3s ease; }.tab:hover { background: #e9ecef; }.tab.active { background: white; border-bottom: 3px solid #667eea; color: #667eea; }.tab-content { display: none; padding: 20px; }.tab-content.active { display: block; }.distribution-selector { margin-bottom: 20px; }.distribution-selector label { font-weight: 600; margin-right: 10px; }/* 表格样式 */.stats-table-container { margin-top: 20px; }.stats-table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }.stats-table th { background: #667eea; color: white; padding: 12px 15px; text-align: left; font-weight: 600; }.stats-table td { padding: 10px 15px; border-bottom: 1px solid #e9ecef; }.stats-table tr:nth-child(even) { background: #f8f9fa; }.stats-table tr:hover { background: #e3f2fd; }.stats-table .metric-name { font-weight: 600; color: #2c3e50; background: #f8f9fa; }.stats-table .file-name { text-align: center; font-weight: 600; }.stats-table .value { text-align: right; font-family: 'Courier New', monospace; }.table-section { margin-bottom: 30px; }.table-section h4 { color: #2c3e50; margin-bottom: 15px; padding-bottom: 5px; border-bottom: 2px solid #667eea; }/* 差异高亮 */.diff-positive { color: #27ae60; font-weight: 600; }.diff-negative { color: #e74c3c; font-weight: 600; }</style>
</head>
<body><div class="container"><div class="header"><h1>广告指标分布对比分析</h1><p>CTR/CVR 分布可视化与多文件对比</p></div><div class="control-panel"><div class="control-group"><div class="control-item"><label>数据文件:</label><input type="file" id="fileInput" accept=".json,.data,.txt"></div></div></div><div class="content"><div id="loading" class="loading">正在加载数据...</div><div id="error" class="error" style="display: none;"></div><div id="fileInfo" class="file-info" style="display: none;"></div><div id="debugInfo" class="debug-info" style="display: none;"></div><div id="chartSection" style="display: none;"><!-- Tab导航 --><div class="tabs-container"><div class="tabs-header" id="tabsHeader"><!-- 动态生成Tab --></div><!-- CTR Tab内容 --><div id="ctr-tab" class="tab-content active"><div class="distribution-selector"><label>选择分布:</label><select id="ctrDistributionSelect"></select></div><div class="chart-section"><div class="chart-header"><h3 id="ctrChartTitle">CTR分布对比图</h3></div><div class="chart-container"><div id="ctrChart" style="width: 100%; height: 100%;"></div></div></div><div class="stats-panel" id="ctrStatsPanel"></div></div><!-- CVR Tab内容 --><div id="cvr-tab" class="tab-content"><div class="distribution-selector"><label>选择分布:</label><select id="cvrDistributionSelect"></select></div><div class="chart-section"><div class="chart-header"><h3 id="cvrChartTitle">CVR分布对比图</h3></div><div class="chart-container"><div id="cvrChart" style="width: 100%; height: 100%;"></div></div></div><div class="stats-panel" id="cvrStatsPanel"></div></div></div></div></div></div><script>let chartData = null;let currentCharts = {ctr: null,cvr: null};let chartOptions = {ctr: null,cvr: null};// 获取实际使用的数据function getDataToUse() {if (!chartData) return null;return chartData.dis_res || chartData;}// 调试函数:显示数据结构function showDebugInfo(data) {const debugDiv = document.getElementById('debugInfo');if (!data) {debugDiv.innerHTML = '数据为空';return;}let debugHTML = '<strong>数据结构:</strong><br>';debugHTML += '顶层键: ' + Object.keys(data).join(', ') + '<br>';if (data.dis_res) {debugHTML += 'dis_res键: ' + Object.keys(data.dis_res).join(', ') + '<br>';if (data.dis_res.q_dis) {debugHTML += 'q_dis键: ' + Object.keys(data.dis_res.q_dis).join(', ') + '<br>';// 显示每个指标的分布键Object.keys(data.dis_res.q_dis).forEach(metric => {debugHTML += `q_dis.${metric}键: ` + Object.keys(data.dis_res.q_dis[metric]).join(', ') + '<br>';});}}debugDiv.innerHTML = debugHTML;debugDiv.style.display = 'block';}// 方法2: 使用多种方式加载数据function loadDataFromPath() {$('#loading').show().text('正在加载数据...');$('#error').hide();const dataFile = 'ctr_q_json.data';console.log('开始加载数据文件:', dataFile);// 方式1: 使用jQuery.getJSON$.getJSON(dataFile).done(function(data) {console.log('✅ jQuery.getJSON 加载成功');chartData = data;showDebugInfo(data);initializePage();}).fail(function(jqXHR, textStatus, errorThrown) {console.log('❌ jQuery.getJSON 失败:', textStatus, errorThrown);// 方式2: 使用原生fetchtryFetch();});function tryFetch() {console.log('尝试使用fetch加载...');fetch(dataFile).then(response => {if (!response.ok) {throw new Error('HTTP ' + response.status);}return response.text();}).then(text => {console.log('fetch获取到文本内容,长度:', text.length);try {const data = JSON.parse(text);console.log('✅ fetch + JSON.parse 成功');chartData = data;showDebugInfo(data);initializePage();} catch (parseError) {console.log('❌ JSON解析失败:', parseError);tryCleanData(text);}}).catch(error => {console.log('❌ fetch失败:', error);showManualSelect();});}function tryCleanData(text) {console.log('尝试清理数据...');const cleanAttempts = [text.trim(),text.replace(/^\uFEFF/, ''),text.replace(/^[^{[]*/, ''),text.replace(/[^}]*$/, '')];for (let i = 0; i < cleanAttempts.length; i++) {try {const cleaned = cleanAttempts[i];const data = JSON.parse(cleaned);console.log(`✅ 清理方式 ${i} 成功`);chartData = data;showDebugInfo(data);initializePage();return;} catch (e) {console.log(`清理方式 ${i} 失败`);}}console.log('所有清理方式都失败');showManualSelect();}function showManualSelect() {showError('自动加载失败,请手动选择数据文件');$('#loading').html(`<div style="text-align: center;"><p>无法自动加载数据文件,请手动选择:</p><input type="file" id="fallbackFileInput" accept=".json,.data,.txt"><br><br><div style="font-size: 12px; color: #666; background: #f5f5f5; padding: 10px; border-radius: 5px;"><strong>调试信息:</strong><br>- 当前目录: <code>${window.location.pathname}</code><br>- 尝试的文件: <code>${dataFile}</code><br>- 请检查文件是否存在且格式正确</div></div>`);$('#fallbackFileInput').on('change', function(e) {loadDataFromFile(e.target.files[0]);});}}// 方法1: 使用 FileReader 读取本地文件document.getElementById('fileInput').addEventListener('change', function(e) {const file = e.target.files[0];if (file) {loadDataFromFile(file);}});function loadDataFromFile(file) {const reader = new FileReader();reader.onload = function(e) {try {const text = e.target.result;console.log('文件内容前100字符:', text.substring(0, 100));chartData = JSON.parse(text);console.log('文件加载成功,数据:', chartData);showDebugInfo(chartData);initializePage();} catch (error) {console.log('文件解析失败,尝试清理...', error);try {const cleaned = e.target.result.trim().replace(/^\uFEFF/, '');chartData = JSON.parse(cleaned);console.log('清理后解析成功');showDebugInfo(chartData);initializePage();} catch (cleanError) {showError('文件解析失败: ' + error.message + '<br>清理后也失败: ' + cleanError.message);}}};reader.onerror = function() {showError('文件读取失败');};reader.readAsText(file);}// 显示错误信息function showError(message) {document.getElementById('loading').style.display = 'none';document.getElementById('error').style.display = 'block';document.getElementById('error').innerHTML = '<h3>错误</h3><p>' + message + '</p>';}// 初始化页面function initializePage() {console.log('初始化页面,数据:', chartData);const dataToUse = getDataToUse();if (!dataToUse) {showError('数据为空');return;}document.getElementById('loading').style.display = 'none';document.getElementById('chartSection').style.display = 'block';updateFileInfo(dataToUse);createTabs(dataToUse);initializeAllCharts(dataToUse);}// 创建Tab导航function createTabs(data) {const tabsHeader = document.getElementById('tabsHeader');tabsHeader.innerHTML = '';const availableMetrics = Object.keys(data.q_dis || {});// 创建CTR Tabif (availableMetrics.includes('ctr')) {const ctrTab = document.createElement('div');ctrTab.className = 'tab active';ctrTab.textContent = 'CTR分布';ctrTab.setAttribute('data-metric', 'ctr');ctrTab.onclick = () => switchTab('ctr');tabsHeader.appendChild(ctrTab);}// 创建CVR Tabif (availableMetrics.includes('cvr')) {const cvrTab = document.createElement('div');cvrTab.className = 'tab';cvrTab.textContent = 'CVR分布';cvrTab.setAttribute('data-metric', 'cvr');cvrTab.onclick = () => switchTab('cvr');tabsHeader.appendChild(cvrTab);}// 如果没有可用的指标,显示提示if (availableMetrics.length === 0) {const noDataTab = document.createElement('div');noDataTab.className = 'tab active';noDataTab.textContent = '无可用数据';tabsHeader.appendChild(noDataTab);}}// 切换Tabfunction switchTab(metric) {// 更新Tab样式document.querySelectorAll('.tab').forEach(tab => {tab.classList.remove('active');});document.querySelector(`.tab[data-metric="${metric}"]`).classList.add('active');// 更新内容显示document.querySelectorAll('.tab-content').forEach(content => {content.classList.remove('active');});document.getElementById(`${metric}-tab`).classList.add('active');// 延迟渲染图表,确保容器可见setTimeout(() => {renderChart(metric);}, 50);}// 渲染图表(处理隐藏状态下的初始化问题)function renderChart(metric) {if (chartOptions[metric] && currentCharts[metric]) {// 如果图表已存在,重新设置大小和选项currentCharts[metric].resize();currentCharts[metric].setOption(chartOptions[metric], true);} else if (chartOptions[metric]) {// 如果只有选项但图表不存在,创建新图表const chartDom = document.getElementById(`${metric}Chart`);currentCharts[metric] = echarts.init(chartDom);currentCharts[metric].setOption(chartOptions[metric]);}}// 初始化所有图表function initializeAllCharts(data) {// 初始化CTR图表if (data.q_dis && data.q_dis.ctr) {updateDistributionSelect('ctr', data);createChartOption('ctr', data);updateStats('ctr', data);// 立即渲染CTR图表(因为它是默认激活的)renderChart('ctr');}// 初始化CVR图表if (data.q_dis && data.q_dis.cvr) {updateDistributionSelect('cvr', data);createChartOption('cvr', data);updateStats('cvr', data);// CVR图表先不渲染,等切换到该Tab时再渲染}}// 创建图表配置选项(不立即渲染)function createChartOption(metric, data) {const distributionSelect = document.getElementById(`${metric}DistributionSelect`);const currentDistribution = distributionSelect.value;if (!data.q_dis || !data.q_dis[metric] || !data.q_dis[metric][currentDistribution]) {return;}const distributionData = data.q_dis[metric][currentDistribution];document.getElementById(`${metric}ChartTitle`).textContent = `${metric.toUpperCase()} 分布 - ${distributionData.d_name || currentDistribution}`;chartOptions[metric] = {title: { text: `${metric.toUpperCase()} 分布对比`, left: 'center' },tooltip: { trigger: 'axis',formatter: function(params) {let result = `值: ${params[0].axisValue.toFixed(3)}<br/>`;params.forEach(param => {result += `${param.seriesName}: ${(param.value[1] * 100).toFixed(2)}%<br/>`;});return result;}},legend: { data: distributionData.series ? distributionData.series.map(s => s.name) : [],top: 30 },grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true },xAxis: { type: 'value', name: metric.toUpperCase(),nameGap: 25,axisLabel: {formatter: function(value) {return value.toFixed(3);}}},yAxis: { type: 'value', name: '频率',nameGap: 25,axisLabel: {formatter: function(value) {return (value * 100).toFixed(1) + '%';}}},series: distributionData.series ? distributionData.series.map((series, index) => ({name: series.name,type: 'line',data: series.data,smooth: true,lineStyle: { width: 2 },symbol: 'circle',symbolSize: 4,itemStyle: { color: index === 0 ? '#5470c6' : '#91cc75' },areaStyle: index === 0 ? {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(84, 112, 198, 0.3)' },{ offset: 1, color: 'rgba(84, 112, 198, 0.1)' }])} : {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(145, 204, 117, 0.3)' },{ offset: 1, color: 'rgba(145, 204, 117, 0.1)' }])}})) : []};}// 更新文件信息function updateFileInfo(data) {const fileNames = data.name || ['未知文件'];const fileInfoDiv = document.getElementById('fileInfo');fileInfoDiv.innerHTML = `<strong>对比文件:</strong> ${fileNames.join(' vs ')}<br><strong>数据路径:</strong> ${data.path || 'N/A'}`;fileInfoDiv.style.display = 'block';}// 更新分布选择器function updateDistributionSelect(metric, data) {const distributionSelect = document.getElementById(`${metric}DistributionSelect`);distributionSelect.innerHTML = '';if (data.q_dis && data.q_dis[metric]) {const qDis = data.q_dis[metric];console.log(`更新${metric}分布选择器:`, Object.keys(qDis));Object.keys(qDis).forEach(distKey => {const option = document.createElement('option');option.value = distKey;option.textContent = qDis[distKey].d_name || distKey;distributionSelect.appendChild(option);});// 默认选择第一个if (Object.keys(qDis).length > 0) {distributionSelect.value = Object.keys(qDis)[0];// 添加事件监听distributionSelect.onchange = () => {createChartOption(metric, data);renderChart(metric);updateStats(metric, data);};}} else {const option = document.createElement('option');option.value = '';option.textContent = '无可用分布';distributionSelect.appendChild(option);}}// 更新统计信息 - 改为表格形式function updateStats(metric, data) {const distributionSelect = document.getElementById(`${metric}DistributionSelect`);const currentDistribution = distributionSelect.value;if (!data.q_dis || !data.q_dis[metric] || !data.q_dis[metric][currentDistribution]) {return;}const distributionData = data.q_dis[metric][currentDistribution];const statsPanel = document.getElementById(`${metric}StatsPanel`);if (!distributionData.series || !distributionData.param) {statsPanel.innerHTML = '<p>无统计信息</p>';return;}// 获取所有统计指标const allMetrics = new Set();distributionData.param.forEach(param => {Object.keys(param).forEach(key => allMetrics.add(key));});// 创建表格let tableHTML = `<div class="table-section"><h4>${metric.toUpperCase()} 统计信息对比 - ${distributionData.d_name || currentDistribution}</h4><div class="stats-table-container"><table class="stats-table"><thead><tr><th>统计指标</th>`;// 添加文件名称表头distributionData.series.forEach(series => {tableHTML += `<th class="file-name">${series.name}</th>`;});tableHTML += `</tr></thead><tbody>`;// 添加数据行Array.from(allMetrics).forEach(metricName => {tableHTML += `<tr>`;tableHTML += `<td class="metric-name">${metricName}</td>`;// 收集所有值用于计算差异const values = [];distributionData.param.forEach((param, index) => {const value = param[metricName];values.push(value);const displayValue = typeof value === 'number' ? value.toFixed(6) : value;tableHTML += `<td class="value">${displayValue}</td>`;});tableHTML += `</tr>`;});tableHTML += `</tbody></table></div></div>`;statsPanel.innerHTML = tableHTML;}// 窗口大小变化时重新调整所有图表window.addEventListener('resize', function() {Object.values(currentCharts).forEach(chart => {if (chart) {chart.resize();}});});// 页面加载时尝试自动加载document.addEventListener('DOMContentLoaded', function() {loadDataFromPath();});</script>
</body>
</html>
http://www.dtcms.com/a/491719.html

相关文章:

  • YOLOv1与YOLOv2:目标检测的快速进化之路
  • 建设网站用什么软件排版网站服务器怎么做的
  • 《算法通关指南---OJ题目常见的错误效果》
  • 好看的创意网站设计蓝牙小程序开发教程
  • 高阶数据结构 --- Trie 树
  • PCIe协议之 flit 模式 之 flit bytes placing 图示说明
  • 如何做网站大管家Apple 手机网站制作
  • Unity 导出 AAR包 到 Android 项目并以 Fragment渲染显示
  • 把 AI“种”进闪存:基于极值量化与分块蒸馏的 7B 大模型 U 盘部署实战
  • 中兴电信B860AV3.2-T/B860AV3.1-T2(S905L3SB)2+8G_安卓9.0_线刷固件包
  • 网站建设主要工作内容动漫制作专业一定要艺术生吗
  • .livp,.HEIC格式图片转换成jpg格式图片
  • NewStarCTF2025-Week1-Web
  • 网站根目录 本地共享阿里指数在哪里看
  • 浏阳市商务局网站溪江农贸市场建设有什么平台可以发广告
  • FPGA强化-VGA显示设计与验证
  • 【2025最新】ArcGIS for JavaScript 快速实现热力图渲染
  • 怎么设置网站的logowordpress通知邮件美化
  • SpringCloud-Gateway实战使用与深度源码分析
  • 上海网站建设|网站制作浙江新手网络推广
  • 健康管理实训室厂家报价:精准明细,按需提供
  • Git学习笔记(三)
  • 通达信组合平台
  • 怎么做微网站推广泉州建设银行网站
  • 企业网站形象建设企业申请完域名以后 怎么把网站运行起来
  • 序列的力量——Python 内置方法的魔法解密
  • 跨数据源操作
  • 数据库圣经——第三章CRUD(一)
  • 信创学习小手册【第一天】
  • 动漫网站建设规划书模板制作网站主要包括哪些步骤