Neo4j - data science - part 2
Exploratory Data Analysis(EDA)
- 이전에는 neo4j sandbox를 설정하였고, 여기서는 데이터를 탐색합니다. 흔히 말하는 Exploratory Data Analysis를 사용하며, 특히 python의 이미 잘 구축된 라이브러리들을 사용하여 진행합니다.
- EDA는 그냥 데이터들의 분포가 어떻고, 어떻게 밀집되어 있고, 구간이 어떻게 나와 있는지 등 다양한 관점에 대해서 차트를 만들어보는 수준이다, 즉, 본격 분석에 들어가기 앞서서, ’몇 가지 가정을 세우는 단계’라고 생각하면 됩니다.
Tools
- 다음의 3가지 툴을 사용합니다. 각각에 대해서 간략하게 설명을 정리하였습니다.
py2neo: py2neo는 neo4j와 파이썬 데이터 분석 라이브러리들을 연결합니다.pandas: 유명한 라이브러리이므로 설명은 생략하고, neo4j로부터 가져온 데이터를 후처리하기 위해서 사용한다 흠…Matplotlib: 파이썬 2D 시각화 라이브러리입니다. 특히, 이 아이는 jupyter notebook 위에서 돌아갈 때, 아주 강력하죠.
Citation Dataset
- 본 프로젝트에서 제시한 Citation Dataset의 데이터 구조는 다음으로 구성되어 있습니다.
- (author)<-[author]-(article)
- (article)<-[cited]-(article)
- (article)<-[venue]-(venue)
exploratory data analysis on jupyter
- 이제 jupyter notebook에서 탐색적 데이터 분석을 해봅시다.
- 우선, 환경 설정을 해줍니다.
!pip install py2neo==4.1.3 pandas matplotlib sklearn
from py2neo import Graph
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
pd.set_option('display.float_format', lambda x: '%.3f' % x)
# Change the line of code below to use the IP Address, Bolt Port, and Password of your Sandbox.
# graph = Graph("<Bolt URL>", auth=("neo4j", "<Password>"))
graph = Graph("bolt://18.207.236.58:35753", auth=("neo4j", "<password>"))
- 환경 설정이 완료되었습니다. 이제
graph에는 우리가 접속한 DB의 Graph가 연결되어 있는 것이죠. - 우선, 이 Graph의 schema부터 파악해봅시다. query를
graph에 넘긴 다음,.data()로 값을 가져옵니다.- 반환되는 값은
list이며, 크기는 1인데, 이는 아마도, 반환되는 그래프가 1개이기 때문이 아닐까 싶습니다.
- 반환되는 값은
db_schema = graph.run("CALL db.schema()").data()
print(type(db_schema))
print(len(db_schema))
print('=='*20)
for key, value in db_schema[0].items():
print(key)
print('--'*20)
for node_or_rel in value:
print(node_or_rel)
print('--'*20)
print('=='*20)
- 위 코드를 수행한 결과를 보면, 아래와 같이 구성되어 있음을 알 수 있습니다.
<class 'list'>
1
========================================
nodes
----------------------------------------
(_-39:Venue {constraints: ['CONSTRAINT ON ( venue:Venue ) ASSERT venue.name IS UNIQUE'], indexes: [], name: 'Venue'})
(_-37:Article {constraints: ['CONSTRAINT ON ( article:Article ) ASSERT article.index IS UNIQUE'], indexes: [], name: 'Article'})
(_-38:Author {constraints: ['CONSTRAINT ON ( author:Author ) ASSERT author.name IS UNIQUE'], indexes: [], name: 'Author'})
----------------------------------------
relationships
----------------------------------------
(Article)-[:VENUE {}]->(Venue)
(Article)-[:AUTHOR {}]->(Author)
(Article)-[:CITED {}]->(Article)
----------------------------------------
========================================
- 이제, schema가 아니라, 데이터를 가져와 봅시다. 각 label별로 노드가 몇 개나 있는지를 한번 확인해볼게요.
import functools
"""
- query의 결과를 가져올 때, 반드시 .data()로 가져와야 하는 것은 아니다.
- .to_series, to_dataframes등의 방법도 있으나,
- 나는 python native 구조인 dictionary, list로 가져오는 것을 선호하여 다음과 같이 가져온 후에 후처리하는 식으로 처리하였다.
"""
labels = graph.run("CALL db.labels()").data()
print(labels)
print("--"*20)
labels = functools.reduce(lambda x, y: x+y, [list(k_v.values()) for k_v in labels])
print(labels)
result_dict = {}
for label in labels:
query = f"""
MATCH (:{label})
RETURN count(*) as count
"""
query_data = graph.run(query).data()
result_dict[label] = query_data[0]['count']
print("--"*20)
for label, count in result_dict.items():
print(f"{label:10s} : {count:7d}")
print("--"*20)
- 실행 결과는 다음과 같다.
[{'label': 'Article'}, {'label': 'Author'}, {'label': 'Venue'}]
----------------------------------------
['Article', 'Author', 'Venue']
----------------------------------------
Article : 51956
Author : 80299
Venue : 4
- 이제는 어떤 relationship들이 있는지를 한번 알아볼게요.
rel_labels = graph.run("CALL db.relationshipTypes()").data()
rel_labels = [k_v['relationshipType'] for k_v in rel_labels]
rel_labels
rel_result_dict = {}
for rel_label in rel_labels:
query = f"""
MATCH ()-[rel:{rel_label}]->()
RETURN count(*) as count
"""
count = graph.run(query).data()
rel_result_dict[rel_label] = count[0]['count']
print(rel_result_dict)
- 결과는 다음과 같습니다.
{'VENUE': 51956, 'AUTHOR': 140575, 'CITED': 28706}
wrap-up
raw-code
!pip install py2neo==4.1.3 pandas matplotlib sklearn
from py2neo import Graph
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
pd.set_option('display.float_format', lambda x: '%.3f' % x)
# Change the line of code below to use the IP Address, Bolt Port, and Password of your Sandbox.
# graph = Graph("<Bolt URL>", auth=("neo4j", "<Password>"))
graph = Graph("bolt://18.207.236.58:35753", auth=("neo4j", "<password>"))
## set environment done.
## db schema check
db_schema = graph.run("CALL db.schema()").data()
print(type(db_schema))
print(len(db_schema))
print('=='*20)
for key, value in db_schema[0].items():
print(key)
print('--'*20)
for node_or_rel in value:
print(node_or_rel)
print('--'*20)
print('=='*20)
## nodes
import functools
"""
- query의 결과를 가져올 때, 반드시 .data()로 가져와야 하는 것은 아니다.
- .to_series, to_dataframes등의 방법도 있으나,
- 나는 python native 구조인 dictionary, list로 가져오는 것을 선호하여 다음과 같이 가져온 후에 후처리하는 식으로 처리하였다.
"""
labels = graph.run("CALL db.labels()").data()
print(labels)
print("--"*20)
labels = functools.reduce(lambda x, y: x+y, [list(k_v.values()) for k_v in labels])
print(labels)
result_dict = {}
for label in labels:
query = f"""
MATCH (:{label})
RETURN count(*) as count
"""
query_data = graph.run(query).data()
result_dict[label] = query_data[0]['count']
print("--"*20)
for label, count in result_dict.items():
print(f"{label:10s} : {count:7d}")
print("--"*20)
## relationship
rel_labels = graph.run("CALL db.relationshipTypes()").data()
rel_labels = [k_v['relationshipType'] for k_v in rel_labels]
rel_labels
rel_result_dict = {}
for rel_label in rel_labels:
query = f"""
MATCH ()-[rel:{rel_label}]->()
RETURN count(*) as count
"""
count = graph.run(query).data()
rel_result_dict[rel_label] = count[0]['count']
print(rel_result_dict)
GitHub에서 이 글의 마크다운 원본 보기 & 수정 제안src/content/posts/2020-01-06-neo4j_ds_part2