pyecharts实现多节点、长路径的sankey桑基图

2021-12-07 15:50:44 浏览数 (1)

1 画桑基图一个容易出错的细节

pyecharts︱交互式pyecharts的相关使用教程

简单的用pyecharts实现超多节点、较长路径的桑基图,一个样图:

起先拿网络教程里的数据跑的时候没有任何问题,然后用自己的数据,就一直显示空白,

内有内容显示,找了很久问题,发现了一个很多网上教程都没有说的点,

需要用pyecharts划超多节点的话,一定需要留意:

举例来说,这个简单的桑基图,如果你的数据里面出现了category6 -> category2这种逆流向的,大概率会显示空白,所以这里可以自行约束一下每一列的列名,让其保持独立。

2 代码案例

主要代码:

代码语言:javascript复制
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")
)

其中,

sankey_data_preprocessing预处理很重要,如果某一列节点太多,可以约束一下,其他数量少的,变成other;另外之前提到的逆向指向的问题,也需要修改列名

sankey_standard_format_generator,变成sankey指定需要的标准格式

最后还有非常多可调节的参数,可以参考以下文献:

  • pyecharts 学习篇☞桑基图实用示例(文末附完整代码)
  • 「Python数据可视化」使用 Pyecharts 制作 Sankey(桑基图)详解
  • 桑基图(Echarts)——自定义风格

0 人点赞