728x90
반응형
오늘은 2014년 8월 4일이에요. 대부분의 사람들에게는 별 의미 없는 날짜일 수 있지만, 유럽, 특히 독일, 벨기에, 영국, 프랑스, 그리고 중부/서부 유럽 국가 출신이라면 더욱 그렇죠. 전 세계적으로도 꽤 의미 있는 날일 텐데요. 왜냐하면 바로 이 해가 제1차 세계대전 발발 100주년이거든요. 독일이 벨기에를 침공하며 중립을 위반했고, 영국은 독일에 전쟁을 선포했죠. 제1차 세계대전 말이에요.
저는 전쟁을 겪어본 적이 없어요. 지난 40년간 앤트워프, 벨기에에서 아주 편안하고 안전한 삶을 살았죠. 하지만 가끔씩 Westvleteren의 성 식스투스 수도원에 가서 맥주도 마시고, 플랜더스 들판을 지나갈 뻔하기도 했어요.
네, 맞아요. 양귀비 추모 상징 말이에요. 적어도 오늘은 그렇죠. 사실 꽤 많은 의미가 담겨 있죠.
이런 전쟁 기념물들은 정말 감동적인 평화주의의 상징이기도 해요. 아이들을 데리고 타인 코트에 방문한 적이 있는데, 기억해야 할 날이었어요. 2만 개의 무덤을 보는 건 정말 잊을 수 없는 경험이죠.
올봄에는 아이들과 "죽음의 참호"에도 갔었고, Diksmuide의 전쟁 기념 박물관인 이세르강 박물관에도 방문했었죠. 저를 바보라고 불러도 좋지만, 이번 여름에는 아이들을 플랑드르 들판에 데려가고 싶어요. 아이들이 충격을 받을 거라는 걸 알면서도 미뤄왔지만, 전쟁은 절대 좋은 것이 아니라는 걸 아이들에게 알려줘야 한다고 생각해요.
특히 요즘처럼 우크라이나 불안이나 가자 지구의 잔혹한 전쟁 같은 소식을 접할 때면, 전쟁이 우리에게서 그리 멀리 떨어져 있지 않다는 생각이 들어요. 우리는 과연 배울 수 있을까요?
그래서 이번 달 초, 인터넷을 돌아다니다가 우연히 Correlates of War 웹사이트를 발견했어요. 이 프로젝트는 1963년 미시간 대학의 정치학자인 J. David Singer가 시작했는데, 다양한 전쟁(또는 그가 "분쟁"이라고 부르는)을 구조화된 방식으로 기록해 왔다고 해요. 바로 이 웹사이트에서 WarGraph라는 것을 알게 되었죠. 로 탐색해볼 만한 멋진 데이터 세트 같지 않나요?

데이터 작업

물론 저도 공개적으로 사용할 수 있는 자료들을 찾아봤어요. 먼저 전쟁 데이터세트의 상관관계 (Correlates of War dataset)를 살펴봤죠. 가져와야 할 데이터는 여러 개가 있었는데요. 국가 (State) 데이터, 분쟁 (Dispute) 데이터, 그리고 흥미로운 '메타데이터'인 종교 (Religion) 데이터 (국가별), 재료 능력 (Material Capabilities) 데이터 (국가별) 였어요. 이 데이터를 가지고 작업을 시작하기 위해, 먼저 모든 데이터를 하나의 큰 Google 스프레드시트에 모았답니다. 요즘 제가 애용하는 도구죠.

전쟁 모델의 그래프

의미 있는 작업을 하려면 먼저 데이터의 그래프 모델을 만들어야 했어요. 제가 완성한 모델은 다음과 같아요.
이 모델에 대해 좀 더 자세히 설명해 드릴게요.
  • 국가 `Node`에는 흥미로운 메타데이터가 많이 있어요. CoW에서는 국가의 경제적, 군사적 능력에 대한 많은 데이터를 수집했는데요. 1800년대 초반부터의 연간 데이터가 있지만, 간단하게 하기 위해 2007년 데이터만 가져왔어요.
    • 군비 지출 (GBP 또는 USD)
    • 군인 수 (천 명)
    • 국가 역량 점수 종합 지수: 위의 6개 역량 구성요소 각각에 대한 모든 관측치를 합산하고, 각 주의 절대 구성요소를 국제 시스템의 점유율로 변환한 다음, 6개 구성요소에 대한 평균을 계산하여 얻은 측정값이에요.
  • 모델을 좀 더 시각적으로 표현하고 정규화하기 위해, 몇 가지 메타데이터를 가져와서 다음과 같이 표현했어요.
    • 다양한 종류의 결과는 하위 그래프로 표시돼요.
    • 다양한 종류의 합의도 하위 그래프로 표시되구요.
    • 다양한 사망률 수준도 하위 그래프로 나타냈어요.
    • 분쟁 (HiAct)에서 다양한 종류의 "최고 수준의 조치"도 하위 그래프로 표현했죠.
    • '가장 높은 수준의 행동'과 관련된 다양한 종류의 적대감 수준도 하위 그래프로 표시하고 라벨을 붙였답니다.
  • 그래프에서 연도별로 작업하기 위해, 타임라인이라고도 불리는 "in-graph-index"에서 연도를 서로 연결했어요. 예전에 제 맥주 그래프에서도 비슷한 작업을 했었죠.
  • 종교 데이터도 가져왔어요. 1800년대 이후의 흥미로운 데이터가 많지만, 저는 2010년의 최신 데이터만 가져왔답니다.
다양한 데이터 요소의 의미에 대해 더 자세한 설명이 필요하다면, 언제든지 코드북 (Codebook)을 참고해 주세요. 자세한 내용을 확인할 수 있을 거예요.

WarGraph를 Neo4j로 가져오기

이제 데이터를 가져올 차례죠! 여기서는 두 가지 기술을 조합해서 사용했어요.
  • 저는 스프레드시트 방식을 사용해서 메타데이터를 가져왔어요. 작은 메타데이터 세트에 대해 굳이 CSV 파일을 준비하는 건 번거로우니까요.
  • 그리고 Google 스프레드시트에서 직접 `loadcsv`를 사용해서 실제 데이터를 가져왔답니다.
가져오기 프로세스에 대한 자세한 개요는 여기서 확인할 수 있어요. 전혀 어렵지 않아요!

WarGraph 쿼리하기

데이터가 Neo4j에 들어왔으니, Neo4j 브라우저를 사용해서 간단하게 데이터를 살펴볼 수 있어요. Cypher 쿼리를 사용하면 되죠!
이 데이터세트에서 미국 (`State`)을 찾을 수 있는지 한번 살펴볼까요?
MATCH (n:State {짧은:”미국”})-[r]-()
RETURN n,r
LIMIT 10
맞는 것 같아요. 이제 '전쟁' 관련 정보를 한번 살펴볼까요? 가장 많은 분쟁에 연루된 국가를 찾아볼게요.
MATCH (n:국가)-[r:PARTICIPATES_IN]->(d:분쟁)
RETURN n.이름, count(r)
ORDER BY count(r) DESC
LIMIT 10;

흥미롭네요! 미국과 영국이 보이네요. 하지만 독일, 프랑스, 그리고 (그때까지 존재하지 않았던 나라) 이스라엘도 있어요.

그럼, 이 데이터를 좀 더 세분화해볼 수 있겠죠? 인구 규모 대비 '1인당' 분쟁이 가장 많은 국가를 한번 살펴볼게요. 쿼리는 다음과 같아요:

MATCH (n:국가)-[r:PARTICIPATES_IN]->(d:분쟁)
WHERE n.totalpop IS NOT NULL
WITH n, count(r) AS NrOfDisputes
RETURN n.name, n.totalpop, NrOfDisputes, 1.0*NrOfDisputes/n.totalpop AS DisputesPerCapita
ORDER BY DisputesPerCapita DESC
LIMIT 10

쿼리의 "1.0*"에는 약간의 트릭이 숨어있어요. 이는 Cypher가 부동 소수점 연산을 수행하기 전에 부동 소수점 연산을 수행하도록 강제하는 것이죠. 나중에 수행하면 쿼리가 제대로 실행되지 않거든요. 이 팁을 알려준 Alistair에게 감사해요!

이제 그래프 내 연도 Index를 따라 실행해서 시간 차원을 한번 살펴볼게요. 20세기 전반의 논쟁을 알아볼까요?

MATCH (y1:연도 {이름:1900})-[:PRECEDES*..51]->(y2),
(d:분쟁)-[:STARTED_IN]->(y2)
RETURN DISTINCT d.name AS Dispute, y2.name AS StartYear

아니면 종교가 전쟁과 관련이 있는지 한번 살펴볼까요? 정말 그럴까요?
분쟁에 참여한 종교 신자가 가장 많은 상위 10개 국가는 다음과 같아요:
MATCH (n:종교)<-[r:HAS_ADHERENTS]-(c:국가)
WHERE n.name <> “비종교적”
WITH c.short AS country, r.number AS nrofadherents
ORDER BY nrofadherents DESC
LIMIT 10
WITH country
MATCH (c:국가 {짧은: country})-[:PARTICIPATES_IN]->(d:분쟁)
RETURN DISTINCT c.short, c.name, count(d);
그리고 마지막으로 몇 가지 경로를 탐색할 수 있는지 살펴볼게요. 예를 들어, 미국("USA")과 이스라엘("ISR")과 같은 두 국가 간의 링크를 살펴볼 수 있어요.
MATCH (u:국가 {짧은:”USA”}), (i:국가 {짧은:”ISR”}),
p = allShortestPaths((u)-[r*]-(i))
RETURN p;
이러한 쿼리에는 세계적으로 충격적인 데이터가 있는 건 아니지만, 그래도 가지고 놀 수 있는 매우 흥미로운 내용들이 있죠. 모든 쿼리는 여기서 요점을 확인해보세요.
평소와 마찬가지로 여러분의 의견을 환영해요. 이것이 도움이 되기를 바라면서, 우리 모두 전쟁이 아닌 그래프를 만들 수 있기를 바랍니다!

에이치시스템즈LogTree는 Neo4j 기반 GraphRAG 플랫폼으로, 데이터를 자동으로 지식그래프화하고 자연어 질의로 즉시 답을 제공합니다.

👉 에이치시스템즈 홈페이지

728x90
반응형

+ Recent posts