< 왼쪽부터 철학과 13 추선식, 경영학전공10 정동철, 철학과 10 이현성>
감사합니다. 모두 수고하셨습니다!
참고 기사 링크
http://news.naver.com/main/read.nhn?mode=LSD&mid=sec&oid=001&aid=0009323651&sid1=001
참가 공모전 관련 링크
https://bigdata.seoul.go.kr/cntst/selectCntst.do?r_id=P600&cntst_seq=33
< 왼쪽부터 철학과 13 추선식, 경영학전공10 정동철, 철학과 10 이현성>
참고 기사 링크
http://news.naver.com/main/read.nhn?mode=LSD&mid=sec&oid=001&aid=0009323651&sid1=001
참가 공모전 관련 링크
https://bigdata.seoul.go.kr/cntst/selectCntst.do?r_id=P600&cntst_seq=33
안녕하세요. 외대 철학과 스터디 그룹 LOPES의 추선식 입니다.
데이터 분석을 공부하면서 느낀 점은 학습에 맞게 구성된 데이터가 적고 이런 형태의 자료를 현실적으로 찾아보기 어렵다는 점입니다. 그래서 대중적으로도 인기가 있는 한국의 프로야구 자료로 데이터 분석을 공부해보기로 했습니다.
자료를 모으기 위해 KBO 홈페이지에 등록된 2010년도부터 2016년도까지의 야구 자료를 모으고 보기 좋게 정리하기 시작했습니다.
저희가 수집하고 가공한 자료는 약 7년 치의 한국 프로야구 데이터가 있습니다. 이 데이터는 점수와 홈 & 원정팀, 시즌정보 등을 구분하여 경기정보를 쉽게 확인해 볼 수 있습니다. 또한 7년치의 데이터로 다양한 기록들을 시각화해볼 수 있습니다. 아래 링크로 가시면 자료를 받으실 수 있습니다.
##응용사례
kbo_total$경기결과 <- ifelse(kbo_total$홈팀점수>kbo_total$원정팀점수,"홈승",ifelse(kbo_total$홈팀점수<kbo_total$원정팀점수,"원정승","무"))
normal_season <- subset(kbo_total,kbo_total$비고=="정규시즌")
doosan <- subset(normal_season,normal_season$홈팀=="두산"|normal_season$원정팀=="두산")
home <- aggregate(홈팀점수~홈팀+원정팀+경기결과,doosan,length)
doosan_home <- home[home$홈팀=="두산",]
away <- aggregate(원정팀점수~홈팀+원정팀+경기결과,doosan,length)
doosan_away <- away[away$원정팀=="두산",]
doosan_home_win <- doosan_home[doosan_home$경기결과=="홈승",]
doosan_away_win <- doosan_away[doosan_away$경기결과=="원정승",]
doosan_home_win <- doosan_home_win[,-1]
doosan_away_win <- doosan_away_win[,-2]
colnames(doosan_home_win) <- c("상대팀","경기결과","승리횟수")
colnames(doosan_away_win) <- c("상대팀","경기결과","승리횟수")
doosan_win <- rbind(doosan_home_win,doosan_away_win)
install.packages("ggplot2")
library(ggplot2)
p <- ggplot(doosan_win,aes(x=상대팀,y=승리횟수,fill= 경기결과))+
geom_bar(stat="identity",colour = "black")+
scale_x_discrete(limits=c("한화","KIA", "SK", "LG", "넥센", "삼성", "롯데", "NC","kt" ))+
theme(axis.text.x= element_text(angle=90, hjust=1))
p+labs(title="두산상대전적그래프")
ps. R에서 플롯을 그렸을 때 한글이 깨질 수 있습니다.
안녕하세요. 외대 철학과 스터디그룹 LOPES의 추선식입니다. 이번에는 오직 R만을 이용하여 와인데이터를 분석하고 군집화 해보겠습니다. 앞서 같은 스터디 그룹인 배서연 학우가 쓴 “와인 구매 데이터를 R로 분석하여 Gephi로 군집화, 시각화 하기“에서 Gephi를 이용해서 군집화 했었습니다. 그렇지만 저는 이 글에서 Gephi 없이 R을 이용해서 와인데이터를 군집화하고 그 결과를 서로 비교해보겠습니다.
원본 와인데이터는 아래 링크로 가서 chapter2를 클릭하여 자료를 다운받으시고, WineKMC 파일 안의 Matrix시트를 R에서 지정한 디렉토리 폴더에 csv형식으로 내보내 사용하시거나 혹은 chapter10를 클릭하여 자료를 다운 받은 다음 그 안의 WineKMC.csv 파일을 사용하시면 됩니다.
Wiley: Data Smart: Using Data Science to Transform Information into Insight – John W. Foreman
와인데이터의 군집화를 위해 먼저 해야 할 작업은 코사인 유사도 행렬을 만드는 것입니다. 코사인유사도는 각 구매자 간 구매데이터의 유사성에 비중을 둔 거리 계산법으로 이 예제에서는 와인 데이터의 구매 유사성을 토대로 구매자들 간의 거리를 측정할 수 있습니다. 코사인 유사도는 0과 1 사이의 값을 가지는데 코사인 유사도로 나타난 구매자들 간의 거리는 구매 데이터의 유사성 정도에 따라서 유사성이 강하면 그값이 1에 가까우며 유사성이 약할수록 0에 가까워지게 됩니다. 즉 이것으로 구매 데이터를 사람과 사람 간의 관계로 바꾸어 이후 군집화의 초석을 쌓는 것입니다.
다음 작업으로는 컷오프를 하는 것입니다. 이것을 하는 이유로는 와인 구매 데이터의 유사성을 통해 고객들 간의 모든 관계를 파악할 수 있지만, 그 관계가 너무 많아 데이터가 복잡하기 때문입니다. 따라서 중요하지 않은 관계는 지우고 강한 관계가 있는 데이터만 남기는 과정이 바로 컷오프입니다. 여기서 중요한 관계만 남기는 방법으로는 앞선 글애서와 똑같이 유사성이 강한 상위 20%만 남기는 것 입니다.
군집화를 위해서는 Igraph 패키지가 필요합니다. 이 패키지가 기존의 Gephi의 기능인 네트워크 그래프를 다루는 일을 수행하기 때문입니다. Igraph 패키지는 컷오프 한 와인데이터 구매자들 간의 관계를 보고 군집화해주는 역할을 합니다. 이것을 이용해 R로 시각화를 해볼 순 있지만, 아직 모듈성 최적화가 되지 않은 상태이기에 군집 간 구분이 힘들기에 모듈성 최적화의 과정을 필요로 합니다.
따라서 Igraph로 군집화 된 것의 모듈성 최적화를 위해walktrap.community 나 multilevel.community 함수를 사용해야 합니다. 각 함수를 사용해서 군집의 최적화 즉 모듈성을 최적화하게 되면 모듈성은 walktrap.community 방식이 더 극대화되지만 두 방식의 모듈성 값 차이는 미세합니다. 두 가지 방식에 대한 더 자세한 설명을 알고 싶다면 이 두 기능이 내포된 Igraph 패키지와 각 함수에 대하여 Inside-r 링크에서 직접 찾아보실 수 있습니다.
먼저 코사인 유사도를 구하는 데 필요한 패키지인 lsa를 다운 받습니다. 그 후 군집화에 사용할 패키지인 Igraph패키지를 다운 받습니다. 이제 와인 데이터를 읽어오고 코사인 유사도 그래프를 만들기 위한 작업(행,렬 변환 과 NA값 변환)을 합니다. 기본 작업이 완료되면 cosine 함수를 이용하여 코사인 유사도를 만들고 for 구문으로 대각선의 값을 0으로 바꿔 줍니다. 코사인 유사도 그래프가 완성되었으면 그것에 대한 컷오프 과정을 진행합니다. 컷오프 과정은 앞서 배서연 학우가 올린 글에서 설명되었기에 넘어가겠습니다.
이제 Igraph 패키지를 사용하게 되는데 가장 처음 할 일은 인접성 행렬 그래프를 만드는 것입니다. 인접성 행렬 그래프는 네트워크 데이터를 나타내는 그래프 중 하나로서 인접성 행렬을 통해 네트워크 데이터가 드러내는 관계를 보여주는 그래프입니다. 간단히 말해서 네트워크 데이터를 노드와 에지로 표현한 그래프라 할 수 있습니다.
코드에서 graph.adjacency 함수가 인접 행렬 그래프를 만들어 줍니다. graph.adjacency 함수는 방향성과 가중치에 따라서 인접 행렬을 다양하게 해석할 수 있기에 여기서는 가중치는 주지 않고 무방향성(undirected)을 설정해 줍니다.
다음으로 Igraph의 walktrap.community 함수를 사용합니다. 이 함수는 무작위 걸음(random walk)을 통해 그 걸음 길이가 짧을수록 같은 군집으로 묶는 역할을 합니다. 따라서 각 사람 간의 군집이 형성되는데 이것을 membership 함수를 통해 확인할 수 있습니다. 이어서 이 군집화 결과를 as.integer 함수를 통해 정수형 벡터로 바꾸어 줍니다.
남은 과정은 이제 기존의 정보들을 덧붙이는 과정입니다. 이것은 기존 와인에 대한 정보를 붙이고 하나의 데이터 벡터로 바꾸는 과정입니다. 이 과정을 진행하고 나면 최종 결과인 각 군집별 구매한 와인의 특성을 파악할 수 있습니다.
비교하는 것에 앞서 결과를 추측해보자면 같은 군집화 알고리즘을 사용한다면 군집화의 결과가 같을 것이고 그렇지 않으면 차이가 있을 것이라 생각되어집니다.
군집화의 결과를 R로 비교해 보기 위해서는 19번째 코드까지 진행하고 그 이후 20번째 코드부터 다르게 진행합니다. 우선 walktrap.community 함수부터 사용해서 사람과 할당 군집목록을 만듭니다. 다음의 코드를 이용합니다.
winedata.wtc <- walktrap.community(winedata.graph)
membership(winedata.wtc)
walktrap.membership <- print(membership(winedata.wtc))
walktrap.community <- data.frame(walktrap.membership)
위 코드로 walktrap 함수의 구매자 별 군집 목록을 만듭니다. 같은 방식으로 multilevel.community 함수를 적용해 구매자 별 군집 목록을 완성합니다. 다음으로는 맨 위 링크에서 Gephi 로 작업한 파일을 Gephi 내부에서 csv 파일 형식으로 내보냅니다. Gephi에서 파일을 내보내는 방법은 gephi의 Data laboratory 섹션에서 Export table을 클릭하여 내보낼 수 있습니다. csv 형식으로 내보낸 파일을 R에서 읽어옵니다. R에서 csv 파일을 읽는 방법은 read.csv 함수를 사용하면 됩니다. 다음과 같이 gephi <- read.csv("CommunitiesGephi.csv") 사용합니다. R에서 불러온 다음엔 Gephi의 군집데이터를 이름을 토대로 다시 정렬해줍니다. 다음의 코드로 정렬할 수 있습니다.
library(plyr)
arrange(gephi,label)
gephi.community <- arrange(gephi,label)
이제 3개 방식의 군집 목록을 R에서 한 번에 비교해보도록 하겠습니다. 3개 방식을 한 번에 보는 방법은 cbind()함수를 사용하여 하나의 목록으로 합칠 수 있습니다.
walktrap.class <- walktrap.community$walktrap.membership
comparison.class <- cbind(gephi.community,walktrap.class)
multilevel.class <- multilevel.community$multilevel.membership
comparison.class <- cbind(comparison.class,multilevel.class)
위의 코드를 통해 하나의 목록으로 만들었다면 arrange() 함수를 통해서 방식별 정렬을 하여 비교할 수 있습니다. 예를 들면 arrange(comparison.class,modularity_class) 와 같이 Gephi의 군집을 기준으로 다른 방식과 비교한 것을 볼 수 있습니다.
3가지 방식의 군집화를 비교해본 결과 처음에는 모듈성이 거의 유사하기에 Gephi와 Igraph의 방식들의 군집화 결과가 서로 같을 것으로 생각했지만, 정렬을 통해 비교해본 결과 분명히 다르게 군집화가 이루어졌음을 확인할 수 있었습니다. 같은 자료, 같은 모듈성이지만 군집화 방식 도구에 따라 군집의 결과가 다르게 나누어진다는 사실에 놀랐습니다. 읽어주셔서 감사합니다
안녕하세요~ LOPES의 배서연 입니다. 저번에 올린 글이 반응이 나쁘지 않아 용기 내어 올려 봅니다. LOPES 스터디 그룹에서도 각자 R을 이용하여 정리한 내용을 공유 차원에서 올릴 예정입니다. 어설프더라도 예쁘게 봐 주세요~^^
이번에는 『DATA SMART』 3장에 나오는 나이브 베이즈(Naive Bayes)를 이용한 트윗 분류하기를 R로 구현해봤습니다. 저희 팀에서는 R로 구현하는 방법을 독자적으로 해결하고자 하다가 다음과 같은 곳을 발견하여 R로 쉽게 구현할 수 있었습니다. 나중에 나오는 설명과 코드 또한 아래 링크에서 많은 부분 빚졌음을 밝혀둡니다. 그리고 두 번째 링크는 동일한 내용을 다르게 해결하는 곳이 있어 추가합니다.
Tweet Bayes Classification: Excel and R : Salem Marafi
Data Smart, Ch3, Classifying Tweets using Naive Bayes – Working Analytics
R로 풀어볼 나이브 베이즈(Naïve Bayes) 모델은 소위 ‘지도 학습법(supervised learning)’에 속합니다. 주로 스팸 메일을 구분할 때 사용합니다. 스팸 메일에서 자주 등장하는 단어들의 확률 값을 통해 다른 메일이 스팸 메일인지 아닌지를 판단할 수 있는 방법입니다.
『DATA SMART』 3장에서는 나이브 베이즈(Naïve Bayes) 모델을 이용해 트위터에서 맨드릴(Mandrill)이라는 단어가 언급되는 경우, 새로 출시한 앱(app)인 맨드릴Mandrill에 대한 트윗인지 아니면 관련없는 것들을 언급하는 것인지 설명하고 있습니다. 진행 과정은 다음과 같습니다.
새로 출시한 ‘맨드릴(Mandrill)’이라는 서비스를 이야기하는 트윗은 (App)이라고 표기하고 다른 것들은 이야기하는 트윗은 (Other)으로 표기하겠습니다. ‘맨드릴(Mandrill)’이라는 단어를 언급하는 트윗을 조사했더니, 예를 들어 그 트윗에는 단어1, 단어2, 단어3 등등으로 구성되었다고 생각해 봅시다. 이때 맨드릴 서비스를 이야기하고 있는 150개의 트윗들을 가지고 있는 문장 안의 단어들을 다 조사할 수 있습니다. 이때 각각의 맨드릴 서비스를 이야기하고 있는 각각의 트윗이 단어1, 단어2, 단어3 등등을 가지고 있는 확률과 다른 것을 이야기하고 있는 트윗이 단어 2, 단어 4, 단어 6 등등을 가질 확률은 다음과 같습니다.
P(App | 단어 1, 단어 3, 단어 5, …)
P(Other | 단어 2, 단어 4, 단어 6, …)
이 확률 조건표를 가지고 후에 테스트할 트윗이 앱에 대해 이야기하는 것인지 아니면 다른 것을 이야기하고 있는지 확인할 수 있습니다. 테스트 할 트윗 단어들이 앱에 관련될 확률과 아니면 다른 것에 관련될 확률을 비교해 가장 확률이 높은 클래스로 분류시키는 것이 바로 최대 후험 규칙(MAP rule, Maxium A Posteriori rule)입니다. 이렇게 만들기 위해서는 베이즈 규칙을 적용하되 바보같이 각 단어들이 문맥과 상관없이 독립적인 사건이라고 가정하여 확률을 구합니다. 이를 나이브 베이즈 규칙이라고 합니다. 예를 들어 감기와 콧물이 문맥 상 전혀 관계없는 단어가 아니더라도 일단 둘 다 독립적인 사건으로 가정하면 다음과 같이 규칙을 쓸 수 있습니다.
P(스팸메일|감기, 콧물 ) = P(감기|스팸메일) * P(콧물|스팸메일)
이를 활용하여 새로 테스트할 트윗의 각 단어들(단어1′, 단어2′, 단어3’…)이 앱에 관련된 것인지 아닌 지에 대해 비교할 확률은 다음과 같이 구할 수 있습니다. 이 두 개의 값 중 더 큰 값에 따라 클래스를 분류할 수 있습니다.
P(App | 단어1′ , 단어2′, 단어3′ …)
= P(App) * (단어1’|App) * P(단어2’|App) * P(단어3’|App)…P(Other |단어1′ , 단어2′, 단어3′ …)
=P(Other) * 단어1’|Other) * P(단어2’|Other) * P(단어3’|Other)…
재밌게도 나이브 베이즈 모델은 대충 부정확한 가정을 세워도 후에 어느 것이 더 큰 확률이냐를 비교하기 때문에 이상하지만 비교적 정확한 모델이라고 평가 받고 있습니다. 그럼 앞의 과정에 따라 R 로 예제 트윗을 분류해봅시다.
『DATA SMART』 3장에서는 엑셀로 작업하고 있지만, 우리는 R에서 작업을 하기 때문에 엑셀 파일에 들어 있는 자료를 CSV 형식 파일로 바꿔서 가져오겠습니다. 이 자료는 맨드릴(Mandrill) 앱(App)에 관련된 트윗과 관련되지 않은(Other) 트윗, 각각 150개로 되어 있습니다. 사용환경에 따라 아래 링크에서 다운 받으시면 됩니다. Mandrill.csv, Other.csv, Test.csv 이렇게 파일 3개가 있을겁니다.
윈도우 사용자분들은 아래 링크에서 다운 받을 수 있습니다.
https://www.dropbox.com/sh/1k3df26mbwwbtcn/AACnrKwvjg6GGtriRAWXWZ-6a?dl=0
맥 사용자분들은 아래 링크에서 다운 받을 수 있습니다.
https://drive.google.com/file/d/0B7Ov50qkZFF5NnJHNzZLalYtcVU/view
혹여 파일에 깨져서 실행이 안되는 분들은 책 홈페이지인 Wiley: Data Smart: Using Data Science to Transform Information into Insight – John W. Foreman에서 Chapter 3 파일을 다운 받으시면 됩니다. 아니면 아래 여기, ch03.zip을 눌러서 다운 받으시면 됩니다. 받으신 파일을 압축을 푼 다음 엑셀에서 열어서 csv 파일로 바꿔주세요. 위의 파일을 다운 받으시면 그 안에 AboutMandrillApp / AboutOther / AppTokens 시트에 A열 만 떼내 각각 Mandrill.csv / Other.csv / Test.csv 파일로 저장한 뒤 진행하면 됩니다 ^^
가져온 파일은 단순하게 각각 150열로 되어 있는, 즉 각각 150개 트윗으로 되어 있는 자료입니다. 앞에서도 이야기했지만, 우리는 각각의 트윗이 가지고 있는 단어를 가지고 분석할 예정입니다. 따라서 우리가 가지고 있는 모든 트윗에서 단어를 다 분리해야 합니다. 『DATA SMART』에서는 엑셀을 이용하여 조금은 복잡하게 단어로 분리하고 있습니다. 그러나 R에서는 ‘tm’이라는 패키지를 이용해서 엑셀에서 하는 것보다는 쉽게 분리할 수 있습니다. 우선 단어로 쪼개기 전에 트윗이 가지고 있는 모든 단어를 소문자로 바꾸고 문장 부호를 빈칸으로 교체합니다. 아래 코드에서는 replacePunctuation()이라는 함수를 만들어서 대문자를 소문자로 만들고, 문장 부호, “.”, “: ”, “?”, “!”, “;”, “,”을 모두 빈칸으로 바꿉니다. 이 함수 안에서는 tolower()는 대문자를 소문자로, gsub()는 문장 부호를 빈칸으로 바꿔주는 역할을 합니다. 이렇게 하는 이유는 ~ 그 다음 빈칸을 기준으로 단어들을 분류 시켜 하나의 matrix로 만들어줍니다.
컷 오프하는 방식은 데이터를 다룰 때 언제나 중요하게 다뤄지는 것 같습니다 ^^. 여기서는 글자 수 3개 이하의 토큰들을 다 컷오프시킵니다. 단순하게 말하자면 영어 단어 중 3글자 아래는 모두 지웁니다. 영어 단어 중 3글자보다 작은 단어는 통상적으로 의미가 없는 단어이기 때문입니다. 보통 이런 작업에서는 몇 개 이하의 짧은 단어를 제거하는 대신 because, instead와 같이 멈춤 단어(stop words)라고 불리는 단어들을 컷오프 하기도 합니다.
본격적으로 앱에 관련된 단어들과 아닌 단어들의 빈도를 확률로 계산하기 전에 크게 두 가지 문제를 풀어야 합니다. 첫 번째로 트윗에 거의 나오지 않은 드문 단어들을 처리 해야 하고, 두 번째로 이런 드문 단어들의 아주 작은 확률 값이 컴퓨터가 잘 처리할 수 있도록 처리해야 합니다.
앱에 관련된 트윗과 아닌 트윗에서 나오지 않은 단어가 후에 나왔다면 이 단어는 어느 쪽에 속해야할까요? 이런 단어는 나이브 베이즈 확률값을 계산할 때 전체 확률 값을 0으로 만들기 때문에 일단 이런 드문 단어는 한 번 나온 것으로 가정합니다. 그러면 정말 한 번 나타난 단어들의 확률이 불공정해지기 때문에 모든 단어들의 빈도 수에 1씩 더해줍니다. 이렇게 쭉 1씩 더해주는 방식을 가산 평활법(addictive smoothing)이라고 합니다. (다른 책에서는 라플라스 추정기라고 하기도 하더군요^^)
예컨대 확률이 0.001인 단어들이 여러 개 있다면 후에 단어들의 확률을 곱해서 클래스를 비교할 때 소숫점 수 백 개 이하의 0이 나오게 됩니다. 이 경우 컴퓨터가 제대로 처리하지 못하기 때문에 언더 플로라고 하는데 이를 해결하기 위해 0과 1 사이의 값에 대해서 자연 로그를 취합니다, 그러면 소수점 몇 백 개 이하의 수가 아니라 비교적 잘 와 닿는 음수가 되게 됩니다. 그 다음 최대 후험 비교를 수행하면 컴퓨터가 무리가 가지 않게 처리할 수 있습니다.
이제 2 가지 문제를 해결했으면 이제 R에서 트윗의 각 단어들이 App에 들어갈 확률과 그 외에 들어갈 확률을 구해줍니다.
앞에서 나이브 베이즈 모델을 이용하여 만든 분류 모델은 사실상 두 개의 조건부 확률 표를 만드는 것에 불과합니다. 이제 이 확률표라는 증거들을 가지고 새로운 트윗이 App에 관련된 트윗인지 아닌지 분류해봅시다. 테스트할 20의 트윗은 반은 App에 관련된 트윗이고 반은 상관 없는 트윗으로 이루어져 있습니다. 앞에서 한 토큰화, 가산 평활, 자연 로그 과정을 거친 뒤 이미 만들어 놓은 조건부 확률을 통해 그 단어들이 app에 관련될 확률과 그 외에 관련된 확률을 구해 비교합니다. 비교했을 때 확률 값이 더 큰 쪽으로 클래스를 분류합니다. 이제 정말 이 모델이 정확한 지 확인해 봅시다. 20개 중에 하나 빼고는 다 맞혔네요^^ R로 구현하는 코드는 다음과 같습니다. 윈도우 사용자 분들은 R코드 마지막 줄에 View(cbind(classified,tweets.test$Tweet))으로 하시면 더 멋진 결과를 보실 수 있습니다. 긴 글 읽어주셔서 감사합니다~!
#와인 구매 데이터를 R로 분석하여 Gephi로 군집화, 시각화 하기
안녕하세요. 외대 철학과 스터디 그룹, LOPES의 배서연 입니다. 저희는 겨울방학에 『DATA SMART』를 교재로 세미나를 했습니다. 생각보다 쉽지 않은 세미나였지만, 많은 것을 배울 수 있는 시간이었습니다. 이 책은 크게 두 부분으로 나눠져 있습니다. 1장부터 9장까지는 엑셀을 가지고 데이터를 분석하고 있으며, 나머지 10장에서는 앞에서 엑셀를 가지고 분석한 것을 다시 R로 구현하고 있습니다. 저희 팀은 이 책에 있는 분석들을 모두 R로 해보자는 목표를 갖고 있었습니다.
그러나 아쉽게도 이 책 10장에서는 2장과 5장에 나와있는 와인 구매자 목록을 이용한 군집화 중 K-평균(정확하게 말하자면, 구형 k-평균(spherical K-means)을 이용한 군집화를 R로 구현하는 방법은 소개했지만, 5장 말미에 나오는 모듈성 극대화(maximize graph modularity)를 통한 군집화를 R로 구현하는 방법은 소개하지 않고 있습니다. 그래서 이 글에서는 모듈성 극대화를 통한 와인 구매자 군집화를 R과 Gephi를 가지고 하는 방법을 소개합니다.
사실 『DATA SMART』 2장에서는 엑셀을 이용한 방법을 소개하고 있지만, 절대 참조를 고려하면서 수식을 짜고 구매자 수만큼 가로로 드래그를 해야 하는 등 약간의 번거로운 작업이 필요하기 때문에 R를 이용하고 최종적으로 이 작업 결과는 가지고 Gephi를 이용하는 편이 더 간편합니다. 참고로 아래 R코드는 처리 방법을 그대로 옮긴 것입니다. 자료 확인하는 경우에는 그 결과가 길어서 일부러 삭제했습니다.
예제 파일이 없으신 분은 Wiley: Data Smart: Using Data Science to Transform Information into Insight – John W. Foreman에서 chapter2를 다운받고, WineKMC 파일 안의 Matrix시트를 R에서 지정한 디렉토리 폴더에 csv형식으로 내보내 사용하거나 혹은 chapter10안의 WineKMC파일을 사용하셔도 됩니다.
나와 비슷한 친구들을 ‘가깝다’고 표현하듯이, 수 많은 데이터의 정보를 유사한 것끼리 군집화 시키기 위해서는 그들 간의 가까운 ‘거리’를 구해야 합니다. 여기서 거리는 코사인 유사도를 사용하기 때문에 데이터를 행렬로 정제하는 것이 필요합니다.
코사인 유사도란 유클리드, 맨하튼 거리법과 같이 대표적인 거리 측정 방법입니다. 이를 통해 각 구매자간의 유사함의 정도를 거리의 개념으로 변환시킬 수 있습니다. 코사인 유사도를 통한 두 구매자간의 거리의 공식은 다음과 같습니다.
cos (45°) =(1 matched purchase)/(√1purchase√2purchase)
= (공통으로 구매한 개수)/(√첫번째 사람의 구매 개수 √두번째 사 람의 구매 개수)
코사인 유사도 값은 0과 1사이의 값을 가집니다. 거리가 가까울수록 1에 도달합니다. 그래서 자기와 자기 자신과의 거리는 모두 1이 나타나게 됩니다. 구해진 코사인 유사도 행렬의 대각선 값을 보면 모두 1인 것을 확인하실 수 있습니다. 하지만 여기서는 자신과의 거리는 무의미 하기 때문에 대각선의 1을 모두 0으로 바꿔줍니다. 아래 cos.sim 함수는 다음 링크를 참고했습니다.
clustering – Is there an R function that will compute the cosine dissimilarity matrix? – Cross Validated
그다지 중요하지 않은 관계는 지우고 강한 관계 들만 남기기 (Cut-off)
데이터가 커질수록 데이터들간의 관계는 복잡해지기 때문에 무의미한 관계들은 정리하는 것이 데이터를 이해하는데 도움이 됩니다. 앞에서 계산한 코사인 유사도는 에지 가중치가 0에서 1 사이의 값을 갖는 유사도 행렬(Affinity matrix)이기 때문에 특정 강도의 에지 이하를 모두 버리는 방법을 사용합니다. 여기서는 상위 20%만을 남길 Cut-off값을 구하고 그 값 이상은 1, 이하는 0인 행렬을 만들어줍니다. 이제 R로 직접 실행해봅시다.
군집화는 크게 두 가지 방법을 이용할 수 있습니다. 일단 인위적으로 군집화할 개수를 정하는 ‘분리형 군집화’ 방법이 있습니다. 예컨대, R의 clusters() 함수에서 군집을 5개로 옵션으로 정해주는 것이 이 군집화 방법을 사용한 것이라고 보면 됩니다.
다른 하나는 바로 ‘결합 형 군집화(aggolomerative)’입니다. ‘결합형 군집화’는 원래 객체가 속해진 그룹에서 시작해 가장 가까운 그룹을 서로 합쳐 나가는 일을 반복하여 modularity의 변화를 계산한 뒤 최적의 군집화를 찾는 방법입니다. 제가 사용하는 ‘Gephi’라는 프로그램은 바로 이 군집화 방법을 사용하고 있습니다.
참고로 Gephi가 받아들이는 실행 형식은 csv에서 쉼표(,)보다는 세미콜론(;)을 이용하기 때문에 텍스트 에디터를 사용하여 쉼표를 모두 세미콜론으로 바꿔주는 과정을 거쳐야합니다. 저는 Atom이라는 에디터를 사용했습니다.
Atom 에서 Atom을 다운 받을 수 있습니다. 저장했던 winedataneighborhood.csv파일을 Atom으로 열어줍니다. 위 Find 탭에서 Replace in Buffer를 눌러 이름에 붙어있는 쌍따옴표(“)를 없애고 쉼표(,)를 세미콜론(;)으로 모두 바꿔줍니다.
Gephi – The Open Graph Viz Platform에서 Gephi를 다운 받을 수 있습니다. Atom으로 바꿔준 winedataneighborhood.csv파일을 Gephi로 열어줍니다.이 파일은 방향성이 없기 때문에 undirected로 graph type을 설정합니다. Node 와 Edge 가 각각 100, 858임을 알 수 있습니다.
일단 오른쪽 창의 Statistics창에서 Modularity를 눌러줍니다.이미 우리가 가중치(Weight)를 인접도 행렬에서 1인 값으로 바꾸어 주었기 때문에 체크되어 있는 use weight를 해제하고 눌러줍니다.Number of Communities: 6에서 군집이 6개로 나누어짐을 알 수 있습니다.
왼쪽 appearance창에서 nodes – attribute를 눌러 modularity별로 색깔을 입혀줍니다. Lay out창을 통해 보기 좋은 형식을 선택해줍니다.밑에 검은 색 T버튼을 누르면 노드의 각 이름을 확인하실 수 있습니다.같은 색상 끼리 묶어져 있는 군집들이 보이시나요?
읽어주셔서 감사합니다 ^^
