# 工具C - Pivot列顺序优化总结 ## 📋 问题描述 **用户需?*:长宽转换后,列的排序应该与上传文件时的列顺序保持一致? **当前问题**:系统按字母顺序排列转换后的列,导致顺序与原文件不一致? --- ## 🎯 解决方案:方案A - Python端排? ### 核心思路 1. Node.js后端从session获取**原始列顺?* 2. Node.js后端从数据中提取**透视列值的原始顺序**(按首次出现顺序? 3. 传递给Python 4. Python在pivot后,按原始顺序重排列 --- ## 🛠?实现细节 ### 1. Python端(pivot.py? **新增参数**? - `original_column_order: List[str]`:原始列顺序(如`['Record ID', 'Event Name', 'FMA', '体重', '收缩?, ...]`? - `pivot_value_order: List[str]`:透视列值的原始顺序(如`['基线', '1个月', '2个月', ...]`? **排序逻辑**? ```python 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模型**? ```python class PivotRequest(BaseModel): # ... 原有字段 ... original_column_order: List[str] = [] # ?新增 pivot_value_order: List[str] = [] # ?新增 ``` **调用pivot_long_to_wide**? ```python 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? **获取原始列顺?*? ```typescript const originalColumnOrder = session.columns || []; ``` **获取透视列值的原始顺序**? ```typescript 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**? ```typescript executeResult = await quickActionService.executePivot( fullData, params, session.columnMapping, originalColumnOrder, // ?新增 pivotValueOrder // ?新增 ); ``` ### 4. Node.js后端(QuickActionService.ts? **方法签名**? ```typescript async executePivot( data: any[], params: PivotParams, columnMapping?: any[], originalColumnOrder?: string[], // ?新增 pivotValueOrder?: string[] // ?新增 ): Promise ``` **传递给Python**? ```typescript 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个月"?基线"的字母顺序) ``` --- ## ?开发完? ### 修改文件清单 1. ?`extraction_service/operations/pivot.py` 2. ?`extraction_service/main.py` 3. ?`backend/src/modules/dc/tool-c/controllers/QuickActionController.ts` 4. ?`backend/src/modules/dc/tool-c/services/QuickActionService.ts` ### 优势 - ?列顺序与原文件一致(用户熟悉? - ?透视值顺序按时间顺序(基线→1个月?个月? - ?未选择的列也保持原始顺? - ?导出Excel时顺序正? --- **开发时?*?025-12-09 **状?*:✅ 已完成,等待测试