需求
把dataframe转成以下格式,且需要后台给每项指明颜色,最高级为不同色系,次级为所在最高级的子色并且要求间色,dataframe中只有最低级的对应的value,而在json中,需要计算出每一层的的value。
pandas dataframe格式如下:
返回格式如下:
分析
首先,转为嵌套字典是最为重要的一步可以用pandas的reindex方法实现,计算每层的value需要进行递归,最后是着色策略可以用itertools包的cycle函数产生无限生成器我们不断调用next()就可以实现隔色,不同色系我们可以提前配置好
实现
dataframe转为堆叠字典
"""
将一个dataframe按照指定的层级堆叠起来返回嵌套dict
"""
dat = pd.DataFrame({'yjnr': ['城乡建设', '城乡建设', '城乡建设', '党务政务', '党务政务', '国土资源'],
'ejnr': ['城市建设和市政管理', '城管执法', '城乡规划', '港澳台侨', '党的建设', '经济管理'],
'sjnr': ['城市建设和市政管理', '城管执法', '城乡规划', '港澳台侨', '党的建设', '经济管理'],
'value': [24, 24, 24, 24, 31, 31]}
)
dat = dat[['yjnr', 'ejnr', 'sjnr','value']]
def multi_stack_produce(dataframe, s_columns=None):
if not s_columns: # 没有需要堆叠的列直接出去
return
df = dataframe
df = df.set_index(s_columns)
res = df_to_stacked_dict(df)
return res
着色策略
# 均返回颜色生成器
# 顶级颜色 不同色
def top_level_color(color_series_list):
return cycle(k[0] for k in color_series_list), cycle(i for i in range(len(color_series_list)))
#次级颜色 隔色
def sub_level_color(i, color_series_list):
return cycle([color_series_list[i][1],color_series_list[i][2]])
递归计算value和插入颜色
# 用于堆叠的字段组必须具有唯一性
# 顶级的递归入口,顶级完毕进入子级递归(由于着色策略的差异需要分别处理)
def df_to_colored_sunburst_chart(res, total):
"""
:param res: dict 堆叠后的dict
"""
top_level_color_generator, top_level_index_generator = top_level_color(color_series_list)
tmp = []
for k, v in res.items():
if type(v) is int or type(v) is float:
return v, v
if type(v) is dict:
v, tmp_total = df_to_sunburst_chart(v, 0,next(top_level_index_generator))
total = total + tmp_total
if type(v) is int or type(v) is float:
tmp_item = {'name': k,'value': v}
else:
tmp_item = {'name': k, 'value': total, 'children': v,'itemStyle':{'color':next(top_level_color_generator)}}
total = 0
tmp.append(tmp_item)
return tmp, total
# 子级递归
def df_to_sunburst_chart(res, total,i):
"""
:param res: dict 堆叠后的dict
"""
sub_level_color_generator = sub_level_color(i,color_series_list)
tmp = []
for k, v in res.items():
if type(v) is int or type(v) is float:
return v, v
if type(v) is dict:
v, tmp_total = df_to_sunburst_chart(v, 0,i)
total = total + tmp_total
if type(v) is int or type(v) is float:
tmp_item = {'name': k, 'value': v,'itemStyle':{'color':next(sub_level_color_generator)}}
else:
tmp_item = {'name': k, 'value': total, 'children': v,'itemStyle':{'color':next(sub_level_color_generator)}}
total = 0
tmp.append(tmp_item)
return tmp, total
按顶级value排序,limit
def order(res: list, order):
order_type = True if '-' in order else False # 升降序确定
res.sort(key=lambda t: t['value'],reverse=order_type)
return res
def limit(res:list,limit):
if limit >= len(res):
limit = len(res)-1
return res[0:int(limit)]
结果
{
"code": 200,
"msg": "success",
"data": [
{
"name": "农村农业",
"value": 121,
"children": [
{
"name": "农副产品流通",
"value": 1,
"children": [
{
"name": "储备供应",
"value": 1,
"itemStyle": {
"color": "#eedd78"
}
}
],
"itemStyle": {
"color": "#eedd78"
}
},
{
"name": "农垦农场",
"value": 3,
"children": [
{
"name": "经营管理",
"value": 3,
"itemStyle": {
"color": "#eedd78"
}
}
],
"itemStyle": {
"color": "#73a373"
}
},
{
"name": "农村农业(其他)",
"value": 3,
"children": [
{
"name": "其他",
"value": 3,
"itemStyle": {
"color": "#eedd78"
}
}
],
"itemStyle": {
"color": "#eedd78"
}
},
]
]