N:::만지작 거리기
N_16. 파이썬으로 RDF 생성하기
joyHong
2021. 9. 8. 22:59
https://github.com/joyhong85/rdf_python 에서 확인할 수 있습니다.
RDF 형태의 데이터를 생성한다.
작성자 : 허홍수
e-mail : su4620@gmail.com
blog : http://joyhong.tistory.com
e-mail : su4620@gmail.com
blog : http://joyhong.tistory.com
CSV 형태의 데이터를 RDF 형태로 변환하기¶
원본데이터
공공데이터포털 - 건강보험심사평가원_전국 병의원 및 약국 현황
https://www.data.go.kr/data/15051059/fileData.do사용하는 라이브러리
RDFLib
(https://rdflib.readthedocs.io/en/6.0.0/index.html)
데이터 확인¶
원본 형태의 데이터를 바로 확인할 수도 있지만 여기서는 판다스를 활용하여 null값 등을 확인하도록 한다.
- 전처리가 잘 되어 있다면 1.1, 1.2 과정을 스킵하고 바로 2번 과정을 수행해도 된다.
In [1]:
import pandas as pd
import numpy as np
df = pd.read_csv('./hospital.csv', header=2)
df.info()
In [2]:
df.count(axis=0)
Out[2]:
null 값이 있는 열 번호 찾기¶
In [3]:
series = df.isnull().sum(axis=0)
np.where((series > 0).tolist())[0]
Out[3]:
11, 12, 13, 15, 16 번 value 값을 처리할 때 null 체크를 해야한다.
CSV 파일 그대로 읽어 RDF로 변환¶
그래프 생성¶
In [4]:
from rdflib import Graph, Literal, RDF, URIRef, Namespace
from rdflib.namespace import FOAF, XSD, RDF, RDFS, SKOS, DCTERMS
from tqdm import tqdm
# 그래프 생성
g = Graph()
# namespace 바인딩
RS = Namespace('http://joyhong.tistory.com/resource/')
ONT = Namespace('http://joyhong.tistory.com/ontology/')
SCHEMA = Namespace("http://schema.org/")
g.bind("rs", RS)
g.bind("ont", ONT)
g.bind("schema", SCHEMA)
g.bind("foaf", FOAF)
g.bind("skos", SKOS)
g.bind("dcterms", DCTERMS)
URI 유효성 검사 모듈 생성¶
RDFLib 에서 URIRef()로 리소스 생성시 유효하지 않은 스트링으로 생성시 시리얼라이즈 되지 않기 때문에 검사를 수행하여야 한다.
In [5]:
_invalid_uri_chars = '<>" {}|\\^`'
def _is_valid_uri(uri):
for c in _invalid_uri_chars:
if c in uri:
return False
return True
매핑 설정¶
In [6]:
from dateutil.parser import parse
def makeTripleForHospital(row):
subject = URIRef(RS+'h_'+row[0])
geo = URIRef(RS+'geo_h'+row[0])
g.add((subject, RDF.type, URIRef(SCHEMA+'Hospital')))
g.add((subject, FOAF.page, URIRef('http://www.hira.or.kr/re/diag/getDiagAmtInfo.do?ykiho='+row[1])))
g.add((subject, DCTERMS.identifier, Literal(row[1])))
g.add((subject, RDFS.label, Literal(row[2])))
if row[11] != '':
g.add((subject, URIRef(SCHEMA+'telephone'), Literal(row[11])))
if row[12] != '' and row[12] != 'http://' and _is_valid_uri(row[12]):
if not row[12].startswith('http'):
g.add((subject, FOAF.homepage, URIRef('http://'+row[12])))
else:
g.add((subject, FOAF.homepage, URIRef(row[12])))
if row[13] != '':
if '-00-' in row[13]:
row[13] = row[13].replace('-00-','-01-')
g.add((subject, URIRef(ONT+'openedDate'), Literal(parse(row[13]).date())))
g.add((subject, URIRef(ONT+'totalNumberOfDoctor'), Literal(row[14])))
#위치
g.add((subject, URIRef(SCHEMA+'geo'), geo))
g.add((geo, URIRef(SCHEMA+'postalCode'), Literal(row[9])))
g.add((geo, URIRef(SCHEMA+'address'), Literal(row[10])))
if row[15] != '':
g.add((geo, URIRef(SCHEMA+'longitude'), Literal(row[15])))
if row[16] != '':
g.add((geo, URIRef(SCHEMA+'latitude'), Literal(row[16])))
#종별 구분
g.add((subject, DCTERMS.subject, URIRef(RS+'cat_'+row[3])))
g.add((URIRef(RS+'cat_'+row[3]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'cat_'+row[3]), RDFS.label, Literal(row[4])))
g.add((URIRef(RS+'cat_'+row[3]), SKOS.prefLabel, Literal(row[4], lang='ko')))
g.add((URIRef(RS+'cat_'+row[3]), DCTERMS.identifier, Literal(row[3])))
#시도
g.add((subject, DCTERMS.subject, URIRef(RS+'rg_'+row[5])))
g.add((URIRef(RS+'rg_'+row[5]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'rg_'+row[5]), RDFS.label, Literal(row[6])))
g.add((URIRef(RS+'rg_'+row[5]), SKOS.prefLabel, Literal(row[6], lang='ko')))
g.add((URIRef(RS+'rg_'+row[5]), DCTERMS.identifier, Literal(row[5])))
g.add((URIRef(RS+'rg_'+row[5]), SKOS.narrower, URIRef(RS+'rg_'+row[7])))
g.add((URIRef(RS+'rg_'+row[7]), SKOS.broader, URIRef(RS+'rg_'+row[5])))
#시군구
g.add((subject, DCTERMS.subject, URIRef(RS+'rg_'+row[7])))
g.add((URIRef(RS+'rg_'+row[7]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'rg_'+row[7]), RDFS.label, Literal(row[8])))
g.add((URIRef(RS+'rg_'+row[7]), SKOS.prefLabel, Literal(row[8], lang='ko')))
g.add((URIRef(RS+'rg_'+row[7]), DCTERMS.identifier, Literal(row[7])))
csv 파일 로딩 및 변환 실행¶
In [7]:
import csv
data = open('./hospital.csv', 'r', encoding='utf-8-sig')
rows = csv.reader(data, delimiter=',')
for row in tqdm(list(rows)[3:]):
makeTripleForHospital(row)
data.close()
파일로 저장¶
In [8]:
g.serialize(destination='./hospital.ttl', format='turtle')
print("Finished..")
Pandas DataFrame 으로부터 RDF 변환¶
데이터 확인을 위해 dataframe으로 불러 들인 데이터를 활용하여 변환하도록 한다.
데이터 전처리¶
In [9]:
df
Out[9]:
In [10]:
df.info()
In [11]:
df= df.astype(str)
In [12]:
df.info()
In [13]:
# 컬렴명 변경
df.columns = list(range(len(df.columns)))
In [14]:
# 문자형으로 강제변환하였기에 NaN 값이 nan으로 변경되어 이를 ''로 처리
df.replace(['None', 'nan'], '', inplace=True)
df
Out[14]:
In [15]:
# 13번 컬럼을 date형으로 변환
from dateutil.parser import parse
df[13] = df[13].map(lambda x: parse(x.replace('-00-','-01-')).date() if x!='' else x)
In [16]:
df
Out[16]:
그래프 생성¶
In [17]:
from rdflib import Graph, Literal, RDF, URIRef, Namespace
from rdflib.namespace import FOAF, XSD, RDF, RDFS, SKOS, DCTERMS
from tqdm import tqdm
# 그래프 생성
g = Graph()
# namespace 바인딩
RS = Namespace('http://joyhong.tistory.com/resource/')
ONT = Namespace('http://joyhong.tistory.com/ontology/')
SCHEMA = Namespace("http://schema.org/")
g.bind("rs", RS)
g.bind("ont", ONT)
g.bind("schema", SCHEMA)
g.bind("foaf", FOAF)
g.bind("skos", SKOS)
g.bind("dcterms", DCTERMS)
URI 유효성 검사 모듈 생성¶
In [18]:
_invalid_uri_chars = '<>" {}|\\^`'
def _is_valid_uri(uri):
for c in _invalid_uri_chars:
if c in uri:
return False
return True
매핑설정(pandas dataframe 활용)¶
In [19]:
def makeTripleForHospitalByDataFrame(row):
subject = URIRef(RS+'h_'+row[0])
geo = URIRef(RS+'geo_h'+row[0])
g.add((subject, RDF.type, URIRef(SCHEMA+'Hospital')))
g.add((subject, FOAF.page, URIRef('http://www.hira.or.kr/re/diag/getDiagAmtInfo.do?ykiho='+row[1])))
g.add((subject, DCTERMS.identifier, Literal(row[1])))
g.add((subject, RDFS.label, Literal(row[2])))
if row[11] != '':
g.add((subject, URIRef(SCHEMA+'telephone'), Literal(row[11])))
if row[12] != '' and row[12] != 'http://' and _is_valid_uri(row[12]):
if not row[12].startswith('http'):
g.add((subject, FOAF.homepage, URIRef('http://'+row[12])))
else:
g.add((subject, FOAF.homepage, URIRef(row[12])))
if row[13] != '':
g.add((subject, URIRef(ONT+'openedDate'), Literal(row[13])))
g.add((subject, URIRef(ONT+'totalNumberOfDoctor'), Literal(row[14])))
#위치
g.add((subject, URIRef(SCHEMA+'geo'), geo))
g.add((geo, URIRef(SCHEMA+'postalCode'), Literal(row[9])))
g.add((geo, URIRef(SCHEMA+'address'), Literal(row[10])))
if row[15] != '':
g.add((geo, URIRef(SCHEMA+'longitude'), Literal(row[15])))
if row[16] != '':
g.add((geo, URIRef(SCHEMA+'latitude'), Literal(row[16])))
#종별 구분
g.add((subject, DCTERMS.subject, URIRef(RS+'cat_'+row[3])))
g.add((URIRef(RS+'cat_'+row[3]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'cat_'+row[3]), RDFS.label, Literal(row[4])))
g.add((URIRef(RS+'cat_'+row[3]), SKOS.prefLabel, Literal(row[4], lang='ko')))
g.add((URIRef(RS+'cat_'+row[3]), DCTERMS.identifier, Literal(row[3])))
#시도
g.add((subject, DCTERMS.subject, URIRef(RS+'rg_'+row[5])))
g.add((URIRef(RS+'rg_'+row[5]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'rg_'+row[5]), RDFS.label, Literal(row[6])))
g.add((URIRef(RS+'rg_'+row[5]), SKOS.prefLabel, Literal(row[6], lang='ko')))
g.add((URIRef(RS+'rg_'+row[5]), DCTERMS.identifier, Literal(row[5])))
g.add((URIRef(RS+'rg_'+row[5]), SKOS.narrower, URIRef(RS+'rg_'+row[7])))
g.add((URIRef(RS+'rg_'+row[7]), SKOS.broader, URIRef(RS+'rg_'+row[5])))
#시군구
g.add((subject, DCTERMS.subject, URIRef(RS+'rg_'+row[7])))
g.add((URIRef(RS+'rg_'+row[7]), RDF.type, URIRef(SKOS.Concept)))
g.add((URIRef(RS+'rg_'+row[7]), RDFS.label, Literal(row[8])))
g.add((URIRef(RS+'rg_'+row[7]), SKOS.prefLabel, Literal(row[8], lang='ko')))
g.add((URIRef(RS+'rg_'+row[7]), DCTERMS.identifier, Literal(row[7])))
변환 실행¶
In [20]:
for value in tqdm(df.values):
# print(value[0] ,'\t', value[1])
makeTripleForHospitalByDataFrame(value)
파일로 저장¶
In [21]:
g.serialize(destination='./hospital_df.ttl', format='turtle')
print("Finished..")
Finish