Summary: - Add PKB module development record for 2026-01-07 - Create PKB module status document (00-模块当前状态与开发指南.md) - Update system status document to v2.7 Documents added: - docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md - docs/03-业务模块/PKB-个人知识库/00-模块当前状态与开发指南.md Documents updated: - docs/00-系统总体设计/00-系统当前状态与开发指南.md PKB module progress: 75% complete - Frontend Dashboard: 90% - Frontend Workspace: 85% - 3 work modes implemented - Batch processing API pending debug
5.6 KiB
5.6 KiB
工具C - Pivot列顺序优化总结
📋 问题描述
用户需求:长宽转换后,列的排序应该与上传文件时的列顺序保持一致。
当前问题:系统按字母顺序排列转换后的列,导致顺序与原文件不一致。
🎯 解决方案:方案A - Python端排序
核心思路
- Node.js后端从session获取原始列顺序
- Node.js后端从数据中提取透视列值的原始顺序(按首次出现顺序)
- 传递给Python
- Python在pivot后,按原始顺序重排列
🛠️ 实现细节
1. Python端(pivot.py)
新增参数:
original_column_order: List[str]:原始列顺序(如['Record ID', 'Event Name', 'FMA', '体重', '收缩压', ...])pivot_value_order: List[str]:透视列值的原始顺序(如['基线', '1个月', '2个月', ...])
排序逻辑:
if original_column_order:
# 1. 索引列始终在最前面
final_cols = [index_column]
# 2. 按原始列顺序添加转换后的列
for orig_col in original_column_order:
if orig_col in value_columns:
# 找出所有属于这个原列的新列
related_cols = [c for c in df_pivot.columns if c.startswith(f'{orig_col}___')]
# ✨ 按透视列的原始顺序排序
if pivot_value_order:
pivot_order_map = {val: idx for idx, val in enumerate(pivot_value_order)}
related_cols_sorted = sorted(
related_cols,
key=lambda c: pivot_order_map.get(c.split('___')[1], 999)
)
else:
related_cols_sorted = sorted(related_cols)
final_cols.extend(related_cols_sorted)
# 3. 添加未选择的列(保持原始顺序)
if keep_unused_columns:
for orig_col in original_column_order:
if orig_col in df_pivot.columns and orig_col not in final_cols:
final_cols.append(orig_col)
# 4. 重排列
df_pivot = df_pivot[final_cols]
2. Python端(main.py)
PivotRequest模型:
class PivotRequest(BaseModel):
# ... 原有字段 ...
original_column_order: List[str] = [] # ✨ 新增
pivot_value_order: List[str] = [] # ✨ 新增
调用pivot_long_to_wide:
result_df = pivot_long_to_wide(
df,
request.index_column,
request.pivot_column,
request.value_columns,
request.aggfunc,
request.column_mapping,
request.keep_unused_columns,
request.unused_agg_method,
request.original_column_order, # ✨ 新增
request.pivot_value_order # ✨ 新增
)
3. Node.js后端(QuickActionController.ts)
获取原始列顺序:
const originalColumnOrder = session.columns || [];
获取透视列值的原始顺序:
const pivotColumn = params.pivotColumn;
const seenPivotValues = new Set();
const pivotValueOrder: string[] = [];
for (const row of fullData) {
const pivotValue = row[pivotColumn];
if (pivotValue !== null && pivotValue !== undefined && !seenPivotValues.has(pivotValue)) {
seenPivotValues.add(pivotValue);
pivotValueOrder.push(String(pivotValue));
}
}
传递给QuickActionService:
executeResult = await quickActionService.executePivot(
fullData,
params,
session.columnMapping,
originalColumnOrder, // ✨ 新增
pivotValueOrder // ✨ 新增
);
4. Node.js后端(QuickActionService.ts)
方法签名:
async executePivot(
data: any[],
params: PivotParams,
columnMapping?: any[],
originalColumnOrder?: string[], // ✨ 新增
pivotValueOrder?: string[] // ✨ 新增
): Promise<OperationResult>
传递给Python:
const response = await axios.post(`${PYTHON_SERVICE_URL}/api/operations/pivot`, {
// ... 原有参数 ...
original_column_order: originalColumnOrder || [], // ✨ 新增
pivot_value_order: pivotValueOrder || [], // ✨ 新增
});
📊 效果对比
修改前(按字母顺序)
Record ID | FMA___基线 | FMA___1个月 | 收缩压___基线 | 收缩压___1个月 | 体重___基线 | 体重___1个月
↑ ↑ ↑ ↑ ↑ ↑ ↑
索引列 F开头 F开头 S开头(拼音) S开头 T开头 T开头
修改后(按原始顺序)
Record ID | FMA___基线 | FMA___1个月 | 体重___基线 | 体重___1个月 | 收缩压___基线 | 收缩压___1个月
↑ ↑ ↑ ↑ ↑ ↑ ↑
索引列 原文件第3列 原文件第3列 原文件第4列 原文件第4列 原文件第5列 原文件第5列
透视值内部顺序(按原始出现顺序)
FMA___基线 | FMA___1个月 | FMA___2个月
↑ ↑ ↑
首次出现 第二次出现 第三次出现
(而不是按"1个月"、"2个月"、"基线"的字母顺序)
✅ 开发完成
修改文件清单
- ✅
extraction_service/operations/pivot.py - ✅
extraction_service/main.py - ✅
backend/src/modules/dc/tool-c/controllers/QuickActionController.ts - ✅
backend/src/modules/dc/tool-c/services/QuickActionService.ts
优势
- ✅ 列顺序与原文件一致(用户熟悉)
- ✅ 透视值顺序按时间顺序(基线→1个月→2个月)
- ✅ 未选择的列也保持原始顺序
- ✅ 导出Excel时顺序正确
开发时间:2025-12-09
状态:✅ 已完成,等待测试