카테고리 보관물: R

DiagrammeR – R 다이어그램 그리기

R 패키지중에 DiagrammeR라는 다이어그램(diagram)을 그릴 수 있게 해주는 것이 있습니다. 다이어그램은 플로우차트(flow chart), 간트 차트(gantt chart), 시퀀스 다이어그램 (sequence diagram)같은 것입니다.

RStudio의 DiagrammeR 스크린샷

다이어그램을 그릴 때 쓰는 도구는 Visio (Windows 쓰시는 분들) 아니면 OmniGraffle (Mac 쓰시는 분들) 아니면 PowerPoint 와 같이 손으로 그리는 것들이 있고 GraphViz 또는 Mermaid와 같이 정해진 문법을 텍스트로 입력하면 해석해서 시각적으로 표현해주는 도구가 있습니다.

DiagrammeR는 GraphViz와 Mermaid를 묶어서 연동해 놓은 패키지인데 3가지 방식으로 그래프를 그리게 해줍니다.

  1. Mermaid 문법을 텍스트로 입력한 후 텍스트를 해석해서 렌더링
  2. GraphViz 문법을 텍스트로 입력한 후 텍스트를 해석해서 렌더링
  3. R함수를 사용해서 노드와 엣지를 구성하고 렌더링

패키지를 사용하던 중 GraphViz는 원래 C로 만들어진 binary이므로 R에서 연동해서 사용할 수 있습니다만 Mermaid는 Javascript로 만들어져서 이 두가지를 어떻게 한꺼번에 연동해서 그래프를 시각적으로 표현하게 했는지 갑자기 궁금했습니다.
그래서 살펴봤더니 GraphViz.js와 Mermaid.js를 가져다 연동한 것이고 렌더링된 결과를 표현할 때는 htmlwidgets 패키지를 사용하는 것입니다.
그러니까 결국 웹브라우저를 열고 Javascript를 이용해서 렌더링하는 방식입니다.

그래서 R-GUI에서 렌더링을 하게 되면 웹브라우저가 열립니다.  RStudio에서 렌더링을 시도하면 연동이 되어서 웹브라우저가 실행되지 않고 그래프 패널에 바로 렌더링된 결과를 표현해 줍니다.  위에 넣어 놓은 스크린샷 이미지에서 확인할 수 있습니다.

앞서 말한 렌더링을 하는 세가지 방식중에 Mermaid 문법을 사용하는 것과 GraphViz 문법을 사용하는 것은  RStudio에 연동되어서 파일을 편집할 수 있게 제공하고 있기때문에 R 코드에서 DiamgrammeR 패키지를 로딩할 일이 없게 만들기도 합니다.

RStudio는 DiagrammeR를 연동해서 .mmd 확장자를 가지는 Mermaid 파일과 .dot 확장자를 가지는 Graphviz dot 파일을 바로 편집하고 코드 하일라이트도 되고 렌더링할 수 있도록 해줍니다.
아래의 링크에서 내용을 확인할 수 있습니다.

https://blog.rstudio.com/2015/05/01/rstudio-v0-99-preview-graphviz-and-diagrammer/

위의 3가지 방식 중 2가지는 앞서 말씀드린 것 처럼 RStudio와 연동으로 인해 DiagrammeR 패키지의 함수를 사용할 일이 없게 만들지만  DiagrammeR 함수를 이용한 방식의 렌더링을 사용하면 data.frame에 있는 데이터를 연동해서 그래프를 그릴 수 있습니다. 이게 가장 큰 장점이지요.

아래는 각각의 방법으로 만든 간단한 장난감 예제입니다. 

Mermaid 문법을 이용한 렌더링

Mermaid 문법을 이용한 예제입니다.

library(DiagrammeR)

DiagrammeR('
graph LR
subgraph ""
   N01[H] --- N02
   N02[E] --- N03
   N03[L] --- N04
   N04[L] --- N05
   N05[O] --- N06
   N06[!]
end
N06 --> N07
subgraph ""
   N07((M)) --- N08
   N08((E)) --- N09
   N09((R)) --- N10
   N10((M)) --- N11
   N11((A)) --- N12
   N12((I)) --- N13
   N13((D))
end

classDef box1 color:white,fill:#eee,stroke:#000,stroke-width:5px 
classDef box2 color:white,fill:#fff,stroke:#33d,stroke-width:5px

class N01,N02,N03,N04,N05,N06 box1
class N07,N08,N09,N10,N11,N12,N13 box2

linkStyle 0 stroke:#ddd,stroke-width:20px
linkStyle 1 stroke:#ddd,stroke-width:20px
linkStyle 2 stroke:#ddd,stroke-width:20px
linkStyle 3 stroke:#ddd,stroke-width:20px
linkStyle 4 stroke:#ddd,stroke-width:20px

linkStyle 6  stroke:#99d,stroke-width:20px
linkStyle 7  stroke:#99d,stroke-width:20px
linkStyle 8  stroke:#99d,stroke-width:20px
linkStyle 9  stroke:#99d,stroke-width:20px
linkStyle 10 stroke:#99d,stroke-width:20px
linkStyle 11 stroke:#99d,stroke-width:20px
')

GraphViz 문법을 이용한 렌더링

GraphViz 문법을 이용한 예제입니다.

grViz('
digraph boxes_and_circles {
  graph [overlap = true, fontsize = 12]
  rankdir="LR"

  subgraph cluster_2 {
    node [shape = circle,
      fixedsize = true,
      width = 0.9]
      color=none
      rank=same
      N07[label="G"]
      N08[label="R"]
      N09[label="A"]
      N10[label="P"]
      N11[label="H"]
      N12[label="V"]
      N13[label="I"]
      N14[label="Z"]
      N07->N08->N09->N10->N11->N12->N13->N14
      }
      
  
  subgraph cluster_1 {
    node [shape = box,
          fontname = Helvetica]
  	color=none
    rank=same
    N01[label="H"]
    N02[label="E"]
    N03[label="L"]
    N04[label="L"]
    N05[label="O"]
    N06[label="!"]
    N01->N02->N03->N04->N05->N06
  }
}
')

GrammerR의 R 함수를 이용한 렌더링

전통적인 스타일의 R 함수를 사용한 예제입니다. 아래의 함수들은 DiagrammeR 최신 패키지를 설치해야만 됩니다. 최근에 함수 이름에 변화가 있었던 모양입니다.

library(DiagrammeR)

nodes <-
  create_node_df(
    n = 7,
    type = "number")

edges <-
  create_edge_df(
    from = c(1, 1, 1, 1, 1, 1),
    to = c(2, 3, 4, 5, 6, 7),
    rel = "related")

graph <-
  create_graph(
    nodes_df = nodes,
    edges_df = edges)

render_graph(graph)

dplyr와 연동한 DiagrammeR

역시 RStudio에서 만은 부분을 기여한 패키지인 만큼 dplyr 스타일의 매우 스타일리쉬한 파이프라인 형태의 코딩도 지원합니다.

library(DiagrammeR)

example_graph <-
  create_graph() %>%
  add_pa_graph(
    n = 50,
    m = 1,
    set_seed = 23) %>%
  add_gnp_graph(
    n = 50,
    p = 1/100,
    set_seed = 23) %>%
  join_node_attrs(
    df = get_betweenness(.)) %>%
  join_node_attrs(
    df = get_degree_total(.)) %>%
  colorize_node_attrs(
    node_attr_from = total_degree,
    node_attr_to = fillcolor,
    palette = "Greens",
    alpha = 90) %>%
  rescale_node_attrs(
    node_attr_from = betweenness,
    to_lower_bound = 0.5,
    to_upper_bound = 1.0,
    node_attr_to = height) %>%
  select_nodes_by_id(
    nodes = get_articulation_points(.)) %>%
  set_node_attrs_ws(
    node_attr = peripheries,
    value = 2) %>%
  set_node_attrs_ws(
    node_attr = penwidth,
    value = 3) %>%
  clear_selection() %>%
  set_node_attr_to_display(
    attr = NULL)
#> `select_nodes_by_id()` INFO: created a new selection of 34 nodes
#> `clear_selection()` INFO: cleared an existing selection of 34 nodes
example_graph %>%
  render_graph(layout = "nicely")

추가로 네트워크 분석 관련 패키지인 igraph에서 생성한 객체를 DiagrammeR 형태로 변환하는 함수들도 제공합니다.  사용해 보지는 않았습니다. ^^;

R 3.5.0 릴리즈 – Joy in playing

지난 2018-04-23에 R 3.5.0이 릴리즈 되었습니다.
이전 버전은 R 3.4.4입니다.
R 3.5.0의 닉네임은 “Joy in playing”이고 늘 그래왔듯이 이 닉네임도 만화 피너츠에 나오는 대사입니다.

https://www.gocomics.com/peanuts/1973/01/27

R 3.4.x에서 앞자리 숫자가 바뀌면서 R 3.5.0으로 올라가면서 이전의 버전업에  비해서 업데이트 내역이 조금 많습니다.

꽤 많아서 나열하기는 힘들고 그 중에서 체감할 수 있는 가장 중요한 업데이트는 R에 설치되는 패키지가 설치할 때 모두 bytecode로 컴파일 된다는 것입니다.

그래서 바로 버전업을 하면 예상치 못한 문제가 발생할 여지가 많아서 사용하던 패키지가 이상하게 작동하거나 RStudio가 오작동 한다거하는 문제가 있을 수 있습니다.

버전업을 조금 미루시거나 RStudio를 최신으로 빠르게 반복해서 업데이트 해 주는 것이 필요할 것 같습니다.

R팁 – 두 벡터의 모든 멤버가 동일한지 비교하기 all.equal

두 벡터가 동일한지 비교하는 간단한 팁입니다.

R은 벡터(vector)와 스칼라(scala)의 구분이 없이 사실은 모든 변수를 벡터로 취급하기 때문에 다른 언어에는 없는 몇 가지 문제가 생깁니다. 이것도 그것과 관련이 있습니다.

두 벡터, 즉 2개의 변수가 있고 변수가 모두 length가 2 이상일 때 두 벡터가 완전히 동일한지 비교할 때 아래의 코드에서 첫번째 if구문과 같은 실수를 합니다.

v1 <- c(1, 2, 3)
v2 <- c(1, 2, 4)

if (v1 == v2) {
  print("same")
} else {
  print("not same")
}

if (all.equal(v1, v2) == TRUE) {
  print("realy same")
} else {
  print("realy not same")
}

위의 예제 코드에서 첫번째 if 구문은 상식적으로 의도한 대로 작동하지 않습니다.   == 연산자가 두 변수의 첫번째 요소(first element)만을 비교하기 때문에 두 벡터가 같다고 나옵니다.

물론 다음과 같은 경고 메세지를 콘솔창에 뿌려주기 때문에 문제가 있다는 것을 알 수는 있습니다. 무심결에 경고메세지를 무시해 버리면 큰 문제가 생길 수 있습니다.

Warning message:
In if (v1 == v2) { :
  the condition has length > 1 and only the first element will be used

만약 두 벡터의 멤버가 모두 동일한지 비교하려면 처음 코드에서 두 번째 사용한 if 구문처럼 all.equal을 사용해야 합니다.

all.equal(v1, v2) == TRUE

코드를 조금 고쳐서 다음과 같은 것을 실행해 보세요.

v1 <- c(1,2,3)
v2 <- c(1,2,4)
v3 <- c(1,2,3)

if (v1 == v2 & v1 == v2) {
  print("same")
} else {
  print("not same")
}

if (all.equal(v1, v2) == TRUE & all.equal(v1, v3)) {
  print("realy same")
} else {
  print("realy not same")
}

all.equal(v1, v2) == TRUE
all.equal(v1, v3) == TRUE

사실 R을 사용해서 작업을 할 때 두 벡터가 완전히 동일한지 비교할 일이 별로 없습니다. 그래서 새까맣게 까먹고 있다고 가끔 실수를 저지를 때가 있습니다.

무선통신서비스 가입회선 통계

과학기술정보통신부 홈페이지에 방문하면 무선통신서비스 가입회선 통계 데이터를 제공하고 있어서 아무나 받아서 사용할 수 있습니다.   수작업으로 하는 것이라서 이전 달의 자료를 다음 달 말일 정도에 업데이트 해줍니다.

그러니까 2월달 자료는 3월말경에 업데이트가 됩니다.

자료가 올라오는 시기가 1개월 가까이 차이가 있어서  이전 달의 내용을 달이 바뀌고 나서 바로 볼 수 없는 것이 흠입니다만
없는 것 보다는 훨씬 낫습니다.  PDF로 제공하는 것도 좀 불편합니다. 포맷을 바꾸기가 조금 번거롭습니다.
엑셀 파일로 해주셨으면 더 좋았을텐데요.

이 자료 얘기를하는 것은 최근에 업무와 관련해서 통신서비스 관련 분석 작업을 조금하게 되었는데 작업을 하고난 김에 저 데이터를 예제로 간단한 시각화 예제를 만들어 보기로 했습니다.

실제 업무에서는 저 데이터와 다른 데이터를 결합해서 확인하거나 하는 것이지만 이 포스트에서는 저 데이터만 이용해서 아주 간단한 EDA작업을 해보겠습니다.

2018년 3월까지의 데이터를 사용했습니다.

이 글을 쓰는 시점은 2018년 5월입니다

소스 코드

플롯(plot)을 그리는데 ggplot2를 사용했고 데이터 랭글링(data wrangling)은 dplyr와 tidyr를 사용했습니다. tidyverse 패키지에 몽땅 같이 들어 있으므로 한 번에 묶음 패키지를 통째로설치하고 싶으면  tidyverse만 설치하면 됩니다.

tidyverse는 ggplot2를 포함한 몇개의 유용한 패키지 를 묶어 놓은 것입니다.

아래 코드에 주석을 적어 두었습니다. 그래서 코드 설명은 따로 하지 않겠습니다.  dplyr와 tidyr에 익숙하지 않은 분들은 패키지 사용법을 잠깐 살펴봐야 할 수있습니다.  이것도 여기서는 설명하지 않겠습니다.  너무 길어집니다.

전체 코드는 다음과 같습니다.

# 무선 통신 서비스
# msu : mobile service users
library(tidyverse)
msu <- read.csv(file="./2018년 3월 기준 - 무선통신서비스 가입회선 통계.csv")
colnames(msu)
msu_molten <- msu %>% gather(월, 가입자, -구분, -통신사)

# 월의 문자열을 날짜 타입으로 바꿉니다
msu_molten <- msu_molten %>% mutate(월=as.Date(paste0(월, ".1"), format="X%Y.%m월.%d"))
msu_molten <- msu_molten %>% filter(구분 != "합 계" & 통신사 != "소계")

# 데이터를 잘 집계해서 플롯을 몇개 그려봅니다

가입구분별_데이터 <- msu_molten %>% group_by(구분, 월) %>% summarise(가입자=sum(가입자))
ggplot(가입구분별_데이터, aes(x=월, y=가입자, fill=구분)) +
   geom_area(colour="black", size=.2, alpha=.4) +
  scale_fill_brewer(breaks=rev(levels(가입구분별_데이터$구분)))

통신사별_데이터 <- msu_molten %>% group_by(통신사, 월) %>% summarise(가입자=sum(가입자))
ggplot(통신사별_데이터, aes(x=월, y=가입자, colour=통신사)) +
  geom_line() +
  scale_fill_brewer(breaks=rev(levels(통신사별_데이터$통신사)))

통신사별_3월_합계 <- msu_molten %>% filter(월=="2018-03-01") %>% group_by(통신사, 월) %>% summarise(가입자=sum(가입자))
ggplot(통신사별_3월_합계, aes(x=통신사, y=가입자, fill=통신사)) + 
  geom_bar(stat="identity")

월별_3월 <- msu_molten %>% filter(월=="2018-03-01" & 구분 != "합 계" & 통신사 != "소계") %>% mutate(구분=as.character(구분), 통신사=as.character(통신사))

# 모자이크 플롯
library(ggmosaic)
ggplot(data=월별_3월) +
  geom_mosaic(aes(weight=가입자, x=product(통신사), fill=구분))

# 카이제곱 검정
표 <- 월별_3월 %>% select(-월) %>% spread(통신사, 가입자) %>% select(-구분)
chisq.test(as.matrix(표))

한글 변수도 몇개 사용했고 줄이 길어서 조금 복잡해 보일텐데요. 복사해서 sublime text 같은 편집기나 Rstudio에서 보세요. 원래 한글 변수명은 잘 안쓰지만 한 번 해보고 싶었습니다.  가끔은 일탈이 필요해요.

에어리어 플롯 – area plot

가입유형별 시계열 에어리어 플롯(time-series area plot)입니다. 케이크 차트(cake chart)라고도 부릅니다.

월별 집계이기 때문에 월별로 가입유형의 변화추세를 볼 수 있습니다.
신규가입자와 기기변경이 많네요.
이전 달에 비해서 큰 폭으로 늘었다는 것을 볼 수 있습니다.

시계열 플롯 – time-series plot

통신사별 시계열 라인플롯입니다.  통신사별, 월별로 가입자를 모두 취합했습니다.

SKT의 가입자가 월등히 많은 것을 볼 수 있습니다.  증가폭도 큽니다. 다른 통신사와  MVNO도 큰폭으로 상승하긴 했습니다.

2월에 가입자가 조금 적은데 어떤 이슈가 있었거나 3월에 있을 이벤트를 사람들이 기다렸을 가능성이 큽니다.
봄 철에는 여러 이벤트가 많은 편인데 새모델이 출시된다거나 요금할인이 된다거나 또는 대학신입생들의 입학 기념품이거나 신학기 행사이거나요. 그래서 2월에는 가입을 하지 않고 3월까지 기다렸을 가능성이 큽니다.
반대로 해석하면 기업들은 통상 3월부터 이벤트를 많이합니다. 주변정보 탐색을 해보지 않았고 부가정보가 없어서 모르지만 상식만으로 그렇게 추측해 봅니다.

위의 가설은 실제로 데이터를 확인하거나 서베이를 해서 확인해 보지 않았기 때문에 논리에 기반한  소설일 뿐입니다

바 플롯 – bar plot

색깔은 기본값으로 막 칠했습니다. 알록달록하게. 나이 먹으면 알록달록한게 좋아집니다.

그냥 2018년 3월의 가입자수를 통신사별로 수치 비교 하기 위해 바 플롯을 그린 것입니다.  바 플롯(bar plot)이라고도 하지만 그냥 막대 차트(bar chart)라고 더 많이 부릅니다. 어쨌는 플롯을 보면 SKT의 가입자가 월등히 많네요.

모자이크 플롯 – mosaic plot

통신사별 구분별로 모자이크 플롯을 그렸습니다.
2차원으로 된 것으로 빈도의 비중을 비교할 때 유용한 플롯입니다.

크기가 큰 것이 많은 것입니다. 사각형의 크기를 보고 비중을 보면 됩니다.

아 쉽다. 전 이런게 좋아요. 쉬운거

그림을 보면 SKT의 기기변경 사용자가 가장 많습니다. 그 다음은 비교하기 애매하지만 KT의 기기변경과 SKT의 신규가입자가 많은 것 같습니다.
MVNO는 신규가입자의 비율이 매우 높습니다. (왜 그럴까요??)
SKT의 기기변경 비율이 다른 통신사에 비해서 높습니다.
KT는 신규가입자의 비율이 다른 통신사에 비해서 높습니다.

2018년 3월은 SKT에 기기변경으로 가입한 사람이 많고 KT는 처음 진입한 사람이 많다고 볼 수 있습니다.  모든 통신사에 걸쳐 처음 가입했다고 하면 이제 막 성인이 되었거나 외국에서 왔거나 일 것 같습니다.

어쨌든 뭐로 보든 SKT 가입자가 많군요. SKT는 통신사 중에서 무선 점유율이 가장 높은 회사로 알려져 있습니다.
여전히 장사 잘되나 봅니다.

마지막으로 소스코드에서 카이제곱 검정(chi-square test)을 했습니다만 별 의미 없는 것입니다.  결과는 귀무가설 기각으로 통신사 구분과 가입종류의 구분은 서로 독립이 아니다. 즉 “영향이 있다” 정도입니다. 이건 가설검정을 하지 않아도 모자이크 플롯으로 봐도 쉽게 알 수 있긴합니다.  하지만 검정법을 사용해서 뭐든 확실하게 한 번 보는게 좋습니다.

여기까지입니다.
사실 너무 대충 하다만 EDA입니다만 데이터를 보고 요약을 정리해 나가다 보면 뜻하지 않는 인사이트를 발견하기도 합니다. 물론 이 데이터는 집계가 너무 많이 되어 있어서 주변정보가 없는 상태에서 특별한 인사이트를 얻기는 어렵습니다.

데이터는 아래 링크를 클릭해서 받으세요.

파일 다운로드: 2018-3-mobile-user-data

Windows 10에서 Rcpp 설치 오류 해결 방법

저는 여러 OS를 사용해서 작업을 여기저기에서 난잡하게 하는 편입니다.  버전도 다 다르고 설치된 패키지들도 달라서 작업을 하기전에 패키지를 종종 재설치하곤 합니다.

Windows 10에서 R로 작업을 하던 중에 tidyverse를 업데이트했는데 그 뒤로 ggplot2를 로딩하니 Rcpp가 없다고 에러가 뱉어내더군요. ggplot2는 그 전까지 이상없이 쓰던 것이었습니다.

> library(ggplot2)
Error: package or namespace load failed for ‘ggplot2’ in loadNamespace(j <- i[[1L]], c(lib.loc, .libPaths()), versionCheck = vI[[j]]):
 ‘Rcpp’이라고 불리는 패키지가 없습니다
In addition: Warning message:
패키지 ‘ggplot2’는 R 버전 3.4.4에서 작성되었습니다 

메세지를 보면 Rcpp가 없다는 것인데 Rcpp를 분명 예전에 설치했었는데 이상하다 싶었습니다. 어쨌든 Rccp 재설치를 시도했더니 이상한 에러가 나더군요.

package ‘Rcpp’ successfully unpacked and MD5 sums checked
Warning in install.packages :
  unable to move temporary installation ‘C:\Users\Aiden\Documents\R\win-library\3.4\file28a8dc15503\Rcpp’ to ‘C:\Users\Aiden\Documents\R\win-library\3.4\Rcpp’

The downloaded binary packages are in
	C:\Users\Aiden\AppData\Local\Temp\RtmpuSdyaV\downloaded_packages

디렉토리를 이동시키지 못했다는 메세지인데
위의 디렉토리는 관리자 권한이 필요한 디렉토리가 아니기 때문에 권한 문제는 아니었습니다.

그래서  검색을 해서 찾아보니 anti-virus의 실시간 탐지가 방해를 한다는 군요.
anti-virus 소프트웨어를 일시 중지하고 Rcpp를 설치하니 그 뒤로는 잘 됩니다.

tidyverse나 ggplot2 업그레이드 후에 ggplot2가 로딩이 되지 않거나 하면 참조하세요.