5년 전 만든 패키지를 다듬어 의학용 커스텀 플로우차트 만들기
R을 이렇게까지 쓴다
Clinical Flowchart
Clinical (Trial) Flowchart란 임상 시험 혹은 치료 과정에서의 각 단계와 절차를 시각적으로 표현하는 방법이다.
즉 몇명의 환자에서 시작하여 어떤 치료적 방법을 사용하고, 어떤 환자들이 어떤 이유로 시험 과정에서 배제 되었는지, 그룹은 어떻게 배정되었는지 등을 나열하는 방법으로 아래의 예시 같은 형태를 갖는다.
이 그림은 다음을 의미한다.
- 처음에 환자가 124명에서 시작
- 조건에 맞지 않거나 참여를 거부하는 등 34명을 제외
- 남은 90명에 대해 치료 방법을 비교하기 위해 랜덤하게 그룹(Extended CBT, CBT, IP)을 배정한다.
- (30명 씩 나누는것이 더 이상적이지만) 각 그룹에서 4, 10, 10명은 과정에서 빠졌고 나머지는 배정된 치료를 받음.
- 1년 이후 환자를 재관찰 했을때 각각 9, 6, 7명이 반응이 없었다.
*시작은 124명이었지만 최종 결과는 44명만 남았다는 점에서 임상시험이 얼마나 어렵고 비싼지 알 수 있다
아무튼 이 Flowchart를 그리기 위해 정해진 방법은 없고, 파워포인트나 키노트 같은 상용 소프트웨어 혹은 draw.io, lucidchart 같은 웹 기반 다이어그램 도구 등을 사용하는 것이 괜찮아보인다.
Clinical Flowchart with R
그런데 정확한 이유는 모르겠지만, 이번에 R을 이용해서 그려야하는 경우가 생겼다.
R 같은 프로그래밍 방식을 사용할 때의 장점은 자동화나 재사용이 가능하다는 것과, 다른 기능과의 연동 (예를 들어 데이터 원본에서 부터 차트까지 쭉 그려내는 프로그램), 상용 프로그램에서 제공하지 않는 수준의 커스터마이즈도 가능하다 정도를 생각해볼 수 있다.
아무튼 이를 위해서 시도해본 방법은 다음과 같다.
패키지의 원래 목적도 participant flow diagram을 그리는 것이 목적인 만큼, 그리고자 했던 결과물에 가장 근접하게 그릴 수 있었다. 특수한 상황이 아니었다면 제일 좋은 선택이라고 생각
그래프, 네트워크 시각화를 위한 라이브러리로 d3, mermaid의 기능들을 활용할 수 있다
- ggplot2 + ggforce
(GPT가 추천해 준) R의 베스트 프랜드 ggplot과 이를 보조하는 패키지.
vis.js를 기반으로 하는 네트워크 시각화 그래프 를 위한 R 패키지.
그러나 결과적으로는 4개의 방법 모두 실패했다.
이유는 그리고자 하는 그림에 특수한 상황이 있었기 때문이다.
이어지는 그림은 실제로 그렸던 그림에서 숫자와 그룹만 1000, 1,2,3… A, B로 바꾼 그림이다.
문제가 되는 부분은 2개 였는데, 중간의 Completed curative surgery 부분으로 이전 단계의 2개 노드에서 길게 이어진 하나의 노드로 연결되는 부분을 구현하기 어려웠고, 엣지의 위치를 지정하기가 곤란했다.
이를 위해 여러 방법을 고민하다가 결국 오래된 옛 친구이자 내가 원하는 대로 커스텀 기능을 추가할 수 있는, shinyCyJS를 사용하기로 했다.
shinyCyJS는 R에서 cytoscape.js의 네트워크 / 그래프 시각화 기능을 활용할 수 있게 패키지로, 졸업할 당시에 유전체 네트워 크 시각화를 찾아보다가 (그 당시에는 igraph와 RCyjs 정도가 있었다) 원하는 기능이 없어서 만든, 지금의 직장에 오게 된 계기가 되기도 한 첫 작품이다.
2 Custom feature with shinyCyJS
- 위치를 지정하는 엣지
아무튼 위 2가지 커스텀 기능 중 엣지의 위치를 구현하기 위해서 처음에는 taxi 엣지를 사용하려고 했다. 그러나 이 또한 마찬가지로 노드의 위치에 따라 엣지가 결정될 뿐, 엣지를 옮기는 것은 불가능하여 아래처럼 중간에 미세하게 작은 노드를 더하고, 그 노드로의 엣지를 거치는 방법으로 선회했다. (미세 노드의 포지션을 지정하는 것은 가능하기 때문)
- 하나의 큰 노드
cytoscape.js에서는 기본적으로 노드들은 엣지로 연결될 때 중심과 중심을 최단거리로 연결하는 것을 고려하고, 만약 앞에서 언급한 taxi 처럼 중간에 다른 지점을 거치는 경우는 그 포인트를 지정할 수 없이 알고리즘에 따라 계산하게 되어 있다.
만약 중간 포인트를 지정할 수 있게 한다면 어떤 문제가 생기느냐, 예시의 bezier나 haystack처럼 노드간 엣지가 여러개일때 전부 중간 포인트를 지정해야 하는 귀찮음이 발생한다.
이전 예시는 3개이기 때문에 큰 문제가 없지만 대학원 시절에 연구했던 유전체 네트워크의 경우라면 하나의 유전자가 수십~수백개의 다른 유전자와 상호작용하는 경우도 종종 있기 때문에 상당히 귀찮은 문제가 발생한다.
최악의 경우 엣지들이 겹쳐서 몇개의 엣지를 누락하는 경우 그래프가 아예 다른 정보를 만들어 낼 수도 있다.
즉, 하나의 긴 노드로 연결하는 문제에서 긴 노드는 사용자에게나 보이기 위한 그래픽(width)적인 관점일 뿐, 컴퓨터의 입장에서는 아래 이미지처럼 있지도 않은 노드에 엣지를 연결하는 비합리적인 행동이므로, 애초에 이러한 옵션을 고려할 이유가 없다.
이 문제를 풀기 위해 이전 엣지 중간 포인트 문제처럼 미세 노드를 만들고, 해당 부분으로 연결하게 끔 수정했다.
최종적으로 R로 만든 그래프의 일부는 아래와 같다. (마찬가지로 그룹과 숫자는 임의로 수정)
또 다른 문제, download as PNG
사실 그래프와는 크게 연관 없는 문제인데, 앞에서 만든 그래프를 파일로 다운로드 할 수 있는 기능이 필요하다. 엄밀히는 아래처럼 Rstudio viewer에서 Export as PNG 버튼으로 가능하고, 정 안되면 스크린샷을 찍는 방법도 있지만 그새 cytoscape.js에서 그래프를 png 로 저장하는 기능이 있어서 활용해봤다.
*실제로 예전에 shinyCyJS에 png로 다운로드 기능 추가 해달라는 요청이 왔었고, 스크린샷 찍으면 되는 거 아님? 이라고 답변한 적 있음.
이를 위해서는 인터넷 브라우저 (chrome)를 사용해야했고 (cytoscape.js는 Javascript다) 그 말은 Shiny를 사용해 R을 넘어 웹으로 구현해야한다는 의미였다. 물론 shinyCyJS는 이름부터 shiny의 연동을 고려하고 만든 패키지인 만큼 별 문제는 없었다.
아래는 다운로드 하기 위해 크롬의 개발자 도구에서 실행해야하는 코드
const pngBlob = await cy.png({
output: 'blob-promise',
});
const fileName = 'myfile.png';
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(pngBlob);
downloadLink.download = fileName;
downloadLink.click();
shinyCyJS는 내가 만든 R 패키지이고, 말 그대로 cytoscape에서 지원하는 기능 모두와 필요하면 이처럼 커스텀 기능도 가능한 만큼, 네트워크 / 그래프 시각화를 R로 해야한다면 사용해보거나 필요한 기능을 요청해도 좋다. 물론 꼭 R을 써야하는 것이 아니라면 draw.io가 더 좋아보이긴 한다.
추가로, 다른 Javascript 라이브러리를 R에서 사용할 수 있게 패키지로 만들고 싶다면 관련해서 메일 줘도 좋다.