很抱歉,如果这是重复的内容-我似乎找不到合适的关键字来获得想要的匹配。
假设我有一个类似的DataFrame
:
name desc class group subgroup
0 abc _d class1 group1 sg1
1 def _g class1 group1 sg1
2 ghi _j class1 group1 sg2
3 jkl _m class1 group1 sg2
4 mno _p class2 group2 sg1
5 pqr _s class2 group2 sg1
6 stu _v class2 group2 sg2
7 vwx _y class2 group2 sg2
最终,我想将框架重塑为如下所示:
class group subgroup name1 desc1 name2 desc2
0 class1 group1 sg1 abc _d def _g
1 class1 group1 sg2 ghi _j jkl _m
2 class2 group2 sg1 mno _p pqr _s
3 class2 group2 sg2 stu _v vwx _y
理想情况下,即使是列表格式(我可以在以后将其拆分的情况下)也是如此:
class group subgroup name desc
0 class1 group1 sg1 [abc, def] [_d, _g]
1 class1 group1 sg2 [ghi, jkl] [_j, _m]
2 class2 group2 sg1 [mno, pqr] [_p, _s]
3 class2 group2 sg2 [stu, vwx] [_v, _y]
我尝试使用df.pivot(index=['class','group','subgroup'],columns=['name','desc'])
,但是显然这不起作用,因为它不会扩展列。它只是给了我一个ValueError
:
ValueError: Length mismatch: Expected 8 rows, received array of length 3
我可以使用df.groupby(['class', 'group', 'subgroup']).sum()
,但它会弄乱name
/ desc
:
name desc
class group subgroup
class1 group1 sg1 abcdef _d_g
sg2 ghijkl _j_m
class2 group2 sg1 mnopqr _p_s
sg2 stuvwx _v_y
我可能可以通过几个步骤来完成此操作,但是我想知道是否有一个明显的解决方案使我彻底丢失了吗?
答案 0 :(得分:2)
这比我预期的要难。基本思想是.apply(list)
到分组的列。但是,在groupby
之后,我无法一次选择多个列。因此,我使用了列表推导,并将得到的两个系列与pd.concat(axis=1)
串联在一起。这也可能是一个班轮,但是我认为如果一行完成它就缺乏可读性
selects = ['desc','name']
list_of_series = [so.groupby(['class', 'group', 'subgroup'])[val].apply(list) for val in selects]
so_new = pd.concat(list_of_series,axis=1)
输出
desc name
class group subgroup
class1 group1 sg1 [_d, _g] [abc, def]
sg2 [_j, _m] [ghi, jkl]
class2 group2 sg1 [_p, _s] [mno, pqr]
sg2 [_v, _y] [stu, vwx]
so_new2 = pd.concat([so.groupby(['class', 'group', 'subgroup'])[val].apply(list) for val in ['desc','name']],axis=1)
selects = ['desc', 'name']
df = pd.concat([pd.DataFrame(lst.values.tolist(),columns=[selects[ii]+"1",selects[ii]+"2"],index=lst.index) \
for ii,lst in enumerate([so.groupby(['class', 'group', 'subgroup'])[val].apply(list) \
for val in selects])],axis=1)
输出
desc1 desc2 name1 name2
class group subgroup
class1 group1 sg1 _d _g abc def
sg2 _j _m ghi jkl
class2 group2 sg1 _p _s mno pqr
sg2 _v _y stu vwx
答案 1 :(得分:2)
这是使用groupby.cumcount
在列名中创建最后的1、2 ..的一种方法。然后set_index
和unstack
。如果您不希望使用多索引列,则将其展平:
df_f = df.assign(cc=df.groupby(['class', 'group', 'subgroup']).cumcount()+1)\
.set_index(['class', 'group', 'subgroup', 'cc']).unstack()
# if you don't want the multiindex columns
df_f.columns = [f'{col[0]}{col[1]}' for col in df_f.columns]
print (df_f)
name1 name2 desc1 desc2
class group subgroup
class1 group1 sg1 abc def _d _g
sg2 ghi jkl _j _m
class2 group2 sg1 mno pqr _p _s
sg2 stu vwx _v _y
答案 2 :(得分:0)
正如我在问题中提到的,我能够通过几个步骤实现这一目标:
df = df.pivot_table(index=['class','group','subgroup'], values=['name','desc'], aggfunc=list)
# desc name
# class group subgroup
# class1 group1 sg1 [_d, _g] [abc, def]
# sg2 [_j, _m] [ghi, jkl]
# class2 group2 sg1 [_p, _s] [mno, pqr]
# sg2 [_v, _y] [stu, vwx]
df = df.reset_index()
# Create new columns and join it back
df = df.join(pd.DataFrame(df.desc.to_list(), columns=['desc0','desc1']))
df = df.join(pd.DataFrame(df.name.to_list(), columns=['name0','name1']))
# Remove the superfluous listed columns
df.drop(['desc','name'], axis=1, inplace=True)
结果:
class group subgroup desc0 desc1 name0 name1
0 class1 group1 sg1 _d _g abc def
1 class1 group1 sg2 _j _m ghi jkl
2 class2 group2 sg1 _p _s mno pqr
3 class2 group2 sg2 _v _y stu vwx
理想情况下,我还是希望减少步骤,并想知道是否存在更好的方法。