-
Notifications
You must be signed in to change notification settings - Fork 6
/
action-transfer.Rmd
259 lines (206 loc) · 7.78 KB
/
action-transfer.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# Uploads and Downloads
### Exercise 9.4.1 {-}
Use the [ambient](https://ambient.data-imaginist.com) package by Thomas Lin
Pedersen to generate [worley noise](https://ambient.data-imaginist.com/reference/noise_worley.html)
and download a PNG of it.
:::solution
#### Solution {-}
A general method for saving a png file is to select the png driver using the
function `png()`. The only argument the driver needs is a filename (this will
be stored relative to your current working directory!). You will not see the
plot when running the `plot` function because it is being saved to that file
instead. When we're done plotting, we used the `dev.off()` command to close the
connection to the driver.
```{r, eval=FALSE}
library(ambient)
noise <- ambient::noise_worley(c(100, 100))
png("noise_plot.png")
plot(as.raster(normalise(noise)))
dev.off()
```
:::
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
### Exercise 9.4.2 {-}
Create an app that lets you upload a csv file, select a variable, and then
perform a `t.test()` on that variable. After the user has uploaded the csv
file, you'll need to use `updateSelectInput()` to fill in the available
variables. See Section
[10.1](https://mastering-shiny.org/action-dynamic.html#updating-inputs)
for details.
:::solution
#### Solution {-}
We can use the `fileInput` widget with the `accept` argument set to `.csv` to
allow only the upload of csv files. In the `server` function we save the
uploaded data to the the `data` reactive and use it to update `input$variable`,
which displays variable (i.e. numeric data column) choices. Note that we put
the `updateSelectInput` within an observe event because we need the
`input$variable` to change if the user selects another file.
```{r, eval=FALSE}
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file", "Upload CSV", accept = ".csv"), # file widget
selectInput("variable", "Select Variable", choices = NULL) # select widget
),
mainPanel(
verbatimTextOutput("results") # t-test results
)
)
)
server <- function(input, output,session) {
# get data from file
data <- reactive({
req(input$file)
# as shown in the book, lets make sure the uploaded file is a csv
ext <- tools::file_ext(input$file$name)
validate(need(ext == "csv", "Invalid file. Please upload a .csv file"))
dataset <- vroom::vroom(input$file$datapath, delim = ",")
# let the user know if the data contains no numeric column
validate(need(ncol(dplyr::select_if(dataset, is.numeric)) != 0,
"This dataset has no numeric columns."))
dataset
})
# create the select input based on the numeric columns in the dataframe
observeEvent(input$file, {
req(data())
num_cols <- dplyr::select_if(data(), is.numeric)
updateSelectInput(session, "variable", choices = colnames(num_cols))
})
# print t-test results
output$results <- renderPrint({
if(!is.null(input$variable))
t.test(data()[input$variable])
})
}
shinyApp(ui, server)
```
:::
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
### Exercise 9.4.3 {-}
Create an app that lets the user upload a csv file, select one variable,
draw a histogram, and then download the histogram. For an additional challenge,
allow the user to select from .png, .pdf, and .svg output formats.
:::solution
#### Solution {-}
Adapting the code from the example above, rather than print a t-test output, we
save the plot in a reactive and use it to display the plot/download. We can use
the `ggsave` function to switch between `input$extension` types.
```{r, eval=FALSE}
library(shiny)
library(ggplot2)
ui <- fluidPage(
tagList(
br(), br(),
column(4,
wellPanel(
fileInput("file", "Upload CSV", accept = ".csv"),
selectInput("variable", "Select Variable", choices = NULL),
),
wellPanel(
radioButtons("extension", "Save As:",
choices = c("png", "pdf", "svg"), inline = TRUE),
downloadButton("download", "Save Plot")
)
),
column(8, plotOutput("results"))
)
)
server <- function(input, output,session) {
# get data from file
data <- reactive({
req(input$file)
# as shown in the book, lets make sure the uploaded file is a csv
ext <- tools::file_ext(input$file$name)
validate(need(ext == "csv", "Invalid file. Please upload a .csv file"))
dataset <- vroom::vroom(input$file$datapath, delim = ",")
# let the user know if the data contains no numeric column
validate(need(ncol(dplyr::select_if(dataset, is.numeric)) != 0,
"This dataset has no numeric columns."))
dataset
})
# create the select input based on the numeric columns in the dataframe
observeEvent( input$file, {
req(data())
num_cols <- dplyr::select_if(data(), is.numeric)
updateSelectInput(session, "variable", choices = colnames(num_cols))
})
# plot histogram
plot_output <- reactive({
req(!is.null(input$variable))
ggplot(data()) +
aes_string(x = input$variable) +
geom_histogram()
})
output$results <- renderPlot(plot_output())
# save histogram using downloadHandler and plot output type
output$download <- downloadHandler(
filename = function() {
paste("histogram", input$extension, sep = ".")
},
content = function(file){
ggsave(file, plot_output(), device = input$extension)
}
)
}
shinyApp(ui, server)
```
:::
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
<!---------------------------------------------------------------------------->
### Exercise 9.4.4 {-}
Write an app that allows the user to create a Lego mosaic from any .png file
using Ryan Timpe's [brickr](https://github.com/ryantimpe/brickr) package. Once
you've completed the basics, add controls to allow the user to select the size
of the mosaic (in bricks), and choose whether to use "universal" or "generic"
colour palettes.
:::solution
#### Solution {-}
Instead of limiting our file selection to a csv as above, here we are going to
limit our input to a png. We'll use the `png::readPNG` function to read in our
file, and specify the size/color of our mosaic in `brickr`'s `image_to_mosaic`
function. Read more about the package and examples
[here](https://github.com/ryantimpe/brickr).
```{r, eval=FALSE}
library(shiny)
library(brickr)
library(png)
# Function to provide user feedback (checkout Chapter 8 for more info).
notify <- function(msg, id = NULL) {
showNotification(msg, id = id, duration = NULL, closeButton = FALSE)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
fileInput("myFile", "Upload a PNG file", accept = c('image/png')),
sliderInput("size", "Select size:", min = 1, max = 100, value = 35),
radioButtons("color", "Select color palette:", choices = c("universal", "generic"))
)
),
mainPanel(
plotOutput("result"))
)
)
server <- function(input, output) {
imageFile <- reactive({
if(!is.null(input$myFile))
png::readPNG(input$myFile$datapath)
})
output$result <- renderPlot({
req(imageFile())
id <- notify("Transforming image...")
on.exit(removeNotification(id), add = TRUE)
imageFile() %>%
image_to_mosaic(img_size = input$size, color_palette = input$color) %>%
build_mosaic()
})
}
shinyApp(ui, server)
```
:::