Dash + Plotly 사용법/튜토리얼
Programming Set-Up

Dash + Plotly 사용법/튜토리얼

일시불

Table of Contents

Introduction

1. 왜 dash를 쓸까요?

dash는 프론트엔드로 plotly.jsReact를 사용하고 백엔드로 Flask를 사용하는 데이터 시각화 프레임워크입니다. dash를 사용하면 파이썬 하나만으로 데이터 분석 결과를 간편하게 웹 서비스로 구현할 수 있습니다.

라이센스

Plot와 Dash 모두 MIT 라이선스의 Open source로 상업적 목적으로 무료로 사용 가능합니다. MIT 라이선스는 오픈소스를 활용하여 개발한 소스의 공개 의무가 없으며, 상업적 이용도 가능합니다. 다만 저작권 고지 및 라이선스 사본을 제품에 포함시키면 됩니다.

자세한 사항은 아래 링크를 참고하세요.

Is Plotly Free
Plotly’s open-source graphing libraries are free to use, work offline and don’t require any account registration. Plotly also has commercial offerings, such as Dash Enterprise and Chart Studio Enterprise.

2. plotly 설치하기

Dash를 본격적으로 시작하기 전 그래프 라이브러리인 plotly 에 대해서 배우겠습니다.

먼저 터미널에서 필요한 패키지들을 설치합니다.

pip install plotly dash kaleido pandas

설치가 완료되었다면 주피터 노트북에서 예제 코드를 실행해 보겠습니다. plotly는 내부적으로 샘플 데이터셋이 포함되어 있어서 코드를 연습하기 편리합니다.

import plotly.express as px

df = px.data.iris()
df

먼저 iris 데이터셋을 불러옵니다. 데이터프레임에서 각 컬럼 정보를 확인할 수 있습니다.

Untitled

여기서 sepal_lengthsepal_width 를 scatter plot으로 그려보겠습니다. xy 축에 어떤 데이터를 넣을지를 정해야 하는데요. xy 파라미터에 원하는 컬럼의 이름을 넣으면 됩니다. color 어트리뷰트에서는 각 포인트의 색상을 어떤 기준으로 넣을지를 선택할 수 있는데, 여기서는 iris의 species를 선택합니다.

fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
fig.show()

plotly 코드를 주피터 노트북에서 실행하면, 각 셀의 출력 부분에 그래프가 그려지게 됩니다.

Untitled

동일한 코드를 파이썬 모듈(.py) 에서 실행하면 어떻게 될까요? 아래 코드를 실행해 보겠습니다.

import plotly.express as px

df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
fig.show()

자동으로 브라우저가 열리고, 127.0.0.1:60274 과 같은 주소에 그래프가 나오게 됩니다.

Untitled

plotly 에서 자동으로 로컬 주소의 랜덤 포트 번호를 할당해 그림을 그리게 됩니다.

그림 파일로 저장하기

이 그래프를 그림 파일로 저장하려면 아래 코드를 마지막에 추가하면 됩니다.

fig.write_image('iris_scatter.png')

여기서 파일 명은 자유롭게 선택이 가능합니다. plotly 에서 그림 파일을 저장할 때 kaleido 라는 패키지를 내부적으로 사용하게 됩니다. 이때 저장 가능한 포맷은 다음과 같습니다.

  • png
  • jpg
  • webp
  • svg
  • pdf
  • eps

3. Plotly 기본 사용법

Plotly express

Ploty express는 Plotly의 Graph object의 쉽고 간편한 버전입니다. 마치 seaborn을 사용해 matplotlib 기반의 좀더 미려한 그래프를 쉽게 만들 수 있는 것과 비슷합니다. 따라서 미리 만들어져 있는 기본 그래프들을 가져와 사용한다고 이해하시면 됩니다.

seabornmatplotlib 에 대해서는 아래 링크를 참고하세요.

An introduction to seaborn - seaborn 0.11.2 documentation

아래 코드는 tips 데이터셋을 시각화하는 예제입니다. 시각화에는 px.pie 함수를 사용합니다. 코드를 실행해 보면,

import pandas as pd
import plotly.express as px

df = px.data.tips()
fig = px.pie(df, values="tip", names="day")
fig.show()

다음과 같은 파이 차트가 생성됩니다.

Untitled

Graph object

plotly express는 사전에 만들어둔 간편한 프리셋이기 때문에, 세부적인 변경이 필요할 경우에는 Graph object를 사용해야 합니다.

또한 3차원 그래프 중 mesh

import plotly.graph_objects as go
import numpy as np

# Download data set from plotly repo
pts = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt'))
x, y, z = pts.T

fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z, color='lightpink', opacity=0.50)])
fig.show()

isosurface 는 graph object를 사용해야만 구현할 수 있습니다.

import plotly.graph_objects as go

fig= go.Figure(data=go.Isosurface(
    x=[0,0,0,0,1,1,1,1],
    y=[1,0,1,0,1,0,1,0],
    z=[1,1,0,0,1,1,0,0],
    value=[1,2,3,4,5,6,7,8],
    isomin=2,
    isomax=6,
))

fig.show()

Layout

레이아웃은 화면에 보여지는 모든 것을 관리하는 클래스입니다. 그래프의 frames, title, color, tick, hover, legend와 같은 모든 부분을 커스터마이징할 수 있습니다.

traces

트레이스는 어떤 데이터를 화면에 표시할지를 나타냅니다. 예를 들어 annotation label에 나타날 값은 다음과 같이 정할 수 있습니다.

import pandas as pd
import plotly.express as px

df = px.data.tips()
fig = px.pie(df, values="tip", names="day")
fig.update_traces(textinfo="label+value")
fig.show()

['label', 'text', 'value', 'percent'] 중 원하는 값을 + 로 묶어서 textinfo 에 전달하면 됩니다.

3. dash 시작하기

이제 드디어 dash로 넘어왔습니다. dash는 plotly 그래프를 웹에서 표현할 수 있도록 하는 라이브러리입니다. 이때 flask 서버를 통해 그래프를 브라우저에 전달합니다. dash는 위에서도 설명했듯이, plotly로 만든 fig 객체를 레이아웃에 넣어 전체 페이지를 렌더링하는 방식으로 구성됩니다.

여기서부터는 노트북이 아닌 파이썬 모듈(.py ) 파일을 만들어 실습을 진행해주세요. 아래 예제 코드를 보면, dash.Dash 클래스가 플라스크의 Flask 객체와 유사하게 서버 인스턴스 app 을 생성하는 패턴을 가지고 있습니다.

import dash
from dash import dcc
from dash import html
import plotly.express as px
import pandas as pd

app = dash.Dash(__name__)

그리고 샘플 데이터셋을 만들어 보겠습니다.

df = pd.DataFrame(
    {
        "Fruit": [
            "Apples",
            "Oranges",
            "Bananas",
            "Apples",
            "Oranges",
            "Bananas",
        ],
        "Amount": [4, 1, 2, 2, 4, 5],
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"],
    }
)

다음으로는 plotly express에서 바 그래프를 위 데이터를 이용해 그리겠습니다.

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")

여기서부터가 중요한데, dash는 HTML 요소들을 layout에 넣어서 브라우저로 전달합니다. 우리가 만든 그래프를 레이아웃에 추가하려면 dcc 모듈이 필요합니다. dcc.Graph 클래스를 이용해 방금 만든 fig 객체를 전달합니다.

app.layout = html.Div(
    children=[
        html.H1(children='Hello Dash'),
        html.Div(
            children='''
        Dash: A web application framework for your data.
    '''
        ),
        dcc.Graph(id='example-graph', figure=fig),
    ]
)

마지막으로 서버를 실행합니다.

if __name__ == '__main__':
    app.run_server(debug=True)

전체 코드는 다음과 같습니다.

import dash
from dash import dcc
from dash import html
import plotly.express as px
import pandas as pd

app = dash.Dash(__name__)

df = pd.DataFrame(
    {
        "Fruit": [
            "Apples",
            "Oranges",
            "Bananas",
            "Apples",
            "Oranges",
            "Bananas",
        ],
        "Amount": [4, 1, 2, 2, 4, 5],
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"],
    }
)

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")

app.layout = html.Div(
    children=[
        html.H1(children='Hello Dash'),
        html.Div(
            children='''
        Dash: A web application framework for your data.
    '''
        ),
        dcc.Graph(id='example-graph', figure=fig),
    ]
)

if __name__ == '__main__':
    app.run_server(debug=True)

dcc 추가 설명

dash의 레이아웃은 HTML 컴포넌트들로 구성돼 있습니다.

app.layout = html.Div(
    children=[
        html.H1(children='Hello Dash'),
        html.Div(
            children='''
        Dash: A web application framework for your data.
    '''
        ),
        dcc.Graph(id='example-graph', figure=fig),
    ]
)

html 태그들은 html 모듈에 속해있고, 코어 클래스들은 dcc 모듈에 속해있습니다. html 모듈의 경우 모든 HTML 태그들이 어트리뷰트와 함께 구현되어 있습니다. React의 컴포넌트를 생각하시면 됩니다. 모든 HTML 컴포넌트의 목록은 아래 링크를 참고하세요.

Dash HTML Components | Dash for Python Documentation | Plotly
Dash provides all of the available HTML tags as user-friendly Python classes. This chapter explains how this works and the few important key differences between Dash HTML components …

HTML 마크업 대신, 마크다운을 활용할 수도 있습니다.

markdown_text = '''
### Dash and Markdown

Dash apps can be written in Markdown.
Dash uses the [CommonMark](http://commonmark.org/)
specification of Markdown.
Check out their [60 Second Markdown Tutorial](http://commonmark.org/help/)
if this is your first introduction to Markdown!

```python
import dash

app = dash.Dash(__name__)
```
'''

app.layout = html.Div([
    dcc.Markdown(children=markdown_text)
])

마크다운 외에도 많은 고레벨 API가 dcc 안에 구현되어 있습니다.

import dash
from dash import dcc
from dash import html

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        html.Div(
            children=[
                html.Label('Dropdown'),
                dcc.Dropdown(
                    options=[
                        {'label': 'New York City', 'value': 'NYC'},
                        {'label': u'Montréal', 'value': 'MTL'},
                        {'label': 'San Francisco', 'value': 'SF'},
                    ],
                    value='MTL',
                ),
                html.Br(),
                html.Label('Multi-Select Dropdown'),
                dcc.Dropdown(
                    options=[
                        {'label': 'New York City', 'value': 'NYC'},
                        {'label': u'Montréal', 'value': 'MTL'},
                        {'label': 'San Francisco', 'value': 'SF'},
                    ],
                    value=['MTL', 'SF'],
                    multi=True,
                ),
                html.Br(),
                html.Label('Radio Items'),
                dcc.RadioItems(
                    options=[
                        {'label': 'New York City', 'value': 'NYC'},
                        {'label': u'Montréal', 'value': 'MTL'},
                        {'label': 'San Francisco', 'value': 'SF'},
                    ],
                    value='MTL',
                ),
            ],
            style={'padding': 10, 'flex': 1},
        ),
        html.Div(
            children=[
                html.Label('Checkboxes'),
                dcc.Checklist(
                    options=[
                        {'label': 'New York City', 'value': 'NYC'},
                        {'label': u'Montréal', 'value': 'MTL'},
                        {'label': 'San Francisco', 'value': 'SF'},
                    ],
                    value=['MTL', 'SF'],
                ),
                html.Br(),
                html.Label('Text Input'),
                dcc.Input(value='MTL', type='text'),
                html.Br(),
                html.Label('Slider'),
                dcc.Slider(
                    min=0,
                    max=9,
                    marks={
                        i: 'Label {}'.format(i) if i == 1 else str(i)
                        for i in range(1, 6)
                    },
                    value=5,
                ),
            ],
            style={'padding': 10, 'flex': 1},
        ),
    ],
    style={'display': 'flex', 'flex-direction': 'row'},
)

if __name__ == '__main__':
    app.run_server(debug=True)