具有多索引列的DataFrame的pd.get_dummies()

时间:2019-05-29 01:57:21

标签: python pandas

使用get_dummies(),可以为分类数据创建一键编码的伪变量。例如:

import pandas as pd
df = pd.DataFrame({'A': ['a', 'b', 'a'], 
                   'B': ['b', 'a', 'c']})
print(pd.get_dummies(df))

#    A_a  A_b  B_a  B_b  B_c
# 0    1    0    0    1    0
# 1    0    1    1    0    0
# 2    1    0    0    0    1

到目前为止,太好了。但是,如何结合使用get_dummies()和多索引列呢?默认行为不是很实际:将多索引元组转换为字符串,并且采用与简单索引列相同的后缀机制。

df = pd.DataFrame({('i','A'): ['a', 'b', 'a'], 
                   ('ii','B'): ['b', 'a', 'c']})
ret = pd.get_dummies(df)
print(ret)
print(type(ret.columns[0]))

#    ('i','A')_a  ('i','A')_b  ('ii','B')_a  ('ii','B')_b  ('ii','B')_c
# 0            1            0             0             1             0
# 1            0            1             1             0             0
# 2            1            0             0             0             1
#
# str

但是,我想得到的是,假人创建了一个新的列级别:

ret = pd.get_dummies(df, ???)
print(ret)
print(type(ret.columns[0]))

#    i    ii
#    A     B
#    a  b  a  b  c
# 0  1  0  0  1  0
# 1  0  1  1  0  0
# 2  1  0  0  0  1
#
# tuple
#

# Note that the ret would be equivalent to the following:
#   ('i','A','a') ('i','A','b') ('ii','B','a') ('ii','B','b') ('ii','B','c')
# 0            1             0              0              1              0
# 1            0             1              1              0              0
# 2            1             0              0              0              1

这怎么实现?

更新:我在get_dummieshttps://github.com/pandas-dev/pandas/issues/26560

中提出了一项功能请求,以更好地支持多索引数据框。

2 个答案:

答案 0 :(得分:1)

您可以解析列名并重命名它们:

with open("myjob.txt", "w+") as fout:
      for x in done:
          line = f"name: {x[0]}    count: {x[1]} \n"
          fout.write(line)

请注意,此DataFrame与具有三级多索引列名称的DataFrame不同。

答案 1 :(得分:0)

我也有类似的需求,但是在一个更复杂的DataFrame中,它具有作为行索引的多索引和不应该转换为虚拟数列的数字列。因此,我的案例需要扫描各列,展开以仅对dtype='object'的列进行虚拟,并建立一个新的列索引,作为该列名称与虚拟变量和虚拟变量本身的值的连接。这是因为我不想添加新的列索引级别。

这是代码 首先以我需要的格式构建一个数据框

import pandas as pd
import numpy as np

df_size = 3
objects = ['obj1','obj2']
attributes = ['a1','a2','a3']
cols = pd.MultiIndex.from_product([objects, attributes], names=['objects', 'attributes'])
lab1 = ['car','truck','van']
lab2 = ['bay','zoe','ros','lol']
time = np.arange(df_size)
node = ['n1','n2']
idx = pd.MultiIndex.from_product([time, node], names=['time', 'node'])
df = pd.DataFrame(np.random.randint(10,size=(len(idx),len(cols))),columns=cols,index=idx)
c1 = map(lambda i:lab1[i],np.random.randint(len(lab1),size=len(idx)))
c2 = map(lambda i:lab2[i],np.random.randint(len(lab2),size=len(idx)))
df[('obj1','a3')]=list(c1)
df[('obj2','a2')]=list(c2)
print(df)
objects    obj1           obj2        
attributes   a1 a2     a3   a1   a2 a3
time node                             
0    n1       6  5  truck    3  ros  3
     n2       5  6    car    9  lol  7
1    n1       0  8  truck    7  zoe  8
     n2       4  3  truck    8  bay  3
2    n1       5  8    van    0  bay  0
     n2       4  8    car    5  lol  4

这是仅对对象列进行虚拟化的代码

for t in [df.columns[i] for i,dt in enumerate(df.dtypes) if dt=='object']:
    dummy_block = pd.get_dummies( df[t] )
    dummy_block.columns = pd.MultiIndex.from_product([[t[0]],[f'{t[1]}_{c}' for c in dummy_block.columns]],
                                                     names=df.columns.names)
    df = pd.concat([df.drop(t,axis=1),dummy_block],axis=1).sort_index(axis=1)

print(df)
objects    obj1                           obj2                               
attributes   a1 a2 a3_car a3_truck a3_van   a1 a2_bay a2_lol a2_ros a2_zoe a3
time node                                                                    
0    n1       6  5      0        1      0    3      0      0      1      0  3
     n2       5  6      1        0      0    9      0      1      0      0  7
1    n1       0  8      0        1      0    7      0      0      0      1  8
     n2       4  3      0        1      0    8      1      0      0      0  3
2    n1       5  8      0        0      1    0      1      0      0      0  0
     n2       4  8      1        0      0    5      0      1      0      0  4

可以很容易地更改它以回答原始用例,将多行添加到多索引列中。

df = pd.DataFrame({('i','A'): ['a', 'b', 'a'], 
                   ('ii','B'): ['b', 'a', 'c']})
print(df)
i ii
   A  B
0  a  b
1  b  a
2  a  c
df.columns = pd.MultiIndex.from_tuples([t+('',) for t in df.columns])
for t in [df.columns[i] for i,dt in enumerate(df.dtypes) if dt=='object']:
    dummy_block = pd.get_dummies( df[t] )
    dummy_block.columns = pd.MultiIndex.from_product([[t[0]],[t[1]],[c for c in dummy_block.columns]],
                                                     names=df.columns.names)
    df = pd.concat([df.drop(t,axis=1),dummy_block],axis=1).sort_index(axis=1)
print(df)
   i    ii      
   A     B      
   a  b  a  b  c
0  1  0  0  1  0
1  0  1  1  0  0
2  1  0  0  0  1

请注意,如果有数字列,它仍然可以工作-只是在列索引中为其添加了一个空的附加级别。