社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

私活300元,Python数据可视化,人物关系网络图

蚂蚁学Python • 4 年前 • 1015 次点击  

  前两天,蚂蚁老师群里发布了一个私活:是将Excel表格中的人员关系数据进行可视化。虽然价格不高,但是蚂蚁老师非常贴心的提供了参考示例,甚至于拿着蚂蚁老师的示例代码改了改数据,居然直接就是个半成品了!好吧,正好爬虫单做多了,搞个可视化示例复习复习代码,盘它!

需求分析

  首先分析需求,客户最终需要的是网页版的人员关系图,而数据源是Excel表,那么总体上需要做的步骤是:

  1. 数据源的读取与加工
  2. pyecharts或echarts的关系图配置
  3. 画图展示

最终按照上述思路做了两个方案:

一是使用pyecharts直接在jupyter notebook上生成关系图;

二是使用echarts生成静态的html网页文件。

案例实现

数据的读取与加工

  • 数据源示例

根据客户的需求,是要将数据源中所有的目标人员发生的各种关系(“同住”、“同飞机”、“同飞机订票”、“同车厢”)画出关系图,此外还要标注上次数,在这个环节是pandas大展身手的时刻,一顿操作:

  转换数据并添加分类

  加工出所有人,并且统计是否存在同名同姓情况,如有,则将人名进行修改,确保姓名全部都是唯一,以便后续花关系图时,所有节点都是唯一

数据处理:加工出所有人,并且统计是否存在重名情况,如有,则将人名进行修改,确保姓名全部都是唯一,以便后续花关系图时,所有节点都是唯一

  统计每个人与他人发生关系的总次数,用于后续设置节点的大小

统计每个人与他人发生关系的总次数,用于后续设置节点的大小

使用pyecharts进行画图

  首先制作节点,这里节点是list中传入dict,每一个dict代表一个节点。设置时主要使用到了节点的3个属性:name(名称)、symbolSize(节点大小)、label(节点标签)。

生成节点列表

  加工出关系,这个步骤只是pandas的加工,这个步骤主要有两点需要说明:

  一是为防止同名同姓影响数据结果,因此是使用证件号作为index,后续再根据此前数据加工步骤生成的name_dict转换回人名;

  二是统计的同住、同飞等关系是双向的,A与B同住,A作为目标人、B作为目标人数据中都会产生一条记录,在画图时没有必要都画出来,因此对数据做了一个去重的处理。

加工关系

  生成节点间的关系列表,关系同样是list中传入dict,每一个dict代表一条关系。设置时主要使用到了节点的4个属性:source(来源)、target(目标)、lineStyle(线条样式)、value(标签值)。

生成节点关系列表

  这里划个重点!!!
  原本计划是将“同住”、“同飞机”等每一种关系都使用一条不同颜色的线条进行表示,但是在使用pyecharts进行制作时发现,原本在echarts中能够实现的“两个点之间多条关系”居然无法实现,相同的来源点、目标点之间,在pyecharts下只显示了第一次出现的那条关系,查询了多遍文档,始终没有找出原因,有可能是pyecharts不支持多条关系(待证实)
。
以至于后续又做了一个echats的方案。

  最后生成关系图,关系图有好几种样式,个人觉得圆环图circular比引力图force更好看一些,当然萝卜青菜各有所爱,修改layout参数即可。

生成图形

虽然密密麻麻的关系看着有点乱,但是请注意,我们这个图是可交互的,例如以下操作:

选择节点查看关系
选择线条查看关系

以及还能放大、缩小,高级点可以设置分类进行筛选等等骚操作,实在是数据分析必备工具!!!

使用echarts生成静态网页文件

  由于前面pyecharts无法显示相同的两个节点之间的多条关系,因此,还制作了一个直接使用echarts生成静态html文件的方案,先展示一下结果。

echarts生成的关系图

  观察细节对比前面的图片可知,相同的两个点之间,有了多条连线,用了不同弧度、不同颜色(红、蓝、绿、灰)表示,想比前一的版本更为明晰。

该方案大部分步骤与pyecharts相同,区别以下几点:

  1. 加工关系步骤不同,没有进行合并,而是生成多条关系
  # 这是关系合并成一条线的代码
  links=[]
  for idx, row in data3.iterrows():
      source, target = name_dict.get(idx[0]), name_dict.get(idx[1])
      value = ','.join([f'{col}{row[col]}次' for col in row.index if row[col] !=0])
      color = [col for col in row.index if row[col] !=0]
      if len(color) > 1:
          color = colors[len(color) + 2]
      else:
          color = colors[['同住人', '同车厢人员', '同飞机', '同飞机订票'].index(color[0])]
      links.append({
          "source": source, 
          "target": target, 
          'lineStyle': {'curveness': 0.1, 'color': color},  # 设置线条曲度、颜色
          "value": value  # 设置线条标签值
      })

# -------------------------------- 分割线 ---------------------------------------------
  # 这是每条关系都生成一条线的代码
  links=[]
  for idx, row in data3.iterrows():
      source, target = name_dict.get(idx[0]), name_dict.get(idx[1])
      for i, col in enumerate(row.index):
          if row[col] != 0:
              links.append({
                  "source": source,
                  "target": target,
                  'lineStyle': {'curveness': (i+1) / 10, 'color': colors[i % 4]},  # 设置线条曲度、颜色
                  "value": f"{col}{row[col]}次"  # 设置线条标签值
              })
  1. pyecharts可以直接pip install、直接在jupyter notebook中生成图形,而echarts需要生成html文件,再使用浏览器打开文件。
  • 步骤1 下载echarts模块

    先到echarts官网https://echarts.apache.org/zh/download.html使用“方法三”定制下载“关系图”模块。

定制下载后的js文件
  • 步骤2 制作模板html文件

下载echarts关系图的圆环关系图示例,链接https://echarts.apache.org/examples/zh/editor.html?c=graph-circular-layout

下载示例html

  按需求修改模板(有修改的部分详见以下代码注释)

html>
<html lang="zh-CN" style="height: 100%">
<head>
    <meta charset="utf-8">
head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%">div>


<script type="text/javascript">
  直接把关系图模块内容粘贴在这里
script>

<script type="text/javascript">
    var dom = document.getElementById('container');
    var myChart = echarts.init(dom, null, {
        renderer: 'canvas',
        useDirtyRect: false
    });
    var app = {};

    var option;

    option = {
        // 图形的标题
        title: {
            text: '人员关系图',
            left: 'center',  // 标题位置
            textStyle: {
                fontSize: 48,  // 标题字体大小
            }
        },
        tooltip: {},  // 图形的工具箱
        animationDurationUpdate: 1500,
        animationEasingUpdate: 'quinticInOut',
        series: [
            {
                type: 'graph',  // 关系图
                layout: 'circular',   // 关系图类型: circular 圆环  force 引力
                symbolSize: 50,
                label: {
                    show: true
                },
                roam: true,  // 图形是否开启鼠标缩放和平移漫游
                edgeSymbol: ['circle',],
                edgeSymbolSize: [4, 10],
                edgeLabel: {
                    fontSize: 20
                },
                // 图形聚焦高亮模式配置项
                emphasis: {
                    focus: 'adjacency',
                    lineStyle: {
                        width: 10
                    }
                },
                // 引力图配置
                force: {
                    repulsion: 500,
                },
                // draggable: true,
                animation: true,
                // 节点
                data: replace_data,  // replace_data占位用于后续被替换成正式节点数据
                // links: [],
                // 关系
                links: replace_links,  // replace_links占位用于后续被替换成正式节点数据
                // 关系线条设置
                lineStyle: {
                    opacity: 0.9,
                    width: 2,
                    curveness: 0
                }
            }
        ]
    };

    if (option && typeof option === 'object') {
        myChart.setOption(option);
    }

    window.addEventListener('resize', myChart.resize);
script>
body>
html>

使用代码将html模板节点和关系数据进行替换

def write_html(nodes: list, links: list, filename: pl.Path):
    """
    写入静态html
    :return:
    """

    with open('model/graph_model.html', 'r', encoding='utf-8') as fin:
        html = fin.read()
    for old, new in {'replace_data': str(self.nodes), 'replace_links': str(links)}.items():
        html = html.replace(old, new)
    out_path = filename.parent / f'{filename.stem}.html'
    with open(out_path, 'w', encoding='utf-8') as fout:
        fout.write(html)
    print(f'完成{filename.name}的读取分析,画图文件输出为{out_path}')

大功告成!!!

图形可放大、缩小、聚焦,效果酷炫
force引力布局关系图

总结

  本案例最花费时间的地方在于对图形的配置项进行配置。echarts非常强大,交互性也非常好,但是配置项确实多,只有多使用才能了解各个配置项的功能,查文档查了好多时间,不过最终得到酷炫的成品,还是很有成就感的!



今晚来蚂蚁老师抖音直播间,Python带副业全套餐有优惠!!!


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/138627