Altair에서 변형 함수 사용하기

Vega-Lite는 몇 가지 간단한 데이터 변형 함수를 제공합니다. 물론 보통의 경우에 차트로 시각화하려는 원본 데이터는 DataFrame의 형태로 나와 있고, 데이터 프레임 자체를 조작하여 원하는 변형을 미리 처리하는 것이 보다 유연하고 강력합니다. (데이터 프레임은 원래 그럴려고 쓰는 것이니까요) 그렇지만 원본 데이터를 미리 조작하기 어려운 경우라면 간단한 변형은 Vega-Lite의 기능을 활용하는 방법도 고려해볼 수 있습니다. 오늘은 이 글을 통해서 Altair에서 데이터 변형을 적용하는 방법을 알아보겠습니다.

인코딩에서 집합 함수를 사용하기

가장 간단하게 쓰이는 방법으로는 차트 정보를 인코딩할 때, 필드 이름 대신에 집합 함수를 적용한 표현식의 문자열을 사용하는 것입니다. average(), count() 같은 것을 이런 식으로 표현할 수 있습니다. 다음은 vega_datasets에서 자동차에 관한 정보를 가져와서 실린더 수 별 가속력의 평균치를 표현하는 코드입니다.

import altair as alt
from vega_datasets import data

cars = data.cars()
alt.Chart(cars).mark_bar().encode(
    y='Cylinders:O',
    x='average(Acceleration):Q'
)

Altair는 가속력에 대한 데이터를 자동으로 반대쪽 축인 실린더 수별로 구분하여 그 평균을 내어 차트를 그립니다. 또 다른 예를 들어보겠습니다. 1~19사이의 정수를 무작위로 생성해서 각 값마다 몇 개씩 만들어졌는지를 살펴봅니다.

import numpy as np
import pandas as pd

ts = pd.DataFrame({'t': np.random.randint(1, 20, (80, ))})
alt.Chart(ts).mark_bar().encode(
    x='t:O', y='count()'
)

사용 가능한 집합 함수의 전체 목록은 이곳에서 확인할 수 있습니다. 집합 함수 외의 데이터 변형이 필요한 경우에는 Altair에서 제공하는 transform_*() 함수를 사용합니다. (전체 목록은 Altair 문서 페이지에서 확인할 수 있습니다.) 이글에서 모든 변형 함수에 대한 설명을 할 필요는 없을 것 같고, 대표적인 몇 가지 예를 보면서 사용 방법을 익혀보도록 하겠습니다.

변형함수 적용하기

Chart 객체에는 “transform_”으로 시작하는 여러 메소드들이 있습니다. 이 메소드들은 Vega-Lite에서 제공하는 여러가지 transformation 함수를 사용하여 기존 필드의 값을 변경하거나, 새로운 필드를 생성합니다. 먼저 앞서 예를 들었던 평균값을 생성하는 trasnformation을 적용해 보겠습니다. 집합 함수를 적용하기 위해서는 transform_aggregateion() 메소드를 사용하며, 집합 함수를 수동으로 적용한 새로운 필드를 생성합니다.

alt.Chart(cars).mark_bar().encode(
    y='mean_acc:Q', x='Cylinders:O'
).transform_aggregation(
    mean_acc='mean(Acceleration)',
    groupby=['Cylinders']
)

위 예에서 mean_acc='mean(Acceleration)' 부분을 보겠습니다. 키워드 인자로 지정된 이름은 평균값을 계산하여 새롭게 생성할 필드명이 됩니다. 그리고 group_by= 키워드 인자를 사용해서 평균을 계산할 때, 그룹핑할 기준을 지정해줄 수 있습니다.

일반적인 데이터 변형 방법

일반적인 변형함수의 사용은 키워드인자로 새로운 필드를 지정하고, 그 필드값은 보통 문자열로 표현되는 어떤 표현식이 됩니다. transform_aggregate()의 예에서 보듯이, mean_acc='mean(Acceleration)'과 같이 함수 mean()Acceleration 필드를 전달하는 표현식을 문자열 그대로 쓰게 됩니다.

이 때 사용하는 표현은 함수의 종류에 따라 조금 달라질 수 있습니다. 집합 함수에서는 주로 필드 이름을 그대로 사용하게 됩니다. 필터 함수와 같이 개별 값의 특성을 사용해야 하는 경우에는 datum이라는 용어를 사용합니다. 간단한 예로 transform_filter() 함수를 사용하는 예를 보겠습니다.

alt.Chart(data.population.url).mark_area().encode(
  x='age:O', y='people:Q'
).transform_filter(
  'datum.year == 2000 & datum.sex == 1'
)

datum은 현재 데이터를 가리키는 용어라고 생각할 수 있습니다. 문자열 내에서 datum은 마치 현재 row에 대한 javascript객체처럼 다룰 수 있습니다. datum.year라고 쓸 수 있으며, datum["year"]와 같이 쓸 수 도 있습니다. 만약 필드 이름에 공백이 있거나 한다면 후자의 표현을 써야겠죠. 문자열 내에 표현식을 쓰는 것이 부담스럽다면, altair.datum을 사용할 수 있습니다. 이는 현재 데이터에 대한 프록시처럼 작동합니다.

chart..transform_filter((alt.datum.year == 2000) & (alt.datum.sex == 1))

필터 함수에 인자가 될 수 있는 값들은 다음과 같습니다.

  1. datum을 포함하는 자바 스크립트 표현식 문자열
  2. alt.datum을 사용한 파이썬 표현식
  3. Field*Predicate를 사용한 표현

Field*Predicate는 같거나 크거나 작은 관계를 나타내는 래퍼 클래스입니다. 특정 필드의 값을 기준으로 필터링할 때는 field=, equal= 인자를 사용하여 관계식을 표현할 수 있습니다.

alt.Chart(data.population.url).mark_line().encode(
    x='year:O', y='sum(people):Q'
).transform_filter(
    alt.FieldEqualPredicate(field='sex', equal=1)
)
  • FieldEqaulPredicate(field=, equal=)
  • FieldLTPredicate(field=, lt=), FieldLTEPrdicate(field=, lte=)
  • FieldGTPredicate(field=, gt=), FieldGTEPredicate(field=, gte=)

“OneOf”, “Range” 를 기준으로 하는 Predicate를 구성할 수도 있습니다. 다음 예시는 FieldRangePredicate를 사용하여 1920년~1990년 범위의 연령별 인구를 각각 다른 색의 선 그래프로 비교할 수 있게 해줍니다.

alt.Chart(data.population.url).mark_line().encode(
    x='age:O', y='sum(people):Q', color='year:O'
).transform_filter(
    alt.FieldRangePredicate(field='year', range=[1920, 1990])
)

여러 Predicate를 연결할 때에는 “and”, “or”, “not”을 키로 하고, 여러 Predicate의 리스트를 값으로 하는 사전을 만들어서 전달합니다. 처음 필터 예제는 연도와 성별을 and 조건으로 적용하여 필터링하였으므로, 다음과 같이 표현할 수 있습니다.

alt.Chart(data.population.url).mark_area(opacity=0.5).encode(
    x='age:O', y='people:Q'
).transform_filter(
    {'and': [
        alt.FieldEqualPredicate(field='year', equal=2000),
        alt.FieldEqualPredicate(field='sex', equal=1)
    ]}
)

매개 변수 그래프 그리기

필터의 표현식을 사용하면 매개 변수를 사용한 그래프를 그리는 것이 가능합니다.

ts = pd.DataFrame({'t': np.arange(800)})
alt.Chart(ts).mark_line(strokeWidth=0.3).encode(
    x='x:Q', y='y:Q', order='t:Q'
).transform_calculate(
    x='cos(datum.t * PI / 50)', y='sin(datum.t * PI / 30)'
)

심화 – Predicate를 사용해야 하는 경우

대부분의 경우에는 자바스크립트 표현식 문자열을 사용하는 것이 가장 간단하게 쓸 수 있기 때문에 더 선호되는 방식일 것지만, 특정 데이터 형식과 관련해서는 그렇지 못한 경우가 있습니다. 예를 들어 날짜 형식이 이와 관련이 있는 경우입니다. 예를 들어 어떤 시계열 데이터에서 2005년 이후의 데이터만 필터링하고 싶은 경우에는 "datuem.date > '2007-01-01'"과 같은 식의 표현식은 작동하지 않습니다. Vega 내부적으로 문자열과 Date 형식의 데이터를 구분하기 때문인 것으로 보입니다.

이 경우에는 alt.Datetime() 클래스와 FieldGTEPredicate()를 사용해서 기간의 비교를 구현할 수 있습니다.

# 전체 기간 영역의 그래프
base = alt.Chart(data.stocks.url,width=200,height=200).mark_line().encode(
    x='date:T',y='price:Q',color='symbol:N'
)

# 2005년 이후.  alt.DateTime(year=2005)보다 큰 데이터만으로 필터링
base | base.transform_filter(
    alt.FieldGTEPredicate('date', gte=alt.DateTime(year=2007))
)

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop