如何使用额外的列和行 headers 格式化 table,包括垂直对齐和反应式输入?
How to format table with additional column and row headers including vertical alignment and reactive inputs?
下面是 运行 反应式转换 table 的 MWE 代码,用户输入开始时间段 (from) 和结束时间段 (to)。在底部的第一张图片中,您可以看到起草 MWE 代码时的输出格式。但是,我想要一个更具描述性的 table 输出,更像是底部第二张图片中显示的输出,其中列标记为“From”(反映转换状态自),行标记为“To”(反映将状态转换为),反应性用户输入反映在两者中。
有什么建议可以实现吗?
MWE 代码:
library(data.table)
library(dplyr)
library(shiny)
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")
)
ui <- fluidPage(
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:")),
tableOutput("results"),
)
server <- function(input, output) {
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"
)
}
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$results <- renderTable(results())
}
shinyApp(ui, server)
所需格式(或多或少...):
请参阅此 post 上的相关问题和解决方案,它针对 to/from 转换矩阵的描述列 headers 提出了一个替代(最终更好)的解决方案:How to merge 2 row cells in data table?
这里还有适用于该解决方案的代码:
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;}")),
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),
radioButtons("transposeDT",
label = "From state along:",
choiceNames = c('Columns','Rows'),
choiceValues = c('Columns','Rows'),
selected = 'Columns',
inline = TRUE
),
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, {
datatable(
#StackPost solution from anuanand added the below...
data = if(input$transposeDT=='Rows')
{results()%>%transpose(make.names = 'to_state',keep.names = 'to_state')}
else {results()},
rownames = FALSE,
filter = 'none',
container = tags$table(
class = 'display',
tags$thead(
tags$tr(
tags$th(rowspan = 2, # Add the below if-else to change to/from column headers when transposing
if(input$transposeDT=='Rows')
{sprintf('From state where initial period = %s', input$transFrom)}
else{sprintf('To state where end period = %s', input$transTo)}
, style = "border-right: solid 1px;"),
tags$th(colspan = 10, # Add the below if-else to change to/from column headers when transposing
if(input$transposeDT=='Rows')
{sprintf('To state where end period = %s', input$transTo)}
else{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"
, autoWidth = T
, info = FALSE
, searching = FALSE
),
class = "display"
) %>%
formatStyle(c(1), `border-right` = "solid 1px")
})
}
shinyApp(ui, server)
下面是 运行 反应式转换 table 的 MWE 代码,用户输入开始时间段 (from) 和结束时间段 (to)。在底部的第一张图片中,您可以看到起草 MWE 代码时的输出格式。但是,我想要一个更具描述性的 table 输出,更像是底部第二张图片中显示的输出,其中列标记为“From”(反映转换状态自),行标记为“To”(反映将状态转换为),反应性用户输入反映在两者中。
有什么建议可以实现吗?
MWE 代码:
library(data.table)
library(dplyr)
library(shiny)
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")
)
ui <- fluidPage(
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:")),
tableOutput("results"),
)
server <- function(input, output) {
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"
)
}
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$results <- renderTable(results())
}
shinyApp(ui, server)
所需格式(或多或少...):
请参阅此 post 上的相关问题和解决方案,它针对 to/from 转换矩阵的描述列 headers 提出了一个替代(最终更好)的解决方案:How to merge 2 row cells in data table?
这里还有适用于该解决方案的代码:
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;}")),
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),
radioButtons("transposeDT",
label = "From state along:",
choiceNames = c('Columns','Rows'),
choiceValues = c('Columns','Rows'),
selected = 'Columns',
inline = TRUE
),
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, {
datatable(
#StackPost solution from anuanand added the below...
data = if(input$transposeDT=='Rows')
{results()%>%transpose(make.names = 'to_state',keep.names = 'to_state')}
else {results()},
rownames = FALSE,
filter = 'none',
container = tags$table(
class = 'display',
tags$thead(
tags$tr(
tags$th(rowspan = 2, # Add the below if-else to change to/from column headers when transposing
if(input$transposeDT=='Rows')
{sprintf('From state where initial period = %s', input$transFrom)}
else{sprintf('To state where end period = %s', input$transTo)}
, style = "border-right: solid 1px;"),
tags$th(colspan = 10, # Add the below if-else to change to/from column headers when transposing
if(input$transposeDT=='Rows')
{sprintf('To state where end period = %s', input$transTo)}
else{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"
, autoWidth = T
, info = FALSE
, searching = FALSE
),
class = "display"
) %>%
formatStyle(c(1), `border-right` = "solid 1px")
})
}
shinyApp(ui, server)