Creating datatables in R shiny

ggplot2
shiny
rstudio

#1

I am making shiny app for football team data. I have created the following code. But Most of the time I get an error "'data' must be 2-dimensional (e.g. data frame or matrix)" . I searched on internet but count find why this error occurs.

As mentioned in the code, The table get display and when I select a team it displays the matches of that particular team.(only when I have "data" in the server part). The code gives me error when I write table1(which I actually want to display).

I also find problem making datatables using DT, IS there any other alternative?

library(shiny)
library(shinydashboard)
library(DT)
library(data.table)
library(stringr)

data <- readRDS('C:\\users\\UJJVAL\\Desktop\\european_soccer.rds');
hometeam<- c(data$home_team)

ui <- 
    dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
      fluidRow(
        box(
          title = "League",
          selectInput("league","selectleague", c(data$home_team, data$away_team),selected = NULL)
        ),
      fluidRow(
        DT::dataTableOutput("table"),
        DT::dataTableOutput("table2")
      )))
    )

server <- function(input, output) { 
  output$table <-DT::renderDataTable(
    DT::datatable({
   #Filter the data based on selected team
      
     if (input$league !="anything"){
       data <- data[(data$home_team) == input$league,]
       toString(input$league) #converet to string for displaying
       freq_hometeam<- str_count(hometeam, c(input$league)) # count number of matches
       numberofmatches<-freq_hometeam[freq_hometeam[1:25979]==1]
       length(numberofmatches)
       table1<- c(input$league, freq_hometeam) #prepare data for table.
       output$table2 <- DT::renderDataTable(DT::datatable({table1}))
   }
       data
      # Actually I want to display table1 data as a table but it pops error data must be two dimeentional metrix or data
    }))
  
    }
  
shinyApp(ui, server)


#2

I don't ever use DT so I can't help you with debugging your specific issue, however I can offer some sage advice that is guaranteed to make debugging your code easier:

Get your basic logic working in R before wrapping it in a Shiny server.

You're trying to debug R issues but they are somewhat hard to track because you are dealing with both R and Shiny wrapped around R. From what I can tell you don't even know which line of code is throwing your error. If you were executing your R logic line by line in R you could figure out exactly where the issue is and debug easier and also ask for help easier. By leaving everything all wrapped up in Shiny, you're making your life harder than it needs to be and setting yourself up for frustration.

-J


#3

Yes you are right. I am doing both R and shiny together. But as you said I should do only one at a time. The problem is I have to take input from the user and then filter the data based on that input. In the above code there is no problem in UI. The only problem is server part in that which code goes where??
As you said you never use DT:: , Can you say what you use as an alternative to DT?


#4

Hi, is the table you're passing to DT a data.table or a data.frame? If the former, I don't think DT supports data.table objects, and if that's the case, I think that simply coercing the table into a data.frame first will do the trick: data<- as.data.frame(data)


#5

In the code the table I can display right now is a data object. I don't think is data.table.
I want to display also the table1 object as a datatable.


#6

It's hard to follow what's going on in your server code without knowing what your data look like. It would really help if you could post some sample data, or even the output from calling str(data).

However, to answer your question about the error message: it's telling you that table1 is not in the right format. If you look at the line that creates table1:

table1<- c(input$league, freq_hometeam) #prepare data for table.

it's taking the returned value from your single-value selectInput and combining it with an integer vector (that's what str_count returns). The output of that operation can only be a vector — meaning, a one-dimensional data structure, not anything table-like.

There's a lot that's confusing to me about the code in your server definition. Like @jdlong, I also encourage you to test this code outside of shiny to make sure it's doing what you think. The code you're passing as the first argument to DT::datatable ought to produce a data frame containing the data you want to display. You can extract just that chunk into an R script and test it, creating variables to stand in for the values that would be supplied by the UI elements in your shiny app. For example, your script might have this at the top:

input <- list(league = "replace this with a value from your data")

#7

Thank you @jcblum for such a nice reply. here is my output from str(data)

I have tried to execute some of the lines in server function and they had worked without error. Right now the question is that when I give input in the select input drop-down it should go in the if loop of the server side and then display in table the name of the input team and the number of matches it has played in a table format.

> str(data)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':	25979 obs. of  12 variables:
 $ id                  : int  1 2 3 4 5 6 7 8 9 10 ...
 $ league_name         : chr  "Belgium Jupiler League" "Belgium Jupiler League" "Belgium Jupiler League" "Belgium Jupiler League" ...
 $ season              : chr  "2008/2009" "2008/2009" "2008/2009" "2008/2009" ...
 $ stage               : int  1 1 1 1 1 1 1 1 1 10 ...
 $ date                : chr  "2008-08-17 00:00:00" "2008-08-16 00:00:00" "2008-08-16 00:00:00" "2008-08-17 00:00:00" ...
 $ match_api_id        : int  492473 492474 492475 492476 492477 492478 492479 492480 492481 492564 ...
 $ home_team           : chr  "KRC Genk" "SV Zulte-Waregem" "KSV Cercle Brugge" "KAA Gent" ...
 $ away_team           : chr  "Beerschot AC" "Sporting Lokeren" "RSC Anderlecht" "RAEC Mons" ...
 $ home_team_goal      : int  1 0 0 5 1 1 2 1 1 4 ...
 $ away_team_goal      : int  1 0 3 0 3 1 2 2 0 1 ...
 $ home_team_possession: num  NA NA NA NA NA NA NA NA NA NA ...
 $ away_team_possession: num  NA NA NA NA NA NA NA NA NA NA ...```

#8

It's not really enough to test lines of code from your server function individually (though that's a good place to start!) — since they act together, you need to look at what the whole expression block produces together.

I'm not totally clear on what you want your table to look like — is it grouped by league or by team? is there more than one row, once the user has filtered it? It would help if you could provide an example. However, here's one way I might make a similar table outside of shiny:

suppressPackageStartupMessages(library(tidyverse))

football_data <- tribble(
  ~league_name, ~season, ~home_team, ~away_team, 
  "League A", "Season 1", "Team A1", "Team A2", 
  "League A", "Season 1", "Team A1", "Team A3", 
  "League A", "Season 1", "Team A2", "Team A1", 
  "League A", "Season 1", "Team A2", "Team A3", 
  "League A", "Season 1", "Team A3", "Team A1", 
  "League A", "Season 1", "Team A3", "Team A2", 
  "League B", "Season 1", "Team B1", "Team B2", 
  "League B", "Season 1", "Team B1", "Team B3", 
  "League B", "Season 1", "Team B2", "Team B1", 
  "League B", "Season 1", "Team B2", "Team B3", 
  "League B", "Season 1", "Team B3", "Team B1", 
  "League B", "Season 1", "Team B3", "Team B2", 
  "League A", "Season 2", "Team A1", "Team A2", 
  "League A", "Season 2", "Team A1", "Team A3", 
  "League A", "Season 2", "Team A2", "Team A1", 
  "League B", "Season 2", "Team B2", "Team B3", 
  "League B", "Season 2", "Team B3", "Team B1", 
  "League B", "Season 2", "Team B3", "Team B2")

# stand-in for user-selected input
input <- list(league = "League A")

football_data %>% 
  filter(league_name == input$league) %>% 
  gather(key = "playing_as", value = "team", home_team, away_team) %>% 
  group_by(season) %>% 
  count(team) %>% 
  rename(matches = n)
#> # A tibble: 6 x 3
#> # Groups:   season [2]
#>   season   team    matches
#>   <chr>    <chr>     <int>
#> 1 Season 1 Team A1       4
#> 2 Season 1 Team A2       4
#> 3 Season 1 Team A3       4
#> 4 Season 2 Team A1       3
#> 5 Season 2 Team A2       2
#> 6 Season 2 Team A3       1

Once I know that my code works to generate the table I want, I can work on dropping it into my shiny app. An extremely simple toy example:

library(tidyverse)
library(shiny)
library(DT)

# create football_data object by reading in data... left this part out to
# keep this example brief, but can use same tribble statement as above

shinyApp(
  ui = fluidPage(

    sidebarLayout(
      sidebarPanel(
        selectInput("league", "Select league", unique(football_data$league_name))
      ),

      mainPanel(
        DT::dataTableOutput("matches_by_team")
      )
    )
  ),

  server = function(input, output) {

    output$matches_by_team <- DT::renderDataTable({
      # here's that same block I tested outside of my shiny app
      football_data %>% 
        gather(key = "playing_as", value = "team", home_team, away_team) %>% 
        group_by(season) %>% 
        filter(league_name == input$league) %>% 
        count(team) %>% 
        rename("matches" = n)
    })
  
   }
)