Observable JS

개요

Quarto는 Observable JS를 기본 지원합니다. 이는 Mike Bostock(D3의 저자)이 만든 표준 JavaScript 확장 집합입니다. Observable JS는 반응형 런타임이 특징이며, 인터랙티브 데이터 탐색과 분석에 특히 적합합니다.

Observable JS의 제작사(Observable, Inc.)는 https://observablehq.com/에서 노트북을 만들고 배포할 수 있는 호스팅 서비스를 제공합니다. 또한 Observable JS(“OJS”)는 코어 라이브러리를 통해 독립형 문서와 웹사이트에서 사용할 수 있습니다. Quarto는 렌더링 시 실행되는 컴파일러와 이 라이브러리들을 함께 사용해 Quarto 문서에서 OJS를 사용할 수 있게 합니다.

OJS는 모든 Quarto 문서(일반 마크다운뿐 아니라 Jupyter 및 Knitr 문서)에서 작동합니다. {ojs} 실행 코드 블록에 코드를 넣기만 하면 됩니다. OJS를 사용하는 문서는 file:// 프로토콜이 아니라 http:// 또는 https:// 프로토콜에서 실행되어야 합니다. 이 글의 나머지 부분에서는 Quarto에서 OJS를 사용하는 기본을 설명합니다.

예시

Allison Horst의 Palmer Penguins 데이터셋을 기반으로 한 간단한 예시부터 시작하겠습니다. 여기서는 펭귄의 체중이 성별과 종에 따라 어떻게 달라지는지 살펴봅니다(부리 길이와 섬으로 데이터셋을 필터링하려면 제공된 입력을 사용하세요).

이 예시의 소스 코드를 살펴보겠습니다. 먼저 FileAttachment로 CSV 파일의 데이터를 읽는 {ojs} 셀을 만듭니다.

```{ojs}
data = FileAttachment("palmer-penguins.csv").csv({ typed: true })
```

위 예시는 전체 데이터가 아니라 필터링된 부분집합을 그립니다. 필터를 만들려면 입력이 필요하며, 필터링 함수에서 입력 값을 사용할 수 있어야 합니다. 이를 위해 viewof 키워드를 사용하고 표준 Inputs를 활용합니다.

```{ojs}
viewof bill_length_min = Inputs.range(
  [32, 50], 
  {value: 35, step: 1, label: "부리 길이(최소):"}
)
viewof islands = Inputs.checkbox(
  ["Torgersen", "Biscoe", "Dream"], 
  { value: ["Torgersen", "Biscoe"], 
    label: "섬:"
  }
)
```

이제 bill_length_minisland 값을 사용해 CSV에서 읽은 data를 변환하는 필터링 함수를 작성합니다.

```{ojs}
filtered = data.filter(function(penguin) {
  return bill_length_min < penguin.bill_length_mm &&
         islands.includes(penguin.island);
})
```

여기에서 반응성을 확인할 수 있습니다. 동적 입력 값을 참조하는 데 특별한 문법이 필요 없고, 입력 값이 바뀌면 필터링 코드는 자동으로 다시 실행됩니다. 이는 스프레드시트에서 셀을 업데이트하면 해당 셀을 참조하는 다른 셀이 자동으로 재계산되는 방식과 유사합니다.

마지막으로 Observable Plot(표 형식 데이터를 빠르게 시각화할 수 있는 오픈 소스 JavaScript 라이브러리)을 사용해 필터링된 데이터를 그립니다.

```{ojs}
Plot.rectY(filtered, 
  Plot.binX(
    {y: "count"}, 
    {x: "body_mass_g", fill: "species", thresholds: 20}
  ))
  .plot({
    facet: {
      data: filtered,
      x: "sex",
      y: "species",
      marginRight: 80
    },
    marks: [
      Plot.frame(),
    ]
  }
)
```

입력과 마찬가지로 filtered 변수는 특별한 문법 없이 참조합니다. filtered가 바뀔 때마다(즉 입력이 바뀔 때마다) 플롯 코드는 자동으로 다시 실행됩니다.

이로써 OJS의 기본적인 end-to-end 사용을 살펴봤습니다(전체 소스 코드는 Penguins 예시를 참고하세요).

Penguins 코드를 보면 흥미로운 점이 있습니다. 입력과 플롯 코드는 데이터 처리 코드 에 정의되어 있습니다. 이는 OJS 셀 실행과 전통적인 노트북의 중요한 차이를 보여줍니다. 셀은 특정 순서대로 정의될 필요가 없습니다.

실행은 완전히 반응형이므로 런타임이 서로 참조하는 관계에 따라 올바른 순서로 자동 실행합니다. 이는 선형적인 셀 실행을 가진 전통적인 노트북보다 스프레드시트에 더 가깝습니다.

라이브러리

위 예시에서는 다음과 같은 표준 라이브러리를 사용했습니다.

  1. Observable stdlib — DOM 조작, 파일 처리, 코드 가져오기 등 핵심 프리미티브 제공.

  2. Observable Inputs — 슬라이더, 드롭다운, 테이블, 체크박스 등 표준 입력 컨트롤 제공.

  3. Observable Plot — 탐색적 데이터 시각화를 위한 고수준 플로팅 라이브러리.

이 라이브러리들은 https://observablehq.com의 노트북과 Quarto 문서의 {ojs} 셀에서 자동으로 사용할 수 있다는 점에서 다소 특별합니다.

다른 JavaScript 라이브러리를 사용하는 것도 간단합니다. 명시적으로 import하면 됩니다. 예를 들어 아래에서는 require 함수를 사용해 라이브러리를 가져옵니다(이는 jsDelivr에서 NPM 모듈을 로드합니다).

```{ojs}
d3 = require("d3@7")
topojson = require("topojson")
```

표준 및 서드파티 라이브러리 사용 방법은 라이브러리 문서를 참고하세요.

데이터 소스

초기 예시에서는 FileAttachment를 데이터 소스로 사용했습니다. 파일 첨부는 CSV, TSV, JSON, Arrow(압축 없음), SQLite 등 여러 형식을 지원하므로 분석에 맞게 준비된 데이터셋을 읽기에 편리합니다.

하지만 데이터가 시각화에 사용되기 전에 Python이나 R에서 전처리가 필요할 때가 많습니다. Quarto에서는 문서 렌더링 중에 전처리를 수행한 뒤 결과를 OJS에서 사용할 수 있게 할 수 있습니다.

Python이나 R에서 ojs_define() 함수를 사용해 JavaScript에서 사용할 변수를 정의합니다. 예를 들어 Python에서 간단한 CSV 읽기를 재현하려면 다음과 같이 할 수 있습니다.

```{python}
import pandas as pd
penguins = pd.read_csv("palmer-penguins.csv")
ojs_define(data = penguins)
```

ojs_define(data = penguins) 호출은 penguins 데이터 프레임 값을 가진 data 변수를 OJS에서 사용할 수 있게 하겠다는 의미입니다.

사용하는 시각화 라이브러리에 따라 JavaScript에서 데이터를 소비하기 위해 추가 단계가 필요할 수 있습니다. 이 경우 Plot 함수는 열이 아니라 행 단위 데이터를 기대하므로 필터링 전에 transpose()를 수행합니다.

```{ojs}
filtered = transpose(data).filter(function(penguin) {
  return bill_length_min < penguin.bill_length_mm &&
         islands.includes(penguin.island);
})
```

데이터를 준비하고 읽는 다양한 방법에 대해서는 데이터 소스 문서를 참고하세요.

OJS 셀

{ojs} 코드 셀의 동작을 사용자화하는 다양한 옵션이 있습니다. 코드 표시/숨김/접기와 출력의 가시성 및 레이아웃 제어가 포함됩니다.

가장 중요한 셀 옵션은 소스 코드 표시 여부를 제어하는 echo 옵션입니다. 아티클에 시각화를 임베드하는지, 노트북이나 튜토리얼을 만드는지에 따라 선호가 달라집니다.

{ojs} 셀의 코드는 기본적으로 표시됩니다. 문서 전체의 코드 표시를 막으려면 YAML 메타데이터에 echo: false 옵션을 설정하세요.

---
title: "My Document"
execute:
  echo: false
---

셀 단위로도 옵션을 지정할 수 있습니다. 예를 들면 다음과 같습니다.

```{ojs}
//| echo: false
data = FileAttachment("palmer-penguins.csv").csv({ typed: true })
```

사용 가능한 모든 옵션은 OJS 셀 문서를 참고하세요.

더 알아보기

다음 문서는 Quarto에서 OJS를 사용하는 방법을 더 깊이 다룹니다.

  • 라이브러리: 표준 라이브러리와 외부 JavaScript 라이브러리 사용.

  • 데이터 소스: 데이터를 읽고 전처리하는 다양한 방법.

  • OJS 셀: 셀 실행, 출력, 레이아웃에 대한 심화 설명.

  • Shiny Reactives: OJS와 Shiny 통합 방법.

  • 코드 재사용: 여러 문서에서 OJS 코드를 재사용하는 방법.

반응성의 내부 동작을 더 알고 싶다면 Mike Bostock의 다음 노트북을 참고하세요.