Org 테이블 스프레드시트

2020년 7월 3일 수정

Org Mode의 테이블은 간단한 스프레드시트(Spreadsheet) 기능을 제공한다. 테이블의 각 셀을 이용해 계산을 하는 것 등인데 마치 엑셀이나 Numbers, 구글 독스에서 제공하는 스프레드시트(Spreadsheet)와 유사하다. 사용법이 좀 어렵긴 하지만 그래도 재미있으니 한번 살펴보자.

참고로 이 글에서 언급하는 단축키는 Doom Emacs 기준이다. 물론 일부 단축키는 바닐라 Emacs와 동일할 수도 있다.

테이블

일단 아래와 같은 테이블을 만들었다고 가정해보자.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |         |
| Apple |   256 |    30 |         |
| Coke  |     4 |     9 |         |
|-------+-------+-------+---------|
|       |       |       |         |

위 테이블을 이용해 계속 살펴보자.

셀 정보 확인하기

스프레드시트에서는 행과 열에 고유번호가 있듯이 테이블에도 고요 번호 시스템이 있다.

우선 커서를 아래 [] 위치에 놨다고 가정하자.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      [] |
| Apple |   256 |    30 |         |
| Coke  |     4 |     9 |         |
|-------+-------+-------+---------|
|       |       |       |         |

커서 위치의 셀 주소(?)를 파악하기 위해 C-c ? 혹은 SPC m b h 키를 눌러보면 하단 미니 버퍼에 커서가 있는 위치의 주소를 표시해준다.

line @2, col $4, ref @2$4 or D2

여기서 @2$4 라는 값이 커서가 위치한 셀의 위치를 한 번에 나타내는 셀 주소다. 복잡하지만 이런 표현 방법을 알아야 이후의 내용을 진행할 수 있다.

셀의 고유번호를 확인하는 다른 방법도 알아보자. C-c } 키를 누르면 테이블 헤더에 주소 정보가 토글된다.

   1| Kind  | Price | Ideal | Earning |
I*1 |$1-----+$2-----+$3-----+$4-------|
   2| Book  |   100 |    99 |      [] |
   3| Apple |   256 |    30 |         |
   4| Coke  |     4 |     9 |         |
I*2 |$1-----+$2-----+$3-----+$4-------|
   5|       |       |       |         |

이 정보를 통해서 $1부터 $4까지 표시되는 것이 칼럼(행, Column) 번호이고 골뱅이가 로우(열, Row) 번호임을 유추할 수 있다.

Lisp 함수 실행시켜 보기

위의 마지막 표에서 커서가 위치한 곳인 [] 위치에서 아래와 같은 내용을 입력해보자.

:='(- @2$3 $2$2);N

위 텍스트를 입력하고 끝에서 TAB 키를 눌러주면 뭔가 변화가 생긴다.

| Kind  | Price | Ideal | Earning            |
|-------+-------+-------+--------------------|
| Book  |   100 |    99 | -1                 |
| Apple |   256 |    30 |                    |
| Coke  |     4 |     9 |                    |
|-------+-------+-------+--------------------|
|       |       |       |                    |
#+TBLFM: @2$4='(- @2$3 @2$2);N

커서가 있던 위치에 -1 이라는 값이 찍힌 것을 볼 수 있다. 즉 위에서 입력한 코드는 3번 칼럼의 값($3)에서 2번 컬럼의 값($2)을 빼라는 의미이고 그래서 커서 위치에 그 결과인 -1 이 찍힌 것이다.

그렇다면 해당 코드의 의미를 이렇게 이해할 수 있다:

  • := 은 이 셀에는 이후에 나오는 내용으로 치환시킨다는 의미다.
  • ' 는 Lisp의 quote 함수다.
  • (- @2$3 @2$2) 는 Lisp 코드로 2열 3행의 값에서 2열 2행의 값을 뺀다는 의미다. 제일 앞의 - 가 바로 빼기 함수다.
  • ;N 은 Lisp 코드의 끝을 의미한다.

몇몇 내용은 아마도 Emacs Lisp을 좀 알아야 이해가 될 것 같다.

그 외에 표의 하단에 아래와 같은 특수한 내용이 추가되어 있는 것이 보인다.

#+TBLFM: @2$4='(- @2$3 @2$2);N

#+ 로 시작하는 표기는 Org Mode 문서에서 프로퍼티를 표현할 때 사용하는 기호다. 아마도 방금 입력했던 식을 다르게 표시한 것으로 유추된다.

그렇다면 이 `#+` 부분을 약간 고치면 아마 식의 내용이 자동으로 바뀔 것 같다. TBLFM의 내용을 아래처럼 고쳐보자.

#+TBLFM: $4='(- $3 $2);N

수정한 뒤에는 C-c C-c 키를 눌러야 반영된다. 그러면 아래처럼 내용이 바뀐다.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |    -226 |
| Coke  |     4 |     9 |       5 |
|-------+-------+-------+---------|
|       |       |       |       0 |
#+TBLFM: $4='(- $3 $2);N

4행의 값은 3행 빼기 2행의 값이다 라는 것을 정의했다. 이제 자동으로 입력된 값 외의 다른 값을 수정하면 제일 오른쪽 행의 값이 자동으로 바뀐다. 그야말로 엑셀 같은 스프레드시트가 되었다. 불행히도 마지막 열 까지 데이터가 입력되어 버린 것은 좀 이상하긴 하지만…

사칙연산 사용해보기

이제 Lisp 코드 말고 calc 를 통해 제공되는 계산기 기능을 써보자. 앞의 내용과 비슷한 기능을 시험해 보기 위해 다시 테이블을 처음의 모양으로 리셋시켰다.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      [] |
| Apple |   256 |    30 |         |
| Coke  |     4 |     9 |         |
|-------+-------+-------+---------|
|       |       |       |         |

커서 위치도 여전히 [] 로 표시한 부분인 Earning 칼럼의 첫 로우에 있다. 이 위치에서 이제 아래와 같은 내용을 입력해보자.

=$3-$2

내용이 아까 보다는 단순하다. 어쨌든 입력을 하고 TAB 키를 누르는 순간 비슷하게 아래와 같이 표시된다.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |         |
| Coke  |     4 |     9 |         |
|-------+-------+-------+---------|
|       |       |       |         |
#+TBLFM: $4=$3-$2

뭔가 더 쉽고 직관적인 문법의 명령어를 썼는데 결과는 아까와 비슷해 보인다. 하지만 여기서 끝이 아니다.

이제 살짝 마법을 써보자. 하단의 #+TBLFM 쪽으로 커서를 가져한 후 C-c C-c 키를 눌러보자.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |    -226 |
| Coke  |     4 |     9 |       5 |
|-------+-------+-------+---------|
|       |       |       |       0 |
#+TBLFM: $4=$3-$2

갑자기 자동으로 계산되는 영역이 늘었다. Earning 칼럼 전체에 데이터가 자동으로 계산되어 들어갔다.

입력한 명령어의 차이에서 : 오퍼레이터 존재의 의미가 이해가 갈 것 같다. := 오퍼레이터는 현재 셀에만 함수를 적용한다는 의미이고, = 오퍼레이터는 이 행이나 열 전체에 해당 함수를 적용한다는 의미일 것 같다.

비록 가장 아래쪽에 이상한 데이터가 하나 들어가긴 했지만…

합계 구하기

이제 Price와 Ideal 수치 합계를 한번 계산해보자. 제일 아래의 빈 로우에 값을 채워 넣을 예정이다. 여전히 커서는 [] 로 표시한 위치에 있다고 가정하자.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |    -226 |
| Coke  |     4 |     9 |       5 |
|-------+-------+-------+---------|
|       |    [] |       |       0 |
#+TBLFM: $4=$3-$2

이번엔 조금 다른 방법을 써서 편집해보자. SPC m b e 키를 누르면 하단에 별도의 버퍼가 열리면서 여기에 해당 셀에 입력할 값을 별도로 입력할 수 있다. 이 특수 버퍼에 아래와 같은 내용을 입력해보자.

:=vsum(@2$2..@4$2)

미니버퍼에 잘 표시가 되어있지만 위의 것을 입력한 후 C-c C-c 키를 눌러야만 입력한 내용이 셀에 반영된다. 뭐… 수동으로 입력하는 거랑 별반 차이는 없겠지만 말이다.

이제 할 일은 입력된 내용 위에서 TAB 키를 눌러보는 것이다.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |    -226 |
| Coke  |     4 |     9 |       5 |
|-------+-------+-------+---------|
|       |   360 |       |       0 |
#+TBLFM: $4=$3-$2::@5$2=vsum(@2$2..@4$2)

아래에 Price 합계가 표시되었다. 그리고 제일 하단에 TBLFM 항목에도 방금 입력한 것과 비슷한 명령이 추가되었다.

입력했던 내용 자체는 앞서 살펴본 것과 비슷하다. 다만 vsum 함수가 특별한데, 이 함수는 지정된 범위 내의 데이터를 합하기 위한 calc 패키지의 함수다.

이제 남은 빈 곳도 채우기 위해 직접 TBLFM 코드를 수동으로 수정해보자.

#+TBLFM: $4=$3-$2::@5=vsum(@2..@4)

vsum 입력 인자에서 모든 행을 대상으로 하도록 수정했다. 이제 다 고쳤으니 C-c C-c 를 눌러보자.

| Kind                | Price | Ideal | Earning |
|---------------------+-------+-------+---------|
| Book                |   100 |    99 |      -1 |
| Apple               |   256 |    30 |    -226 |
| Coke                |     4 |     9 |       5 |
|---------------------+-------+-------+---------|
| Book + Apple + Coke |   360 |   138 |    -222 |
#+TBLFM: $4=$3-$2::@5=vsum(@2..@4)

뭔가 다 채워지긴 했는데 이상하다.🤦

계산 영역 제한하기

위 결과에서 좌측 하단의 이상한 것(?)을 지워보자. 방금 명령으로 입력되어 버린 모든 내용을 초기화하기 위해 제일 아래 로우 각 셀의 내용을 수동으로 다 지웠다.

그리고 TBLFM의 가장 마지막 항목을 아래처럼 고쳐봤다.

#+TBLFM: $4=$3-$2::@5$2..@5$4=vsum(@2..@4)

그리고 TBLFM 위에서 C-c C-c 를 눌러보자.

| Kind  | Price | Ideal | Earning |
|-------+-------+-------+---------|
| Book  |   100 |    99 |      -1 |
| Apple |   256 |    30 |    -226 |
| Coke  |     4 |     9 |       5 |
|-------+-------+-------+---------|
|       |   360 |   138 |    -222 |
#+TBLFM: $4=$3-$2::@5$2..@5$4=vsum(@2..@4)

이상한 Book + Apple + Coke 라는 이상한 데이터가 사라졌다.

원리는 간단하다. 위에서 사용한 코드는 값이 들어가는 부분을 영역으로 표현했다는 점을 잘 보자. @5$2..@5$4 까지만 vsum의 결과를 적용하겠다는 의미로 사용했다.

이상한 경고 메시지가 뜨긴 하는데 뭐 잘 되었으니 무시하자.😏

어쨌거나 이제 우리는 완벽한(?) 테이블 스프레드시트를 하나 만들었다. 참 먼 길이었다. 역시 구글 스프레드시트나 Numbers를 쓰는게 현명할 것 같다.

calc 함수 목록

아래 함수는 스프레드시트 연산에 사용할 수 있는 다양한 함수들이다. 모든 것을 정리한 것은 아니고 일단 정보를 구한 것만 정리한다.

  • exp(c) 지수(exponential)
  • log(c) log
  • log10(c) log10
  • sqrt(c) 루트(SQRT, Square-Root)
  • vcor(v1, v2) 상관계수(correlation)
  • vcount(v) 개수
  • vcov(v1, v2) 공분산(covariance)
  • vlen(v) 길이(length)
  • vmax(v) 최댓값(maximum)
  • vmean(v) 평균(average)
  • vmedian(v) 중간값
  • vmin(v) 최솟값(minimum)
  • vprod(v) 곱(product)
  • vsdev(v) 표준편차(standard deviation)
  • vsum(v) 합계
  • vvar(v) 분산(variance)