shinydashboardでWebアプリを作ろう!
導入・練習編

author: Unadon (見習い飯炊き兵)


動作環境:Mac OS Sierra 10.12.1; R version3.3.1; rstan 2.10.1 shinydashboardSample.png
Figure Web上でアニメTwitterフォロワー数を放送年・シーズンごとに可視化して共有


 

はじめに


shinydashboardというWebアプリ作成パッケージを使ってみます。

ゆくゆくは機械学習のモデルなどをWeb上で動かしてみたいのですが(できるんかな)、

まずはAPIからのデータ取得と成形、可視化をWeb上で勝手にやらせてみたいとおもいます。


本稿の目的


  1. Twitterのアニメ公式アカウントのフォロワーデータを抽出する
  2. フォロワー数の推移と増加量を時系列で可視化する
  3. plotlyとformattableで結果をまとめて、shinydashboardに整理。
  4. Web上にデプロイ(アップ)して、1~3を全部Web上でやらせる
  5. 要するにこれ: AnimeTwitterShinyDashboardをつくる。
レスポンスに時間がめっちゃかかるので、気長にお待ち下さい。

Webアプリのユーザーインターフェース構成


今から行う方法は、たぶんアプリ開発では一般に取られない手段です。 というのも、インターフェースの定義から中身で行う処理まで、全部一つのファイルに書いてしまうからです。普通こんなことしません。 ですが練習になりますし、自分にとって最初わかりやすかったので(説明もしやすいので), 1ファイルで1アプリを作ってしまいましょう。

今回のshinydashboardは、大きく分けて、次のようなパーツから構成しました。

shinydashboardExplain.png

見た目(ユーザーインターフェース, ui)は3つのパーツでできています。

1.dashbordHeader

–テキストでタイトルを表示させています

2.dashboardSidebar

–SlideInputという入力ウィジェットで、値を入力してもらいます

3.dashboardBody

–SlideInputで入力された値を使って、色々処理(後述)した結果を表示(output)させています。


Webアプリのserver(裏で動く処理内容)


およその流れは以下です。

  1. スライドバーからの入力情報を受け取る
  2. APIからフォロワー数を抜き出す
  3. plotlyとformattableで可視化
  4. 結果をユーザーインターフェースに返す

まず、サイドバーにつけたスライドバーから得た値、つまり放送年とシーズンの情報を使って、APIからアニメの公式アカウントTwitterフォロワー数を取得します。

APIから取得したデータを整形し、plotlyとformattableで可視化。

結果をdashbordBodyに返します。


実装コード


まずは、好きなディレクトリにフォルダを作成し、Rprojectを立ち上げてください。

shinydashboardは、ほとんどshinyと同じです(というかshinyがベースです)。

したがって、こちらが最強の資料になります。 細かい部分は、最強の資料を御覧ください。

プロジェクトがたちあがったところで、 フォルダの中身はプロジェクト名が入ったものと、app.Rにしましょう。

shinydashboardProject.png

そして、app.Rの中身に、次のコードを転記してください。

(未インストールのライブラリはインストールしておきましょう)

library(shinydashboard)
library(shiny)
library(RCurl)
library(rlist)
library(rjson)
library(tidyverse)
library(formattable)
library(anytime)
library(RcppRoll)
library(scales)
library(shinydashboard)
library(shiny)
library(formattable)
library(plotly)


ui <- dashboardPage(
    dashboardHeader(title = "アニメフォロワー"),
    dashboardSidebar(
            sliderInput("Year",
                    "放送年",
                    min = 2013,
                    max = 2017,
                    value = 2017),
            sliderInput("Season",
                        "1=冬; 2=春; 3=夏; 4=秋",
                        min = 1,
                        max = 4,
                        value = 2)
        ),
    dashboardBody(
        fluidRow(
        box(title = "アニメTwitter公式アカウントのフォロワー推移(過去30日)\nレスポンスがめちゃくちゃ遅いので、気長にお待ち下さい。",plotlyOutput("AnimeFollower",width = "1250" ,height = "1200"),width = "1300",height = "1250")
        ),        
        fluidRow(
            box(title = "現在のフォロワー数ランキング",formattableOutput("RankTable"))
        )
        
    )
)
server <- function(input, output) {
    output$RankTable <- renderFormattable({
        yearsBC<-input$Year
        seasons<-input$Season
        master<-data.frame(matrix(NA,0,4))
                tmpFull <- paste("http://api.moemoe.tokyo/anime/v1/master",yearsBC,seasons,sep = "/") %>% 
                    sprintf("Array") %>%
                    list.load() %>% list.ungroup() 
                N_anime<-length(tmpFull)/15
                for (k in 1:N_anime){
                    tmpTwAcName<-unlist(tmpFull[c((k*15)-13)][1])
                    tmpAnime<-unlist(tmpFull[c((k*15)-11)][1])
                    master[(length(master[,1])+1),]<-c(yearsBC,N_anime,tmpTwAcName,tmpAnime)
                }
        #Follower Historyの抽出
        FollowerHistory<-data.frame(matrix(NA,1,3))
        names(FollowerHistory)<-c("Anime","Date","Follower")
        for(i in 1: length(master$X1)){
            tmpRes <- paste("http://api.moemoe.tokyo/anime/v1/twitter/follower/history/daily?account=",master[i,3],"&days=30",sep="") %>%
                sprintf("account") %>%
                list.load("") %>% list.ungroup()
            data<-data.matrix(tmpRes)
            constant<-length(data)
            up_date<-data[-1+3*(1:c(constant/3))]
            date<-as.POSIXct(as.numeric(up_date), origin="1970-01-01")
            follower<-as.numeric(data[-2+3*(1:c(constant/3))])
            tmpDf<-data.frame(Anime=c(rep(master[i,4],length(date))),Date=date,Follower=follower)
            FollowerHistory<-rbind(FollowerHistory,tmpDf)
        }
        #データの列名や時間情報を整理
        TidyAnime<-FollowerHistory %>% tidyr::drop_na()
        TidyAnime$Date<-as.POSIXct(TidyAnime$Date, origin="1970-01-01")
        
        #Table用データ        
        forTable<-TidyAnime%>%group_by(Anime)%>%
            dplyr::arrange(desc(Date))%>%
            dplyr::distinct(Anime,.keep_all=T)%>%
            ungroup()%>%dplyr::select(Follower,Anime)%>%
            dplyr::arrange(desc(Follower))
        formattable(forTable, list(
            Follower = color_bar("tomato"),
            Anime = formatter("span",#ランキング昨日の追加
                              style = x ~ formattable::style(color = ifelse(rank(-forTable$Follower) <= 10, "gray20", "gray")),
                              x ~ sprintf("%s (rank: %g)", x, rank(-forTable$Follower)))
        ))
    })
    output$AnimeFollower <- renderPlotly({
        yearsBC<-input$Year
        seasons<-input$Season
        master<-data.frame(matrix(NA,0,4))
        tmpFull <- paste("http://api.moemoe.tokyo/anime/v1/master",yearsBC,seasons,sep = "/") %>% 
            sprintf("Array") %>%
            list.load() %>% list.ungroup() 
        N_anime<-length(tmpFull)/15
        for (k in 1:N_anime){
            tmpTwAcName<-unlist(tmpFull[c((k*15)-13)][1])
            tmpAnime<-unlist(tmpFull[c((k*15)-11)][1])
            master[(length(master[,1])+1),]<-c(yearsBC,N_anime,tmpTwAcName,tmpAnime)
        }
        #Follower Historyの抽出
        FollowerHistory<-data.frame(matrix(NA,1,3))
        names(FollowerHistory)<-c("Anime","Date","Follower")
        for(i in 1: length(master$X1)){
            tmpRes <- paste("http://api.moemoe.tokyo/anime/v1/twitter/follower/history/daily?account=",master[i,3],"&days=30",sep="") %>%
                sprintf("account") %>%
                list.load("") %>% list.ungroup()
            data<-data.matrix(tmpRes)
            constant<-length(data)
            up_date<-data[-1+3*(1:c(constant/3))]
            date<-as.POSIXct(as.numeric(up_date), origin="1970-01-01")
            follower<-as.numeric(data[-2+3*(1:c(constant/3))])
            tmpDf<-data.frame(Anime=c(rep(master[i,4],length(date))),Date=date,Follower=follower)
            FollowerHistory<-rbind(FollowerHistory,tmpDf)
        }
        #データの列名や時間情報を整理
        TidyAnime<-FollowerHistory %>% tidyr::drop_na()
        TidyAnime$Date<-as.POSIXct(TidyAnime$Date, origin="1970-01-01")
        
      ggplotly(ggplot(TidyAnime) +theme_bw(base_size = 8,base_family = "HiraKakuProN-W6")+
            geom_line(aes(x=Date,y=Follower,colour=Anime),size=1)+
                geom_point(aes(x=Date,y=Follower,colour=Anime),size=1)+
                facet_wrap(~Anime,scales = "free",ncol = 4)+
            guides(colour=FALSE))
    })
    
}
shinyApp(ui = ui, server = server)

上記コード、長くて申し訳ないのですが、結局定義されているのは、

ui(ユーザインタフェース)とserver(裏側のの処理)の2つだけです。それらを最後にshinyAppで起動させている仕組みです。

uiでは、冒頭で紹介したdashbordHeader(タイトル)、dashboardSidebar(サイドバー)、dashboardBody(メインの部分)の3つが定義されています。

Sidebarからの情報をinput$Yearなどでserver側が受取り、output$AnimeFollowerなどでserverの処理結果をuiのdashboardBodyに返し、表示させる。

uiとserverの間で情報が行ったり来たりする仕組みになっています。

serverの中で使っているggplotとformattableの詳しい使い方は、ggplot2の前記事formattableの前記事をご参照ください。

RでAPIからデータを取得する方法は、いずにゃんの研究日記にまとめられています。 または、前記事を御覧ください。


コードの実行とデプロイ(Web上にアップ)


では、コードを実行してみましょう。

次のような画面が立ち上がるはずです。

shinydashboardProjectRun.png

うまくいきましたでしょうか。

続いて、ここまでで作成してきたものを、Web上にデプロイ(アップ)します。

先程紹介した最強の資料の最後の項に紹介されている、 Webで共有するあたりを参照してください。

ShinyServerへのアカウント登録については、こちらが参考になります。

最近、この辺の手続きが簡単になったみたいですが、わたくしまだ試しておりません。 アカウントの登録が済んだら、次のコードを実行しましょう。

username, token, secretをそれぞれ取得して入力し、deployApp()で今回作ったプロジェクトのディレクトリを指定します。

library(rsconnect)
rsconnect::setAccountInfo(name='アカウント名',
                          token='トークン',
                          secret='シークレットキー')

#プロジェクトのあるディレクトリに合わせる
rsconnect::deployApp('ディレクトリを指定')

おわりに

上手くWebアプリが起動しましたでしょうか。

shinydashboardSample.png

こんな感じで、でてきたURLを共有しましょう!

今回ご紹介したものはほんの一部の機能ですので、

みなさまがそれぞれに工夫した、面白いWebアプリを心待ちにしております。

そして最後に、今回作ったWebアプリの構造を復習しておきます。

こういう感じなっているんですね(平易な言葉で表現している部分があります)

ShinyAppSystem.png

それでは、

Enjoy!!

Written on April 3, 2017