根据标签值对桑基图进行垂直重新排序

如何解决根据标签值对桑基图进行垂直重新排序

我正在尝试绘制 Sankey diagram 中 3 个集群之间的患者流量。我有一个带有 from-to 值的 pd.DataFrame counts,见下文。要重现此 DF,here 是应该加载到 pd.DataFrame(这是visualize_cluster_flow_counts 函数的输入)的 counts dict。

    from    to      value
0   C1_1    C1_2    867
1   C1_1    C2_2    405
2   C1_1    C0_2    2
3   C2_1    C1_2    46
4   C2_1    C2_2    458
... ... ... ...
175 C0_20   C0_21   130
176 C0_20   C2_21   1
177 C2_20   C1_21   12
178 C2_20   C0_21   0
179 C2_20   C2_21   96

DataFrame 中的 fromto 值表示集群编号(0、1 或 2)和 x 轴的天数(1 到 21 之间)。如果我用这些值绘制桑基图,结果如下:

sankey plot

代码:

import plotly.graph_objects as go

def visualize_cluster_flow_counts(counts):
    all_sources = list(set(counts['from'].values.tolist() + counts['to'].values.tolist()))            
    
    froms,tos,vals,labs = [],[],[]
    for index,row in counts.iterrows():
        froms.append(all_sources.index(row.values[0]))
        tos.append(all_sources.index(row.values[1]))
        vals.append(row[2])
        labs.append(row[3])
                
    fig = go.Figure(data=[go.Sankey(
        arrangement='snap',node = dict(
          pad = 15,thickness = 5,line = dict(color = "black",width = 0.1),label = all_sources,color = "blue"
        ),link = dict(
          source = froms,target = tos,value = vals,label = labs
      ))])

    fig.update_layout(title_text="Patient flow between clusters over time: 48h (2 days) - 504h (21 days)",font_size=10)
    fig.show()

visualize_cluster_flow_counts(counts)

但是,我想对条形进行垂直排序,以便 C0 始终在顶部,C1 始终在中间,C2 >总是在底部(或相反,无关紧要)。我知道我们可以将 node.xnode.y 设置为 manually assign the coordinates。因此,我将 x 值设置为天数 *(1/天数范围),增量为 +- 0.045。我根据集群值设置 y 值:0、0.5 或 1。然后我获得了下面的图像。垂直顺序很好,但条形之间的垂直边距明显偏离;它们应该与第一个结果相似。

enter image description here

产生这个的代码是:

import plotly.graph_objects as go

def find_node_coordinates(sources):
    x_nodes,y_nodes = [],[]
    
    for s in sources:
        # Shift each x with +- 0.045
        x = float(s.split("_")[-1]) * (1/21)
        x_nodes.append(x)
        
        # Choose either 0,0.5 or 1 for the y-value
        cluster_number = s[1]
        if cluster_number == "0": y = 1
        elif cluster_number == "1": y = 0.5
        else: y = 1e-09
        
        y_nodes.append(y)
                
    return x_nodes,y_nodes


def visualize_cluster_flow_counts(counts):
    all_sources = list(set(counts['from'].values.tolist() + counts['to'].values.tolist()))    
        
    node_x,node_y = find_node_coordinates(all_sources)
    
    froms,color = "blue",x = node_x,y = node_y,),font_size=10)
    fig.show()
    
    
visualize_cluster_flow_counts(counts)

问题:如何固定条形的边距,使结果看起来像第一个结果?因此,为了清楚起见:应该将条形推到底部。或者有没有另一种方式让桑基图可以根据标签值自动对条形进行垂直重新排序?

解决方法

首先,我认为当前公开的 API 没有办法顺利实现您的目标,您可以查看源代码 here

尝试按如下方式更改您的 find_node_coordinates 函数(请注意,您应该将计数 DataFrame 传递给):

counts = pd.DataFrame(counts_dict) 
def find_node_coordinates(sources,counts):
    x_nodes,y_nodes = [],[]

    flat_on_top = False
    range = 1 # The y range
    total_margin_width = 0.15
    y_range = 1 - total_margin_width 
    margin = total_margin_width / 2 # From number of  Cs
    srcs = counts['from'].values.tolist()
    dsts = counts['to'].values.tolist() 
    values = counts['value'].values.tolist() 
    max_acc = 0

    def _calc_day_flux(d=1):
        _max_acc = 0 
        for i in [0,1,2]:
            # The first ones
            from_source = 'C{}_{}'.format(i,d) 
            indices = [i for i,val in enumerate(srcs) if val == from_source]
            for j in indices: 
                _max_acc += values[j]
        
        return _max_acc

    def _calc_node_io_flux(node_str): 
        c,d = int(node_str.split('_')[0][-1]),int(node_str.split('_')[1])
        _flux_src = 0 
        _flux_dst = 0 

        indices_src = [i for i,val in enumerate(srcs) if val == node_str]
        indices_dst = [j for j,val in enumerate(dsts) if val == node_str]
        for j in indices_src: 
            _flux_src += values[j]
        for j in indices_dst: 
            _flux_dst += values[j]

        return max(_flux_dst,_flux_src) 

    max_acc = _calc_day_flux() 
    graph_unit_per_val = y_range / max_acc
    print("Graph Unit per Acc Val",graph_unit_per_val) 
 
    
    for s in sources:
        # Shift each x with +- 0.045
        d = int(s.split("_")[-1])
        x = float(d) * (1/21)
        x_nodes.append(x)
        
        print(s,_calc_node_io_flux(s))
        # Choose either 0,0.5 or 1 for the y-v alue
        cluster_number = s[1]

        
        # Flat on Top
        if flat_on_top: 
            if cluster_number == "0": 
              y = _calc_node_io_flux('C{}_{}'.format(2,d))*graph_unit_per_val + margin + _calc_node_io_flux('C{}_{}'.format(1,d))*graph_unit_per_val + margin +  _calc_node_io_flux('C{}_{}'.format(0,d))*graph_unit_per_val/2
            elif cluster_number == "1": y = _calc_node_io_flux('C{}_{}'.format(2,d))*graph_unit_per_val + margin +  _calc_node_io_flux('C{}_{}'.format(1,d))*graph_unit_per_val/2
            else: y = 1e-09
        # Flat On Bottom
        else: 
            if cluster_number == "0": y = 1 - (_calc_node_io_flux('C{}_{}'.format(0,d))*graph_unit_per_val / 2)
            elif cluster_number == "1": y = 1 - (_calc_node_io_flux('C{}_{}'.format(0,d)) * graph_unit_per_val /2 )
            elif cluster_number == "2": y = 1 - (_calc_node_io_flux('C{}_{}'.format(0,d)) * graph_unit_per_val + margin + _calc_node_io_flux('C{}_{}'.format(2,d)) * graph_unit_per_val /2 )
            
        y_nodes.append(y)
                
    return x_nodes,y_nodes

桑基图应该通过它们相应的归一化值来衡量它们的连接宽度,对吗?这里我也一样,首先计算每个节点的通量,然后通过计算归一化坐标,根据每个节点的通量计算每个节点的中心。

这是您的代码的示例输出和修改后的函数,请注意,我试图尽可能地遵守您的代码,因此它有点未优化(例如,可以将节点的值存储在每个指定的源节点之上以避免其通量重新计算)。

带标志 flat_on_top = True enter image description here

带标志 flat_on_top = False enter image description here

flat_on_bottom 版本中存在一些不一致,我认为这是由 Plotly API 的填充或其他内部来源引起的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res