Spaces:
Running
Running
0.1.0 first prototype
Browse files
app/app.R
ADDED
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
library(shiny)
|
2 |
+
library(shinydashboard)
|
3 |
+
library(shinydashboardPlus)
|
4 |
+
library(shinyWidgets)
|
5 |
+
library(shinycssloaders)
|
6 |
+
library(data.table)
|
7 |
+
library(Rnumerai)
|
8 |
+
library(DT)
|
9 |
+
|
10 |
+
|
11 |
+
# ==============================================================================
|
12 |
+
# Leaderboard
|
13 |
+
# ==============================================================================
|
14 |
+
|
15 |
+
# Download latest leaderboard from Numerai and get a list of all models
|
16 |
+
d_lb <- get_leaderboard()
|
17 |
+
ls_model <- sort(d_lb$username)
|
18 |
+
|
19 |
+
|
20 |
+
# ==============================================================================
|
21 |
+
# Helper Functions
|
22 |
+
# ==============================================================================
|
23 |
+
|
24 |
+
# Download raw data
|
25 |
+
download_raw_data <- function(model_name) {
|
26 |
+
|
27 |
+
# Download data from Numerai
|
28 |
+
d_raw <- round_model_performances(model_name)
|
29 |
+
|
30 |
+
# Remove rows without CORR
|
31 |
+
d_raw <- d_raw[!is.na(d_raw$corr), ]
|
32 |
+
|
33 |
+
# Add the model name
|
34 |
+
d_raw$model <- model_name
|
35 |
+
|
36 |
+
# Return
|
37 |
+
return(d_raw)
|
38 |
+
|
39 |
+
}
|
40 |
+
|
41 |
+
# Reformat
|
42 |
+
reformat_data <- function(d_raw) {
|
43 |
+
|
44 |
+
# Keep some columns only
|
45 |
+
col_keep <- c("model", "corr", "corrPercentile", "corrWMetamodel",
|
46 |
+
"fncV3", "fncV3Percentile", "payout", "roundPayoutFactor",
|
47 |
+
"roundNumber", "roundResolved", "selectedStakeValue",
|
48 |
+
"tc", "tcPercentile")
|
49 |
+
d_munged <- as.data.table(d_raw[, col_keep])
|
50 |
+
|
51 |
+
# Reformat percentile
|
52 |
+
d_munged[, corrPercentile := round(corrPercentile * 100, 6)]
|
53 |
+
d_munged[, fncV3Percentile := round(fncV3Percentile * 100, 6)]
|
54 |
+
d_munged[, tcPercentile := round(tcPercentile * 100, 6)]
|
55 |
+
|
56 |
+
# Reorder columns
|
57 |
+
setcolorder(d_munged, c("model", "roundNumber", "roundResolved",
|
58 |
+
"selectedStakeValue",
|
59 |
+
"corr", "corrPercentile",
|
60 |
+
"fncV3", "fncV3Percentile",
|
61 |
+
"tc", "tcPercentile",
|
62 |
+
"corrWMetamodel",
|
63 |
+
"roundPayoutFactor", "payout"))
|
64 |
+
|
65 |
+
# Rename columns
|
66 |
+
colnames(d_munged) <- c("model", "round", "resolved",
|
67 |
+
"stake",
|
68 |
+
"corr", "corr_pct",
|
69 |
+
"fncv3", "fncv3_pct",
|
70 |
+
"tc", "tc_pct",
|
71 |
+
"corr_meta",
|
72 |
+
"pay_ftr", "payout")
|
73 |
+
|
74 |
+
# Return
|
75 |
+
return(d_munged)
|
76 |
+
|
77 |
+
}
|
78 |
+
|
79 |
+
|
80 |
+
# ==============================================================================
|
81 |
+
# UI
|
82 |
+
# ==============================================================================
|
83 |
+
|
84 |
+
ui <- shinydashboardPlus::dashboardPage(
|
85 |
+
|
86 |
+
title = "Shiny Numerati",
|
87 |
+
|
88 |
+
skin = "black-light",
|
89 |
+
|
90 |
+
options = list(sidebarExpandOnHover = TRUE),
|
91 |
+
|
92 |
+
header = shinydashboardPlus::dashboardHeader(
|
93 |
+
title = "✨ Shiny Numerati",
|
94 |
+
userOutput("user")
|
95 |
+
),
|
96 |
+
|
97 |
+
sidebar = shinydashboardPlus::dashboardSidebar(
|
98 |
+
id = "sidebar",
|
99 |
+
sidebarMenu(
|
100 |
+
menuItem(text = "Start Here", tabName = "start", icon = icon("play")),
|
101 |
+
menuItem(text = "Performance", tabName = "performance", icon = icon("line-chart")),
|
102 |
+
menuItem(text = "Payout", tabName = "payout", icon = icon("credit-card")),
|
103 |
+
menuItem(text = "About", tabName = "about", icon = icon("question-circle"))
|
104 |
+
),
|
105 |
+
minified = TRUE,
|
106 |
+
collapsed = FALSE
|
107 |
+
),
|
108 |
+
|
109 |
+
body = dashboardBody(
|
110 |
+
|
111 |
+
tabItems(
|
112 |
+
|
113 |
+
# ========================================================================
|
114 |
+
|
115 |
+
tabItem(tabName = "start",
|
116 |
+
|
117 |
+
fluidPage(
|
118 |
+
|
119 |
+
markdown("# **Shiny Numerati**"),
|
120 |
+
markdown("### Community Dashboard for the Numerai Classic Tournament"),
|
121 |
+
|
122 |
+
br(),
|
123 |
+
|
124 |
+
fluidRow(
|
125 |
+
|
126 |
+
column(6,
|
127 |
+
|
128 |
+
markdown("## **Step 1 - Select Your Models**"),
|
129 |
+
|
130 |
+
markdown("### First, click this ⬇"),
|
131 |
+
|
132 |
+
pickerInput(inputId = "model",
|
133 |
+
label = " ",
|
134 |
+
choices = ls_model,
|
135 |
+
multiple = TRUE,
|
136 |
+
width = "100%",
|
137 |
+
options = list(
|
138 |
+
`title` = "---------->>> HERE <<<----------",
|
139 |
+
`header` = "Notes: 1) Use the search box below to find and select your models. 2) Use 'Select All' for quick selection.",
|
140 |
+
size = 20,
|
141 |
+
`actions-box` = TRUE,
|
142 |
+
`live-search` = TRUE,
|
143 |
+
`live-search-placeholder` = "For example, try lgbm_v4 or integration_test",
|
144 |
+
`virtual-scroll` = TRUE,
|
145 |
+
`multiple-separator` = ", ",
|
146 |
+
`selected-text-format`= "count > 3",
|
147 |
+
`count-selected-text` = "{0} models selected (out of {1})",
|
148 |
+
`deselect-all-text` = "Deselect All",
|
149 |
+
`select-all-text` = "Select All"
|
150 |
+
)
|
151 |
+
)
|
152 |
+
),
|
153 |
+
|
154 |
+
column(6,
|
155 |
+
|
156 |
+
markdown("## **Step 2 - Download Data**"),
|
157 |
+
|
158 |
+
markdown("### Next, click this ⬇ (it may take a while)"),
|
159 |
+
|
160 |
+
br(),
|
161 |
+
|
162 |
+
actionBttn(inputId = "button_download",
|
163 |
+
label = "Download Data from Numerai",
|
164 |
+
color = "primary",
|
165 |
+
icon = icon("cloud-download"),
|
166 |
+
style = "gradient",
|
167 |
+
block = TRUE
|
168 |
+
)
|
169 |
+
)
|
170 |
+
),
|
171 |
+
|
172 |
+
br(),
|
173 |
+
|
174 |
+
h3(strong(textOutput(outputId = "text_download"))),
|
175 |
+
verbatimTextOutput(outputId = "print_download"),
|
176 |
+
|
177 |
+
br(),
|
178 |
+
|
179 |
+
h3(strong(textOutput(outputId = "text_preview"))),
|
180 |
+
shinycssloaders::withSpinner(DTOutput("dt_model")),
|
181 |
+
|
182 |
+
br(),
|
183 |
+
|
184 |
+
h3(strong(textOutput(outputId = "text_next")))
|
185 |
+
|
186 |
+
)
|
187 |
+
),
|
188 |
+
|
189 |
+
# ========================================================================
|
190 |
+
|
191 |
+
tabItem(tabName = "performance",
|
192 |
+
fluidPage(
|
193 |
+
markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)")
|
194 |
+
)
|
195 |
+
),
|
196 |
+
|
197 |
+
tabItem(tabName = "payout",
|
198 |
+
fluidPage(
|
199 |
+
markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)")
|
200 |
+
)
|
201 |
+
),
|
202 |
+
|
203 |
+
# ========================================================================
|
204 |
+
|
205 |
+
tabItem(tabName = "about",
|
206 |
+
markdown("## **About this App**"),
|
207 |
+
markdown('#### Yet another Numerai community dashboard by <b><a href="https://linktr.ee/jofaichow" target="_blank">Jo-fai Chow</a></b>.'),
|
208 |
+
|
209 |
+
br(),
|
210 |
+
markdown("## **Acknowledgement**"),
|
211 |
+
markdown("#### This hobby project was inspired by Rajiv's <a href='https://huggingface.co/spaces/rajistics/shiny-kmeans' target='_blank'>shiny-kmeans</a> on 🤗 Spaces."),
|
212 |
+
|
213 |
+
br(),
|
214 |
+
markdown("## **Changelog**"),
|
215 |
+
markdown(
|
216 |
+
"
|
217 |
+
- #### **0.1.0** — First prototype with an interactive table output
|
218 |
+
"),
|
219 |
+
br(),
|
220 |
+
markdown("## **Session Info**"),
|
221 |
+
verbatimTextOutput(outputId = "session_info")
|
222 |
+
)
|
223 |
+
|
224 |
+
# ========================================================================
|
225 |
+
|
226 |
+
) # end of tabItems
|
227 |
+
|
228 |
+
),
|
229 |
+
|
230 |
+
footer = shinydashboardPlus::dashboardFooter(
|
231 |
+
left = "Powered by ❤️, ☕, Shiny, and 🤗 Spaces",
|
232 |
+
right = paste0("Version 0.1.0"))
|
233 |
+
|
234 |
+
)
|
235 |
+
|
236 |
+
|
237 |
+
|
238 |
+
# ==============================================================================
|
239 |
+
# Server
|
240 |
+
# ==============================================================================
|
241 |
+
|
242 |
+
server <- function(input, output) {
|
243 |
+
|
244 |
+
# About Joe
|
245 |
+
output$user <- renderUser({
|
246 |
+
dashboardUser(
|
247 |
+
name = "JC",
|
248 |
+
image = "https://numerai-public-images.s3.amazonaws.com/profile_images/aijoe_v5_compressed-iJWEo1WeHkpH.jpg",
|
249 |
+
subtitle = "@matlabulous",
|
250 |
+
footer = p('"THE NMR LIFE CHOSE ME."', class = 'text-center')
|
251 |
+
)
|
252 |
+
})
|
253 |
+
|
254 |
+
|
255 |
+
# ============================================================================
|
256 |
+
# Reactive --> Download Model Data
|
257 |
+
# ============================================================================
|
258 |
+
|
259 |
+
react_download <- eventReactive(input$button_download, {sort(input$model)})
|
260 |
+
|
261 |
+
output$print_download <- renderPrint({react_download()})
|
262 |
+
|
263 |
+
output$text_download <- renderText({
|
264 |
+
if (length(react_download()) >= 1) "Your Selection:" else " "
|
265 |
+
})
|
266 |
+
|
267 |
+
output$text_preview <- renderText({
|
268 |
+
if (length(react_download()) >= 1) "Data Preview:" else " "
|
269 |
+
})
|
270 |
+
|
271 |
+
output$text_next <- renderText({
|
272 |
+
if (length(react_download()) >= 1) "⬅ [Coming Soon] Performance and Payout Charts 📈📊🔥" else " "
|
273 |
+
})
|
274 |
+
|
275 |
+
react_d_model <- eventReactive(
|
276 |
+
input$button_download,
|
277 |
+
{
|
278 |
+
|
279 |
+
# Download dataframes one by one (may parallelise this in the future)
|
280 |
+
d_raw <- c()
|
281 |
+
for (item in input$model) d_raw <- rbind(d_raw, download_raw_data(item))
|
282 |
+
|
283 |
+
# Data munging
|
284 |
+
d_munged <- reformat_data(d_raw)
|
285 |
+
|
286 |
+
# Return final result
|
287 |
+
d_munged
|
288 |
+
|
289 |
+
}
|
290 |
+
)
|
291 |
+
|
292 |
+
output$dt_model <- DT::renderDT({
|
293 |
+
|
294 |
+
DT::datatable(
|
295 |
+
|
296 |
+
# Data
|
297 |
+
react_d_model(),
|
298 |
+
|
299 |
+
# Other Options
|
300 |
+
rownames = FALSE,
|
301 |
+
extensions = "Buttons",
|
302 |
+
options =
|
303 |
+
list(
|
304 |
+
dom = 'Bflrtip', # https://datatables.net/reference/option/dom
|
305 |
+
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html
|
306 |
+
order = list(list(0, 'asc'), list(1, 'asc')),
|
307 |
+
pageLength = 5,
|
308 |
+
lengthMenu = c(5, 10, 20, 100, 500, 1000, 50000),
|
309 |
+
columnDefs = list(list(className = 'dt-center', targets = "_all")))
|
310 |
+
) |>
|
311 |
+
|
312 |
+
# Reformat individual columns
|
313 |
+
formatRound(columns = c("corr", "tc", "fncv3", "corr_meta", "pay_ftr"), digits = 4) |>
|
314 |
+
formatRound(columns = c("corr_pct", "tc_pct", "fncv3_pct"), digits = 1) |>
|
315 |
+
formatRound(columns = c("stake", "payout"), digits = 2) |>
|
316 |
+
|
317 |
+
formatStyle(columns = c("model"),
|
318 |
+
fontWeight = "bold") |>
|
319 |
+
|
320 |
+
formatStyle(columns = c("stake"),
|
321 |
+
fontWeight = "bold",
|
322 |
+
color = styleInterval(cuts = -1e-15, values = c("#D24141", "#2196F3"))) |>
|
323 |
+
|
324 |
+
formatStyle(columns = c("corr", "fncv3"),
|
325 |
+
color = styleInterval(cuts = -1e-15, values = c("#D24141", "black"))) |>
|
326 |
+
|
327 |
+
formatStyle(columns = c("tc"),
|
328 |
+
color = styleInterval(cuts = -1e-15, values = c("#D24141", "#A278DC"))) |>
|
329 |
+
|
330 |
+
formatStyle(columns = c("corr_pct", "tc_pct", "fncv3_pct"),
|
331 |
+
color = styleInterval(cuts = c(1, 5, 15, 85, 95, 99),
|
332 |
+
values = c("#692020", "#9A2F2F", "#D24141",
|
333 |
+
"#D1D1D1", # light grey
|
334 |
+
"#00A800", "#007000", "#003700"))) |>
|
335 |
+
|
336 |
+
formatStyle(columns = c("payout"),
|
337 |
+
fontWeight = "bold",
|
338 |
+
color = styleInterval(cuts = c(-1e-15, 1e-15),
|
339 |
+
values = c("#D24141", "#D1D1D1", "#00A800")))
|
340 |
+
|
341 |
+
})
|
342 |
+
|
343 |
+
|
344 |
+
# ============================================================================
|
345 |
+
# Session Info
|
346 |
+
# ============================================================================
|
347 |
+
|
348 |
+
output$session_info <- renderPrint({
|
349 |
+
sessionInfo()
|
350 |
+
})
|
351 |
+
|
352 |
+
}
|
353 |
+
|
354 |
+
|
355 |
+
# ==============================================================================
|
356 |
+
# App
|
357 |
+
# ==============================================================================
|
358 |
+
|
359 |
+
shinyApp(ui, server)
|
360 |
+
|