Altair로 시각화하기
Altair는 Vega 및 Vega-Lite에 기반한 Python용 선언적 통계 시각화 라이브러리입니다. GitHub에서 개발현황을 볼 수 있습니다.
Altair를 사용하면 데이터와 의미를 이해하는 데 더 많은 시간을 쓸 수 있습니다. Altair의 API는 간단하고 친숙하며 일관성이 있는 Vega-Lite 문법 위에 구축되었습니다. 이 단순함은 최소한의 코드로 아름답고 효과적인 시각화를 할 수 있습니다.
특징¶
- 신중하게 설계된 선언적 Python API
- 자동 생성 되는 내부 API는 Vega-Lite과 완전히 일치합니다.
- Vega-Lite JSON 사양에 맞는 코드를 자동 생성합니다.
- Jupyter Notebook, JupyterLab, Nteract, nbviewer에서 시각화를 표시합니다.
- 시각화를 PNG, SVG, HTML로 내보낼수 있습니다.
- 갤러리에서 수십 가지 예제를 제공합니다.
Altair + Jupyter notebook¶
Jupyter notebook을 사용하는 경우, 버전 5.3 이상에서 가장 잘 작동합니다. 또한 노트북에서 Altair를 사용하려면 vega 패키지를 추가로 설치해야 합니다.
Altair 설치방법¶
Conda를 이용해 Altair를 설치하려면 다음 명령을 실행하십시오.
install
conda install -c conda-forge altair vega_datasets vega
간단한 맛보기¶
산포도(Scatter plot)를 한 번 그려보겠습니다.
import altair as alt
from vega_datasets import data
alt.renderers.enable("notebook")
iris = data.iris()
alt.Chart(iris).mark_point().encode(x="petalLength", y="petalWidth", color="species")
사용되는 데이터의 형태¶
Altair의 데이터는 Pandas
Dataframe을 기반으로 구축되었습니다. 이 튜토리얼에서는 아래와 같은 간단한 Dataframe을 작성해 사용할 겁니다. 그리고 데이터의 레이블이있는 열은 Altair의 시각화에 필수적입니다.
import pandas as pd
data = pd.DataFrame({"a": list("CCCDDDEEE"), "b": [2, 7, 4, 1, 2, 6, 8, 4, 7]})
차트 개체(Chart Object)¶
Altair의 기본 객체는 데이터(Dataframe)를 단일 인수로 취하는 Chart
입니다.
import altair as alt
chart = alt.Chart(data)
위에서 Chart
객체를 정의했지만 아직 차트에 데이터를 처리는 하지 않았습니다. 인코딩과 마크작업을 통해 데이터를 처리해보도록 하겠습니다.
인코딩 과 마크(Encodings and Marks)¶
차트 개체를 사용하여 데이터를 시각화하는 방법을 지정할 수 있습니다. 이 작업은 Chart
객체의 mark
속성을 통해 수행됩니다. Chart.mark_*
메소드를 통해 사용합니다. 예를 들어 mark_point()
를 사용하여 데이터를 점으로 표시 할 수 있습니다.
alt.Chart(data).mark_point()
여기서 렌더링은 데이터 세트의 한 행당 하나의 점으로 구성되며,이 점들에 대한 위치를 아직 지정하지 않았기 때문에 모두 겹쳐져서 표시됩니다.
포인트를 시각적으로 분리하기 위해 다양한 인코딩 채널을 데이터의 열에 매핑 할 수 있습니다. 예를 들어 데이터의 변수 a를 x축 위치를 나타내는 x 채널로 인코딩 할 수 있습니다. 이것은 Chart.encode()
메소드로 할 수 있습니다.
encode()
메서드는 인코딩 채널(x, y, 색상, 모양, 크기 등)을 열 이름으로 접근 할 수 있게 합니다. Pandas
dataframe의 경우 Altair가 각각의 열에 적합한 데이터 유형을 자동으로 정해 줍니다.
이제 예시들를 통해 확인해 봅시다.
막대그래프(bar graph)¶
# simple barplot
import altair as alt
import pandas as pd
data = pd.DataFrame(
{
"a": ["A", "B", "C", "D", "E", "F", "G", "H", "I"],
"b": [28, 55, 43, 91, 81, 53, 19, 87, 52],
}
)
alt.Chart(data).mark_bar().encode(x="a", y="b")
선그래프(line graph)¶
import altair as alt
import numpy as np
import pandas as pd
x = np.arange(100)
data = pd.DataFrame({"x": x, "sin(x)": np.sin(x / 5)})
alt.Chart(data).mark_line().encode(x="x", y="sin(x)")
선그래프에 데이터를 점으로 표시하기¶
import altair as alt
import numpy as np
import pandas as pd
x = np.arange(100)
data = pd.DataFrame({"x": x, "sin(x)": np.sin(x / 5)})
alt.Chart(data).mark_line(point=True).encode(x="x", y="sin(x)")
히트맵(heat map)¶
import altair as alt
import numpy as np
import pandas as pd
# Compute x^2 + y^2 across a 2D grid
x, y = np.meshgrid(range(-5, 5), range(-5, 5))
z = x**2 + y**2
# Convert this grid to columnar data expected by Altair
data = pd.DataFrame({"x": x.ravel(), "y": y.ravel(), "z": z.ravel()})
alt.Chart(data).mark_rect().encode(x="x:O", y="y:O", color="z:Q")
히스토그램(histogram)¶
import altair as alt
from vega_datasets import data
movies = data.movies.url
alt.Chart(movies).mark_bar().encode(
alt.X("IMDB_Rating:Q", bin=True),
y="count()",
)
면적그래프(area graph)¶
import altair as alt
from vega_datasets import data
iowa = data.iowa_electricity()
alt.Chart(iowa).mark_area().encode(x="year:T", y="net_generation:Q", color="source:N")
스트립 플롯(strip plot)¶
import altair as alt
from vega_datasets import data
source = data.cars()
alt.Chart(source).mark_tick().encode(x="Horsepower:Q", y="Cylinders:O")
더 복잡한 그래프 예제¶
alt.renderers.enable("notebook")
alt.data_transformers.enable("json")
data = pd.DataFrame(
{
"Day": range(1, 16),
"Value": [
54.8,
112.1,
63.6,
37.6,
79.7,
137.9,
120.1,
103.3,
394.8,
199.5,
72.3,
51.1,
112.0,
174.5,
130.5,
],
}
)
data2 = pd.DataFrame([{"ThresholdValue": 300, "Threshold": "hazardous"}])
bar1 = alt.Chart(data).mark_bar().encode(x="Day:O", y="Value:Q")
bar2 = (
alt.Chart(data)
.mark_bar(color="#e45755")
.encode(x="Day:O", y="baseline:Q", y2="Value:Q")
.transform_filter("datum.Value >= 300")
.transform_calculate("baseline", "300")
)
rule = alt.Chart(data2).mark_rule().encode(y="ThresholdValue:Q")
text = (
alt.Chart(data2)
.mark_text(align="left", dx=215, dy=-5)
.encode(
alt.Y("ThresholdValue:Q", axis=alt.Axis(title="PM2.5 Value")),
text=alt.value("hazardous"),
)
)
bar1 + text + bar2 + rule
population = data.population.url
# Define aggregate fields
lower_box = 'q1(people):Q'
lower_whisker = 'min(people):Q'
upper_box = 'q3(people):Q'
upper_whisker = 'max(people):Q'
# Compose each layer individually
lower_plot = alt.Chart(population).mark_rule().encode(
y=alt.Y(lower_whisker, axis=alt.Axis(title="population")),
y2=lower_box,
x='age:O'
)
middle_plot = alt.Chart(population).mark_bar(size=5.0).encode(
y=lower_box,
y2=upper_box,
x='age:O'
)
upper_plot = alt.Chart(population).mark_rule().encode(
y=upper_whisker,
y2=upper_box,
x='age:O'
)
middle_tick = alt.Chart(population).mark_tick(
color='white',
size=5.0
).encode(b
y='median(people):Q',
x='age:O',
)
lower_plot + middle_plot + upper_plot + middle_tick
countries = alt.topo_feature(data.world_110m.url, "countries")
base = (
alt.Chart(countries)
.mark_geoshape(fill="#666666", stroke="white")
.properties(width=300, height=180)
)
projections = ["equirectangular", "mercator", "orthographic", "gnomonic"]
charts = [base.project(proj).properties(title=proj) for proj in projections]
alt.vconcat(alt.hconcat(*charts[:2]), alt.hconcat(*charts[2:]))
barley = data.barley()
points = (
alt.Chart(barley)
.mark_point(filled=True)
.encode(
alt.X(
"mean(yield)",
scale=alt.Scale(zero=False),
axis=alt.Axis(title="Barley Yield"),
),
y="variety",
color=alt.value("black"),
)
)
error_bars = (
alt.Chart(barley).mark_rule().encode(x="ci0(yield)", x2="ci1(yield)", y="variety")
)
points + error_bars
마치며¶
Altair의 예제를 살펴보면서 이 도구의 잠재력과 간결함을 느끼셨을 것입니다. 하지만 Altair는 아래와 같이 몇가지 주의사항이 있습니다.
- API는 여전히 꽤 새로운 것입니다. 따라서 일부에 버그가 존재할 수 있습니다.
- 문서화가 아직 부족합니다. 때때로 Vega-Lite 문서를보고 답을 찾아야합니다.
- 처리 할 수있는 데이터 포인트의 수는 현재 매우 적습니다. 지금은 5,000으로 제한되어 있지만 앞으로 늘어 날 것입니다.
그러나 이런 주의사항에도 Altair은 많은 발전가능성을 가지고 있습니다. 앞으로 matplotlib
의 아성을 뛰어 넘을 수 있을지 지켜보도록 하죠.