含多值单元格的DataFrame可视化及数据库存储规范咨询
问题背景
我有如下的DataFrame:
import pandas as pd import numpy as np date_vals = ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06', '2022-01-07', '2022-01-08', '2022-01-09', '2022-01-10', '2022-01-11', '2022-01-12', '2022-01-13', '2022-01-14', '2022-01-15', '2022-01-16', '2022-01-17', '2022-01-18', '2022-01-19', '2022-01-20'] machine_vals = ['M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2', 'M-1', 'M-2'] shift_vals = ['Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night', 'Day', 'Night'] type_vals = [['Type A', 'Type B'], 'Type B', 'Type A', 'Type B', 'Type A', 'Type A', 'Type B', 'Type B', 'Type A', 'Type B', ['Type A', 'Type B'], 'Type B', ['Type A', 'Type B'], 'Type A', 'Type B', ['Type A', 'Type B'], 'Type A', 'Type B', 'Type A', 'Type B'] meter_vals = [[1000, 800], 1500, 900, 1700, 1200, 1300, 1600, 1400, 1300, 1100, [1400, 200], 1200, [1000, 700], 1500, 1600, [1300, 900], 1200, 1100, 1300, 1700] data = { 'Date': date_vals, 'Machine': machine_vals, 'Shift': shift_vals, 'Type': type_vals, 'Meter': meter_vals } df = pd.DataFrame(data) df

Type和Meter列的部分单元格包含两个值,例如['Type A', 'Type B']对应[1000, 800],代表Type A产量1000米、Type B产量800米。
技术问题
1. 数据库单元格存储多变量的合理性、问题与优化建议
在数据库中,一个单元格存储多个变量是否为合理的方式?会给后续工作带来哪些问题?对此有何优化建议?
2. 基于DataFrame的可视化需求
基于上述DataFrame,如何完成以下两类可视化图表:
- X轴为日期,Y轴展示各机器每日Type A和Type B的产量
- X轴为日期,Y轴展示各机器每日白班(Day)和夜班(Night)的产量
核心需求为如何处理单元格中存在多个值的场景。
问题解答
1. 数据库单元格存储多变量的合理性分析
这种存储方式完全不合理,违背了数据库设计的第一范式(1NF)——每个属性必须是原子值,不可再分。
带来的问题包括:
- 查询效率极低:无法直接通过SQL筛选某类Type的产量,必须先解析单元格内容,大量增加计算开销;
- 数据一致性难以保障:多值的格式容易出现错误(比如列表长度不匹配、类型拼写错误),后期校验和修复成本极高;
- 统计分析困难:无法直接使用聚合函数(SUM、AVG等)进行分组统计,必须先做数据拆分;
- 扩展性差:如果后续新增Type类型,现有存储结构无法兼容,需要修改所有相关的解析逻辑。
优化建议:
- 采用关系型数据库的范式设计:拆分出独立的产量明细表,包含字段
Date、Machine、Shift、Type、Meter,每个记录对应单一的Type和产量值; - 如果是NoSQL数据库,也应保证每条文档的字段是原子化的,避免嵌套列表存储多变量,可采用数组对象的形式(比如
[{"Type": "Type A", "Meter": 1000}, {"Type": "Type B", "Meter": 800}]),但仍建议尽量扁平化存储以提升查询效率。
2. 处理多值单元格并实现可视化
核心步骤是先将包含多值的行拆分成多行,让每一行对应单一的Type和Meter值,之后再进行分组聚合和可视化。
步骤1:数据预处理(拆分多值单元格)
# 先将Type和Meter列统一转为列表格式,方便处理 df['Type'] = df['Type'].apply(lambda x: x if isinstance(x, list) else [x]) df['Meter'] = df['Meter'].apply(lambda x: x if isinstance(x, list) else [x]) # 拆分多值行,生成新的DataFrame exploded_df = df.explode(['Type', 'Meter'], ignore_index=True) # 将Meter转为数值类型 exploded_df['Meter'] = exploded_df['Meter'].astype(int)
步骤2:实现第一类可视化(按Type分机器每日产量)
import matplotlib.pyplot as plt import seaborn as sns # 按日期、机器、Type分组求和(因为同一日期+机器+Shift可能对应多个Type,这里按日聚合) daily_type_df = exploded_df.groupby(['Date', 'Machine', 'Type'])['Meter'].sum().reset_index() # 绘制图表 plt.figure(figsize=(12, 6)) sns.lineplot(data=daily_type_df, x='Date', y='Meter', hue='Type', style='Machine', marker='o') plt.title('各机器每日Type A/B产量') plt.xticks(rotation=45) plt.tight_layout() plt.show()
步骤3:实现第二类可视化(按Shift分机器每日产量)
# 按日期、机器、Shift分组求和 daily_shift_df = exploded_df.groupby(['Date', 'Machine', 'Shift'])['Meter'].sum().reset_index() # 绘制图表 plt.figure(figsize=(12, 6)) sns.lineplot(data=daily_shift_df, x='Date', y='Meter', hue='Shift', style='Machine', marker='s') plt.title('各机器每日白班/夜班产量') plt.xticks(rotation=45) plt.tight_layout() plt.show()
说明:
- 预处理时用
explode方法直接拆分列表类型的单元格,是Pandas处理这类场景最简洁的方式; - 分组聚合时按需求选择分组维度,确保每个日期-机器的分组下能正确统计对应类别的总产量;
- 使用Seaborn或Matplotlib绘制折线图可以清晰对比不同机器、不同类别(Type/Shift)的产量趋势。
内容的提问来源于stack exchange,提问作者volkan
相关产品推荐
相关产品推荐

