2
votes

I am making a simple Shiny app that inputs a player's name and statistic and then returns the percentile that the player is in for that statistic. I am currently running into an issue where the 'statistic' widget is causing an error (see title).

Here are the packages I am using and a sample of the data:

library(shiny)
library(dplyr)
library(mosaic)

player <- c("John", "Mike", "Devon", "Greg", "Bruce", "Zachary", "Jack", "Graham", "Jordan", "Sandy")
team <- c("A", "B", "A", "B", "A", "B", "A", "B", "A", "B")
wins <- c(1:10)
losses <- c(10:1)

sampledata <- data.frame(player, team, wins, losses)

On the app, there are three widgets: (1) input a player's name, (2) select a stat, and (3) execute the selections.
The output is a single line of text.
Here is the ui.r:

ui <- fluidPage(
  titlePanel("Percentile Generator"),
    sidebarLayout(
        sidebarPanel(
            textInput("playerfind",
                      "Player:",
                      value = "Devon"),
            selectInput("stat1", "Select Statistic:",
                        choices = list("wins", "losses", "ties"),
                        selected = "wins"),
            actionButton("action", label = "Generate Percentile!")
    ),
        mainPanel(
           textOutput("percentmachine")
        ))
    )

The server is a little bit more complicated. Step 1 filters the sample data and produces a 1x3 dataframe based on the inputs. Step 2 pulls the necessary value out of the matric and stores it. Lastly, step 3 takes the input from step 2 and produces a percentile.

server <- function(input, output) {
    step1 <- reactive({sampledata %>%
        transmute(player, stat = zscore(input$stat1)) %>%
        filter(player == input$playerfind)})

    step2 <- reactive({step1()[1,2]})

    step3 <- reactive({round(pnorm(step2())*100, digits = 1)})

    output$percentmachine <- renderText ({
        input$action
        isolate(paste(input$playerfind, 
                      "had more",
                      input$stat1,
                      "than",
                      step3(),
                      "percent of players."))})
}

I believe that the error comes from input$stat1 in step 1. If I replace this input with a specific stat like 'wins', then the shiny app runs fine although the statistic cannot be changed. I have been struggling with this for quite some time so I figured I would ask on here.

Thanks in advance! xD

2
You're feeding a character value (input$stat1) into zscore, a function that requires numeric inputs. If you try zscore("A"), you'll get the same error. - r2evans
Further, because zscore is looking for a distribution of data in order to determine the z-scores for each of them, perhaps you should first calculate your z-scores with something like sampledata$z <- zscore(sampledata$wins). *shrug* - r2evans
I cannot imagine any instance of zscore(some_strings) ever working, looking at its source. Perhaps your R script is feeding a different variable? Are you intending to get the input$stat1 column from the data and put it in there? Perhaps you mean zscore(sampledata[,input$stat1])? (Except that the frame doesn't have a "ties" column ...) - r2evans
I thought that I was accessing the column from the data because sample data was piped to transmute, although I wasn't! Thanks for the clarification and if you write that comment as an answer, I will mark it as solved :) - wickmanb22

2 Answers

0
votes

You are feeding a character value into a function that takes numeric.

I'll simulate outside of a shiny/reactive environment.

input <- list(playerfind="Devon", stat1="wins")
# I don't have mosaic installed
zscore <- function( x, na.rm=getOption("na.rm", FALSE) ) ( x - mean(x, na.rm=na.rm)) / sd(x, na.rm=na.rm)
sampledata %>%
  transmute(player, stat = zscore(input$stat1))
# Warning in mean.default(x, na.rm = na.rm) :
#   argument is not numeric or logical: returning NA
# Error in x - mean(x, na.rm = na.rm) (from #1) : 
#   non-numeric argument to binary operator

One can to fix this with a trick using get:

sampledata %>%
  transmute(player, stat = zscore(get(input$stat1)))
#     player       stat
# 1     John -1.4863011
# 2     Mike -1.1560120
# 3    Devon -0.8257228
# 4     Greg -0.4954337
# 5    Bruce -0.1651446
# 6  Zachary  0.1651446
# 7     Jack  0.4954337
# 8   Graham  0.8257228
# 9   Jordan  1.1560120
# 10   Sandy  1.4863011

### which is effectively this
zscore(sampledata$wins)
#  [1] -1.4863011 -1.1560120 -0.8257228 -0.4954337 -0.1651446  0.1651446
#  [7]  0.4954337  0.8257228  1.1560120  1.4863011

(and then filter as needed).

Another way to fix this is perhaps overkill, but in case it provides insight into other actions in your project, we can reshape the data from wide to long. Since we're already using dplyr, I'll include tidyr:

library(tidyr)

### this is just a demo of reshaping from wide to long
sampledata %>%
  pivot_longer(c(-player, -team), names_to = "winlose", values_to = "val")
# # A tibble: 20 x 4
#    player  team  winlose   val
#    <fct>   <fct> <chr>   <int>
#  1 John    A     wins        1
#  2 John    A     losses     10
#  3 Mike    B     wins        2
#  4 Mike    B     losses      9
#  5 Devon   A     wins        3
#  6 Devon   A     losses      8
#  7 Greg    B     wins        4
#  8 Greg    B     losses      7
#  9 Bruce   A     wins        5
# 10 Bruce   A     losses      6
# 11 Zachary B     wins        6
# 12 Zachary B     losses      5
# 13 Jack    A     wins        7
# 14 Jack    A     losses      4
# 15 Graham  B     wins        8
# 16 Graham  B     losses      3
# 17 Jordan  A     wins        9
# 18 Jordan  A     losses      2
# 19 Sandy   B     wins       10
# 20 Sandy   B     losses      1


### this is the actual work
sampledata %>%
  pivot_longer(c(-player, -team), names_to = "winlose", values_to = "val") %>%
  filter(winlose == input$stat1) %>%
  mutate(z = zscore(val))
# # A tibble: 10 x 5
#    player  team  winlose   val      z
#    <fct>   <fct> <chr>   <int>  <dbl>
#  1 John    A     wins        1 -1.49 
#  2 Mike    B     wins        2 -1.16 
#  3 Devon   A     wins        3 -0.826
#  4 Greg    B     wins        4 -0.495
#  5 Bruce   A     wins        5 -0.165
#  6 Zachary B     wins        6  0.165
#  7 Jack    A     wins        7  0.495
#  8 Graham  B     wins        8  0.826
#  9 Jordan  A     wins        9  1.16 
# 10 Sandy   B     wins       10  1.49 
0
votes

According to this page, zscore first argument (x) is a numeric vector.

input$stat1 is character.