如何在多列中使用 pd.melt()?

eug*_*lor 5 numpy dataframe python-3.x pandas

我目前在创建一个名为的维度表时遇到问题,该表payment_types_Owned列出了客户拥有的产品数量、余额以及每次付款的限制。目前,我有一个看起来像这样的表:

    cust_id 支付类型 X 拥有 支付类型 Y 拥有 支付类型 Z 拥有 已用信用_X 限制_X 已用信用_Y 限制_Y 已用信用_Z 限制_Z
0 人_A 1 3 4 300 700 700 800 400 900
1 人_B 2 1 3 400 600 100 150 400 500
2 人_C 2 4 4 500 600 700 800 100 500

我想要的输出:

        cust_id 变量值 Credit Used Limit
0 Person_A_key 付款类型 X 1 300 700
1 Person_A_key 付款类型 Y 3 700 800
2 Person_A_key 付款类型 Z 4 400 900
3 Person_B_key 支付类型 X 2 400 600
4 Person_B_key 付款类型 Y 1 100 150
5 Person_B_key 付款类型 Z 3 400 500

假设我已经有另外 2 个维度表可以捕获以下信息:

  1. Customer Dimension Table - 包含 cust_id 主键
  2. Product Dimension Table - 包含唯一的产品主键

使用pd.melt(),我得到以下内容,但它只能部分解决我的问题:

(pd.melt(df, id_vars=['cust_id'], value_vars=['付款类型 X 拥有','付款类型 Y 拥有','付款类型 Z 拥有'])).sort_values(by=['cust_id' ])
    cust_id 变量值
0 人_A 付款类型 X 1
3 Person_A 付款类型 Y 3
6 Person_A 付款类型 Z 4
1 人_B 付款类型 X 2
4 Person_B 付款类型 Y 1
7 Person_B 付款类型 Z 3
2 Person_C 付款类型 X 2
5 Person_C 付款类型 Y 4
8 Person_C 付款类型 Z 4


有什么建议?

jot*_*tbe 0

如果您可以将列组织为具有第一级的多索引'Payment Type X'...则有一个相对简单的解决方案(在本文末尾,您会找到将数据框引入该形式的代码)。

如上所述,在列上使用多重索引,以下代码会生成输出:

result= None
for col_group in set(df.columns.get_level_values(0)):
    df_group= df[col_group].assign(variable=col_group).set_index('variable', append=True)
    if result is None:
        result= df_group
    else:
        result= pd.concat([result, df_group], axis='index')
result.sort_index(inplace=True)
Run Code Online (Sandbox Code Playgroud)

执行后变量结果包含一个数据帧,如下所示:

                         owned  Credit Used  Limit
cust_id  variable                                 
Person_A Payment Type X      1          300    700
         Payment Type Y      3          700    800
         Payment Type Z      4          400    900
Person_B Payment Type X      2          400    600
         Payment Type Y      1          100    150
         Payment Type Z      3          400    500
Person_C Payment Type X      2          500    600
         Payment Type Y      4          700    800
         Payment Type Z      4          100    500
Run Code Online (Sandbox Code Playgroud)

以下代码创建测试数据并重新组织上面使用的列:

import pandas as pd
import io
raw=\
"""   cust_id  Payment Type X owned  Payment Type Y owned  Payment Type Z owned  Credit Used_X  Limit_X  Credit Used_Y  Limit_Y  Credit Used_Z  Limit_Z
0  Person_A                     1                     3                     4            300      700            700      800            400      900
1  Person_B                     2                     1                     3            400      600            100      150            400      500
2  Person_C                     2                     4                     4            500      600            700      800            100      500"""

df= pd.read_csv(io.StringIO(raw), sep='  +', engine='python')
df.set_index(['cust_id'], inplace=True)

new_cols= list()

for col in df.columns:
    if 'X' in col:
        lv1= 'Payment Type X'
    elif 'Y' in col:
        lv1= 'Payment Type Y'
    elif 'Z' in col:
        lv1= 'Payment Type Z'
    else:
        lv1= col
    if col[-2:-1] == '_':
        lv2= col[:-2]
    elif col.endswith(' owned'):
        lv2= 'owned'
    else:
        lv2= col
    new_cols.append((lv1, lv2))

df.columns= pd.MultiIndex.from_tuples(new_cols)
Run Code Online (Sandbox Code Playgroud)

一种更激进的一步法如下所示:

flat= df_orig.melt(id_vars=['cust_id'], var_name='column')
flat['variable']= ''
flat.loc[flat['column'].str.match('.*[_ ]X.*'), 'variable']= 'Payment Type X'
flat.loc[flat['column'].str.match('.*[_ ]Y.*'), 'variable']= 'Payment Type Y'
flat.loc[flat['column'].str.match('.*[_ ]Z.*'), 'variable']= 'Payment Type Z'

flat['column']= flat['column'].str.replace('[_ ][XYZ]', '').str.replace('Payment Type owned', 'Owned')

flat.set_index(['cust_id', 'variable', 'column'], inplace=True)
result= flat.unstack().droplevel(0, axis='columns')
Run Code Online (Sandbox Code Playgroud)

它更激进,因为它完全分解原始数据帧以重建它。它的效率可能低于第一种方法。