1 画桑基图一个容易出错的细节
起先拿网络教程里的数据跑的时候没有任何问题,然后用自己的数据,就一直显示空白,
内有内容显示,找了很久问题,发现了一个很多网上教程都没有说的点,
需要用pyecharts划超多节点的话,一定需要留意:
举例来说,这个简单的桑基图,如果你的数据里面出现了category6 -> category2
这种逆流向的,大概率会显示空白,所以这里可以自行约束一下每一列的列名,让其保持独立。
2 代码案例
主要代码:
import pandas as pd
import asyncio
import pyecharts.options as opts
from pyecharts.charts import Sankey
from collections import Counter
'''
数据处理核心环节
每列保留节点数:
每一列最多保留5个节点,其他都是other,同时按照列进行编码
冲突的节点改名
'''
# reserved_num = 10
def sankey_data_preprocessing(sankey_data_2,reserved_num):
# 1 定位哪些节点需要变成other 以及 改名
other_cols_dict = {}
pred_cols = set()
alter_col_dict = {}
for col in sankey_data_2.columns :
# 每列保留节点数:
other_cols_dict[col] = list(sankey_data_2[col].value_counts().index[reserved_num:])
# 冲突节点处理
alter_col_dict[col] = {}
if col != 0:
for pc in set(sankey_data_2[col]):
if (pc in pred_cols)&(pc != None):
alter_col_dict[col][pc]=f'{pc}_{col}'
[pred_cols.add(pc) for pc in set(sankey_data_2[col]) if pc != None]
# 2 修改源数据
for col in sankey_data_2.columns :
# 变成other
sankey_data_2[col] = sankey_data_2[col].map(lambda x : f'other_{col}' if x in other_cols_dict[col] else x if x == x else np.nan )
# 改名
if col in alter_col_dict.keys():
sankey_data_2[col] = sankey_data_2[col].map(lambda x : alter_col_dict[col][x] if x in alter_col_dict[col].keys() else x if x == x else np.nan )
return sankey_data_2
# 3 生成最终sankey格式
def sankey_standard_format_generator(sankey_data_2):
num = sankey_data_2.shape[1]
num_l = list(zip(list(range(num-2)),list(range(2,num))))
result = {}
for part in num_l:
text_list = sankey_data_2.iloc[:,part[0]:part[1]].apply(lambda x: list(x) ,axis = 1 )
text_list = [str(tl) for tl in text_list if len(tl) == 2 ]
print('正在生成区间:',part)
#out.extend([eval(k)+[v] for k,v in Counter(text_list).items()])
result[part] = [eval(k)+[v] for k,v in Counter(text_list).items()]
result = [ p for part in num_l for p in result[part] if p[1] != None ]
sankey_standard_format = pd.DataFrame(result,columns = ['source','target','value'])
sankey_standard_format = sankey_standard_format[sankey_standard_format['source'] == sankey_standard_format['source']]
sankey_standard_format = sankey_standard_format[sankey_standard_format['target'] == sankey_standard_format['target']]
links = list(sankey_standard_format.T.to_dict().values())
nodes = list(sankey_standard_format['source'].value_counts().index) + \
list(sankey_standard_format['target'].value_counts().index)
nodes = [{'name':l} for l in list(set(nodes))]
return links,nodes
data_list = [['模板消息', 'order-list', 'order-detail', 'order-list'],
['菜单', 'activity'],
['支付卡券', 'product-detail', 'checkout', 'product-detail'],
['支付卡券', 'home'],
['搜索', 'home', 'user', 'webview', 'user', 'ugc-list', 'shopping-cart', 'home'],
['我的小程序', 'home', 'user', 'order-list', 'order-detail'],
['搜索', 'home', 'shopping-cart', 'home'],
['模板消息', 'order-list', 'order-detail'],
['我的小程序', 'product-detail'],
['菜单', 'home', 'star', 'product-list', 'product-detail', 'pgc-detail', 'product-detail', 'product-list']]
sankey_data_2 = pd.DataFrame(data_list)
# 数据预处理
sankey_data_2 = sankey_data_preprocessing(sankey_data_2,10)
# 生成标准的sankey格式
links,nodes = sankey_standard_format_generator(sankey_data_2)
sankey = {"nodes":nodes,"links":links}
(
Sankey(init_opts=opts.InitOpts(width="3200px", height="1600px")) # 图像大小
.add(
series_name="",
nodes=sankey["nodes"],
links=sankey["links"],
itemstyle_opts=opts.ItemStyleOpts(border_width=0.1, border_color="#1b6199"),
linestyle_opt=opts.LineStyleOpts(color="source", ##颜色设置,source表示使用节点的颜色
curve=0.5, ###信息流的曲线弯曲度设置
opacity=0.5),##颜色设置,source表示使用节点的颜色
tooltip_opts=opts.TooltipOpts(trigger_on="mousemove"),
node_align='left' # 桑基图中节点的对齐方式,默认是双端对齐,可以设置为左对齐或右对齐,对应的值分别是:
# justify: 节点双端对齐。
# left: 节点左对齐。
# right: 节点右对齐
#,orient='vertical' # 桑基图中节点的布局方向,可以是水平的从左往右,也可以是垂直的从上往下。
# 对应的参数值分别是 horizontal, vertical。
,is_selected=True #图例是否选中
,pos_left='20%'##图距离容器左边的距离
,pos_top='20%'##图距离容器上端的距离
,pos_right='20%' #图距离容器右侧的距离
,pos_bottom='20%' ###图距离容器下端的距离
#,node_width=10 #桑基图中每个矩形节点的宽度
,node_gap=20 #每一列两个桑基图之间的距离
,is_draggable=True ##是否能够拖动节点,默认拖动,可以不配置
)
.set_global_opts(title_opts=opts.TitleOpts(title="Sankey Diagram"))
.render("sample.html")
)