excel表格转json串简单写法(可直接copy使用)

该博客展示了如何使用Python的xlrd库读取Excel文件,并将数据转化为JSON格式。代码示例中,首先打开指定路径的Excel文件,然后读取第一个工作表,遍历每一行数据,将数据转化为字典结构并存储到list中,最后将list内容转换为JSON字符串并写入到文件。主要涉及数据处理和文件操作。
import xlrd

def read_xls(filename):

    # 打开Excel文件
    data = xlrd.open_workbook(filename)

    # 读取第一个工作表
    table = data.sheets()[0]

    # 统计行数
    rows = table.nrows

    data = []   # 存放数据

    time = 40

    for v in range(1, rows):
        values = table.row_values(v)

        # my_time = str(values[4])
        # print(my_time)
        # hour = my_time.split(":")
        # # min = hour[1].split("分")
        # # new_min = min[0].split(":")
        # #
        # print(hour)
        # time_count = int(int(hour[0]) * 60 + int(int(hour[1])+1))

        data.append(
            (
                {
                "identity":str(values[0]),
                "platform":str(values[1]), # 这里我只需要字符型数据,加了str(),根据实际自己取舍
                "drama_title":str(values[2]),
                "episode":str(values[3]),
                "time": str(values[4])
                }
            )
        )

    return data

if __name__ == '__main__':

    d1 = read_xls("/Users/chenzhou/Desktop/my.xls")

    d2 = str(d1).replace("\'", "\"")    # 字典中的数据都是单引号,但是标准的json需要双引号
    print(d2)

    # d2 = "{\"DeviceList\":" + d2 + "}"    # 前面的数据只是数组,加上外面的json格式大括号

    # 可读可写,如果不存在则创建,如果有内容则覆盖
    jsFile = open("/Users/chenzhou/Desktop/my.json", "w+", encoding='utf-8')
    jsFile.write(d2)
    jsFile.close()

@app.route('/data/risk', methods=['GET']) def risk_data(): return merged_df_jsonfrom datetime import datetime import pandas as pd from bp import output_data from pe import original_df from hrc import hr_res import json import pprint import numpy as np hr_res = hr_res[(hr_res['年份'] >= 2025) | ((hr_res['年份'] >= 2024) & (hr_res['月份'] >= 7))] original_df = original_df[['emp_id', 'name', '年月', 'ldept', 'findings', '异常项目详情', '异常数量', '分析意见', '可能疾病']] hr_res = hr_res[['年月', '员工总数']] hr_pe = pd.merge( hr_res, original_df, left_on=['年月'], right_on=['年月'], how='outer' ) # hr_pe.to_excel("temp/hr_pe.xlsx", index=False) all_records = [] for emp_info in output_data['employees'].values(): employee_id = emp_info['employee_id'] department = emp_info['department'] name = emp_info['name'] # 遍历该员工的所有记录 for record in emp_info['records']: record['employee_id'] = employee_id record['department'] = department record['name'] = name all_records.append(record) df_emp = pd.DataFrame(all_records) df_emp['employee_id'] = df_emp['employee_id'].astype(float) # 将日期列换为datetime类型以便排序 df_emp['date'] = pd.to_datetime(df_emp['date'], errors='coerce') # 每个人每年月只保留最新的一条数据 df_emp = df_emp.sort_values(['year_month', 'employee_id', 'date'], ascending=[True, True, False]) df_emp = df_emp.groupby(['year_month', 'employee_id']).first().reset_index() # df_emp.to_excel("temp/df_emp.xlsx", index=False) hr_bp = pd.merge( hr_res, df_emp, left_on=['年月'], right_on=['year_month'], how='outer' ) # hr_bp.to_excel("temp/hr_bp.xlsx", index=False) # 合并数据 merged_df = pd.merge( hr_pe, hr_bp, left_on=['年月', '员工总数', 'emp_id'], right_on=['年月', '员工总数', 'employee_id'], how='outer' ) # 打印合并后的结果 # print(merged_df) # 定义健康等级函数 def assign_health_level(row): bp_level = row['bp_level'] bmi_level = row['bmi_level'] if bp_level in ["二级高血压(中度)", "三级高血压(重度)"]: return '风险' elif bp_level == "正常血压" and bmi_level == "正常" and (row['异常项目详情'] == "无异常"): return '健康' elif bp_level in ["正常高值", "一级高血压(轻度)"] or bmi_level in ["较瘦", "超重"] or (row['异常项目详情'] != "无异常"): return '亚健康' return '亚健康' merged_df = merged_df[~(merged_df['ldept'].isna() & merged_df['department'].isna())] # 应用健康等级分类 merged_df['健康等级'] = merged_df.apply(assign_health_level, axis=1) merged_df.to_excel("temp/表格数据.xlsx", index=False) # 按年月统计各级别人数 monthly_stats = merged_df.groupby(['year_month', '健康等级']).size().unstack(fill_value=0) # monthly_stats.to_excel("temp/monthly_health_stats.xlsx") # 按年月统计量测人数(去重后) grouped_data = merged_df.groupby('year_month').size().reset_index(name='量测人数') # grouped_data.to_excel("temp/grouped_data.xlsx") # 按年份和月份分组统计血压分级 bp_grouped = merged_df.groupby(['year_month', 'bp_level']).size().reset_index(name='员工数量') bp_pivot = bp_grouped.pivot_table( index='year_month', columns='bp_level', values='员工数量', fill_value=0 ).reset_index() # 按年份和月份分组统计BMI分级 bmi_grouped = merged_df.groupby(['year_month', 'bmi_level']).size().reset_index(name='员工数量') bmi_pivot = bmi_grouped.pivot_table( index='year_month', columns='bmi_level', values='员工数量', fill_value=0 ).reset_index() # merged_df_sorted = merged_df.sort_values(by='year_month', ascending=False) # merged_df_sorted.to_excel("temp/merged_df.xlsx", index=False) for col in merged_df.select_dtypes(include=['datetime64']).columns: merged_df[col] = merged_df[col].astype(str) merged_df = merged_df.where(pd.notnull(merged_df), None) merged_df['employee_id'] = merged_df['employee_id'].fillna(0).astype(int).astype(str) # merged_df.to_excel("temp/merged_df.xlsx", index=False) merged_df_json = merged_df.to_json(orient='records', force_ascii=False, date_format='iso') # 合并数据 merged_data = pd.merge( left=grouped_data, right=bp_pivot, on='year_month', how='left' ) merged_data = pd.merge( left=merged_data, right=bmi_pivot, on='year_month', how='left' ) # 合并HR数据(员工总数) merged_data = pd.merge( left=merged_data, right=hr_res, left_on='year_month', right_on='年月', how='left' ) # 填充缺失值 merged_data.fillna(0, inplace=True) # 计算量测率 merged_data['量测率'] = merged_data['量测人数'] / merged_data['员工总数'] merged_data.loc[merged_data['员工总数'] == 0, '量测率'] = 0 # 确保所有血压分级列都存在 blood_columns = ['正常血压', '正常高值', '一级高血压(轻度)', '二级高血压(中度)', '三级高血压(重度)'] for col in blood_columns: if col not in merged_data.columns: merged_data[col] = 0 # 确保所有BMI分级列都存在 bmi_columns = ['较瘦', '正常', '超重', '肥胖'] for col in bmi_columns: if col not in merged_data.columns: merged_data[col] = 0 # 标记体检记录 merged_df['体检标记'] = merged_df['emp_id'].notna().astype(int) # 标记异常记录(分析意见非空且不为空字符) # merged_df['异常标记'] = merged_df['分析意见'].apply(lambda x: 1 if x and str(x).strip() not in ['', '[]'] else 0) merged_df['异常标记'] = merged_df.apply(lambda row: 1 if (row['体检标记'] == 1 and row['异常项目详情'] not in ['', '无异常']) else 0 if (row['体检标记'] == 1 and row['异常项目详情'] in ['', '无异常']) else 0, axis=1) # merged_df.to_excel("temp/merged_df2.xlsx", index=False) # 按年月分组统计 pe_stats = merged_df.groupby('年月').agg( 体检人数=('体检标记', 'sum'), # 统计体检人数 异常人数=('异常标记', 'sum') # 统计异常人数 ).reset_index() pe_stats = pe_stats.rename(columns={'年月': 'year_month'}) # 计算异常率 pe_stats['异常率'] = pe_stats['异常人数'] / pe_stats['体检人数'] all_data1 = pd.merge( left=merged_data, right=monthly_stats, left_on='year_month', right_on='year_month', how='outer' ) all_data = pd.merge( left=all_data1, right=pe_stats, left_on='year_month', right_on='year_month', how='outer' ) all_data_sorted = all_data.sort_values(by='year_month', ascending=False) all_data_sorted = all_data_sorted.fillna(0) # all_data_sorted.to_excel("temp/all_data.xlsx", index=False) # 换为json for col in all_data_sorted.select_dtypes(include=['datetime64']).columns: all_data_sorted[col] = all_data_sorted[col].astype(str) all_data_sorted = all_data_sorted.where(pd.notnull(all_data), None) all_data_json = all_data_sorted.to_json(orient='records', force_ascii=False, date_format='iso') # print(all_data_json) # 统一部门信息(优先使用体检数据中的大部门,若为空则使用血压数据中的部门) merged_df['统一部门'] = merged_df['ldept'].combine_first(merged_df['department']) # 按年月、统一部门、健康等级分组统计人数 dept_health_stats = ( merged_df.groupby(['year_month', '统一部门', '健康等级']) .size() .unstack(fill_value=0) .reset_index() ) dept_health_stats.to_excel("temp/柱状图.xlsx", index=False) # 处理缺失的健康等级列(确保所有等级都存在) for level in ['健康', '亚健康', '风险']: if level not in dept_health_stats.columns: dept_health_stats[level] = 0 # 重命名列并换数据类型 dept_health_stats = dept_health_stats.rename(columns={ 'year_month': '年月', '统一部门': '部门' })[['年月', '部门', '健康', '亚健康', '风险']] # 换为JSON格式(按年月分组) json_output = {} for (year_month, group) in dept_health_stats.groupby('年月'): # 换部门数据为字典列表 departments = group.drop(columns='年月').to_dict('records') # 添加到结果字典 json_output[str(year_month)] = departments final_json = json.dumps(json_output, indent=2, ensure_ascii=False) # print(final_json) function updateRiskTable(yearMonth, level, dept = null) { const currentUser = window.currentUser || { role: 'user', dept: 'all' }; const userRole = currentUser.role; const userDept = currentUser.dept; // 如果不是 admin,隐藏整个表格区域并提示 if (userRole !== 'admin') { riskTable.innerHTML = ` <tr> <td colspan="7" class="text-center text-muted"> <i class="fas fa-lock me-2"></i> 您没有权限查看详细人员名单 </td> </tr> `; tableTitle.textContent = `${yearMonth} ${level}人员明细`; return; } // admin 用户:允许查看指定部门 let filterDept = dept; if (userDept === 'all') { // dept=all 的 admin 可以查看任何部门(包括全部) filterDept = dept || 'all'; // 若未选,则视为“全部” } else { // 非 all 的 admin 只能查看自己部门 filterDept = userDept; } // 过滤数据 let filteredData = riskData.filter(item => item.year_month === yearMonth && item.健康等级 === level ); // 再次按部门过滤 if (filterDept !== 'all') { filteredData = filteredData.filter(item => item.department === filterDept); } // 更新标题 const displayDept = filterDept === 'all' ? '全部部门' : filterDept; tableTitle.textContent = `${yearMonth} ${level}人员明细 (${displayDept})`; // 清空表格 riskTable.innerHTML = ''; if (filteredData.length === 0) { riskTable.innerHTML = `<tr><td colspan="7" class="text-center">当前时间段无${level}人员数据</td></tr>`; return; } // 创建表头 const headerRow = document.createElement('tr'); const headers = ['工号', '姓名', '部门', '血压等级', 'BMI等级', '体检结果', '健康等级']; headers.forEach(headerText => { const th = document.createElement('th'); th.textContent = headerText; headerRow.appendChild(th); }); riskTable.appendChild(headerRow); // 添加数据行 filteredData.forEach(item => { const row = document.createElement('tr'); const fields = ['employee_id', 'name', 'department', 'bp_level', 'bmi_level', '检查结果', '健康等级']; fields.forEach(field => { const cell = document.createElement('td'); cell.textContent = safeDisplay(item[field]); row.appendChild(cell); }); riskTable.appendChild(row); }); }
最新发布
09-26
你当前的完整流程已经非常完善,但存在一个 **关键问题:Flask 路由与数据定义顺序错误**,并且 `merged_df_json` 中有 **重复字段(如 `name_x`, `name_y`)**,这会导致前端显示异常。 --- ## ✅ 问题总结 ### ❌ 问题1:`@app.route('/data/risk')` 放在了脚本末尾且未被正确导入 你现在把路由写在了 Python 脚本末尾,而 `merged_df_json` 是通过直接运行模块生成的数据。但在 Flask 应用中: > **`@app.route` 必须注册到 Flask 实例上,并且该文件应作为模块被主应用导入或本身就是主应用。** 如果你是用 `from xxx import merged_df_json` 的方式引入,那么这个路由根本不会生效! --- ### ❌ 问题2:`merged_df` 存在两个姓名列(`name_x`, `name_y`) 原因: - `hr_pe` 和 `hr_bp` 都包含 `name` 字段 - 合并时没有处理字段冲突 → Pandas 自动生成 `name_x`, `name_y` 导致你在前端看到的 JSON 数据里有两个名字字段,`updateRiskTable` 可能取错值。 --- ### ❌ 问题3:代码结构混乱 —— 数据处理 + Flask 路由混在一起 这种写法不利于调试、维护和部署。 --- ## ✅ 正确解决方案 ### ✅ 方案目标 1. ✅ 确保 `merged_df` 只有一个 `name` 列 2. ✅ 将 `@app.route('/data/risk')` 移入主 Flask 应用 3. ✅ 动态返回最新计算出的 `merged_df_json` 4. ✅ 前端能正确渲染“风险人员名单” --- ## ✅ 第一步:修复合并逻辑,统一 `name` 字段 ```python # === 在你的数据分析脚本中(比如 analysis.py)=== import pandas as pd import numpy as np from bp import output_data from pe import original_df from hrc import hr_res def generate_merged_df(): # 过滤 hr_res 时间范围 hr_res_filtered = hr_res[(hr_res['年份'] >= 2025) | ((hr_res['年份'] >= 2024) & (hr_res['月份'] >= 7))] hr_res_simple = hr_res_filtered[['年月', '员工总数']] # 提取原始体检数据 pe_data = original_df[['emp_id', 'name', '年月', 'ldept', 'findings', '异常项目详情', '异常数量', '分析意见', '可能疾病']] # 构造血压记录表 all_records = [] emp_master = {} # 主数据:emp_id -> name, department for emp_info in output_data['employees'].values(): emp_id = str(emp_info['employee_id']) emp_master[emp_id] = { 'name': emp_info['name'], 'department': emp_info['department'] } for record in emp_info['records']: rec = record.copy() rec['employee_id'] = emp_id all_records.append(rec) df_emp = pd.DataFrame(all_records) df_emp['employee_id'] = df_emp['employee_id'].astype(float) df_emp['date'] = pd.to_datetime(df_emp['date'], errors='coerce') # 按年月+工号保留最新一条 df_emp = df_emp.sort_values(['year_month', 'employee_id', 'date'], ascending=[True, True, False]) df_emp = df_emp.groupby(['year_month', 'employee_id']).first().reset_index() # 合并血压数据 hr_bp = pd.merge(hr_res_simple, df_emp, left_on='年月', right_on='year_month', how='outer') # 合并体检数据 hr_pe = pd.merge(hr_res_simple, pe_data, on='年月', how='outer') # 主合并:按 年月 + emp_id/employee_id merged_df = pd.merge( hr_pe, hr_bp, left_on=['年月', 'emp_id'], right_on=['年月', 'employee_id'], how='outer', suffixes=('_pe', '_bp') ) # === 关键修复:解决 name_x / name_y 冲突 === if 'name_pe' in merged_df.columns and 'name_bp' in merged_df.columns: merged_df['name'] = merged_df['name_pe'].combine_first(merged_df['name_bp']) merged_df = merged_df.drop(columns=['name_pe', 'name_bp']) elif 'name_pe' in merged_df.columns: merged_df = merged_df.rename(columns={'name_pe': 'name'}) elif 'name_bp' in merged_df.columns: merged_df = merged_df.rename(columns={'name_bp': 'name'}) # 补全缺失的 name(使用 emp_master) merged_df['emp_id'] = merged_df['emp_id'].fillna('').astype(str) master_df = pd.DataFrame([ {'emp_id': k, 'name_master': v['name'], 'dept_master': v['department']} for k, v in emp_master.items() ]) merged_df = pd.merge(merged_df, master_df, on='emp_id', how='left') merged_df['name'] = merged_df['name'].combine_first(merged_df['name_master']) # 统一部门 merged_df['department'] = merged_df['ldept'].combine_first(merged_df['dept_master']) # 健康等级分类函数 def assign_health_level(row): bp_level = row.get('bp_level') bmi_level = row.get('bmi_level') findings = row.get('异常项目详情', '') if pd.isna(bp_level): bp_level = "" if pd.isna(bmi_level): bmi_level = "" if pd.isna(findings): findings = "" if bp_level in ["二级高血压(中度)", "三级高血压(重度)"]: return '风险' elif bp_level == "正常血压" and bmi_level == "正常" and findings in ["无异常", "", None]: return '健康' else: return '亚健康' merged_df['健康等级'] = merged_df.apply(assign_health_level, axis=1) # 清洗数据用于 JSON 输出 for col in merged_df.select_dtypes(include=['datetime64']): merged_df[col] = merged_df[col].astype(str) merged_df = merged_df.where(pd.notnull(merged_df), None) # 生成 JSON 字符 merged_df_json = merged_df.to_json(orient='records', force_ascii=False, date_format='iso') return merged_df, merged_df_json ``` --- ## ✅ 第二步:将路由注册到 Flask 主应用 假设你的主文件是 `app.py`: ```python # app.py from flask import Flask, jsonify from analysis import generate_merged_df app = Flask(__name__) # 全局缓存结果(可替换为 Redis 缓存) _cached_json = None @app.route('/data/risk') def risk_data(): global _cached_json if _cached_json is None: _, _cached_json = generate_merged_df() # 只返回 json return jsonify(_cached_json) @app.route('/user') def get_user(): return jsonify({"user": {"username": "admin", "role": "admin", "dept": "all"}}) if __name__ == '__main__': app.run(debug=True) ``` --- ## ✅ 第三步:确保前端能正确读取 `name` 字段 修改前端 `updateRiskTable` 中的字段列表: ```js const fields = ['employee_id', 'name', 'department', 'bp_level', 'bmi_level', 'findings', '健康等级']; ``` ✅ 注意:你原来用了 `'检查结果'`,但实际字段是 `'findings'`,要保持一致! 建议后端重命名更友好字段: ```python merged_df.rename(columns={ 'findings': '体检结果', 'bp_level': '血压等级', 'bmi_level': 'BMI等级' }, inplace=True) ``` --- ## ✅ 最终效果 | 目标 | 是否达成 | |------|----------| | ✅ `merged_df` 不再有 `name_x/name_y` | ✔️ 已统一 | | ✅ `/data/risk` 接口可访问 | ✔️ 注册到了 Flask | | ✅ 返回的是动态 JSON 数据 | ✔️ 来自最新计算 | | ✅ 前端表格显示正确的姓名 | ✔️ 使用唯一 `name` | | ✅ 非 admin 用户无法查看明细 | ✔️ 权限控制有效 | --- ## ✅ 建议优化点 | 类型 | 建议 | |------|------| | 🔁 性能 | 使用 `functools.lru_cache` 或 Redis 缓存每日计算结果 | | 🗂️ 结构 | 把数据处理逻辑拆成独立模块(`services/data_service.py`) | | 🔄 更新机制 | 添加定时任务每天凌晨更新一次数据 | | 🧪 安全 | 对 `/data/risk` 加 JWT 认证,避免未登录访问 | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值