如何 collapse/pivot 多个 pandas 列

How to collapse/pivot multiple pandas columns

在下面的数据集中,

# DataFrame using arrays.
import pandas as pd
import numpy as np

 
# create dataset
data = {'Gender':['287F', '287F', '287F', '287F','287F', '287F', '189M', '189M','189M', '189M', 
                  '189M', '189F','287M', '189F', '287M', '287M','287M','189F', '189F', '287M'],
        'code_num':[1001,1001,1002,1002,1003,1003,1004,1004,1005,1005,
                    1006,1006,1007,1007,1008,1008,1009,1009,1010,1010],
        'Date':['10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923','10-22-1923'],
        'Location':['PHX','PHX','PHX','PHX','PHX','PHX','PHX','PHX','PHX','PHX',
                    'MIA','MIA','MIA','MIA','MIA','MIA','MIA','MIA','MIA','MIA'],
        'Age':['18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr','18yr'],
        'Group':['F1', 'D1', 'F2', 'D2','F1', 'D1', 'F2', 'D2','F1', 'D1', 'F3', 'D3','F2', 'D2', 'F4', 'D4','F3','D3', 'F4', 'D4'],
        'Dog_10_UID': ['T-X', 'T-X', 'G-A', 'G-A','T-X', 'T-X', 'G-A', 'G-A','T-X', 'T-X', 'C-A', 'C-A','G-A', 'G-A', 'F-L', 'F-L','C-A','C-A', 'F-L', 'F-L'],
        'Dog_10_name': ['Tex', 'Tex', 'Gina', 'Gina','Tex', 'Tex', 'Gina', 'Gina','Tex', 'Tex', 'Carla', 'Carla','Gina', 'Gina', 'Flora', 'Flora','Carla','Carla', 'Flora', 'Flora'],
        'Dog_10_txt':['>11','51','61','>11','>91','61','51','>11','>91','>11','61','>11','>71','51','>11','61','>11','>71','>91','51'],
         'Dog_10_index':[11,51,61,11,91,61,51,11,91,11,61,11,71,51,11,61,11,71,91,51],
         'Dog_20_UID': ['T-X', 'T-X', 'G-A', 'G-A','T-X', 'T-X', 'G-A', 'G-A','T-X', 'T-X', 'C-A', 'C-A','G-A', 'G-A', 'F-L', 'F-L','C-A','C-A', 'F-L', 'F-L'],
        'Dog_20_name': ['Tex', 'Tex', 'Gina', 'Gina','Tex', 'Tex', 'Gina', 'Gina','Tex', 'Tex', 'Carla', 'Carla','Gina', 'Gina', 'Flora', 'Flora','Carla','Carla', 'Flora', 'Flora'],
        'Dog_20_txt':['>12','52','62','>12','>92','62','52','12','>92','>12','62','>12','>72','52','>12','62','>12','>72','>92','52'],
         'Dog_20_index':[12,52,62,12,92,62,52,12,92,12,62,12,72,52,12,62,12,72,92,52]
       }

data = pd.DataFrame(data)
data

我想折叠(或旋转)以下对应的列

Dog_10_UID & Dog_20_UID 导致单列 Dog_UID

Dog_10_name & Dog_20_name 导致单列 Dog_name

Dog_10_txt & Dog_20_txt 导致单列 Dog_txt

Dog_10_index & Dog_20_index 导致单列 Dog_index

在collapsing/pivoting之后,最终的dataframe应该有以下列名称

Gender, code_num, Date, Location, Age, Group, Dog_UID, Dog_name,Dog_txt,Dog_index

我的尝试

# 'Gender','code_num', 'Date', 'Location', 'Age', 'Group' should remain constant while collapsing/pivoting Columns starting with 'Dog_'

keys = [x for x in data if x.startswith('Dog_')]

df  = data.melt(id_vars=['Gender','code_num', 'Date', 'Location', 'Age', 'Group'], var_name=['Dog_UID','Dog_name', 'Dog_txt', 'Dog_index'], 
          value_name='keys')

我对其他方法持开放态度,请分享您的完整代码。谢谢

第一步是DataFrame.set_index, Create MultiIndex by all columns which are not processing by split and reshape by DataFrame.stack

df = data.set_index(['Gender','code_num', 'Date', 'Location', 'Age', 'Group'])
df.columns = df.columns.str.split('_', expand=True)
df = df.stack(1)
df.columns = df.columns.map(lambda x: f'{x[0]}_{x[1]}')
cols = ['Dog_UID', 'Dog_name', 'Dog_txt', 'Dog_index']
df = df.reset_index(level=-1, drop=True)[cols].reset_index()
print (df.head())
  Gender  code_num        Date Location   Age Group Dog_UID Dog_name Dog_txt  \
0   287F      1001  10-22-1923      PHX  18yr    F1     T-X      Tex     >11   
1   287F      1001  10-22-1923      PHX  18yr    F1     T-X      Tex     >12   
2   287F      1001  10-22-1923      PHX  18yr    D1     T-X      Tex      51   
3   287F      1001  10-22-1923      PHX  18yr    D1     T-X      Tex      52   
4   287F      1002  10-22-1923      PHX  18yr    F2     G-A     Gina      61   

   Dog_index  
0         11  
1         12  
2         51  
3         52  
4         61  

一个选项是pd.wide_to_long;首先,需要在转换前对列进行整形:

temp = data.copy()
cols = ['Gender', 'code_num', 'Date', 'Location', 'Age', 'Group']
stubnames = ['Dog_UID', 'Dog_name', 'Dog_txt', 'Dog_index']
pattern = r"(?P<first>.+)_(?P<num>\d+)_(?P<last>.+)"
repl = lambda m: f"{m.group('first')}_{m.group('last')}-{m.group('num')}"
temp.columns = temp.columns.str.replace(pattern, repl, regex=True)
out = (pd.wide_to_long(temp, 
                       stubnames = stubnames, 
                       i = cols, 
                       j = 'num', 
                       sep = '-', 
                      suffix = '.+')
        .reset_index()
       )

out.head()

  Gender  code_num        Date Location   Age Group  num Dog_UID Dog_name Dog_txt  Dog_index
0   287F      1001  10-22-1923      PHX  18yr    F1   10     T-X      Tex     >11         11
1   287F      1001  10-22-1923      PHX  18yr    F1   20     T-X      Tex     >12         12
2   287F      1001  10-22-1923      PHX  18yr    D1   10     T-X      Tex      51         51
3   287F      1001  10-22-1923      PHX  18yr    D1   20     T-X      Tex      52         52
4   287F      1002  10-22-1923      PHX  18yr    F2   10     G-A     Gina      61         61

另一个选项是 pivot_longer from pyjanitor -> 你的列有一个模式,(以 UID 或名称或 txt 或索引结尾);我们将使用该模式重塑数据:

# pip install pyjanitor
import janitor
import pandas as pd
outcome = (data.pivot_longer(index=cols,, 
                             names_to=stubnames, 
                             names_pattern=['UID$', 'name$', 'txt$', 'index$'])
)

outcome.head()

  Gender  code_num        Date Location   Age Group Dog_UID Dog_name Dog_txt  Dog_index
0   287F      1001  10-22-1923      PHX  18yr    F1     T-X      Tex     >11         11
1   287F      1001  10-22-1923      PHX  18yr    D1     T-X      Tex      51         51
2   287F      1002  10-22-1923      PHX  18yr    F2     G-A     Gina      61         61
3   287F      1002  10-22-1923      PHX  18yr    D2     G-A     Gina     >11         11
4   287F      1003  10-22-1923      PHX  18yr    F1     T-X      Tex     >91         91