Python pandas: 高效數據重構(樞紐分析) — 使用 groupby(“Name”).cumcount, set_index 與 unstack 將長格式轉為寬格式

在數據分析中,數據結構的轉換對於後續處理至關重要。
本文將介紹如何使用 Pandas 中的 set_indexunstack
將”長格式”數據轉換為更易分析的”寬格式”,以 PCB 走線設計數據為例。

什麽是長格式與寬格式?

  • 長格式(Long Format): 每個觀測值占一row,信息分散在多rows中
  • 寬格式(Wide Format): 每個主體占一col,相關信息橫向展開為多col

實例數據

假設我們有以下 PCB 走線數據(長格式):

# 示例數據 - 長格式
data = {
    'Name': ['Signal_A', 'Signal_A', 'Signal_A', 'Signal_B', 'Signal_B'],
    'Layer Name': ['TOP', 'TOP', 'L10', 'L10', 'BOT'],
    'Line Width (mils)': [5.0, 6.5, 4.3, 5.2, 6.0],
    'Length at Width (mils)': [120.5, 450.8, 30.2, 215.6, 320.4]
}
df = pd.DataFrame(data)

# 輸出結果:
#       Name Layer Name  Line Width (mils)  Length at Width (mils)
# 0  Signal_A        TOP               5.0                   120.5
# 1  Signal_A        TOP               6.5                   450.8
# 2  Signal_A        L10               4.3                    30.2
# 3  Signal_B        L10               5.2                   215.6
# 4  Signal_B        BOT               6.0                   320.4

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

轉換函數詳解

讓我們分解 restructure_trace_data_alt 函數的每個步驟:

def restructure_trace_data_alt(df):
    """使用set_index和unstack重構PCB走線數據"""
    
    # 步驟1: 處理Layer Name - 合並每個信號的所有層信息
    layer_info = df.groupby('Name')['Layer Name'].apply(
        lambda x: ' | '.join(sorted(x.unique()))
    ).reset_index()
    
    # 步驟2: 為寬度分組創建序號
    df_sorted = df.sort_values(['Name', 'Line Width (mils)'])
    df_sorted['segment_num'] = df_sorted.groupby('Name').cumcount() + 1
    
    # 步驟3: 轉換寬度數據
    width_df = df_sorted.set_index(['Name', 'segment_num'])['Line Width (mils)'].unstack()
    width_df.columns = [f'Line Width {i} (mils)' for i in width_df.columns]
    
    # 步驟4: 轉換長度數據
    length_df = df_sorted.set_index(['Name', 'segment_num'])['Length at Width (mils)'].unstack()
    length_df.columns = [f'Length at Width {i} (mils)' for i in length_df.columns]
    
    # 步驟5: 合併結果
    result = pd.merge(layer_info, width_df.reset_index(), on='Name')
    result = pd.merge(result, length_df.reset_index(), on='Name')
    
    return result

分步驟圖解轉換過程

步驟1: 處理層信息

# 合併每個信號的所有層信息
layer_info = df.groupby('Name')['Layer Name'].apply(
    lambda x: ' | '.join(sorted(x.unique()))
).reset_index()

# 輸出結果
#       Name     Layer Name
# 0  Signal_A  L10 | TOP
# 1  Signal_B  BOT | L10

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

這一步將每個信號的所有層信息合並為一個字符串,
使用豎線分隔,並按字母順序排序。

步驟2: 創建段序號

# 先按信號名稱和線寬排序
df_sorted = df.sort_values(['Name', 'Line Width (mils)'])
# 為每個信號的每段走線創建序號 (1, 2, 3...)
df_sorted['segment_num'] = df_sorted.groupby('Name').cumcount() + 1

# 輸出結果
#       Name Layer Name  Line Width (mils)  Length at Width (mils)  segment_num
# 2  Signal_A        L10               4.3                    30.2            1
# 0  Signal_A        TOP               5.0                   120.5            2
# 1  Signal_A        TOP               6.5                   450.8            3
# 3  Signal_B        L10               5.2                   215.6            1
# 4  Signal_B        BOT               6.0                   320.4            2

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

這一步為每個信號的各段走線創建順序編號,
確保寬度從小到大排列。

步驟3-4: 使用 set_index 和 unstack 創建寬格式

# %%
# 轉換寬度數據
width_df = df_sorted.set_index(['Name', 'segment_num'])['Line Width (mils)'].unstack()
width_df.columns = [f'Line Width {i} (mils)' for i in width_df.columns]

# 輸出結果
# segment_num  Line Width 1 (mils)  Line Width 2 (mils)  Line Width 3 (mils)
# Name                                                                      
# Signal_A                     4.3                  5.0                  6.5
# Signal_B                     5.2                  6.0                  NaN

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

set_index 創建多級索引,
unstack 將 segment_num 變為列名,
每個單元格包含對應的寬度值。

同樣的方法用於長度數據:

# %%
# 轉換長度數據
length_df = df_sorted.set_index(['Name', 'segment_num'])['Length at Width (mils)'].unstack()
length_df.columns = [f'Length at Width {i} (mils)' for i in length_df.columns]

# 輸出結果
# segment_num  Length at Width 1 (mils)  Length at Width 2 (mils)  Length at Width 3 (mils)
# Name                                                                                     
# Signal_A                         30.2                     120.5                     450.8
# Signal_B                        215.6                     320.4                       NaN

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

步驟5: 合併結果

# %%
# %%
# 合併層信息和寬度/長度數據
result = pd.merge(layer_info, width_df.reset_index(), on='Name')
result = pd.merge(result, length_df.reset_index(), on='Name')

# 最終結果
#       Name     Layer Name  Line Width 1 (mils)  Line Width 2 (mils)  Line Width 3 (mils)  Length at Width 1 (mils)  Length at Width 2 (mils)  Length at Width 3 (mils)
# 0  Signal_A  L10 | TOP                    4.3                  5.0                  6.5                      30.2                     120.5                     450.8
# 1  Signal_B  BOT | L10                    5.2                  6.0                  NaN                     215.6                     320.4                       NaN

輸出結果:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

merge分解動作:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

也可使用concat() 合併:

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

result= pd.concat([layer_info.set_index("Name"),width_df,length_df],axis=1)
#axis=1,橫向合併
#index 要一模一樣,才不會有缺失值

Python pandas: 高效數據重構(樞紐分析) — 使用 groupby("Name").cumcount, set_index 與 unstack 將長格式轉為寬格式 - 儲蓄保險王

技術要點

  1. cumcount() 才知道有幾個Name (幾種不同的Width/Length)
  2. set_index: 將一個或多個列設為索引,創建分層結構
  3. unstack: 將行索引轉換為列,實現長格式到寬格式的轉換
  4. groupby + apply: 用於聚合並轉換組內數據
  5. reset_index: 將索引轉回普通列,便於後續合併
  6. pd.merge: 基於共同列合併多個DataFrame
    或者使用pd.concat()基於共同index合併多個DataFrame

優勢分析

與傳統的循環方法(如手動構建字典列表)相比,這種方法具有以下優勢:

  1. 性能更高: 利用了Pandas的向量化操作,大數據集下速度明顯更快
  2. 代碼更簡潔: 8-10行代碼完成轉換,而循環方法通常需要20-30行
  3. 內存效率: 避免了多次創建中間數據結構
  4. 自動處理缺失值: 對於不同行數的信號,自動填充NaN

結論

使用 set_indexunstack 轉換數據格式不僅提高了代碼效率,也使數據結構更適合後續分析。這種方法特別適合處理PCB設計數據、時間序列數據和各類分類數據的重構。掌握這種技術將極大提升您的數據處理能力。

推薦hahow線上學習python: https://igrape.net/30afN

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *