What is the best way to combine two rows in a data table?

As I progress with my endeavor to construct a user-friendly transition matrix in R, following up on the previous post regarding adding a vertical line to the first column header in a data table, I am faced with a new challenge.

Upon executing the provided code snippet at the end of this message, a transition table is generated as depicted in the image below (with my annotations overlaid). My goal is to combine the top 2 cells (rows) in the left-most column and align the column header "to_state" vertically at the center. Are there any suggestions on achieving this? Preferably using DT for table rendering.

https://i.sstatic.net/2MJnP.png

It's worth noting that in the comprehensive code from which this example is derived, the table dynamically adjusts its size based on the number of unique states identified in the underlying dataset.

I came across some potentially helpful insights in the discussion about Shiny: Merge cells in DT::datatable on Stack Overflow. However, it appeared that the merging was focused on row cells within the body of the table rather than the header, making it incompatible with my scenario.

Although I lack expertise in HTML and CSS, there are valuable online resources offering tips on structuring HTML tables, including techniques for combining column and row merges. You may refer to these links here and here. This made me contemplate whether abandoning my current DT/html combination in favor of designing the table entirely in html might yield better guidance for someone like me who is relatively inexperienced.

Here is the provided MWE code:

library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)

data <- 
  data.frame(
    ID = c(1,1,1,2,2,2,3,3,3),
    Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
    Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
    State = c("X0","X1","X2","X0","X2","X0", "X2","X1","X0")
  )

numTransit <- function(x, from=1, to=3){
  setDT(x)
  unique_state <- unique(x$State)
  all_states <- setDT(expand.grid(list(from_state = unique_state, to_state = unique_state)))
  dcast(x[, .(from_state = State[from], 
              to_state = State[to]), 
          by = ID]
        [,.N, c("from_state", "to_state")]
        [all_states,on = c("from_state", "to_state")], 
        to_state ~ from_state, value.var = "N"
  )
}

ui <- fluidPage(
  tags$head(tags$style(".datatables .display {margin-left: 0;}")), # < left-align the table
  h4(strong("Base data frame:")), 
  tableOutput("data"),
  h4(strong("Transition table inputs:")),
  numericInput("transFrom", "From period:", 1, min = 1, max = 3),
  numericInput("transTo", "To period:", 2, min = 1, max = 3),
  h4(strong("Output transition table:")), 
  DTOutput("resultsDT"),
)

server <- function(input, output, session) {
  results <- 
    reactive({
      results <- numTransit(data, input$transFrom, input$transTo) %>% 
        replace(is.na(.), 0) %>%
        bind_rows(summarise_all(., ~(if(is.numeric(.)) sum(.) else "Sum")))
      results <- cbind(results, Sum = rowSums(results[,-1]))
    })
  
  output$data <- renderTable(data)
  
  output$resultsDT <- renderDT(server=FALSE, {
    req(results())
    datatable(
      data = results(),
      rownames = FALSE,
      filter = 'none',
      container = tags$table(
        class = 'display',
        tags$thead(
          tags$tr(
            tags$th(colspan = 1, '', style = "border-right: solid 1px;"),
            tags$th(colspan = 10, sprintf('From state where initial period = %s', input$transFrom))
          ),
          tags$tr(
            mapply(tags$th, colnames(results()), style = sprintf("border-right: solid %spx;", c(1L, rep(0, ncol(results())-1L))), SIMPLIFY = FALSE)
          )
        )
      ),
      options = list(scrollX = F
                     , dom = 'ft'
                     , lengthChange = T
                     , pagingType = "numbers"  # hides Next and Previous buttons
                     , autoWidth = T
                     , info = FALSE #  hide the "Showing 1 of 2..." at bottom of table
                     , searching = FALSE  # removes search box
      ),
      class = "display"
    ) %>%
      formatStyle(c(1), `border-right` = "solid 1px")
  })
  
}

shinyApp(ui, server)

Answer №1

The top header should contain the initial cell text, not the second one.

library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)

data <- 
  data.frame(
    ID = c(1,1,1,2,2,2,3,3,3),
    Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
    Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
    State = c("X0","X1","X2","X0""X2","X0", "X2","X1","X0")
  )

numTransit <- function(x, from=1, to=3){
  setDT(x)
  unique_state <- unique(x$State)
  all_states <- setDT(expand.grid(list(from_state = unique_state, to_state = unique_state)))
  dcast(x[, .(from_state = State[from], 
              to_state = State[to]), 
          by = ID]
        [,.N, c("from_state", "to_state")]
        [all_states,on = c("from_state", "to_state")], 
        to_state ~ from_state, value.var = "N"
  )
}

ui <- fluidPage(
  tags$head(tags$style(".datatables .display {margin-left: 0;}")), # < left-align the table
  h4(strong("Initial data frame:")), 
  tableOutput("data"),
  h4(strong("Transition table inputs:")),
  numericInput("transFrom", "From period:", 1, min = 1, max = 3),
  numericInput("transTo", "To period:", 2, min = 1, max = 3),
  h4(strong("Final transition table:")), 
  DTOutput("resultsDT"),
)

server <- function(input, output, session) {
  results <- 
    reactive({
      results <- numTransit(data, input$transFrom, input$transTo) %>% 
        replace(is.na(.), 0) %>%
        bind_rows(summarise_all(., ~(if(is.numeric(.)) sum(.) else "Sum")))
      results <- cbind(results, Sum = rowSums(results[,-1]))
    })
  
  output$data <- renderTable(data)
  
  output$resultsDT <- renderDT(server=FALSE, {
    req(results())
    datatable(
      data = results(),
      rownames = FALSE,
      filter = 'none',
      container = tags$table(
        class = 'display',
        tags$thead(
          tags$tr(
            tags$th(rowspan = 2, colnames(results())[1], style = "border-right: solid 1px;"),
            tags$th(colspan = 10, sprintf('From state where initial period = %s', input$transFrom))
          ),
          tags$tr(
            mapply(tags$th, colnames(results())[-1], style = sprintf("border-right: solid %spx;", rep(0, ncol(results()) - 1L)), SIMPLIFY = FALSE)
          )
        )
      ),
      options = list(scrollX = F
                     , dom = 'ft'
                     , lengthChange = T
                     , pagingType = "numbers"  # hides Next and Previous buttons
                     , autoWidth = T
                     , info = FALSE #  hide the "Showing 1 of 2..." at bottom of table
                     , searching = FALSE  # removes search box
      ),
      class = "display"
    ) %>%
      formatStyle(c(1), `border-right` = "solid 1px")
  })
  
}

shinyApp(ui, server)

https://i.sstatic.net/IKRfe.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If the background image on a div is not visible, then the alt text of the

Here is my unique HTML code consisting of a single div and an image positioned outside the div. <div runat="server" id="ProductThumbnail" style="width:140px;height:140px;position:relative;background-position:center;background-repeat:no-repeat;"> ...

Getting the value of a CSS variable under <script> in Vue.js

I am working on implementing a Doughnut chart using chartJs in my application. However, I want to set the color of the chart within the <script> tag and retrieve the color from theme/variables.css. In the current code snippet below, there is a hardc ...

Tips for assigning a class to an Angular accordion header when the panel is in the open state

I am currently utilizing the Angular UI Bootstrap accordion feature, which can be found here. <div ng-controller="AccordionDemoCtrl"> <div accordion close-others="oneAtATime"> <div accordion-group heading ...

Obtain an Element Using Puppeteer

Currently grappling with a sensitive issue concerning puppeteer. The HTML structure in question is as follows: <tbody> <tr rel="0" class="disabled" id="user6335934" class="odd"> ...

What could be causing my custom cursor to malfunction?

I've been attempting to customize the cursor image, but despite following all provided instructions, it's still not working: CSS: body, html { margin: 0px; padding: 0px; border: 1px solid red; cursor: url(images/rsz_red_crosshair.gi ...

Issue with jquery curvy corners not functioning properly on Internet Explorer 8

Check out my website at If you view the page in IE8 and then in IE7 compatibility mode, you'll notice a strange issue. The box on the right disappears in IE8 but displays perfectly rounded corners in IE7. I am currently using the jQuery Curvy Corner ...

Inspired by the organization and depth provided by nested lists

I am facing an issue with my ul list where adding a nested ul causes the li items above to move. Can anyone explain why this is happening and suggest a solution? Here is an example: http://jsfiddle.net/y5DtE/ HTML: <ul> <li> first ...

Choosing headers for columns in a dataset imported into R

I am currently working on extracting network data (graph of ids) in R. The file I am using is named 'network.txt' and the data structure is shown below: 4 0 5 0 6 0 7 0 8 0 9 0 4029 1 4030 1 4031 1 4032 1 4033 1 19088 9040 19089 9040 19090 9040 ...

Tips on sending expressions to the bench::mark exprs parameter

Let's say I have a collection of functions stored in a list: functions <- list( loop = function(n) { result <- 0 for (i in seq_len(n)) { result <- result + i } result }, vectorized = function(n) sum(seq_len(n)) ) ...

Disregarding 'zIndex' directive within JavaScript function, an image stands apart

There is an issue with the z-index values of rows of images on my webpage. Normally, the z-index values increase as you scroll further down the page. However, I want certain items to have a lower z-index than the rest (except on hover). These items are i ...

Activate the initial tab in JQuery UI accordion upon initialization

Hello, I have implemented a simple sidenav menu on my website. It consists of parent items and child items structured according to the h3 > div format as suggested by JQuery's documentation. My challenge now is to automatically open the "active" t ...

Adjusting cell width based on content in CSS to handle overflows automatically

I am facing a challenge with a table that has varying widths of content in each cell. I need the lengthy content to be displayed next to nowrap and overflow. After trying the solution mentioned here, all cells ended up with the same width. However, I requ ...

Learn how to dynamically set the "selected" option in Vue based on object data

I've done some digging on SO but haven't found exactly what I need. So, here's the situation - I've got a sorting function in progress. I have an array of date ranges (PayPeriods) that I want to render into a select with option compone ...

What is the best way to extract pricing information from Udemy?

Important: I haven't been able to find a relevant solution in similar questions. How can I extract prices from Udemy using web scraping? Scraping Data From Udemy's AngularJs Site Using PHP How do I obtain promotional prices using the Udemy API ...

Is there a way to ensure the content of two divs remains aligned despite changing data within them?

Currently, I have two separate Divs - one displaying temperature data and the other showing humidity levels. <div class="weatherwrap"> <div class="tempwrap" title="Current Temperature"> ...

Customize the appearance of radio buttons in HTML by removing the bullets

Is there a way for a specific form component to function as radio buttons, with only one option selectable at a time, without displaying the actual radio bullets? I am looking for alternative presentation methods like highlighting the selected option or ...

Utilizing SVG and CSS together: Implementing clip-path in a flexbox layout

This task may seem simple to some, but I am struggling to mask an image with an SVG graphic. I have created an SVG with the clipPath element: <svg id="heart-path-container" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 ...

Alter the button ID based on the currently displayed div

My mind is being driven crazy by a particular issue I am experiencing. Let me explain... I have a lengthy scrolling page with approximately 10 divs stacked one after the other without any spacing in between. Positioned at the bottom of the viewport is a bu ...

Alignment vertically in a Razor view

Here is a snippet of code I am working with: <p> @Html.LabelFor(m => m.address, new { style="vertical-align: middle;" }) @Html.TextAreaFor(m => m.address, new { @class = "addition ", value = "",rows="4", required = "required" }) </p> A ...

Tips for increasing a variable by one with each button click?

I have a simple JavaScript function called plusOne() that is designed to increment a variable by 1 each time a button is clicked, and then display the updated value on a webpage. However, I'm encountering an issue where the addition only occurs once. ...