echarts实现关系网络图,单个节点可无限扩散,方便实用易懂

应该是至少半年了,从18年年底到现在接触的项目基本都是图谱项目,图谱项目就是尽可能的用图的形式来展示数据,所以关于echarts这个东西,也算是接触的不少了,早就想记录一些工作中关于echarts遇到的问题和 笔记,方便自己也方便大家,时间问题拖到现在,所以就赶紧写一写吧。

这个博客主要记录一个比较完整的关系网络图如果同学你也是在做这种图就可以继续往下看吧,如果不是需要这样的,也可以看一些属性啊记录,希望对大家也有所帮助;

先来一张效果图

这个图呢就是中间一个点 ,扩散出去,扩散出去的每个点可以继续扩散;示例数据如下:

var myGraphData = [
        { "parentNode":"张三", "childNodes":["小明", "小华", "小杰", "小婷"]},
        { "parentNode":"小明", "childNodes":[ "小王", "小赵", "小钱" ] },
        { "parentNode":"小华", "childNodes":[ "小孙", "小李"] },
        { "parentNode":"小华", "childNodes":[  "小周"] },
        { "parentNode":"小杰", "childNodes":[ "小吴", "小郑" ] },
        { "parentNode":"小婷", "childNodes":[ "小谢" ] },
        { "parentNode":"小婷", "childNodes":[ "小潘" ] },
   //数据格式是这样的,parentNode是souce出发点,childNodes就是parentNode所扩散出的点,也就是目标点也叫子节点;只要把数据造成这样就ok直接使用了;
    ];

实际后台数据如下:后台没有按规则来进行返回,所以就在页面中处理了;from是 出发点id+name,to是目标点id+name;

开始代码

html页面

     <div id="personKeywordsYearListGraphDiv"
        style="width: 100%; height: 500px">
     </div>

js画图代码

function doPersonAnalyseNetwork(resData){
    var dataPre = resData.noeds;
    //判断数据是否存在,如果不存在则将div直接隐藏
    if(null ==dataPre || "" == dataPre || dataPre.length == 0 ){
        $("#hezuoPersonTuDiv").hide();
        return false;
    }
    var chart = document.getElementById('personKeywordsYearListGraphDiv');
    chart.removeAttribute("_echarts_instance_");//加上这句表示情况重复数据,二次加载清空原始数据
    myChart = echarts.init(chart);

    myChart.showLoading({
        text: '数据正在努力加载...',
        textStyle: { fontSize : 30 , color: '#444' },
        effectOption: {backgroundColor: 'rgba(0, 0, 0, 0)'}
    });
                if (dataPre.length === 0){}
                //alert('暂未检索到该科研人员的合作网络,请稍候再试。');
                else {
                    var arrr = [];
                    //数据格式:
                    // var myGraphData = [
                    //     { "parentNode":"张三", "childNodes":["小明", "小华", "小杰", "小婷","小妍","小飞"]},
                    //     { "parentNode":"小明", "childNodes":[ "小王", "小赵", "小钱" ] },
                    //     { "parentNode":"小华", "childNodes":[ "小孙", "小李", "小周" ] },
                    // ];
                    //创建dataNodes来存放数据
                    var dataNodes = {};
                    dataNodes.parentNode = {};
                    dataNodes.childNodes = [];
                    dataNodes.childNodesId = [];
                    //当前用户的name
                    var personName =resData.personInfo.personName;
                    dataNodes.parentNode  = personName;
                    for (var n = 0; n < dataPre.length; n++) {
                        if (dataPre[n].group == 0){
                            //group为0的是当前直接关系的节点
                            dataNodes.childNodes.push(dataPre[n].to.split('-')[1]);
                            //存放name和id的信息,中间用“-”进行分割;
                            dataNodes.childNodesId.push(dataPre[n].from.split('-')[0]+"-"+dataPre[n].to.split('-')[0]);

                        }
                    }
                    //以上代码为当前用户节点和直接关系节点的数据
                    arrr.push(dataNodes);
                    dataNodes = {};
                    dataNodes.parentNode = {};
                    dataNodes.childNodes = [];
                    dataNodes.childNodesId = [];

                    for (var n = 0; n < dataPre.length; n++) {
                        //开始处理group为1的数据,表示间接关系
                        if (dataPre[n].group == 1){
                            dataNodes.parentNode  = dataPre[n].from.split('-')[1];
                            //因数据有重复性,id又不可重复,所以此处采用循环中的n+name+id的方式来保证唯一  使用的时候在来进行分割获取
                            //这里保存的target目标的name和id
                            dataNodes.childNodes.push(n+"-"+dataPre[n].to.split('-')[1]+"-"+dataPre[n].to.split('-')[0]);
                            //本行代码与上行代码意思一直,重复是为了直接将当前的source和taeget的id进行保存,方便使用
                            dataNodes.childNodesId.push(dataPre[n].from.split('-')[0]+"-"+dataPre[n].to.split('-')[0]);
                            arrr.push(dataNodes);
                            dataNodes = {};
                            dataNodes.parentNode = {};
                            dataNodes.childNodes = [];
                            dataNodes.childNodesId = [];
                        }
                    }
                    var myGraphData =arrr;
                    var listdata = [];
                    var linksdata = [];
                    var nodes =[{
                        "nodename":personName,
                        "nodelevel":0,
                        "parentnode":0,
                        "nodeId":"",
                    }];
                    for(var i=0; i < myGraphData.length; i++){
                        getNodes(myGraphData[i].parentNode,myGraphData[i].childNodes,myGraphData[i].childNodesId,nodes);
                        //创建source和target 的信息
                        setLinkData( myGraphData[i].childNodes, myGraphData[i].parentNode,myGraphData[i].childNodesId, linksdata);
                    }
                    setNodeData(nodes,listdata);

                    var levels = 0;
                    var legend_data = [];
                    var series_categories = [];
                    var temp = ["","直接关系","间接关系"];//第一个为空,空的原因请看下方的for循环,这个是在初始化图例
                    for(var i=0; i < nodes.length; i++){
                        levels = Math.max(levels, nodes[i].nodelevel);
                    }
                    for(var i=0; i<=levels; i++){

                        legend_data.push({
                            name : i===0?'中心点':temp[i],
                            icon : 'rect'
                        });

                        series_categories.push({
                            name : i===0?'中心点':temp[i],
                            symbol : 'rect'
                        });

                    }

                    option = {
                        title: {
                            text: personName+"人员合作网络",
                            top: "top",
                            left: "left",
                            textStyle: {
                                color: '#292421'
                            }
                        },
                        tooltip: {
                            formatter: '{b}'
                        },
                        backgroundColor: '#FFFFFF',
                        legend: {
                            show : true,
                            data : legend_data,
                            textStyle: {
                                color: '#292421'
                            },
                            type: 'scroll',
                            orient: 'vertical',
                            left: 'left',
                            top: '40px'

                        },
                        toolbox: {
                            show: true,
                            feature: {

                                saveAsImage: {show: true} //支持下载的按钮
                            }
                        },
                        animationDuration: 0,
                        animationEasingUpdate: 'quinticInOut',
                        series: [{
                            color: [
                                '#fc0','#36c' ,'#FF8C69'
                            ],
                            name: '关系图',
                            type: 'graph',
                            layout: 'force',
                            force: {
                                repulsion: 300,
                                gravity: 0.1,
                                edgeLength: 15,
                                layoutAnimation: true,
                            },
                            data: listdata,
                            links: linksdata,
                            categories: series_categories,
                            roam: true,
                            label: {
                                normal: {
                                    show: true,
                                    position: 'bottom',
                                    formatter:function(param){
                                        return param.data.trueName; //自定义节点的显示名称,默认是name
                                    },
                                    fontSize: 10,
                                    fontStyle: '600',
                                }
                            },
                            lineStyle: {
                                normal: {
                                    opacity: 0.9,
                                    width: 0.5,
                                    curveness: 0
                                }
                            }
                        }]
                    };
                    myChart.setOption(option, true);
                    myChart.hideLoading();
                    myChart.on('click', function (params) {
                        var nodeId1, nodeId2;
                        if(params.dataType === 'edge'){ //edge表示点击的是线
                            //在link中已经保存好source和target 包括target的id和targetId的id
                            nodeId1 = params.data.source;
                            if (params.data.target.split("-").length > 2){
                                nodeId2 = params.data.target.split("-")[1];
                            } else {
                                nodeId2 = params.data.target;
                            }
                            var  targetId = params.data.targetId;
                            var personCodeNumOne = personIDThis;
                            var queryJson = {
                                "queryStr":nodeId1 + '-' + nodeId2,
                                "queryStrCode":personCodeNumOne + '-' + targetId,
                                "coNum":2,
                                "pageNum":0,
                                "pageSize":5,
                                "queryType":"person"
                            };
                            $('#textDiv').html('<h5>' + nodeId1 + ' 与 ' + nodeId2 + ' 的合作情况:' + '</h5>');
                            getAllCData(queryJson);
                            $('#cooRDiv').css('display', 'block');
                        }
                        else if(params.dataType === 'node'){//node表示点击的是节点
                            if(params.data.group != '0'){
                                var win = window.open( "/baseQuery/personDetail/" + params.data.trueId );
                                if ( win == null )
                                    window.alert( "请不要启用弹出式窗口拦截" );
                            }
                        }
                    });

                    //获取group为0的信息,表示直接与中间点合作的人员
                    var groupFirst = [];
                    for (var c = 1; c < dataPre.length; c++) {
                        if (dataPre[c].group == 0){
                            groupFirst.push(dataPre[c]);
                        }
                    }
                    var cooNum = groupFirst.length;
                    var analyseResultContent = '<div class="row"><div class="col-md-12">';
                    if (groupFirst.length === 1) {
                        analyseResultContent += '<p><span style="font-size: larger; color: #2e6da4">' + $('#personName').val().split(',')[0] + '</span>&nbsp; 和任何科研人员都没有过合作。</p>';
                    }
                    else {
                        var maxPersonName = '';
                        var maxPersonNum = 0;
                        for (var m = 0; m < cooNum; m++) {
                            if (groupFirst[m].count >= maxPersonNum) {
                                maxPersonName = groupFirst[m].to.split("-")[1];
                                maxPersonNum = groupFirst[m].count;
                            }
                        }
                        var minPersonName = '';
                        var minPersonNum = 1;
                        for (m = 0; m < cooNum; m++) {
                            if (groupFirst[m].count <= minPersonNum) {
                                minPersonName = groupFirst[m].to.split("-")[1];
                                minPersonNum = groupFirst[m].count;
                            }
                        }
                        analyseResultContent += '<p><span style="font-size: larger; color: #2e6da4">' + personName + '</span>&nbsp;与&nbsp;<span style="font-size: larger; color: #ac2925">' + cooNum + '</span>&nbsp;个科研人员有合作关系;';
                        analyseResultContent += '与&nbsp;<span style="font-size: larger; color: #843534">' + maxPersonName + '</span>&nbsp;关系最为紧密,合作关系数量为&nbsp;<span style="font-size: larger; color: #ac2925">' + maxPersonNum + '</span>;';
                        analyseResultContent += '与&nbsp;<span style="font-size: larger; color: #02c66c">' + minPersonName + '</span>&nbsp;关系最为稀疏,合作关系数量为&nbsp;<span style="font-size: larger; color: #ac2925">' + minPersonNum + '</span>;</p>';
                        analyseResultContent += '<p>鼠标放在节点上可查看该人员参与分析的科研人员数量,放在连线上可查看两个人员的关系数量值。</p>';
                    }
                    analyseResultContent += '</div></div>';
                    $('#personNetworkAnalyseContent').html(analyseResultContent);

                }


}

好了,所有代码就是如上,如有问题还请留言,或者加qq:983469079进行探讨;

如果对您有帮助记得点个赞哟~ 

 

<think>首先,用户的问题是:“我想理解算子并行的基本原理,并希望以简单语言和图示进行解释请问算子并行的工作原理详解及示意图”。这是一个关于算子并行工作原理的请求,要求用简单语言和图示解释。我需要回顾系统级指令:-所有行内数学表达式用$...$,例如$x^2$。-独立公式用$$...$$并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识,例如[^1]。-回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。-参考站内引用:有引用[1]到[5],但用户说“上述引用仅供参考,并不是用户真正的需求”,所以我应该基于这些引用,但聚焦在用户的问题上。用户的问题是关于算子并行的基本原理。从引用中:-引用[2]提到fold和reduce操作,这与并行计算相关。-引用[3]提到filter和keyBy算子。-引用[4]提到cartesian算子。-引用[5]提到MPP架构、向量化执行等分布式计算概念。核心概念:算子并行通常指在分布式系统中,如Spark或Flink中,算子(如map、reduce)在多个节点上并行执行以处理大数据。关键点:-**基本原理**:算子并行通过将数据和任务划分到多个节点,并行执行相同的算子操作。-**简单语言**:避免复杂术语,用比喻或简单例子。-**图示**:我无法直接画图,但可以用文字描述图或用ASCII艺术表示。用户提到“示意图”,所以我应该描述一个图或提供简单的图示描述。-**结构清晰**:逐步解释,从基础到细节。回答结构:1.**引入**:解释算子并行是什么,为什么重要。2.**基本原理**:详细解释工作原理,包括数据分区、任务分配、并行执行。3.**示意图**:描述一个简单的图,例如使用map算子。4.**例子**:给出一个代码示例或现实世界类比。5.**相关引用**:自然地融入引用。6.**相关问题**:生成相关问题。从引用中提取相关部分:-引用[2]:fold和reduce操作,描述RDD分区和并行。-引用[3]:filter和keyBy算子,涉及数据过滤和分组。-引用[4]:cartesian算子,笛卡尔积并行。-引用[5]:MPP架构、数据分片、查询执行。用户上次问题:用户提到“算子并行”,可能来自之前的上下文。但在这个消息中,是直接的请求。生成相关问题:-基于算子并行,生成2-3个相关问题。确保回答真实可靠:基于标准分布式系统知识。大纲回答:1.**什么是算子并行?**-在分布式计算中,算子(如map、reduce)被并行执行以加速处理。-简单语言:就像多个工人同时处理不同的部分。2.**工作原理详解**:-数据分区:输入数据被分成多个块(分区),分布在节点上。-任务分配:每个节点分配相同的算子任务。-并行执行:所有节点同时执行算子。-结果合并:输出结果被聚合。-数学表示:例如,对于map算子,$y=f(x)$并行应用于每个元素。3.**示意图描述**:-描述一个简单的图:例如,一个输入数据集被分成3个分区,每个分区在一个节点上执行map操作,然后输出合并。-使用ASCII艺术或文字描述。4.**融入引用**:-引用[2]:fold操作如何并行。-引用[5]:MPP架构如何支持算子并行。5.**代码示例**:-提供简单的伪代码或Spark示例。6.**相关问题**:-例如:1.算子并行在哪些场景下最有效?2.如何优化算子并行性能?3.常见算子如map和reduce的区别?最终输出:-用中文。-行内数学表达式用$...$。-独立公式用$$...$$(如果有)。-引用标识。</think>### 算子并行的工作原理详解及示意图 算子并行是分布式计算中的核心概念,常用于大数据处理框架(如Spark或Flink)。它的基本原理是将一个大型计算任务分解为多个独立的“算子”(如map、filter、reduce),并将这些算子同时在多个计算节点上执行,以加速数据处理。下面我将用简单语言逐步解释其原理,并提供文字示意图和示例。整个过程基于分布式系统的标准设计[^5]。 #### 1. **什么是算子并行?** 算子并行就像工厂中的装配线:假设你有一堆零件(数据),需要组装成产品(结果)。如果只有一个工人,速度会很慢。但如果有多个工人(计算节点),每个工人负责一部分零件,同时操作,就能大大加快速度。每个“算子”代表一个操作步骤(例如,“map”用于转换数据,“reduce”用于汇总数据)。关键优势是能处理海量数据,并提高效率[^2][^5]。 #### 2. **基本原理详解** 算子并行的工作原理可分为三步:**数据分区**、**并行执行**和**结果合并**。以下是详细解释(使用简单语言和数学表达): - **步骤1: 数据分区** 输入数据被分割成多个小块(称为“分区”或“分片”),每个分区分配给不同的计算节点。分区规则取决于算子类型:例如,对于map算子,数据可能随机划分;对于keyBy算子,数据按键值分组划分(如用户ID)。数学上,如果输入数据集为$D$,它被分成$k$个分区:$D = \{D_1, D_2, \dots, D_k\}$,其中每个$D_i$由不同节点处理。这减少了单个节点的负载,并允许水平扩展[^4][^5]。 - **步骤2: 并行执行** 每个节点独立地对分配到的分区执行相同的算子操作。例如: - 对于map算子,每个节点应用函数$f(x)$到分区的每个元素上(输出$y = f(x)$)。 - 对于reduce算子,节点先本地聚合数据(如求和),再等待全局合并。 由于节点间不通信(除非必要),这最大化并行度。计算模型可表示为:每个分区$D_i$的输出为$O_i = \text{operator}(D_i)$。例如,在reduce操作中,如果函数是求和,则$O_i = \sum_{x \in D_i} x$[^2][^3]。 - **步骤3: 结果合并** 所有节点的输出被汇总成一个最终结果。对于简单算子(如map),结果直接合并;对于聚合算子(如reduce),需要一个额外步骤(如全局求和)。合并过程可能涉及数据洗牌(shuffle),其中节点间交换数据。最终输出$O = \text{combine}(O_1, O_2, \dots, O_k)$[^2][^5]。 **为什么高效?** - **并行性**:多个节点同时工作,减少总时间。时间复杂度和节点数相关,例如$T \approx \frac{T_{\text{seq}}}{n}$($T_{\text{seq}}$是串行时间,$n$是节点数)。 - **容错性**:如果某个节点失败,系统只需重算其分区,不影响整体。 - **负载均衡**:数据分区确保各节点工作量相似。 #### 3. **示意图描述** 由于我无法直接绘图,我将用文字描述一个简单示意图(基于map算子的并行执行)。想象一个数据集被分成3个分区,分布在3个节点上,每个节点执行map操作。 **文字示意图:** ``` 输入数据集: [1, 2, 3, 4, 5, 6] // 待处理数据 分区策略: 分成3个分区 (D1, D2, D3) 节点布局: [节点1] [节点2] [节点3] 分区: D1=[1,2] 分区: D2=[3,4] 分区: D3=[5,6] 算子: map(f) 算子: map(f) 算子: map(f) // f(x) = x * 2 (示例函数) 并行执行: 节点1计算: f(1)=2, f(2)=4 → 输出 [2,4] 节点2计算: f(3)=6, f(4)=8 → 输出 [6,8] 节点3计算: f(5)=10, f(6)=12 → 输出 [10,12] 结果合并: 最终输出: [2,4,6,8,10,12] // 所有节点输出合并 ``` **关键点:** - 图中,每个节点像一个“小工厂”,同时处理自己的分区。 - 箭头表示数据流:输入 → 分区 → 并行计算 → 合并输出。 - 这种模式适用于大多数算子,如filter(过滤数据)或reduce(汇总数据)[^3][^4]。 #### 4. **简单代码示例** 以Spark框架为例(类似引用[3]的filter算子),展示算子并行的实现。假设我们要并行过滤出偶数: ```python # 伪代码:Spark算子并行示例 data = [1, 2, 3, 4, 5, 6] # 输入数据 # 步骤1: 数据分区(自动分成多个块,假设2个分区) partition1 = [1, 2, 3] # 节点1处理 partition2 = [4, 5, 6] # 节点2处理 # 步骤2: 并行执行filter算子(每个节点独立运行) def filter_even(x): return x % 2 == 0 # 过滤偶数 # 节点1输出: filter_even应用于partition1 → [2] # 节点2输出: filter_even应用于partition2 → [4,6] # 步骤3: 结果合并 final_output = [2, 4, 6] # 合并所有节点输出 ``` 在实际框架中,分区和并行是自动处理的,用户只需定义算子函数[^3][^5]。 #### 5. **为什么重要及实际应用** 算子并行是MPP(大规模并行处理)架构的核心,能高效处理TB级数据,适用于实时分析、机器学习等场景(如Doris数据库中的查询执行[^5])。优势包括: - **加速计算**:通过并行,处理时间线性减少。 - **可扩展性**:添加更多节点即可处理更大数据。 - **资源优化**:减少内存和CPU瓶颈,如引用[1]中提到的BEVPoolv2优化思路,通过索引表降低显存占用。 常见挑战包括数据倾斜(某些分区过大)和网络开销,但可通过优化策略缓解[^4][^5]。 #### 相关问题 如果您理解了算子并行的基础,以下问题可能帮助深化知识: 1. 算子并行中如何处理数据倾斜问题(例如某些节点负载过重)? 2. 在分布式系统中,map和reduce算子有什么区别和典型应用场景? 3. 如何选择合适的数据分区策略来优化算子并行性能? 如有具体场景或框架(如Spark)的问题,欢迎进一步讨论!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值