Shiny for Python

Python으로 반응형 웹 어플리케이션을 만들수 있는 Shiny for Python에 대해 소개합니다.
python
shiny
Author

Chaehee Lee

Published

August 19, 2022

1 Shiny for python 소개

이제 파이썬에서도 Dash 이외에도 shiny를 사용하여 반응형 웹앱을 쉽게 구현할 수 있게 되었습니다.
아직 초기 단계라 많은 것이 구현되지는 않았고 Alpha단계이므로 API가 변경되거나 아래의 설명과 다른 점이 생길 수 있습니다. 글의 내용은 Shiny for Python를 참고했습니다.

2 Shiny for Python setup

2.1 python과 Shiny 설치하기

우선 Shiny app을 저장할 디렉토리를 만듭니다.

# make new directory for Shiny App
mkdir myapp

# change directory
cd myapp

python은 Python.org Anaconda 에서 설치할 수 있습니다.

python이 설치되었다면 사용하는 디렉토리의 폴더에 python 가상 환경을 만들고 이를 활성화합니다.

# Create a virtual environment in the .venv subdirectory
python3 -m venv venv

# Activate the virtual environment
source venv/bin/activate

shiny 를 설치합니다.

# install shiny
pip install shiny

2.2 Shiny Server에서 이용하기

Shiny for python은 Shinyapps.io, Shiny server, shinylive와 같은 다양한 방법으로 배포될 수 있습니다. 그중 shiny server에 배포하는 방법을 소개합니다.

Shiny Server는 v1.5.19 이상이 필요합니다. 만약 이전 버전을 사용하고 있다면 업데이트 합니다.

#shiny-server v1.4.19
wget https://download3.rstudio.org/ubuntu-18.04/x86_64/shiny-server-1.5.19.995-amd64.deb
gdebi shiny-server-1.5.19.995-amd64.deb

shiny server에서 python 파일을 실행하기 위해 config file을 수정해야 합니다.

config file 은 /etc/shiny-server/shiny-server.conf 에 위치해있습니다.

# Edit the file /etc/shiny-server/shiny-server.conf
sudo vim /etc/shiny-server/shiny-server.conf

config file 을 열어 코드 상단에 python 경로를 추가합니다.

예를 들어 /home/js/myapp/venv/bin/python3를 사용하고 싶다면 코드는 아래와 같습니다.

# Use system python3 to run Shiny apps
python /home/js/myapp/venv/bin/python3;

# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;

# Define a server that listens on port 3838
server {
  listen 3838;

  # Define a location at the base URL
  location / {

    # Host the directory of Shiny Apps stored in this directory
    site_dir /srv/shiny-server;

    # Log all Shiny output to files in this directory
    log_dir /var/log/shiny-server;

    # When a user visits the base URL rather than a particular application,
    # an index of the applications available in this directory will be shown.
    directory_index on;
  }
}

3 Shiny for Python 사용

샤이니 앱은 크게 ui, server 부분으로 구성되어 있습니다.
ui는 사용자가 보는 화면에 해당하는 부분이며, server는 ui에서 받은 값들을 계산하는 부분입니다. 사용자가 ui에 값들을 입력하면 이들을 바탕으로 server에서 계산하고, 이 결과물을 다시 ui 부분에 출력하는 방식으로 작동합니다.

간단한 예시를 통해 이를 이해하고, python의 코드가 R과 비교해 어떤 문법을 가지는지 알아보겠습니다.

3.1 Shiny App

# import shiny
from shiny import ui, render, App

# ui
app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40),
    ui.output_text_verbatim("txt"),
)

# server
def server(input, output, session):
    @output
    @render.text
    def txt():
        return f"n*2 is {input.n() * 2}"

# This is a shiny.App object. It must be named `app`.
app = App(app_ui, server)

library(shiny)

# ui
ui = fluidPage(
    sliderInput("n", "N", 0, 100, 40),
    textOutput("txt")
  )

server = function(input, output,session) {
     output$txt = renderText("n*2 is",input$n * 2,"}")
  }

shinyApp(ui, server)

위는 ui에서 입력 받은 값을 server에서 2를 곱해주어 계산하고 이를 ui부분에서 출력하여 보여주는 예시입니다.

  • 먼저 from shiny import *를 통해 필요한 shiny 모듈을 불러옵니다.

  • ui의 ui.input_slider() 함수를 통해 입력값을 받습니다. 이처럼 인풋에 해당하는 부분은 ui.input_*() 함수를 통해 만들 수 있습니다. “n”은 해당 input의 이름에 해당하는 부분이며, N은 label, 0,100,40은 각각 min, max,value에 해당하는 인자입니다.

  • 입력된 인풋값을 서버에 전송하면 이를 토대로 계산하여 값을 return하고 @render.text 라는 decorator를 통해 텍스트 형태로 렌더링합니다.

  • 다시 ui 부분에서는 ui.output_text_verbatim() 함수를 통해 텍스트를 출력합니다. 이처럼 ui부분에서 출력할 때는 ui.output_* 함수가 사용됩니다.

3.2 Shiny for Python의 문법

위의 예시를 이용하여 R과 Python의 문법을 간단하게 비교해보면 다음과 같습니다.

python R
import shiny from shiny import * library(shiny)
UI app_ui = ui.page_fluid( ui = fluidPage(
input   ui.input_slider(“n”,“N”,0,100,40),   ui.output_text_verbatim(“txt”)   )   sliderInput(“n”,“N”,0,100,40)   textOutput(“txt”))
Server def server(in, out, session): server=function(in,out,session){
decorator @output @render.text
output   def txt:
    return input.x()
 output$txt = renderText (input $ x)   }
app app = App(app_ui, server) shinyApp(ui, server)

인풋에 해당하는 부분은 ui.input_*(), ui 부분에서 출력할 때는 ui.output_*()함수들을 사용합니다.
서버 부분에서 def server(input, output, session): def txt(): 와 같이 파이썬의 함수 문법을 사용합니다.

python R
decorater @render.text …
@reactive.event() @reactive.Calc() …
@output
renderText() …
reactive({})
output$id

예시의 server부분에서 @render.text @output이 사용된 것을 볼 수 있습니다. 이것은 함수를 인자로 받아 함수를 출력하는 decorator 라는 함수입니다. 보통 python 에서 여러 함수들이 부분적으로 중복될때 코드의 재사용을 용이하게 하기 위해 사용됩니다. 여기서는 그냥 함수의 일종이라고 생각하면 될 것 같습니다.

R에서는 렌더링을 위해 renderPlot, renderText 같은 함수를 사용하지만 Python에서는 @render.text 같은 decorator를 사용합니다. 위의 예시에서는 txt() 라는 텍스트를 반환하는 함수에 @render.text라는 decorator가 적용됩니다. 이는 텍스트를 반환하는 함수를 리턴하는 함수로 텍스트를 렌더링해 주는 함수라고 생각할 수 있습니다.

decorator는 output, module, reactivity, rendering 등 많은 부분에서 사용됩니다. 하나의 함수에 여러개가 적용될 수 있으며 @render.text 는 @output 보다 먼저 적용되어야 하는 것과 같이 순서나 parameter가 정해져 있습니다.

python R
HTML ui.div(), ui.a() tags$div, tags$a

HTML 태그의 경우는 ui.tags.*() 를 통해서 사용할 수 있습니다. 예를 들어 li 태그의 경우 ui.tags.li()로 사용 가능합니다.
일반적으로 많이 사용되는 div, span, a 같은 태그들은 ui.div()와 같이 ui 서브모듈에서 직접 사용할 수 있습니다.

python R
mutability objects can be modified objects cannot be modified

python으로 shiny앱을 작성할때 가변성 처리(Handling mutability)도 고려해야합니다. Python에서 문자,숫자열,튜플 같은 간단한 객체들은 변경할 수 없지만 딕셔너리,리스트 같은 대부분의 객체들은 수정할 수 있습니다.

이로 인해 반응형 프로그래밍에서 문제가 발생할 수 있습니다. 즉, 프로그램의 한 부분에서 객체를 수정하면 프로그램의 다른 부분과 값이 다른 문제가 발생할 수 있습니다.

이러한 문제를 해결하기 위한 몇가지 방법들이 있습니다.

첫번째는 두 값이 객체를 copy해서 동일한 객체를 먼저 가리키는 것을 피하는 것입니다. a = [1,2], b = a.copy와 같이 사용하게 되면 a의 값이 바뀌어도 b는 바뀌기 이전의 값을 가리키게 됩니다. 두번째는 객체를 변경하는 연산자나 매서드를 사용하는 것입니다. 예를 들어 a = [1,2]를 a = [1,2,3]로 만들고 싶을 때 a = a+[3]보다는 a.append(3)을 사용하는것이 바람직합니다. 마지막은 변경 불가능한 객체를 사용하는 것입니다. 리스트 대신 튜플을 사용하거나 pyrsistent 패키지의 리스트나 딕셔너리를 사용할 수 있습니다.

4 Shinylive

Shinylive는 Shiny for Python에서 사용할 수 있는 실험 단계의 모드로 앱이 브라우저에서 별도의 서버없이 실행될수 있습니다.

기존에는 서버에서 파이썬과 샤이니를 실행하고, 클라이언트가 웹브라우저를 통해 접속하였습니다. 그러나 앱이 shinylive와 배포된다면 파이썬 컴퓨팅이 서버가 아닌 클라이언트의 웹 브라우저 에서 수행됩니다. 따라서 앱을 실행할 때 서버보다 클라이언트의 컴퓨팅 성능에 의존하며, 파일을 제공하는 서버는 존재하지만 파이썬과 샤이니를 실행하지는 않습니다.

shinylive와 배포된 앱은 컴퓨터에 Python 또는 Shiny를 설치할 필요가 없이 URL만으로 공유 실행될수 있습니다.

4.1 shinylive 배포

shinylive editor을 통해 간단하게 shinylive를 통해 배포해볼 수 있습니다 .

이외에도 Netlify, GITHUB gist를 통해서도 가능하며 Sharing Shinylive applications를 참고할 수 있습니다.

Citation

BibTeX citation:
@online{lee2022,
  author = {Lee, Chaehee},
  title = {Shiny for {Python}},
  date = {2022-08-19},
  url = {https://blog.zarathu.com/posts/2022-08-26-shinyforpython},
  langid = {en}
}
For attribution, please cite this work as:
Lee, Chaehee. 2022. “Shiny for Python.” August 19, 2022. https://blog.zarathu.com/posts/2022-08-26-shinyforpython.