Files
AIclinicalresearch/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md
HaHafeng dac3cecf78 feat(iit): Complete IIT Manager Agent Day 1 - Environment initialization and WeChat integration
Summary:
- Complete IIT Manager Agent MVP Day 1 (12.5% progress)
- Database: Create iit_schema with 5 tables (IitProject, IitPendingAction, IitTaskRun, IitUserMapping, IitAuditLog)
- Backend: Add module structure (577 lines) and types (223 lines)
- WeChat: Configure Enterprise WeChat app (CorpID, AgentID, Secret)
- WeChat: Obtain web authorization and JS-SDK authorization
- WeChat: Configure trusted domain (iit.xunzhengyixue.com)
- Frontend: Deploy v1.2 with WeChat domain verification file
- Frontend: Fix CRLF issue in docker-entrypoint.sh (CRLF -> LF)
- Testing: 11/11 database CRUD tests passed
- Testing: Access Token retrieval test passed
- Docs: Create module status and development guide
- Docs: Update MVP task list with Day 1 completion
- Docs: Rename deployment doc to SAE real-time status record
- Deployment: Update frontend internal IP to 172.17.173.80

Technical Details:
- Prisma: Multi-schema support (iit_schema)
- pg-boss: Job queue integration prepared
- Taro 4.x: Framework selected for WeChat Mini Program
- Shadow State: Architecture foundation laid
- Docker: Fix entrypoint script line endings for Linux container

Status: Day 1/14 complete, ready for Day 2 REDCap integration
2026-01-01 14:32:58 +08:00

219 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工具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<OperationResult>
```
**传递给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个月→2个月
- ✅ 未选择的列也保持原始顺序
- ✅ 导出Excel时顺序正确
---
**开发时间**2025-12-09
**状态**:✅ 已完成,等待测试