Inne interesujące tematy

Podczas pracy z Shiny istnieją typowe zadania, których można użyć do dostosowania aplikacji internetowych. Niektóre z tych zadań obejmują dodawanie obrazów statycznych, HTML i CSS. W kolejnych sekcjach pokrótce przyjrzymy się, jak można to osiągnąć za pomocą Shiny. Na koniec wspomnimy również o niektórych opcjach, które musisz udostępnić innym osobom, bez konieczności konfigurowania własnego serwera internetowego, aby mogli z niego korzystać w swojej przeglądarce internetowej za pośrednictwem połączenia internetowego.

Dodawanie obrazów statycznych

Obrazy mogą poprawić wygląd aplikacji i pomóc użytkownikom zrozumieć zawartość. Shiny szuka funkcji img() dla umieszczania plików graficznych w aplikacji. Aby wstawić obraz, po prostu wywołaj  src z określeniem lokalizacji obrazów. Możesz również dołączyć inne parametry przyjazne dla HTML, takie jak wysokość i szerokość (zostaną przekazane jako wartości w pikselach):

img(sr = „image.png”, height = 250, width = 250)

Plik image.png musi znajdować się w folderze o nazwie www w tym samym katalogu co skrypt app.R. Shiny udostępni każdy umieszczony tutaj plik przeglądarce internetowej użytkownika, co jest doskonałym miejscem do umieszczania obrazów, arkuszy stylów i innych rzeczy, których przeglądarka będzie potrzebować do zbudowania komponentów WAP aplikacji Shiny.

Stylizacja naszej aplikacji za pomocą motywów

Stylizacja naszej aplikacji za pomocą motywów

Do tej pory korzystaliśmy z motywu dostarczonego domyślnie przez Shiny, ale teraz, gdy nasza aplikacja jest ukończona, chcemy stylizować ją za pomocą kolorów przypominających technologię. W takim przypadku możemy użyć pakietów shinythemes i ggthemr, które zapewniają nam łatwy sposób stosowania motywów odpowiednio do aplikacji Shiny i wykresów ggplot2. Wszystko, co musimy zrobić, aby zastosować motywy, to powiedzieć frameworkowi ggplot2, aby zastosował płaski ciemny motyw dostarczony przez pakiet ggthemr i aby upewnić się, że zewnętrzna strona wykresu jest również stylizowana, używając parametru type = outer, jak pokazano tutaj. Kod należy umieścić w dowolnym miejscu, w którym umieściliśmy nasz kod ggplot2 dla spójności, co znajduje się w pliku funtions.R :

library(ggthemr)

ggthemr(‘flat dark’, type = ‘outer’)

Aby wystylizować samą aplikację Shiny, wysyłamy parametr theme, używając funkcji shinytheme(), do funkcji fluidPage() tuż przed naszym wywołaniem funkcji titlePanel(), jak pokazano tutaj:

ui <-fluidPage (

theme = shinytheme(„suerhero”),

titlePanel(„Cryptourrency Markets”),

Zmieniamy również linię SMA na wykresach na białą, co już wiesz, jak to zrobić, a dzięki tym zmianom nasza aplikacja wygląda teraz całkiem nowocześnie. Poniżej przedstawiono kartę Prosta średnia krocząca:

Tutaj możesz zobaczyć zrzut ekranu karty Przegląd danych:

Aby znaleźć inne motywy, możesz spojrzeć na repository

Dodanie interaktywności z dodatkowym wykresem zoomin

Na koniec dodamy trochę interaktywności do naszego wykresu, implementując inny podobny wykres, który będzie wykazywał efekt powiększenia tego, który stworzyliśmy wcześniej. Chodzi o to, że możemy wybrać obszar wykresu, który właśnie utworzyliśmy, a ten, który umieścimy poniżej, zostanie zaktualizowany, aby pokazać tylko określony obszar, który wybraliśmy. Wydaje się interesujące, prawda? Aby to osiągnąć, musimy zmodyfikować plotOutput() wstawiony na końcu poprzedniej sekcji parametr brush z wywołaniem funkcji brushOpts() , która z kolei otrzyma nazwę unikalnego identyfikatora dla tworzonego przez nas wejścia pędzla. Ten parametr służy do tworzenia specjalnego typu danych wejściowych, który pobiera wybrany obszar z wykresu wyświetlanego w przeglądarce internetowej. Dodajemy również kolejny fluidRow()  z innym plotOutput() tuż pod nim, aby zawierał wykres, który zapewni efekt powiększenia. Kod wygląda następująco:

tabPanel(

„Simple Moving Averages”,

value =1 ,

fluidRow(plotOutput(„graph_top”, brush = brushOpts(„graph_brush”))),

fluidRow(plotOutput(„graph_bottom”))

Teraz wartość reaktywna input$graph_brush będzie zawierała listę z czterema elementami wewnątrz xmin,xmax, ymin i ymax, które są współrzędnymi odpowiadającymi obszarowi wybranemu na górnym wykresie. Nasza funkcja reaktywna ranges() użyje ich do wysłania odpowiednich wartości jako granic do dolnego wykresu. Sposób działania polega na tym, że sprawdzi, czy input$graph_brush jest NULL, a jeśli nie, to znaczy, że obszar jest zaznaczony, zwróci listę z dwoma elementami x i y, gdzie każdy z tych elementów zawiera odpowiednie współrzędne. Jeśli input$graph_brush jest NULL, to elementy x i y zwróconej listy będą NULL, co sygnalizuje funkcję coord_cartesian(), której będziemy używać na wierzchu sma_graph(), aby uniknąć umieszczania jakichkolwiek ograniczeń na osiach wykresu. Rzeczywista funkcja jest pokazana w poniższym kodzie i podobnie jak inne funkcje utworzone za pomocą reactive() powinna zostać umieszczona wewnątrz funkcji server. Zauważ również, że musimy dokonać małej transformacji wartości dla osi x, ponieważ są one zwracane jako liczby całkowite, a nie daty, które są typem obiektu używanego  ggplot() dla tęj osi. Po prostu używamy funkcji as.POSIXct() do przekształcania takich liczb całkowith do prawidłowych dat, używając oring =
„1970-01-01”, który jest tym, co ggplot() używa domyślnie. Jeśli nie dokonamy transformacji, otrzymamy błąd:

ranges <- reative ({

if (!is.null(input$graph_brush)) {

return(list (

x = c(as.POSIXct(input$graph_brush$xmin, origin = „1970-01-01”),

as.POSIXct(input$grpah_brush$xmax,

origin = „1970-01-01”)),

y = (input$graph_brush$ymin,       

iput$graph_brush$ymax)

))

}

return(list(x = NULL, y = NULL))

}

Teraz jesteśmy w stanie utworzyć obserwatora output$bottom_graph, tak jak stworzyliśmy poprzedni wykres, ale w tym przypadku dodamy funkcję coord_cartesian() na górze obiektu wykresu zwróconego przez sma_graph(), aby ograniczyć wartości osi. Zauważ, że używamy expand = FALSE do wymuszenia ograniczeń pochodzących z funkcji reaktywnej ranges(), którą właśnie utworzyliśmy w poprzednim kodzie:

output$graph_bottom <- renderPlot( {

return(sma_graph(data () , sma() )  +

coord_cartesian(xlim = ranges () $x,

ylim = ranges()$y, expand = FALSE))

)

Po wprowadzeniu tych zmian powinniśmy osiągnąć zamierzony efekt. Aby to przetestować, możemy napisać aplikację i zobaczyć dwa identyczne wykresy jeden na drugim, jak pokazano na poniższym zrzucie ekranu:

Jeśli jednak wybierzemy obszar na górnym wykresie, to wykres na dole powinien się zaktualizować, pokazując tylko tę konkretną część wykresu. Całkiem fajnie, prawda?

Na koniec powinieneś wiedzieć, że innym sposobem wprowadzenia interaktywnej grafiki jest użycie dobrze znanego języka JavaScript  jak  Plot.ly. Shiny tworzy strony internetowe, które używają JavaScript w tle, więc ta technika jest naturalna. Jest to jednak zaawansowana technika i jej użycie jest bardziej skomplikowane niż to, co tutaj pokazaliśmy, więc nie będziemy jej pokazywać, ale powinieneś wiedzieć, że jest to możliwe, jeśli chcesz wykonać ją samodzielnie.

Dodanie prostego wykresu średniej ruchomej

Teraz utworzymy nasz pierwszy prosty wykres średniej ruchomej (SMA). Ten wykres zostanie utworzony wraz z pakietem i pokaże dwie linie. Czarna linia to aktualne dane cenowe, a niebieska to SMA. Zanim zaczniemy, a ponieważ wykresy ggplot2 korzystające z dat są lepiej tworzone z rzeczywistymi datami zamiast ciągów znaków czasu, dodajemy kolumnę time do ramki danych ORIGINAL_DATA z odpowiednimi datami. Powinien on zostać umieszczony natychmiast po załadowaniu danych:

ORIGINAL_DATA$time <- timestamp_to_time.TimeStamp(ORIGINAL_DATA$timestamp)

Następnie pokażemy, jak jest zaimplementowana nasza funkcja sma_graph(). Jak widać, otrzyma dwa parametry, ramkę danych  data i wektor sma wychodzący z jednej ze wspomnianych wcześniej implementacji SMA. Funkcja jest bardzo prosta, tworzy wykres z time  na osi x i price_usd na osi y, dodaje punkty i linie dla takich danych, a następnie dodaje drugą niebieską linię z wartościami z wektora sma. Parametr group – 1 służy do unikania błędów, informując funkcję ggplot(), że w tych danych znajduje się pojedyncza grupa, a parametr size = 1 służy tylko do tego, aby linia wyróżniała się nieco bardziej. Zwróć uwagę, że zwracamy obiekt wykresu. Na koniec należy pamiętać, że użycie funkcji geom_line() wprowadza do przykładu interpolację, która może zniekształcić dyskretne dane, które mamy dla cen, ale może być również pomocna w zrozumieniu dynamiki cen i dlatego ją używamy:

sma_graph <- function(data, sma) {

g <- ggplot(data, aes(time, price_usd))

g <- g + geom_point()

g <- geom_line(group = 1)

g <- g + geom_line(aes_stringly(y=sma(,

group = 1, color = „blue”, size =1 )

return(g)

Teraz, zgodnie z dobrymi praktykami, umieszczamy obliczenia SMA w ich własnej funkcji  reactive() (tuż poniżej funkcji reaktywnej, którą utworzyliśmy wcześniej). Zauważ, że jest to funkcja reaktywna, która zależy od innej funkcji reaktywnej, a dokładniej data(). Poniższy kod (który pomija resztę funkcji server) pokazuje, że ta definicja sma wykorzystuje funkcję do.call () do wykonania nazwy implementacji, którą otrzymujemy jako wartość reaktywną z widżetu input$sma_implementation. do.call() otrzymuje również listę jako drugi parametr, a ta lista zawiera parametry, które zostaną wysłane do rzeczywistej funkcji, którą chcemy wywołać. W tym przypadku jest to input$sm_period , symbol, (co w tym przypadku będzie pojedynczym, ponieważ ograniczyliśmy dane dla tej karty do jednego zasobu) i rzeczywiste dane poprzez wywołanie funkcji reaktywnej data():

sma <- reative ({

return(do.call (

input$sma_implementation,

list(input$sma_period, data() [1, „symbol”] , data() ]

))

)

Po zaimplementowaniu tej funkcji reaktywnej sma() możemy zaimplementować obserwatora output$graph_top() w następujący sposób (ponownie pominęliśmy kod):

output4graph_top <- renderPlot ( {

return(sma_graph(data (), sma() ))

)

Na koniec musimy zaktualizować nasz obiekt ui, aby zastąpić symbol zastępczy „Content 1” z fluidRow() i ploutOutput() wewnątrz. Wysyłamy unikalny identyfikator „graph_top” obserwatorowi, który nas interesuje:

fluidRow(plotOutput(„graph_top”))

To było dość proste, prawda? Teraz możemy uruchomić naszą aplikację, która powinna pokazać nam wykres dla pierwszych dwóch dni danych, z niebieskim SMA (30) na górze, jak pokazano na poniższym zrzucie ekranu:

Pamiętaj, że możesz zmienić opcje, a wykres zostanie odpowiednio zaktualizowany. Na przykład, jeśli wybierzemy tylko pierwszy dzień w danych i zdecydujemy się na wykresie tylko SMA (5).

Wreszcie, jeśli twój komputer sobie z tym poradzi, możesz zdecydować się na pokazanie pełnych danych (co jest dość dużą liczbą obserwacji, więc bądź ostrożny). W takim przypadku SMA nie byłaby widoczna, ale nadal byłaby dla nas wykreślona. Wynik jest pokazany na poniższym obrazku:

Dodanie tabeli zbiorczej z udostępnionymi danymi

Teraz dodamy tabelę podsumowującą nad naszą dynamiczną tabelą danych. Tabelę podsumowującą należy aktualizować zgodnie z aktywami zaznaczonymi (zwróć uwagę na liczbę mnogą, ponieważ zezwalamy na przypadek All w tej zakładce). Poświęć chwilę i zastanów się, jak sam byś to zaimplementował? Jeśli spróbujesz odtworzyć wzór pokazany wcześniej dla obiektu data, który pokazaliśmy wcześniej, uzyskasz poprawne, ale nieefektywne rozwiązanie. Powodem jest to, że logika filtrowania danych byłaby zduplikowana, co jest niepotrzebne. Aby uniknąć tej pułapki, pokażemy dalej, jak udostępniać strumienie między różnymi funkcjami reaktywnymi za pomocą funkcji reactive(), która jest funkcją, która służyła do przygotowania reaktywnych wartości dla innych funkcji reaktywnych. W tym przypadku przenosimy całą logikę, którą stworzyliśmy wcześniej, do wyrażenia przesłanego jako parametr do tej funkcji i przypisujemy ją do obiektu data , który sam jest teraz funkcją reaktywną. Zwróć uwagę, że dodaliśmy również trochę kodu, aby sprawdzić, czy bieżący wybór zasobów różni się od All, a jeśli tak, użyj tej wartości do filtrowania danych, podobnie jak filtrujemy je za pomocą dat. Gdy już to zrobimy, możemy zastąpić logikę, którą mieliśmy wewnątrz funkcji datatable(), prostym wywołaniem funkcji reaktywnej data(), która zapewni oczekiwaną ramkę danych. A teraz, gdy wyodrębniliśmy tę logikę, możemy ponownie użyć wywołania data() w innym miejscu, tak jak robimy to w obserwatorze output$summary_table utworzonym za pomocą funkcji rednerTable(). Jak widać, jest przekazywana ramka danych utworzona ze statystykami dla minimum, mediany, średniej i maksimum ramki danych zwróconej przez funkcję data(). W takim przypadku możemy zagwarantować, że dane użyte w funkcjach output$table() i  output$summary_table() są takie same:

server <- function (input, output) {

data ,<- ORIGINAL_DATA

start <- input$date_range[1]

end <- input$date_range[2]

if (input$asset != „All”){

data <- data[data$name == input$asset, ]

}

if (time_to_date_string.TimeStamp(start) != DATE_MIN |

time_to_date_string.TimeStamp(end) != DATE_MAX) {

data <- data [

data$timestamp >= time_to_timestamp.TimeStamp(start) &

data$timestamp <=  time_to_timestamp.TimeStamp(end), ]

}

return(data)

})

output$table <- DT::renderDataTable(DT::datatable({return(data()) }))

output$select_asset <- renderUI ({

assets <- DATA_ASSETS

if (input$tab_selected ==2)}

assets <- c(„All”, assets)

}

return(selectInput(„asset”, „Asset : „, assets))

})

output$summary_table <- renderTable(data.frame (

Minimum = min(data() $price_usd),

Median = mean(data() $price_usd),

Mean = mean(data)() $price_usd),

Max = max(data () $price_usd)

))

Zachowaj ostrożność, jeśli używasz danych stochastycznych (na przykład liczb losowych), jeśli powielasz logikę danych zamiast używać funkcji reactive(), ponieważ prawdopodobnie nie uzyskasz tych samych danych w obu miejscach. Musimy również wprowadzić odpowiednie wywołanie funkcji w obiekcie ui , które umieszczamy w odpowiednim tabPanel(). Aby umieścić w tym ramkę danych, którą właśnie utworzyliśmy, używamy funkcji tableOutput() z odpowiednim ciągiem znaków summary_table jako parametrem. Kod jest następujący (zauważ, że pomijam kod ui wokół tego fragmentu):

tabPanel (

„Data Overview”,

value = 2,

fluidRow(tableOutput(„summary_table”)),

fluidRow(DT::dataTableOutput(„table”))

)

Po wprowadzeniu tych zmian, u góry dynamicznej tabeli danych powinna pojawić się tabela podsumowująca ze wspomnianymi statystykami, które powinny zostać zaktualizowane, gdy różne wartości są wysyłane jako dane wejściowe dla dat i wyboru zasobów.

Konfigurowanie paneli dynamicznego wprowadzania danych

Ostatnie dwie dane wejściowe, które wprowadzimy, zostaną później wykorzystane do wykresów SMA. Pierwsza z nich służy do wyboru implementacji SMA, z której chce skorzystać użytkownik. Dostępne opcje to sma_efficient_1() , sma_efficient_2() , sma_delegated_fortran(), i funkcja sms_delegated_cpp(), które utworzyliśmy wcześniej. Drugi służy do określenia okresu używanego do obliczeń SMA i jest używany jako dane wejściowe w jednej z poprzednich funkcji. Ponieważ kod może zacząć być zbyt powtarzalny i zajmować zbyt dużo miejsca, a tak najprawdopodobniej zrozumiałem wzorce zagnieżdżania użyte podczas tworzenia ui , będę unikać powtarzania pełnej deklaracji obiektu ui i po prostu wskażę, gdzie należy wprowadzić zmiany. W tym przypadku chcemy dodać następujący kod po zakończeniu funkcji wellPanel() i przed zakończeniem funkcji sidedbarPanel(). Poniższy kod będzie drugim parametrem sidebarPanel(), więc nie zapomnij dodać przecinka („ ,”) po zakończeniu funkcji wellPanel(), w przeciwnym razie wystąpi błąd.

Funkcja conditionalPanel() sprawdza warunek JavaScript określony za pomocą ciągu znaków, aby zdecydować, czy panel powinien zostać pokazany użytkownikowi. Ponieważ obiekt input jest wysyłany do przeglądarki internetowej za pośrednictwem obiektu JavaScript o dogodnej nazwie input, możemy go użyć do uzyskania wartości, której szukamy, czyli tego, czy użytkownik patrzy na pierwszą kartę „Simple Moving Averages”. Jeśli tak, to pokażemy panel.  JavaScript używa notacji z kropką (“.”), Aby uzyskać dostęp do elementów zamiast notacji money ($) używanej w R. Na pokazanym panelu wellPanel() znajdują się dwa obiekty wejściowe: radioButtons() i sliderInput(). Pierwsza z nich otrzymuje opcje dostępne dla użytkownika na liście przesłanej przez parametr choice (nazwa każdego elementu jest wyświetlana użytkownikowi, podczas gdy wartość każdego elementu jest używana wewnętrznie w R, które w tym przypadku są nazwami implementacji SMA), jak również selected domyślnie. Drugi otrzymuje min, max  i domyślnie value dla suwaka numerycznego. Obie otrzymują unikalny identyfikator i etykietę jako pierwsze dwa argumenty, tak jak każda inna funkcja *Input():

conditionalPanel (

condition =  „input.tab_selected == 1”,

wellPanel (

radioButtons (

„sma_implementation”,

„Implementation:”,

choices = list (

„C++ = sma_delegated_cpp”,

„Fortran” – „sma_delegated_fortran”,

„Efficient 1 „ = „sma_efficient_1”,

„Efficient 2 „ = „sma_efficient_2”,

),

selected = „sma_delegated_cpp”

),

sliderInput (

„sma_period”,

„Period:”,

min = 5,

max = 200 ,

value = 30

)

)

)

Rzeczywiste kreacje wykresów zostawimy na później, więc nie musimy nic zmieniać na stronie server. W tym momencie wartości reaktywne wychodzące input$sma_implementation  i  input$sma_period nie będą używane. Zrzut ekranu pokazujący, jak te dane wejściowe są pokazane w następujący sposób. Jeśli przejdziesz do zakładki Simple Moving Averages, powinny zostać pokazane, ale jeśli przejdziesz do zakładki Data Overview, powinny zostać ukryte:

Jak widać, umożliwienie użytkownikom interakcji z aplikacją nie jest zbyt trudne i odbywa się za pomocą funkcji *Input() w obiekcie ui, których wartości reaktywne z kolei można wykorzystać w funkcji server

Konfigurowanie opcji dynamicznych w menu rozwijanym

Teraz zobaczymy, jak dodać rozwijane dane wejściowe, których wpisy dostosowują się do zakładki, którą użytkownik aktualnie przegląda. W szczególności dodamy możliwość wybrania przez użytkownika zasobu, którego chce użyć do filtrowania danych. Jeśli przyjrzałeś się uważnie, być może zauważyłeś, że dynamiczna tabela danych zawiera obserwacje zarówno dla Bitcoin, jak i Litecoin, co może być w porządku, gdy tylko patrzymy na tabelę, ale będzie to problem, gdy spróbujemy pokazać serie czasowe cen, ponieważ będziemy mieć dane dla więcej niż jednego kapitał. Chcemy zapewnić mechanizm wyboru tylko jednego z nich, ale chcemy zachować możliwość przeglądania ich wszystkich razem w dynamicznej tabeli danych, tak jak robimy to teraz. Zaczynamy od utworzenia obiektu, który zawiera unikalne nazwy zasobów, które obecnie mamy w danych. Jest to o wiele lepsze niż zakodowanie ich nazw bezpośrednio w kodzie, ponieważ będą one automatycznie aktualizowane, gdy zmieniają się nasze dane, co nie miałoby miejsca, gdybyśmy je zakodowali. Ta linia powinna znajdować się tuż pod poprzednimi obiektami globalnymi, które należy utworzyć tylko raz:

DATA_ASSETS <- unique(ORIGNAL_DATA$name)

Ponieważ widget wejściowy w tym przypadku ma dynamiczną logikę, nie możemy po prostu tworzyć wewnątrz obiektu ui, musimy go utworzyć w funkcji server i przekazać do obiektu ui . Można to zrobić, wprowadzając nowego obserwatora do parametru output, nazwanego select_asset w tym przypadku, który jest tworzony za pomocą funkcji renderUI() , ponieważ będzie zawierał funkcję *Input() Shiny. Podobnie jak data w poprzedniej sekcji, przypiszemy domyślne nazwy zasobów i tylko w przypadku, gdy użytkownik znajduje się na drugiej karcie, która jest kartą Data Overview (więcej informacji na temat tego, skąd pochodzi poniżej), doda również opcję  All do listy rozwijanej. W przeciwnym razie po prostu zachowa nazwy zasobów bez opcji All, czego chcemy dla wykresów SMA, które utworzymy później:

 server <- function(input, output) {

output$table <- DT:: renderDataTable(DT:: datatable ({

data <- ORIGINAL_DATA

start <- input$date_range[1]

end <- input$date_range[2]

if (time_to_date_string.TimeStamp(start) != DATE_MIN |

time_to_date_string.TimeStamp(end) != DATE_END) {

data <- data[

data$timestamp >= time_to_timestamp.TimeStamp(start) &

data$timestamp <= time_to_timestamp.TimeStamp(end), ]

}

return(data)

}))

output$selet_asset <- renderUI ({

assets <- DATA_ASSETS

if (input$tab_selected == 2_ {

assets <- c(„ALL” , assets)

}

return(selectInput(„asset”, „Asset :” , assets))

})

Aby faktycznie zapewnić mechanizm dla server umożliwiający zrozumienie, jaką kartę aktualnie przegląda użytkownik, obiekt ui należy dostosować tak, aby funkcja tabsetPanel() otrzymywała parametr id z nazwą obiektu, który zawiera bieżący numer karty, tab_selected w tym przypadku (którym jest nazwa użyta do sprawdzenia funkcji). Ponadto każda karta w obrębie musi mieć przypisaną wartość z parametrem value, jak pokazano. W ten sposób upewniamy się, że karta Data Overview  jest identyfikowana z wartością 2. Zwróć również uwagę, że dodaliśmy wywołanie funkcji htmlOutput() w ramach właśnie wprowadzonego wywołania funkcji  wellPanel() w funkcji sidePanel(). wellPanel grupuje wizualnie panele, aby zapewnić użytkownikom bardziej intuicyjne interfejsy, a funkcja htmlOutput() wykorzystuje nazwę obserwatora, aby wiedzieć, co pokazać w przeglądarce internetowej, element select_asset obiektu output w tym przypadku:

ui <- fluidPage (

titlePanel („Cryptourrency Markets”),

sidebarLayout (

sidebarPanel (

wellPanel (

dateRangeInput (

„date_range”,

label = paste(„Date range :”),

start = DATE_MIN,

end =DATE_END,

min = DATE_MIN,

max = DATE_MAX,

separator = „ to „,

format = „yyyy-mm-dd”,

weeekstart = 1,

startview – „year”

),

htmlOutput(„select_asset”)

)

),

mainPanel (

tabsetPanel (

id = „tab_selected”,

tabPanel (

„Simple Moving Averages”,

value = 1,

„Content 1”

(,

tabPabel (

„Data Overview”,

value = 2,

fluidRow(DT::dataTableOutput („table”))

)

)

)

)

)

Po dokonaniu tych zmian, teraz widzimy, że nasza aplikacja wyświetla listę rozwijaną z nazwą zasobu z opcjami  Bitcoin i Litecoin kiedy użytkownik znajduje się na karcie Simple Moving Averages, a także zawiera opcję All, gdy znajdują się na karcie Data Overview, tak jak chcieliśmy i jak widać na poniższym zrzucie ekranu :

Wprowadzenie interaktywności z danymi wejściowymi użytkownika

Interaktywność, którą widzieliśmy wcześniej z dynamiczną tabelą danych, działa w samej przeglądarce internetowej przy użyciu JavaScript i nie musi przechodzić przez funkcję server , aby zapewnić interaktywność, a jedynie przekazać samą tabelę. Jednak wiele interesujących funkcji interaktywnych wymaga przejścia server , abyśmy mogli zapewnić dla nich niestandardowe odpowiedzi. W tej sekcji pokażemy, jak dodać różne typy danych wejściowych do naszej aplikacji.

Konfigurowanie statycznych danych wejściowych użytkownika

Najpierw pokażemy, jak filtrować znaczniki czasu w danych, aby wyświetlać tylko obserwacje mieszczące się w zakresie zdefiniowanym przez użytkownika. Aby to zrobić, musimy najpierw zdefiniować cztery znaczniki czasu: minimalny, początkowy lewy limit, początkowy prawy limit i maksymalny. Te cztery wartości będą używane przez nasz widżet zakresu dat do zdefiniowania dozwolonego zakresu dla użytkownika (używane są do tego wartości minimalne i maksymalne), a także początkowego zakresu dat (używane są do tego początkowe limity lewy i prawy), który może różnić się od limitów dozwolonego zakresu. Dlatego musimy wyodrębnić takie wartości i robimy to, wykorzystując klasę TimeStamp, którą stworzyliśmy wcześniej. Zauważ, że używamy funkcji days() z pakietu lubridate (powinieneś dodać linię library(lubridate)na początku pliku. Ponieważ wystarczy, że utworzymy te obiekty tylko raz, powinny one znajdować się tuż pod kodem używanym do załadowania ORIGINAL_DATA, przed definicją obiektu ui:

DATE__MIN <-

timestamp_to_date_string.TimeStamp(min(ORIGINAL_DATA$timestamp))

DATE__MAX <-

timestamp_to_date_string.TimeStamp(max(ORIGINAL_DATA$timestamp))

DATE__END <-

timestamp_to_date_string.TimeStamp(time_to_timestamp.TimeStamp(

timestamp_to_time.TimeStamp(min(ORIGIAL_DATA$timestamp)) + days(2)))

Funkcja timestamp_to_date_string() w klasie TimeStamp nie została utworzona i dodaliśmy ją tu. Jest to bardzo proste i zostało pokazane w poniższym kodzie. Jego celem jest po prostu uzyskanie pierwszych 10 znaków TimeStamp, które odpowiadają formatowi RRRR-MM-DD:

timestamp_to_date_string.TimeStamp <- function(timestamp) {

return(substr(timestamp ,1,10))

Teraz, gdy stworzyliśmy te obiekty, możemy użyć następującego kodu do rozwinięcia obiektu ui. To, co zrobiliśmy, to zastąpienie ciągu „Options” przez wywołanie funkcji dateRangeInput(), która jest funkcją używaną do tworzenia zakresu dat, jak sugeruje nazwa. Otrzymuje jako parametry unikalny identyfikator, który będzie używany do pobierania jego reaktywnych wartości wewnątrz server  przez parametr input, pokazanego użytkownikowi label, start ,end, min i max wspomnianych wcześniej wartości, separator których chcemy użyć w polach wejściowych przeglądarki internetowej,  fomrat daty, której chcemy użyć i jaki dzień tygodnia ma się rozpocząć (0 niedziela, 1 poniedziałek itd.):

ui <- fluidPage (

titlePanel („Crytpocurrency Markets”),

sidebarLayout {

sidebarPanel (

dateRangeInput (

„date_range”,

label = paste(„Date range:”),

start = DATE_MIN,

end = DATE_END,

min = DATE_MIN,

max = DATE_MAX,

separator = „ to „ ,

format = „yyyy-mm-dd”,

weekstart = 1

)

),

mainPanel (

tabsetPanel (

tabPanel(„Simple Moiving Averages”, „Content 1”),

tabPanel („Data Overview”, fluidRow(DT :: dataTableOutput(„table”))

)

)

)

)

)

Po stronie serwera dodamy więcej logiki w wyrażeniu reaktywnym przekazanym jako parametr do funkcji datatable(). Zamiast po prostu wysyłać surową ramkę danych ORIGINAL_DATA, przefiltrujemy ją przed wysłaniem. Aby to zrobić, najpierw przypisujemy jego kopię do obiektu data i wyodrębniamy dwie wartości dat z widżetu, który utworzyliśmy w obiekcie ui , używając ich odniesień w parametrze input. Następnie sprawdzamy, czy któryś z nich różni się od ich wartości początkowych. Jeśli tak jest, aktualizujemy obiekt data tylko o te obserwacje, które mieszczą się w określonym zakresie, co osiągamy za pomocą standardowego wyboru ramki danych. Na koniec wysłaliśmy to filtrowane data do funkcji datatable() i postępowaliśmy tak jak wcześniej. Rezultatem tych zmian w ui i  server jest to, że możemy teraz filtrować daty dozwolone w dynamicznej tabeli pokazanej na karcie Data Overview, czego wcześniej nie mogliśmy zrobić. Na poniższym zrzucie ekranu pokazano działający widżet zakresu dat. Spróbuj zmienić jego daty i zobacz, jak aktualizuje się tabela dynamiczna:

Wstawianie dynamicznej tabeli danych

Teraz dodamy dynamiczną tabelę z danymi, które zasymulowaliśmy w poprzedniej części, więc przede wszystkim musimy wprowadzić te dane do aplikacji i robimy to za pomocą linii pokazanej poniżej. Powinieneś umieścić tę linię ładowania danych powyżej obiektu ui w swojej aplikacji. W ten sposób zostanie uruchomiony tylko raz, podczas uruchamiania aplikacji Shiny, jak każdy kod, który normalnie byłby uruchamiany podczas wykonywania skryptu R:

ORIGINAL_DATA <-

read.csv(„../chapter-09/data.csv” , stringAsFators = FALSE)

W tym momencie musimy wprowadzić pakiet DT. Zapewnia łatwy sposób tworzenia dynamicznych tabel dla aplikacji Shiny. Ponieważ będziemy odwoływać się do niego poprzez nazwę pakietu, nie musimy go ładować z library(DT). Odwołanie się do niego przez nazwę pakietu pomaga nam oddzielić natywne funkcje Shiny od tych, które pochodzą z pakietów zewnętrznych. Aby to zaimplementować, musimy zmodyfikować funkcję server, której do tej pory nie ruszaliśmy. Musimy wprowadzić do niego pewną logikę, która pozwoli nam przenieść dane z R do interfejsu internetowego. Aby to osiągnąć, przypisujemy go do elementu table w jego parametrze output, który będzie działał jako obserwator. Nazwa elementu, który mu przypisujemy, może być dowolnym prawidłowym elementem listy, jaki sobie życzymy, ale dobrym pomysłem jest użycie po prostu nazw opisujących zawartość obserwatora. Pamiętaj, że te nazwy muszą być unikalne, ponieważ Shiny użyje ich do zidentyfikowania obiektów, które mają przechodzić między obiektem ui  a funkcją server. Obserwator jest tworzony za pomocą funkcji  renderDataTable() z pakietu DT. Ta funkja działa jak każda inna funkcja render*(), otrzymuje pojedynczy parametr, który jest wyrażeniem zwracającym wartość, która będzie treścią obserwatora. W tym przypadku tabela danych utworzona za pomocą funkcji datatable(), ponownie z pakietu DT. Aby utworzyć tę tabelę danych, po prostu przekazujemy obiekt ORIGINAL_DATA, który wcześniej załadowaliśmy. Teraz, gdy regulacja po stronie serwera jest zakończona, dodajemy w obiekcie ui  fluidRow() zamiast „Content 2” a, aby wprowadzić wiersz, który dostosuje swoją długość do wymiarów przeglądarki internetowej, a wewnątrz niej wywołujemy funkcję dataTableOutput() z pakietu DT. Zauważ, że ciąg wysłany jako jedyny parametr do tej funkcji to nazwa elementu, który przypisaliśmy do parametru output w funkcji server. Oto mechanizm, którego używa Shiny do przenoszenia danych z server do ui:

ui <- fluidPage (

titlePanel(„Cryptourrency Markets”),

sidebarLayout (

sidebarPanel(„Options”),

mainPanel (

tabsetPanel (

tabPanel(„Simple Moving Averges”, „Content  1”),

tabPanel(„Data Overview”,

fluidRow(DT::dataTableOutput(„table”))

)

)

)

)

)

server <- function(input, output) {

output$table <-

DT :: renderDataTable(DT :: datatable({return(ORIGINAL_DATA)}))

Teraz, gdy nasz kod jest gotowy, na karcie Data Overview powinna pojawić się tabela. Ta tabela jest dynamiczna w tym sensie, że można uporządkować jej kolumny, klikając tytuły kolumn, a także zmienić liczbę wyświetlanych obserwacji i przeszukiwać jej zawartość. Zwróć również uwagę, że mechanizm paginacji został automatycznie dodany wraz z licznikiem obserwacji. Te tabele są bardzo łatwe do utworzenia, a jednocześnie bardzo przydatne, a w dalszej części zobaczymy, jak jeszcze bardziej rozszerzyć ich funkcjonalność.

 

Przedstawiamy sekcje z panelami

Aby pokazać sekcję dla wykresów i osobną dla tabel danych, użyjemy funkcji tabsPanel() w połączeniu z funkcją tabPanel(). Funkcja tabsPanel() odbiera i organizuje jedno lub więcej wywołań funkcji tablePanel(), przy czym każda z nich otrzymuje nazwę karty i jej rzeczywistą zawartość:

ui <- fluiPage (

titlePanel(„Cryptocurreny Markets”),

sidebarLayout (

sidebarPanel(„Options”),

mainPanel (

tabsetPanel (

tabPanel(„Simple Moving Averages”, „Content 1”),

tabPanel(„DataOverview”, „Content 2”)

)

)

)

)

Ponieważ stworzyliśmy dwie zakładki z tytułami, odpowiednio, Simple Moving Averages i Data Overview, to właśnie widzimy jako nazwy kart. Jeśli sam uruchamiasz aplikację w tym momencie, możesz na nie kliknąć, a zobaczysz ciąg znaków lub, w zależności od tego, który klikniesz:

Zauważ, że funkcja tabsetPanel() zajęła miejsce łańcucha „Content”, który wcześniej mieliśmy na swoim miejscu. To będzie powszechny wzorzec. W miarę jak zaczniemy wprowadzać do aplikacji coraz więcej elementów, będą one zastępować dotychczasowe symbole zastępcze. Gdy przyzwyczaisz się do Shiny, możesz całkowicie uniknąć tworzenia symboli zastępczych