在本系列的前文 [1,2]中,我们介绍了如何使用 Python 语言图分析库 NetworkX [3] + Nebula Graph [4] 来进行中人物关系图谱分析。

在本文中我们将介绍如何使用 Java 语言的图分析库 JGraphT [5] 并借助绘图库 mxgraph [6] ,可视化探索 A 股的行业个股的相关性随时间的变化情况

数据集的处理

本文主要分析方法参考了[7,8],有两种数据集:

股票数据(点集)

从 A 股中按股票代码顺序选取了 160 只股票(排除摘牌或者 ST 的)。每一支股票都被建模成一个点,每个点的属性有股票代码,股票名称,以及证监会对该股票对应上市公司所属板块分类等三种属性;

表1:点集示例

股票关系(边集)

边只有一个属性,即权重。边的权重代表边的源点和目标点所代表的两支股票所属上市公司业务上的的相似度——相似度的具体计算方法参考 [7,8]:取一段时间(2014 年 1 月 1 日 - 2020 年 1 月 1 日)内,个股的日收益率的时间序列相关性

再定义个股之间的距离为 (也即两点之间的边权重):

通过这样的处理,距离取值范围为 [0,2]。这意味着距离越远的个股,两个之间的收益率相关性越低

表2: 边集示例

这样的点集和边集构成一个图网络,可以将这个网络存储在图数据库 Nebula Graph 中。

JGraphT

JGraphT 是一个开放源代码的 Java 类库,它不仅为我们提供了各种高效且通用的图数据结构,还为解决最常见的图问题提供了许多有用的算法:

  • 支持有向边、无向边、权重边、非权重边等;
  • 支持简单图、多重图、伪图;
  • 提供了用于图遍历的专用迭代器(DFS,BFS)等;
  • 提供了大量常用的的图算法,如路径查找、同构检测、着色、公共祖先、游走、连通性、匹配、循环检测、分区、切割、流、中心性等算法;
  • 可以方便地导入 / 导出 GraphViz [9]。导出的 GraphViz 可被导入可视化工具 Gephi[10] 进行分析与展示;
  • 可以方便地使用其他绘图组件,如:JGraphX,mxGraph,Guava Graphs Generators 等工具绘制出图网络。

下面,我们来实践一把,先在 JGraphT 中创建一个有向图:

import org.jgrapht.*;import org.jgrapht.graph.*;import org.jgrapht.nio.*;import org.jgrapht.nio.dot.*;import org.jgrapht.traverse.*;import java.io.*;import java.net.*;import java.util.*;Graph g = new DefaultDirectedGraph<>(DefaultEdge.class);

添加顶点:

URI google = new URI("http://www.google.com");URI wikipedia = new URI("http://www.wikipedia.org");URI jgrapht = new URI("http://www.jgrapht.org");// add the verticesg.addVertex(google);g.addVertex(wikipedia);g.addVertex(jgrapht);

添加边:

// add edges to create linking structureg.addEdge(jgrapht, wikipedia);g.addEdge(google, jgrapht);g.addEdge(google, wikipedia);g.addEdge(wikipedia, google);

图数据库 Nebula Graph Database

JGraphT 通常使用本地文件作为数据源,这在静态网络研究的时候没什么问题,但如果图网络经常会发生变化——例如,股票数据每日都在变化——每次生成全新的静态文件再加载分析就有些麻烦,最好整个变化过程可以持久化地写入一个数据库中,并且可以实时地直接从数据库中加载子图或者全图做分析。本文选用 Nebula Graph 作为存储图数据的图数据库。

Nebula Graph 的 Java 客户端 Nebula-Java [11] 提供了两种访问 Nebula Graph 方式:一种是通过图查询语言 nGQL [12] 与查询引擎层 [13] 交互,这通常适用于有复杂语义的子图访问类型; 另一种是通过 API 与底层的存储层(storaged)[14] 直接交互,用于获取全量的点和边。除了可以访问 Nebula Graph 本身外,Nebula-Java 还提供了与 Neo4j [15]、JanusGraph [16]、Spark [17] 等交互的示例

在本文中,我们选择直接访问存储层(storaged)来获取全部的点和边。下面两个接口可以用来读取所有的点、边数据:

// space 为待扫描的图空间名称,returnCols 为需要读取的点/边及其属性列,// returnCols 参数格式:{tag1Name: prop1, prop2, tag2Name: prop3, prop4, prop5}Iterator scanVertex(            String space, Map> returnCols);Iterator scanEdge(            String space, Map> returnCols);

第一步:初始化一个客户端,和一个 ScanVertexProcessor。ScanVertexProcessor 用来对读出来的顶点数据进行解码:

MetaClientImpl metaClientImpl = new MetaClientImpl(metaHost, metaPort);metaClientImpl.connect();StorageClient storageClient = new StorageClientImpl(metaClientImpl);Processor processor = new ScanVertexProcessor(metaClientImpl);

第二步:调用 scanVertex 接口,该接口会返回一个 scanVertexResponse 对象的迭代器:

Iterator iterator =                storageClient.scanVertex(spaceName, returnCols);

第三步:不断读取该迭代器所指向的 scanVertexResponse 对象中的数据,直到读取完所有数据。读取出来的顶点数据先保存起来,后面会将其添加到到 JGraphT 的图结构中:

while (iterator.hasNext()) {  ScanVertexResponse response = iterator.next();  if (response == null) {    log.error("Error occurs while scan vertex");    break;  }    Result result =  processor.process(spaceName, response);  results.addAll(result.getRows(TAGNAME));}

读取边数据的方法和上面的流程类似。

在 JGraphT 中进行图分析

第一步:在 JGraphT 中创建一个无向加权图 graph:

Graph graph = GraphTypeBuilder                .undirected()    .weighted(true)    .allowingMultipleEdges(true)    .allowingSelfLoops(false)    .vertexSupplier(SupplierUtil.createStringSupplier())    .edgeSupplier(SupplierUtil.createSupplier(MyEdge.class))    .buildGraph();

第二步:将上一步从 Nebula Graph 图空间中读出来的点、边数据添加到 graph 中:

for (VertexDomain vertex : vertexDomainList){    graph.addVertex(vertex.getVid().toString());    stockIdToName.put(vertex.getVid().toString(), vertex);}for (EdgeDomain edgeDomain : edgeDomainList){    graph.addEdge(edgeDomain.getSrcid().toString(), edgeDomain.getDstid().toString());    MyEdge newEdge = graph.getEdge(edgeDomain.getSrcid().toString(), edgeDomain.getDstid().toString());    graph.setEdgeWeight(newEdge, edgeDomain.getWeight());}

第三步:参考 [7,8] 中的分析法,对刚才的图 graph 使用 Prim 最小生成树算法(minimun-spanning-tree),并调用封装好的 drawGraph 接口画图:

普里姆算法(Prim's algorithm),图论中的一种算法,可在加权连通图里搜索最小生成树。即,由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

SpanningTreeAlgorithm.SpanningTree pMST = new PrimMinimumSpanningTree(graph).getSpanningTree();Legend.drawGraph(pMST.getEdges(), filename, stockIdToName);

第四步:drawGraph 方法封装了画图的布局等各项参数设置。这个方法将同一板块的股票渲染为同一颜色,将距离接近的股票排列聚集在一起

public class Legend {  ...    public static void drawGraph(Set edges, String filename, Map idVertexMap) throws IOException {     // Creates graph with model     mxGraph graph = new mxGraph();     Object parent = graph.getDefaultParent();     // set style     graph.getModel().beginUpdate();     mxStylesheet myStylesheet =  graph.getStylesheet();     graph.setStylesheet(setMsStylesheet(myStylesheet));     Map idMap = new HashMap<>();     Map industryColor = new HashMap<>();     int colorIndex = 0;     for (MyEdge edge : edges) {       Object src, dst;       if (!idMap.containsKey(edge.getSrc())) {         VertexDomain srcNode = idVertexMap.get(edge.getSrc());         String nodeColor;         if (industryColor.containsKey(srcNode.getIndustry())){           nodeColor = industryColor.get(srcNode.getIndustry());         }else {           nodeColor = COLOR_LIST[colorIndex++];           industryColor.put(srcNode.getIndustry(), nodeColor);         }         src = graph.insertVertex(parent, null, srcNode.getName(), 0, 0, 105, 50, "fillColor=" + nodeColor);         idMap.put(edge.getSrc(), src);       } else {         src = idMap.get(edge.getSrc());       }       if (!idMap.containsKey(edge.getDst())) {         VertexDomain dstNode = idVertexMap.get(edge.getDst());         String nodeColor;         if (industryColor.containsKey(dstNode.getIndustry())){           nodeColor = industryColor.get(dstNode.getIndustry());         }else {           nodeColor = COLOR_LIST[colorIndex++];           industryColor.put(dstNode.getIndustry(), nodeColor);         }         dst = graph.insertVertex(parent, null, dstNode.getName(), 0, 0, 105, 50, "fillColor=" + nodeColor);         idMap.put(edge.getDst(), dst);       } else {         dst = idMap.get(edge.getDst());       }       graph.insertEdge(parent, null, "", src, dst);     }     log.info("vertice " + idMap.size());     log.info("colorsize " + industryColor.size());     mxFastOrganicLayout layout = new mxFastOrganicLayout(graph);     layout.setMaxIterations(2000);     //layout.setMinDistanceLimit(10D);     layout.execute(parent);     graph.getModel().endUpdate();     // Creates an image than can be saved using ImageIO     BufferedImage image = createBufferedImage(graph, null, 1, Color.WHITE,                                               true, null);     // For the sake of this example we display the image in a window     // Save as JPEG     File file = new File(filename);     ImageIO.write(image, "JPEG", file);   }    ...    }

第五步:生成可视化:

图1中每个顶点的颜色代表证监会对该股票所属上市公司归类的板块

可以看到,实际业务近似度较高的股票已经聚拢成簇状(例如:高速板块、银行版本、机场航空板块),但也会有部分关联性不明显的个股被聚类在一起,具体原因需要单独进行个股研究。

图1: 基于 2015-01-01 至 2020-01-01 的股票数据计算出的聚集性

第六步:基于不同时间窗口的一些其他动态探索

上节中,结论主要基于 2015-01-01 到 2020-01-01 的个股聚集性。这一节我们还做了一些其他的尝试:以 2 年为一个时间滑动窗口,分析方法不变,定性探索聚集群是否随着时间变化会发生改变

图2:基于 2014-01-01 至 2016-01-01 的股票数据计算出的聚集性

图3:基于 2015-01-01 至 2017-01-01 的股票数据计算出的聚集性

图4:基于 2016-01-01 至 2018-01-01 的股票数据计算出的聚集性

图5:基于 2017-01-01 至 2019-01-01 的股票数据计算出的聚集性

图6:基于 2018-01-01 至 2020-01-01 的股票数据计算出的聚集性

粗略分析看,随着时间窗口变化,有些板块(高速、银行、机场航空、房产、能源)的板块内部个股聚集性一直保持比较好——这意味着随着时间变化,这个版块内各种一直保持比较高的相关性;但有些板块(制造)的聚集性会持续变化——意味着相关性一直在发生变化。

Disclaim

本文不构成任何投资建议,且作者不持有本文中任一股票。

受限于停牌、熔断、涨跌停、送转、并购、主营业务变更等情况,数据处理可能有错误,未做一一检查。

受时间所限,本文只选用了 160 个个股样本过去 6 年的数据,只采用了最小扩张树一种办法来做聚类分类。未来可以使用更大的数据集(例如美股、衍生品、数字货币),尝试更多种图机器学习的办法。

本文代码可见[18]

Reference

[1] 用 NetworkX + Gephi + Nebula Graph 分析人物关系(上篇)https://nebula-graph.com.cn/posts/game-of-thrones-relationship-networkx-gephi-nebula-graph/

[2] 用 NetworkX + Gephi + Nebula Graph 分析人物关系(下篇) https://nebula-graph.com.cn/posts/game-of-thrones-relationship-networkx-gephi-nebula-graph-part-two/

[3] NetworkX: a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks. https://networkx.github.io/

[4] Nebula Graph: A powerfully distributed, scalable, lightning-fast graph database written in C++. https://nebula-graph.io/

[5] JGraphT: a Java library of graph theory data structures and algorithms. https://jgrapht.org/

[6] mxGraph: JavaScript diagramming library that enables interactive graph and charting applications. https://jgraph.github.io/mxgraph/

[7] Bonanno, Giovanni & Lillo, Fabrizio & Mantegna, Rosario. (2000). High-frequency Cross-correlation in a Set of Stocks. arXiv.org, Quantitative Finance Papers. 1. 10.1080/713665554.

[8] Mantegna, R.N. Hierarchical structure in financial markets. Eur. Phys. J. B 11, 193–197 (1999).

[9] https://graphviz.org/

[10] https://gephi.org/

[11] https://github.com/vesoft-inc/nebula-java

[12] Nebula Graph Query Language (nGQL). https://docs.nebula-graph.io/manual-EN/1.overview/1.concepts/2.nGQL-overview/

[13] Nebula Graph Query Engine. https://github.com/vesoft-inc/nebula-graph

[14] Nebula-storage: A distributed consistent graph storage. https://github.com/vesoft-inc/nebula-storage

[15] Neo4j. www.neo4j.com

[16] JanusGraph. janusgraph.org

[17] Apache Spark. spark.apache.org.

[18] https://github.com/Judy1992/nebula_scan

数据结构实验之图论四:迷宫探索_用图机器学习探索 A 股个股相关性变化相关推荐

  1. 数据结构实验之图论四:迷宫探索_迷宫搜索类的双向bfs问题(例题详解)

    前言 文章若有疏忽还请指正! 更多精彩还请关注公众号:bigsai 头条号:一直码农一直爽 在搜索问题中,以迷宫问题最具有代表性,无论是八皇后的回溯问题,还是dfs找出口,bfs找最短次数等等题目的问 ...

  2. d - 数据结构实验之查找四:二分查找_【数据结构】资料

    春天来了,关注我们吧!! 数据结构资料 1.栈和队列的共同特点是( ). A.只允许在端点处插入和删除元素 B.都是先进后出 C.都是先进先出 D.没有共同点 答案:A 解析:栈的操作只允许在栈的一端 ...

  3. d - 数据结构实验之查找四:二分查找_数据结构与算法笔记

    二分查找(上):如何用最省内存的方式实现快速查找功能? 二分查找(下):如何快速定位IP对应的省份地址? 变体一:查找第一个值等于给定值的元素 变体二:查找最后一个值等于给定值的元素 变体三:查找第一 ...

  4. 用图机器学习探索 A 股个股相关性变化

    在本系列的前文 [1,2]中,我们介绍了如何使用 Python 语言图分析库 NetworkX [3] + Nebula Graph [4] 来进行<权力的游戏>中人物关系图谱分析. 在本 ...

  5. 数据结构实验之图论七:驴友计划(最新版)

    数据结构实验之图论七:驴友计划 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 做为一个资深驴友,小新有一张珍藏的自驾游 ...

  6. SDUT OJ 数据结构实验之图论五:从起始点到目标点的最短步数(BFS)

    数据结构实验之图论五:从起始点到目标点的最短步数(BFS) Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss P ...

  7. oj 数据结构实验之图论三:判断可达性

    数据结构实验之图论三:判断可达性 Description 在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫.在他们所在的地域,有n个隘口,编号为1-n,某些隘口之间是有通道连接的.其中近卫军团在 ...

  8. 2138 数据结构实验之图论三:判断可达性

    数据结构实验之图论三:判断可达性 Time Limit: 1000MS Memory Limit: 65536KB Problem Description 在古老的魔兽传说中,有两个军团,一个叫天灾, ...

  9. 数据结构实验之图论三:判断可达性

    /*[添加链接描述](http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/2139.html)*/ 数 ...

最新文章

  1. 面试:为什么foreach中不允许对元素进行add和remove
  2. 【已解决】ReferenceError: $ is not defined
  3. 第二课计算机ppt,第二课计算机系统.ppt
  4. github打开前端样式丢失_工具资源系列之 github 上各式各样的小徽章从何而来?...
  5. cafffe---之params参数
  6. [每日一题] 11gOCP 1z0-053 :2013-10-1 persistent lightweight jobs...........................11
  7. 关于浮点数的问题,我再做一个字体设置的时候总有问题,现在看了这个文章,受到了启发...
  8. 页面删除android4.0 Launcher仿三星平板workspace页面编辑(即页面增减)
  9. 集成极光推送和厂商通道相关总结
  10. 旭日x3派,手势识别之Momo Quanghuang学习记录
  11. 用英雄联盟的方式讲解JavaScript设计模式
  12. 如何检测页面是否允许访问Cookie
  13. can总线不加末端电阻_【干货】80%修理工不知道的CAN线电阻知识点
  14. 10bit视频是什么?
  15. 江西师范大学教育心理测量计算机,江西师范大学重点和特色专业介绍
  16. windows mobile数据同步方案
  17. 读《深入理解计算机系统》
  18. 计算机 发声原理,模拟电子琴演奏程序设计。微机中扬声器控制发声原理如练习图10.1所示,其中用到8255与8253两个芯片。...
  19. 阿里OCR扫描字识别demo
  20. 2020年全球SDDC市场将达到1390亿美元

热门文章

  1. 转 jquery插件--241个jquery插件—jquery插件大全
  2. ListView上移 和下移
  3. olr 性能调优 NO_NORMS
  4. .Net 2.0中使用扩展方法
  5. 关于RAC中监听配置IP=FIRST的说明
  6. MFC中Windows窗口消息循环及多线程之间关系
  7. c++中.dll与.lib文件的生成与使用的详解
  8. .net remoting 与webservice
  9. visio2016中插入大括号
  10. vue中实现美团双级联动菜单