Padas的pivot_table做交叉表变换后支持总计功能,但是分类的小计功能需要自己动手实现。
在实现这个小计的功能时,好好研究了下交叉表的属性,写了下开发流程,当初走了点弯路,总结如下:
下图是开发前手写的流程:
按着这个流程开发到中途遇到点麻烦,又转而尝试了其它方式,因为我们的应用是把交叉表导出到html, 调用了to_html方法,所以,想直接在导出的html字符串上面插入小计,最后确认这种注入方式有三个问题:首先不通用,就算实现了注入小计到导出的html,但是支持交叉表下载到excel文件还是没有小计功能(这个是主要问题),其次对字符串操作效率太低,遇到大的交叉表,开销过大,最后实现起来很麻烦,因为交叉表的变化随着index,column,metric的参数不同而不同,相应的导出的html结构也会变化,很难保证不出异常。
所以最后还是放弃这种尝试,直接操作pandas来变换实现才是最直接,一劳永逸的方式。
直接贴代码吧:
def add_subtotal(self, pivot_table):
indexs = pivot_table.index
names = indexs.names
if len(names) < 2:
return pivot_table
else:
columns = pivot_table.columns
levels = pivot_table.index.levels[0]
table_splites = []
for loc in levels:
loc_rows = pivot_table.loc[loc]
if loc_rows.shape[0] <= 1:
loc_rows = loc_rows.reset_index()
loc_rows.insert(0, names[0], loc)
table_splites.append(loc_rows)
continue
if loc == '_Total':
loc_rows = loc_rows.reset_index()
loc_rows.insert(0, names[0], '_Total')
table_splites.append(loc_rows)
continue
total_row = loc_rows[columns].sum()
total_row = pd.DataFrame(data=total_row).T
total_row.index = ['_Subtotal']
loc_rows = loc_rows.reset_index()
rev_names = names[::-1]
first_name = rev_names[-1]
second_name = rev_names[-2]
for name in rev_names[:-1]:
if name != second_name:
_value = ''
else:
_value = '_Subtotal'
total_row.insert(0, name, _value)
loc_rows = loc_rows.append(total_row)
loc_rows.insert(0, first_name, loc)
table_splites.append(loc_rows)
result = pd.concat(table_splites, ignore_index=True)
return result.pivot_table(index=names)
这段代码单独抽出来独立为PivotTable类的一个实力方法,参数为做过交叉表变换后的df(dataframe)对象,返回的也是一个添加了小计后的df。
如果只有一个Rows,则肯定是不需要分类小计的情况,只有总计_Total。
当Row大于一个后:
效果如上。
二级下多于一项才会出现_Subtotal小计行。
导出交叉表到excel,如下:
到此就实现了交叉表新增分类小计功能和导出带有小计功能。