harpomaxx commited on
Commit
8eab031
1 Parent(s): 82d538f

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.R +221 -37
  2. goat_behavior_model_caret.rds +0 -0
app.R CHANGED
@@ -1,51 +1,235 @@
 
1
  library(shiny)
2
- library(bslib)
3
- library(dplyr)
 
4
  library(ggplot2)
5
 
6
- df <- readr::read_csv("penguins.csv")
7
- # Find subset of columns that are suitable for scatter plot
8
- df_num <- df |> select(where(is.numeric), -Year)
9
 
10
- ui <- page_fillable(theme = bs_theme(bootswatch = "minty"),
11
- layout_sidebar(fillable = TRUE,
12
- sidebar(
13
- varSelectInput("xvar", "X variable", df_num, selected = "Bill Length (mm)"),
14
- varSelectInput("yvar", "Y variable", df_num, selected = "Bill Depth (mm)"),
15
- checkboxGroupInput("species", "Filter by species",
16
- choices = unique(df$Species), selected = unique(df$Species)
17
- ),
18
- hr(), # Add a horizontal rule
19
- checkboxInput("by_species", "Show species", TRUE),
20
- checkboxInput("show_margins", "Show marginal plots", TRUE),
21
- checkboxInput("smooth", "Add smoother"),
 
 
 
 
 
 
 
 
22
  ),
23
- plotOutput("scatter")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  )
25
  )
26
 
27
- server <- function(input, output, session) {
28
- subsetted <- reactive({
29
- req(input$species)
30
- df |> filter(Species %in% input$species)
 
 
 
 
 
 
 
 
 
31
  })
32
 
33
- output$scatter <- renderPlot({
34
- p <- ggplot(subsetted(), aes(!!input$xvar, !!input$yvar)) + list(
35
- theme(legend.position = "bottom"),
36
- if (input$by_species) aes(color=Species),
37
- geom_point(),
38
- if (input$smooth) geom_smooth()
39
- )
40
-
41
- if (input$show_margins) {
42
- margin_type <- if (input$by_species) "density" else "histogram"
43
- p <- p |> ggExtra::ggMarginal(type = margin_type, margins = "both",
44
- size = 8, groupColour = input$by_species, groupFill = input$by_species)
 
 
 
 
 
 
 
 
 
 
45
  }
 
 
 
 
 
 
 
46
 
47
- p
48
- }, res = 100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
- shinyApp(ui, server)
 
 
1
+ # Import necessary libraries
2
  library(shiny)
3
+ library(caret)
4
+ library(readr)
5
+ library(catboost)
6
  library(ggplot2)
7
 
8
+ # Load the pre-trained model
9
+ model <- readRDS("goat_behavior_model_caret.rds")
 
10
 
11
+ # Define UI for application
12
+ ui <- fluidPage(
13
+
14
+ # App title ----
15
+ titlePanel("Detecting Goat Behaviors"),
16
+
17
+ # Sidebar layout with input and output definitions ----
18
+ sidebarLayout(
19
+
20
+ # Sidebar panel for inputs ----
21
+ sidebarPanel(
22
+
23
+ # Input: Select a file ----
24
+ fileInput("file1", "Choose TSV File",
25
+ accept = c(
26
+ "text/tsv",
27
+ "text/tab-separated-values,text/plain",
28
+ ".tsv")
29
+ )
30
+
31
  ),
32
+
33
+ # Main panel for displaying outputs ----
34
+ mainPanel(
35
+
36
+ # Output: Tabset with data, confusion matrix, and download button
37
+ tabsetPanel(
38
+ id = "dataset",
39
+ tabPanel("About",
40
+ HTML("
41
+ <h5> The following model was part of the the research article:
42
+ <h4>Developing an Interpretable Machine Learning Model for the Detection of Mimosa Grazing in Goats</h4>
43
+
44
+ <em>In the last years, several machine learning approaches for detecting animal behaviors have been proposed.
45
+ However, despite their successful application, their complexity and lack of explainability have difficulty in their
46
+ application to real-world scenarios. The article presents a machine-learning model for differentiating between grazing mimosa and other activities
47
+ (resting, walking, and grazing ) in goats using sensor data. Boruta, an algorithm for selecting the most relevant features, and SHAP,
48
+ a technique for interpreting the decision of a machine learning model are two fundamental components of the methodology used for creating the model.
49
+ The resulting model, a gradient boost algorithm with 15 selected features proved to be extremely accurate in detecting Grazing activities.
50
+ The study demonstrates the fundamental role of model explainability in identifying model weaknesses and errors, thereby creating a path for future
51
+ improvements. In addition, the simplicity of the resulting model not only reduces computational complexity and processing time but also enhances
52
+ interpretability and facilitates the deployment of real-life scenarios.</em>
53
+ <p>
54
+ <p>This application allows users to test the pre-trained machine learning model that predicts goat behavior based on input sensor data.
55
+ The input data should be a tab-separated value (.tsv) file containing specific sensor data related to the goat's activity.
56
+ <p>The application then generates predictions, provides a confusion matrix result, and offers the option to download the predictions.
57
+ <p>The key features expected in the dataset are:
58
+ <table>
59
+ <thead>
60
+ <tr>
61
+ <th>No</th>
62
+ <th>Feature</th>
63
+ <th>Definition</th>
64
+ </tr>
65
+ </thead>
66
+ <tbody>
67
+ <tr>
68
+ <td>1</td>
69
+ <td>Steps</td>
70
+ <td>Number of steps</td>
71
+ </tr>
72
+ <tr>
73
+ <td>2</td>
74
+ <td>HeadDown</td>
75
+ <td>% time with head down</td>
76
+ </tr>
77
+ <tr>
78
+ <td>3</td>
79
+ <td>Standing</td>
80
+ <td>% time Standing</td>
81
+ </tr>
82
+ <tr>
83
+ <td>4</td>
84
+ <td>Active</td>
85
+ <td>% time Active</td>
86
+ </tr>
87
+ <tr>
88
+ <td>5</td>
89
+ <td>MeanXY</td>
90
+ <td>Arithmetic mean between X and Y positions</td>
91
+ </tr>
92
+ <tr>
93
+ <td>6</td>
94
+ <td>Distance</td>
95
+ <td>Distance in meters</td>
96
+ </tr>
97
+ <tr>
98
+ <td>7</td>
99
+ <td>prev_steps1</td>
100
+ <td>Number of steps one step backward</td>
101
+ </tr>
102
+ <tr>
103
+ <td>8</td>
104
+ <td>X_Act</td>
105
+ <td>X position actuator</td>
106
+ </tr>
107
+ <tr>
108
+ <td>9</td>
109
+ <td>prev_Active1</td>
110
+ <td>% time Active one step backward</td>
111
+ </tr>
112
+ <tr>
113
+ <td>10</td>
114
+ <td>prev_Standing1</td>
115
+ <td>% time Standing one step backward</td>
116
+ </tr>
117
+ <tr>
118
+ <td>11</td>
119
+ <td>DFA123</td>
120
+ <td>Accumulative Euclidean distance from actual position to three positions forward</td>
121
+ </tr>
122
+ <tr>
123
+ <td>12</td>
124
+ <td>prev_headdown1</td>
125
+ <td>% time with head down one step backward</td>
126
+ </tr>
127
+ <tr>
128
+ <td>13</td>
129
+ <td>Lying</td>
130
+ <td>% time Lying</td>
131
+ </tr>
132
+ <tr>
133
+ <td>14</td>
134
+ <td>Y_Act</td>
135
+ <td>Y position actuator</td>
136
+ </tr>
137
+ <tr>
138
+ <td>15</td>
139
+ <td>DBA123</td>
140
+ <td>Accumulative Euclidean distance from actual position to three positions backward</td>
141
+ </tr>
142
+ </tbody>
143
+ </table>
144
+
145
+ <p><p> <h5> Experiments, source code and more <a href=https://github.com/harpomaxx/goat-behavior/> here<a/> </h5>")
146
+ ),
147
+
148
+ tabPanel("Results",
149
+ tableOutput("contents"),
150
+ verbatimTextOutput("confusionMatText"),
151
+ plotOutput("confusionMatPlot"),
152
+ downloadButton("downloadData", "Download Predictions"))
153
+ )
154
+ )
155
  )
156
  )
157
 
158
+ # Define server logic
159
+ server <- function(input, output) {
160
+
161
+ # For the predictions dataset
162
+ predictions <- reactive({
163
+
164
+ if (is.null(input$file1))
165
+ return(NULL)
166
+
167
+ inFile <- input$file1
168
+ dataset <- readr::read_delim(inFile$datapath,delim='\t')
169
+ predict(model, dataset)
170
+
171
  })
172
 
173
+ # For the table
174
+ output$contents <- renderTable({
175
+
176
+ # input$file1 will be NULL initially. After the user selects
177
+ # a file, it will be a data frame with 'name', 'size', 'type', and 'datapath' variables.
178
+ inFile <- input$file1
179
+
180
+ if (is.null(inFile))
181
+ return(NULL)
182
+
183
+ # Read the file from the input$file1 path and return it
184
+ dataset <- readr::read_delim(inFile$datapath,delim='\t')
185
+ head(dataset, n = 5)
186
+ })
187
+
188
+ # Download function for predictions
189
+ output$downloadData <- downloadHandler(
190
+ filename = function() {
191
+ paste("predictions-", Sys.Date(), ".csv", sep="")
192
+ },
193
+ content = function(file) {
194
+ write.csv(data.frame(Index = 1:length(predictions()), Prediction = predictions()), file, row.names = FALSE)
195
  }
196
+ )
197
+
198
+ # Confusion Matrix
199
+ output$confusionMatText <- renderPrint({
200
+
201
+ if (is.null(input$file1))
202
+ return(NULL)
203
 
204
+ inFile <- input$file1
205
+ dataset <- readr::read_delim(inFile$datapath,delim='\t',progress = FALSE)
206
+ predictions <- predict(model, dataset)
207
+ cm<-caret::confusionMatrix(reference=as.factor(dataset$Activity),predictions,mode="everything")
208
+ cm$overall
209
+ })
210
+
211
+ output$confusionMatPlot <- renderPlot({
212
+
213
+ if (is.null(input$file1))
214
+ return(NULL)
215
+
216
+ inFile <- input$file1
217
+ dataset <- readr::read_delim(inFile$datapath,delim='\t')
218
+ predictions <- predict(model, dataset)
219
+ cm<-caret::confusionMatrix(reference=as.factor(dataset$Activity),predictions,mode="everything")
220
+
221
+ # Extract table data from confusion matrix
222
+ confusionMatrixTable <- as.table(cm$table)
223
+
224
+ # Plot the confusion matrix
225
+ ggplot(as.data.frame(confusionMatrixTable), aes(x=Reference, y=Prediction)) +
226
+ geom_tile(aes(fill = log(Freq)), colour = "white") +
227
+ geom_text(aes(label = sprintf("%1.0f", Freq)), vjust = 1) +
228
+ scale_fill_gradient(low = "white", high = "steelblue") +
229
+ theme_minimal() +
230
+ theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))
231
+ })
232
  }
233
 
234
+ # Create a Shiny app object
235
+ shinyApp(ui = ui, server = server)
goat_behavior_model_caret.rds ADDED
Binary file (537 kB). View file