目录
数据分析(data profiling)能够用于分析数据源的结构、内容和元数据,是关系型数据库广泛应用的方法。通常情况下,数据剖析包含对数据源的一系列操作,以获取底层数据的统计信息、信息摘要。一般来说,数据是随着时间而发展的。几年之后,数据库中存储和使用的实际数据可能与人们认为的有很大的不同,或者与数据库最初设计的目的有很大的不同。数据分析不仅有助于理解异常和评估数据质量,而且有助于发现、注册和评估企业元数据。Neo4j图形数据库是分析连接的、高容量的和可变结构的数据资产的最佳工具,这使得数据分析更加关键,因为它将帮助我们更好地理解数据,更容易地识别隐藏的模式,并可能提高查询性能。
本文将使用Cypher图形查询语言共享实用的数据分析技术。
1. 数据准备
(1)数据下载
数据采用截止2015年8月22日的Stack Overflow论坛中neo4j板块讨论数据,其下载地址 http://example-data.neo4j.org/files/stackoverflow/stackoverflow-2015-08-22.db224.tgz?_ga=2.207962901.662760037.1557307514-1383806388.1557307514
(2)数据格式
Nodes:
posts.csv数据格式 postId:ID(Post),title,postType:INT,createdAt,score:INT,views:INT,answers:INT,comments:INT,favorites:INT,updatedAt,body
users.csv数据格式 userId:ID(User),name,reputation:INT,createdAt,accessedAt,url,location,views:INT,upvotes:INT,downvotes:INT,age:INT,accountId:INT
tags.csv数据格式 tagId:ID(Tag),count:INT,wikiPostId:INT
Relationships:
posts_answers.csv:ANSWER -> :START_ID(Post),:END_ID(Post)
posts_rel.csv:PARENT_OF -> :START_ID(Post),:END_ID(Post)
tags_posts_rel.csv:HAS_TAG > :START_ID(Post),:END_ID(Tag)
users_posts_rel.csv:POSTED > :START_ID(User),:END_ID(Post)
2. 数据导入
2.1 常见数据导入方式概览
(1) Cypher create语句,为每一条数据写一个create;
(2) Cypher load csv语句,将数据转成CSV格式,通过LOAD CSV读取数据;
(3) 官方提供的neo4j-import工具,未来将被neo4j-adminimport代替;
(4) 官方提供的Java API BatchInserter;
(5) 大牛编写的 batch-import 工具;
(6) neo4j-apocload.csv +apoc.load.relationship;
(7) 针对实际业务场景,定制化开发。
2.2 导入工具对比
create语句 |
load csv语句 |
neo4j-import |
BatchInster |
batch-import |
apoc |
|
适用场景
|
初始化导入 增量更新
|
初始化导入
|
初始化导入
|
初始化导入 增量更新(有限制)
|
增量更新 |
|
导入速度 |
很慢1000/s |
数k /s |
数w/s |
数w/s |
数w/s |
数k/s |
实际测试 |
无 |
9.5k/s (节点+关系) |
12w/s (节点+关系) |
1w/s (节点+关系) |
1w/s (节点+关系) |
4k/s(1亿数据上增量更新) 1w/s(百万数据上增量更新) |
优点 |
1.使用方便 2.可实时插入 |
1.官方ETL工具 2.可以加载本地/远程CSV 3.可实时插入 |
1.官方工具 2.占用资源少 |
1.官方API |
1.可以增量更新 2.基于BatchInserter |
1.官方ETL工具 2.可以增量更新 3.支持在线导入 4.支持动态传Label RelationShip |
缺点 |
1.速度慢 2.处理数据,拼CQL复杂 |
1.导入速度较慢 2.不能动态传Label RelationShip |
1.需要脱机导入 停止Neo4j数据库 2.只能用于初始化导入 |
1.需要脱机导入 停止Neo4j数据库 2.需要在JAVA环境中使用
|
1.需要脱机导入 停止Neo4j数据库 |
1.速度一般 |
2.3 该实例中数据导入
该实例中文件大小共9.46GB,31138559 nodes、77930024 relationships、260665346 properties,数据量大,采用初始导入 neo4j-import工具,此时须关闭neo4j进程,并删除neo4j数据库中其他数据。
Import工具命令格式如下:
neo4j-admin import [--mode=csv] [--database=<name>]
[--additional-config=<config-file-path>]
[--report-file=<filename>]
[--nodes[:Label1:Label2]=<"file1,file2,...">]
[--relationships[:RELATIONSHIP_TYPE]=<"file1,file2,...">]
[--id-type=<STRING|INTEGER|ACTUAL>]
[--input-encoding=<character-set>]
[--ignore-extra-columns[=<true|false>]]
[--ignore-duplicate-nodes[=<true|false>]]
[--ignore-missing-nodes[=<true|false>]]
[--multiline-fields[=<true|false>]]
[--delimiter=<delimiter-character>]
[--array-delimiter=<array-delimiter-character>]
[--quote=<quotation-character>]
[--max-memory=<max-memory-that-importer-can-use>]
[--f=<File containing all arguments to this import>]
[--high-io=<true/false>]
对于该实例,进入bin文件夹,打开终端,命令如下:
提示:由于neo4j-import只能初始导入,须删除/data/databases/中graph.db数据库文件
./neo4j-import \
--into ../data/databases/graph.db \
--id-type string \
--nodes:Post ../../stackoverflow-2015-08-22/posts.csv \
--nodes:User ../../stackoverflow-2015-08-22/users.csv \
--nodes:Tag ../../stackoverflow-2015-08-22/tags.csv \
--relationships:PARENT_OF ../../stackoverflow-2015-08-22/posts_rel.csv \
--relationships:ANSWER ../../stackoverflow-2015-08-22/posts_answers.csv \
--relationships:HAS_TAG ../../stackoverflow-2015-08-22/tags_posts_rel.csv \
--relationships:POSTED ../../stackoverflow-2015-08-22/users_posts_rel.csv
2.4 neo4j进程开启
(1)由于数据量巨大,须更改neo4j配置信息
打开conf/neo4j.conf,配置dbms.memory.heap.max_size=10240m
(2)然后输入 ./neo4j.bat start 开启数据库
在网页端打开http://localhost:7474/browser/,并检验数据是否导入成功。
输入:
match (n) return head(labels(n)) as label, count(*);
输出:
输入:
match ()-[r]-() with type(r) as RelationshipName,
count(r) as RelationshipNumber
return RelationshipName, RelationshipNumber;
输出:
(3)为节点属性添加索引和约束
输入:
create index on :Post(title);
create index on :Post(createdAt);
create index on :Post(score);
create index on :Post(views);
create index on :Post(favorites);
create index on :Post(answers);
create index on :Post(score);
create index on :User(name);
create index on :User(createdAt);
create index on :User(reputation);
create index on :User(age);
create index on :Tag(count);
create constraint on (t:Tag) assert t.tagId is unique;
create constraint on (u:User) assert u.userId is unique;
create constraint on (p:Post) assert p.postId is unique;
3. 数据库模式分析
数据库模式分析通常是数据分析的第一步。它的简单目的是了解数据模型是什么样子的,以及有哪些对象可用。
3.1 图数据模型展示
输入:
CALL db.schema()
该数据库包含User、Post和Tag三类节点,并且具有User POSTED Post、Post HAS_TAG Tag、Post is PARENT_OF Post和Post ANSWER another Post四类关系。
3.2 现有约束和索引展示
输入:
:schema
输出:
索引告诉我们用于匹配时具有最佳查询性能的属性,约束告诉我们哪些属性是惟一的,可以用来标识节点或关系。
3.3 所有关系类型展示
输入:
CALL db.relationshipTypes()
输出:
3.4 所有节点标签(类型)展示
输入:
CALL db.labels()
输出:
3.5 节点个数统计
输入:
MATCH (n) RETURN count(n)
输出:
3.6 关系个数统计
输入:
MATCH ()-[r]->() RETURN count(*)
输出:
3.7 数据存储空间展示
输入:
:sysinfo
输出:
3.8 数据采样
采样一些节点,统计节点的属性、关系
输入:
MATCH (n) WHERE rand() <= 0.1
RETURN
DISTINCT labels(n),
count(*) AS SampleSize,
avg(size(keys(n))) as Avg_PropertyCount,
min(size(keys(n))) as Min_PropertyCount,
max(size(keys(n))) as Max_PropertyCount,
avg(size( (n)-[]-() ) ) as Avg_RelationshipCount,
min(size( (n)-[]-() ) ) as Min_RelationshipCount,
max(size( (n)-[]-() ) ) as Max_RelationshipCount
输出:
结果分析:随机选取10%节点,统计其节点个数、平均属性个数、最小属性个数、最大属性个数、平均关系个数、最小关系个数、最大关系个数
4. 节点分析
节点分析或多或少类似于关系数据库(RDBMS)概要分析的表和列分析。节点分析的目的是揭示节点的事实以及节点的属性。
4.1 根据节点的标签/类型统计
输入:
MATCH (n) RETURN labels(n) AS NodeType, count(n) AS NumberOfNodes;
输出:
结果分析:统计每一类节点的个数
4.2 属性分析
(1)列出一类节点的属性
输入:
MATCH (u:User) RETURN keys(u) LIMIT 1
输出:
结果分析:统计属性名称
(2)列出一类关系的属性
输入:
MATCH ()-[t:POSTED]-() RETURN keys(t) LIMIT 1
输出:
无
结果分析:该数据关系没有属性
(3)列出属性的唯一性
输入:
MATCH (u:User)
RETURN count(DISTINCT u.name) AS DistinctName,
count(u.name) AS TotalUser,
100*count(DISTINCT u.name)/count(u.name) AS Uniqueness
输出:
结果分析:统计用户名称的唯一性
(4)属性的非空性
输入:
MATCH (u:User) WHERE u.name IS null RETURN count(u);
输出为空
结果分析:说明所有用户都有名字
(5)属性值的最小值、最大值、平均值和标准差
输入:
MATCH (p:Post)
RETURN min(p.favorites) AS MinFavorites,
max(p.favorites) AS MaxFavorites,
avg(p.favorites) AS AvgFavorites,
stDev(p.favorites) AS StdFavorites;
输出:
(6)属性值的出现次数
输入:
MATCH (p:Post)
RETURN p.answers AS Answers, count(p.answers) AS CountOfAnswers
ORDER BY Answers ASC;
输出:
结果分析:117万问题有0个回答,1413个问题有15个回答
4.3 节点等级
(1)用户重要性
统计用户的出度、入度
输入:
MATCH (u:User)
WITH u,size( (u)-[:POSTED]->()) AS OutDepth, size( (u)<-[:POSTED]-()) AS InDepth
ORDER BY OutDepth, InDepth
WHERE u.name STARTS WITH 'T'
RETURN u.name, min(OutDepth), max(OutDepth), min(InDepth), max(InDepth)
输出:
输入:
MATCH (u:User) return u,size( (u)-[:POSTED]->()) AS OutDepth, size( (u)<-[:POSTED]-()) AS InDepth order by OutDepth desc limit 10
输出:
(2)问题重要性
统计问题被回答的数量
输入:
match (p:Post) return p.name, size(()-[:ANSWER]->(p)) as InDepth, size((p)-[:ANSWER]->(p)) as OutDepth order by InDepth desc limit 10
输出:
4.4 孤立节点
输入:
match (u:User)
with u,size( (u)-[:POSTED]->()) as posts where posts = 0
return u.name, posts;
输出:
5. 关系分析
关系分析有助于理解节点间的完整性、密度。与普通RDBMS相比,图形数据库的独特之处在于可用于揭示隐藏的连接数据知识的强大分析。 一个例子是找出两个节点之间的最短路径。 另一个是识别关系三角形。
5.1 关系统计分析
输入:
match (u)-[p]-() with type(p) as RelationshipName,
count(p) as RelationshipNumber
return RelationshipName, RelationshipNumber;
输出:
5.2 两个节点间最短路径
输入:
MATCH path =
allShortestPaths((u:User {name:"Darin Dimitrov"})-[*]-(me:User {name:"Michael Hunger"}))
RETURN path;
输出:
结果分析:两个用户之间的最短路径 - 在上图中用蓝色箭头突出显示 - 告诉我们两个用户通过具有相同标签的帖子(黄色节点)连接。 这些不一定是唯一的路径,因为用户可以发帖回答彼此的问题或帖子,但在这种情况下,通过相同标签的连接 - 即,感兴趣的公共区域 - 是连接两个用户的最快方式。
在像这样的大图中,计算任何两个用户之间的最短路径可能是不可行的。 但是,检查最重要的人之间或最感兴趣的帖子之间的连接可能是有价值的。
5.3 三角性分析
输入:
match (u:User)-[p1:POSTED]-(x1),(u)-[p2:POSTED]-(x2),(x1)-[r1]-(x2)
where x1 <> x2 return u,p1,p2,x1,x2 limit 10;
输出:
输入:
match (u:User)-[p1:POSTED]-(x1),(u)-[p2:POSTED]-(x2),(x1)-[r1]-(x2)
where x1 <> x2 return count(p1);
输出:三角形个数
结果分析:其中x1 <> x2为x1!=x2,三角形是图论中的另一个关键概念。 三角形由三个连接的节点表示,方向或单向。 识别三角形 - 或缺少三角形 - 提供有关基础数据资产的有趣见解。三角形也被称为三元闭包,根据图形数据库,第2版(O'Reilly Media),三元闭包是社交图的共同属性,我们观察到如果两个节点通过涉及第三节点的路径连接,则两个节点在将来的某个点上将直接连接的可能性增加。
把这个概念融入我们的日常生活中,这是一个熟悉的社会事件。 如果我们碰巧是两个不认识的人的朋友,那么这两个人将来会在某个时刻成为直接朋友的可能性会增加。
通过在图形数据库中发现三角形的存在,我们可以创建更有效的查询以避免循环遍历。
6. 使用APOC库
从Neo4j 3.0开始,用户可以使用Java实现定制功能,从而将Cypher扩展为高度复杂的图形算法。 这就是所谓的用户定义程序概念。
APOC库(详见https://github.com/neo4j-contrib/neo4j-apoc-procedures)是最强大和最受欢迎的Neo4j库之一。 它包含许多算法(在编写本文时大约514个),以帮助处理数据集成,图形算法或数据转换等领域的许多不同任务。 毫不奇怪,它还有几个用于分析图数据库元数据的功能。要在Neo4j 3.x中启用APOC,有几个简单的步骤:
(1)停止Neo4j服务
(2)将最新版本的APOC JAR文件下载并复制到数据库下的plugins文件夹,例如graph.db \ plugins
(3)将以下行添加到neo4j.conf文件中:dbms.security.procedures.unrestricted = apoc. *
(4)再次启动Neo4j服务
用于分析的开箱即用功能都在apoc.meta下,以下是一些示例:
6.1元数据
输入:
CALL apoc.meta.data()
输出:
结果分析:这将列出所有节点和关系以及每个节点的属性,也就是元数据
6.2 模型展示
输入:
CALL apoc.meta.graph()
等价于3.1
6.3 统计信息
输入:
CALL apoc.meta.stats()
输出:
6.4 元数据分析
输入:
CALL apoc.meta.schema()
这将返回所有节点标签,关系类型和属性的元数据。
6.5 子集分析
输入:
CALL apoc.meta.subGraph({labels:['Character'],rels:['INTERECTS']})
输出:
结果分析:这是一个非常有用的函数,尤其适用于非常大的图形,因为它允许您分析节点和关系(子图)的子集。
7. 深入探讨
将数据存储为图形具有巨大的优势。图形数据模型使我们能够对大量数据的关系进行更强大的分析,并挖掘大量单个数据元素(节点)之间的隐藏连接。对与Neo4j这样的图形数据库进行数据分析,可以让我们更深入地了解我们正在处理的实际数据。然后,获得的结果可用于进一步的详细分析,性能调整,数据库模式优化和数据迁移。
作为本地图形数据库,Neo4j通过Cypher查询语言提供本机图形数据存储和本机查询处理。如本文所示,使用Cypher可以轻松完成一些最有用的数据分析任务。还有一些扩展支持更复杂和更先进的图形分析;例如:中介中心性和连接组件。 Neo4j图算法(详见https://neo4j.com/docs/graph-algorithms/current/)是我用来执行更复杂的数据分析的算法。安装后,可以直接调用这些函数作为Cypher查询的一部分,并在Neo4j浏览器中显示结果。我打算在即将发表的文章中对此进行更多介绍。
参考:https://neo4j.com/blog/data-profiling-holistic-view-neo4j/
https://neo4j.com/blog/import-10m-stack-overflow-questions/