Перевод-пересказ руководства.
Этот урок покажет вам как загружать данные, а также использовать скрипты и пакеты R в ваших приложениях на Shiny. На этом пути мы создадим довольно продвинутое приложение, отображающее данные переписи населения в США (US Census).
counties.rds
counties.rds
— это набор (dataset) демографических данных по каждому округу США, собранный в пакете расширения R UScensus2010
. Вы можете скачать этот файл здесь.
После того как скачаете файл:
- создайте новую папку с именем
data
в каталоге вашего приложенияcensus-app
; - переместите
counties.rds
в папкуdata
.
Когда всё будет готово, каталог вашего приложения census-app
будет выглядеть так:
Набор данных в counties.rds
включает в себя:
- имя (name) каждого округа (county) в США;
- общую численность населения округа (total.pop);
- процент жителей округа, являющихся белыми (white), чёрными (black), латиноамериканцами (hispanic) или азиатами (asian).
counties <- readRDS("census-app/data/counties.rds")
head(counties)
name total.pop white black hispanic asian
1 alabama,autauga 54571 77.2 19.3 2.4 0.9
2 alabama,baldwin 182265 83.5 10.9 4.4 0.7
3 alabama,barbour 27457 46.8 47.8 5.1 0.4
4 alabama,bibb 22915 75.0 22.9 1.8 0.1
5 alabama,blount 57322 88.9 2.5 8.1 0.2
6 alabama,bullock 10914 21.9 71.0 7.1 0.2
helpers.R
helpers.R
— это скрипт на R, который поможет вам создать фоновую картограмму (или хороплет, по англ.: choropleth map), вроде той, что показана выше. Фоновая картограмма — это карта, данные на которую наносятся при помощи окраски разной степени насыщенности. В нашем случае helpers.R
создаёт percent_map
— функцию, отображающую на карте данные из counties.rds
. Здесь вы можете скачать helpers.R
.
helpers.R
использует пакеты R maps
и mapproj
. Если вы ещё не установили эти пакеты, сделайте это перед тем как приступить к созданию приложения. Выполните
> install.packages(c("maps", "mapproj"))
Сохраните helpers.R
в каталоге вашего приложения census-app
, как показано ниже.
Функция percent_map
из helpers.R
принимает на вход пять аргументов:
Аргумент | Значение |
---|---|
var | вектор-столбец из набора данных counties.rds |
color | строка, задающая название цвета (см. функцию colors() ) |
legend.title | строка заголовка подписи к легенде на графике |
max | максимальное значение насыщенности (по умолчанию, 100) |
min | минимальное значение насыщенности (по умолчанию, 0) |
Чтобы построить картограмму по данным округов, вы можете запустить percent_map
в командной строке:
library(maps)
library(mapproj)
source("census-app/helpers.R")
counties <- readRDS("census-app/data/counties.rds")
percent_map(counties$white, "darkgreen", "% white")
Примечание. В приведенном выше коде предполагается, что census-app
является подкаталогом вашего рабочего каталога. Удостоверьтесь, что это действительно так. Чтобы изменить расположение рабочего каталога, выберите в меню RStudio последовательно Session > Set Working Directory > Choose Directory… или используйте функцию setwd()
в командной строке.
percent_map
строит картограмму по данным для округов. Вот как выглядит картограмма, отображающая процент белого населения округа, выполненная в оттенках зелёного цвета:
Загрузка файлов и пути к ним
Взгляните на размещенный выше код. Чтобы использовать percent_map
, мы сначала запускаем helpers.R
с помощью функции source
, а затем загружаем counties.rds
функцией readRDS
. Кроме того, мы загружаем библиотеки library(maps)
и library(mapproj)
.
Чтобы использовать percent_map в вашем приложении нужно, чтобы Shiny вызвал те же самые функции, но с другими значениями аргументов. source
и readRDS
используют в качестве аргументов путь к файлу и эти пути в приложении Shiny отличаются от тех, что используются в командной строке.
Когда Shiny выполняет команды из скрипта server.R
, он трактует все пути к файлам так, словно бы они все начинаются от каталога, в котором находится server.R
. Другими словами, каталог где вы сохранили server.R
становится рабочим каталогом вашего Shiny-приложения.
Так как вы сохранили helpers.R
в том же каталоге, что и server.R
, то указать Shiny загрузить его можно командой
source("helpers.R")
Поскольку вы сохранили counties.rds
в подкаталоге (с именем data
) каталога с server.R
, то загрузить counties.rds
можно так:
counties <- readRDS("data/counties.rds")
Пакеты maps
и mapproj
загружаются обычным способом
library(maps)
library(mapproj)
не требующим указания пути к файлу.
Выполнение
Shiny выполнит все команды, если вы поместите их в скрипт server.R
. Но то как часто будет выполняться команда зависит от места в скрипте, куда вы её поместите. Соотвественно, расположение команды скажется на производительности вашего приложения.
Shiny выполняет некоторые разделы server.R
чаще, чем остальные.
Shiny выполнит скрипт целиком как только вы первый раз вызовите runApp
. Это заставит Shiny запустить shinyServer
. shinyServer
в свою очередь в своём первом аргументе передаёт Shiny безымянную функцию.
Shiny сохраняет безымянную функцию до тех пор, пока ваше приложение не посетит новый пользователь. С его приходом Shiny запустит безымянную функцию снова, всего один раз — и так до следующего посетителя. Такое поведение безымянной функции помогает Shiny строить различные наборы реагирующих объектов для каждого из пользователей приложения.
Как только пользователь изменит состояние виджета, Shiny перезапустит команды на языке R, описывающие поведение объекта, который отвечает за реакцию виджета. Если пользователь окажется слишком активными, эти команды будут выполнятся очень часто, много раз в секунду.
Итак, вот что мы установили:
- скрипт
server.R
выполняется один раз, когда вы запускаете ваше приложение; - безымянная функция внутри
shinyServer
запускается один раз за каждое посещение пользователем вашего приложения; - команды R внутри функций
render*
выполняются много раз. Shiny запускает их всякий раз как только пользователь изменяет виджет.
Как можно использовать эту информацию?
Скрипты с источниками функций (загружаемые с помощью source
), загрузка библиотек и чтение наборов данных должны помещаться в начале файла server.R
, вне функции shinyServer
. Shiny запустит этот код всего один раз — ровно столько, сколько нужно для настройки параметров вашего сервера, позволяющих выполнить команды R, что находятся внутри shinyServer
.
Определять пользовательские объекты нужно внутри безымянной функции из shinyServer
, но за пределами вызовов функций render*
. Это касается тех объектов, для которых предполагается, что каждый пользователь должен располагать своим личным экземпляром такого объекта. Например, это может быть объект, который записывает информацию о сеансе работы конкретного пользователя. Этот код будет выполняться один раз для каждого пользователя.
Внутри функций render*
должен размещаться только код, который Shiny будет выполнять всякий раз, чтобы заново перестроить объект. Shiny будет повторно выполнять весь код в render*
каждый раз, как только пользователь изменяет виджет, за состояние которого этот фрагмент кода отвечает. Это может происходить довольно часто.
В целом следует избегать размещения внутри функций render
любого кода, кроме того, что там нужен. Иначе избыточный код будет замедлять работу вашего приложения.
Ваша очередь. Часть 1
Скопируйте и вставьте следующие файлы ui.R
и server.R
в каталог census-app
вашего приложения. Затем добавьте в server.R
следующие строки:
source("helpers.R")
counties <- readRDS("data/counties.rds")
library(maps)
library(mapproj)
Убедитесь, что вы поместили эти строки в нужное место файла.
Примечание: это первый из двух шагов, которые нужны, чтобы доделать наше приложение. Выберите наилучшее место для вставки показанного выше кода, но пока не пытайтесь запускать приложение. Оно будет возвращать ошибку, пока мы не заменим строку-заглушку # some arguments
на настоящий код в разделе Ваша очередь. Часть 2.
ui.R
# ui.R
shinyUI(fluidPage(
titlePanel("censusVis"),
sidebarLayout(
sidebarPanel(
helpText("Create demographic maps with
information from the 2010 US Census."),
selectInput("var",
label = "Choose a variable to display",
choices = c("Percent White", "Percent Black",
"Percent Hispanic", "Percent Asian"),
selected = "Percent White"),
sliderInput("range",
label = "Range of interest:",
min = 0, max = 100, value = c(0, 100))
),
mainPanel(plotOutput("map"))
)
))
server.R
# server.R
shinyServer(
function(input, output) {
output$map <- renderPlot({
percent_map( # some arguments )
})
}
)
Ответы. Часть 1
Так как вашему приложению нужно загрузить helpers.R
и counties.rds
лишь один раз, то разместить команды загрузки следует за пределами функции shinyServer
. Сделаем это перед кодом shinyServer
. Здесь же удобно поместить команду загрузки библиотеки maps
(которую использует percent_map
).
# server.R
library(maps)
library(mapproj)
source("helpers.R")
counties <- readRDS("data/counties.rds")
shinyServer(
function(input, output) {
output$map <- renderPlot({
percent_map( # some arguments )
})
}
)
Вы можете спросить: "Разве не каждому пользователю нужна своя копия counties
и percent_map
?" (что означает, что этот код нужно поместить внутрь безымянной функции в shinyServer
). Отвечаем: нет, не каждому.
Имейте в виду, что компьютер пользователя не будут запускать ни строчки кода вашего Shiny-приложения. В действительности, компьютер пользователя даже не увидит этот код. Вместо этого другой компьютер, используемый в качестве сервера, будет выполнять весь R-код, необходимый для всех пользователей. Затем он будет посылать результаты этой работы пользователям в виде HTML-элементов.
Так вот, чтобы проделать все эти расчеты, ваш сервер будет опираться на единственную глобальную копию counties.rds
и percent_map
. Создавать отдельные объекты для каждого пользователя нужно только тогда, когда эти объекты должны иметь разные значения для каждого из пользователей.
Доделываем приложение
Приложение censusVis
содержит один реактивный объект — график с именем "map". Этот график строится с помощью функции percent_map
, которая принимает пять аргументов:
- три первых аргумента,
var
,color
иlegend.title
зависят от значения, выбранного в виджете-списке; - последние два аргумента,
max
иmin
, должны быть равны максимальному (max
) и минимальному (min
) значениям виджета-ползунка.
Скрипт server.R
, приведенный ниже, показывает один из способов передачи "реактивных" аргументов в percent_map
. R-функция switch
может преобразовать вывод из списка в нужные вам значения. Однако наш скрипт всё ещё неполон. В нем не указаны значения для color
, legend.title
, max
и min
.
Примечание: скрипт в его нынешнем виде работать не будет. Его ещё нужно доработать и это будет вашем заданием в разделе Ваша очередь. Часть 2.
# server.R
library(maps)
library(mapproj)
counties <- readRDS("data/counties.rds")
source("helpers.R")
shinyServer(
function(input, output) {
output$map <- renderPlot({
data <- switch(input$var,
"Percent White" = counties$white,
"Percent Black" = counties$black,
"Percent Hispanic" = counties$hispanic,
"Percent Asian" = counties$asian)
percent_map(var = data, color = ?, legend.title = ?, max = ?, min = ?)
})
}
)
Ваша очередь. Часть 2.
Пора довести наше приложение censusVis
до рабочего состояния.
Когда вы будете готовы к выгрузке вашего приложения на сервер, сохраните файлы server.R
и ui.R
и запустите runApp("census-app")
. Если всё работает как должно,то ваше приложение будет выглядеть так как показано на картинке ниже.
Вам нужно решить
- как создать значения аргумента для
percent_map
и - куда поместить код, создающий эти аргументы.
Помните, вы что вы работаете со значениями аргументов, которые должны переключатся всякий раз, когда пользователь изменяет соответствующий виджет. Когда закончите, ну или если застрянете, можете прочесть наш вариант ответа ниже.
Ответы. Часть 2
Простая версия server.R
:
# server.R
library(maps)
library(mapproj)
counties <- readRDS("data/counties.rds")
source("helpers.R")
shinyServer(
function(input, output) {
output$map <- renderPlot({
data <- switch(input$var,
"Percent White" = counties$white,
"Percent Black" = counties$black,
"Percent Hispanic" = counties$hispanic,
"Percent Asian" = counties$asian)
color <- switch(input$var,
"Percent White" = "darkgreen",
"Percent Black" = "black",
"Percent Hispanic" = "darkorange",
"Percent Asian" = "darkviolet")
legend <- switch(input$var,
"Percent White" = "% White",
"Percent Black" = "% Black",
"Percent Hispanic" = "% Hispanic",
"Percent Asian" = "% Asian")
percent_map(var = data,
color = color,
legend.title = legend,
max = input$range[2],
min = input$range[1])
})
}
)
Более короткая версия server.R
:
# server.R
library(maps)
library(mapproj)
counties <- readRDS("data/counties.rds")
source("helpers.R")
shinyServer(
function(input, output) {
output$map <- renderPlot({
args <- switch(input$var,
"Percent White" = list(counties$white, "darkgreen", "% White"),
"Percent Black" = list(counties$black, "black", "% Black"),
"Percent Hispanic" = list(counties$hispanic, "darkorange", "% Hispanic"),
"Percent Asian" = list(counties$asian, "darkviolet", "% Asian"))
args$min <- input$range[1]
args$max <- input$range[2]
do.call(percent_map, args)
})
}
)
и
# ui.R
shinyUI(fluidPage(
titlePanel("censusVis"),
sidebarLayout(
sidebarPanel(
helpText("Create demographic maps with
information from the 2010 US Census."),
selectInput("var",
label = "Choose a variable to display",
choices = c("Percent White", "Percent Black",
"Percent Hispanic", "Percent Asian"),
selected = "Percent White"),
sliderInput("range",
label = "Range of interest:",
min = 0, max = 100, value = c(0, 100))
),
mainPanel(plotOutput("map"))
)
))
Резюме
Теперь вы умеете создавать приложения на Shiny, которые загружают скрипты, библиотеки R и наборы данных.
Имейте в виду:
- Каталог, где находится файл
server.R
, станет рабочим каталогом вашего приложения. - Shiny выполнит код, помещенный в начале
server.R
доshinyServer
, только один раз в течение жизни приложения. - Shiny будет выполнять код, помещенный внутри
shinyServer
, множество раз. Так что это может замедлить работу приложения.
Кроме того вы узнали, что функция switch
является полезным дополнениям к виджетам, реализующим выбор из нескольких вариантов. Используйте switch
для изменения значений таких виджетов в командах R.
Становясь более сложными, ваши приложения могут превратиться в неэффективные и медленные. Урок 6 покажет вам как разработать быстрые модульные приложения, реагирующие на действия пользователей.
Комментарии
comments powered by Disqus