如何在 R 中正确地聚合具有反应性的嵌套行?
How can I correctly aggregate nested rows with reactable in R?
我正在开发一个闪亮的应用程序,其中 reactable
包由于我的数据的嵌套结构而非常有用。它允许我折叠和汇总更高级别类别中的行,并且仅展开以显示所需的 'subrows' if/when。
对于只有 1 个嵌套级别(例如,品牌中的汽车模型),来自 reactable
的聚合函数(unique
、count
等)开箱即用.然而,当添加额外的嵌套级别时,事情就会崩溃,甚至像 unique
这样的聚合器也会呈现重复的值(!)。我怀疑这是因为子类别并没有全部汇集在一个平面结构中,只对所有子类别执行 1 个聚合步骤,而是唯一值保持子类别特定,然后只是连接在一起,导致重复。此问题也影响其他聚合器,而不仅仅是 unique
。
我在下面的 R 中添加了一个 MWE,因为我一直无法解决这个问题。由于 JS 也远不是我的强项,我一直无法插入任何 JS 来更灵活地解决这个问题,正如建议的那样 here。我怎样才能调整下面的聚合器以获得正确显示的输出(即不重复)?
library(shiny)
library(shinydashboard)
library(reactable)
library(stringr)
if (interactive()) {
ui <- shinyUI(basicPage(
box(title = "mtcars data",
width = 12,
reactableOutput("car_tab")
)
))
server <- function(input, output, session) {
output$car_tab <- renderReactable({
df <- mtcars
df$make <- str_extract(rownames(df), "[[:alpha:]]+")
df$model <- rownames(df)
rownames(df) <- NULL
df <- df[ , c("make", "model", "mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb")]
reactable(df,
groupBy = c("cyl", "am", "make"),
columns = list(
cyl = colDef(name = "Number of cylinders"),
am = colDef(name = "Transmission",
aggregate = "frequency"),
make = colDef(name = "Make",
aggregate = "frequency"),
model = colDef(name = "Model",
aggregate = "unique"),
mpg = colDef(name = "Miles/gallon",
aggregate = "mean",
format = colFormat(digits = 2)),
disp = colDef(name = "Displacement"),
hp = colDef(name = "Horsepower"),
drat = colDef(name = "Rear axle ratio"),
wt = colDef(name = "Weight"),
qsec = colDef(name = "1/4 mile time"),
vs = colDef(name = "Engine",
aggregate = "count"),
gear = colDef(name = "Number of forward gears"),
carb = colDef(name = "Number of carburetors")
)
)
})
}
shinyApp(ui = ui, server = server)
}
要获得0(3), 1(8)
,您需要
groupBy = c("cyl", "am")
我认为这可以通过使用聚合器来解决,因为您刚才正在使用它们,然后通过 colDef
的 aggregated
参数提供自定义 JavaScript 渲染器。这个想法是 reactable
将通过内置运算符进行聚合,并将使用自定义渲染器在聚合单元格中渲染输出。
自定义 JavaScript 渲染器应该采用看起来像 0, 0(2), 1(6), 1(2)
的字符串并进行一些字符串操作以产生像 0(3), 1(8)
.
这样的输出
如果像这样定义JavaScript函数并保存在R变量中,就可以重复使用:
jsRenderer <- "
function(cellInfo) {
const handleBrackets = (item) => {
const currentName = item.replace(/\([0-9]+\)/g, '')
const currentCountsArr = item.match(/\(([0-9]+)\)/)
let currentCount = 1
if (currentCountsArr && currentCountsArr.length === 2) {
currentCount = parseInt(currentCountsArr[1], 10)
}
return {
currentName,
currentCount
}
}
const getCounts = (input) => {
const trimmedInput = input.replace(/\s+/g, '')
const items = trimmedInput.split(',')
const namesWithoutBrackets = trimmedInput.replace(/\(([0-9]+)\)/g, '').split(',')
const itemObj = items.reduce((prev, current, index) => {
const itemWithoutBrackets = handleBrackets(current)
let {
currentName,
currentCount
} = itemWithoutBrackets
if (namesWithoutBrackets.indexOf(currentName) !== index) {
currentCount += prev[currentName]
}
return {
...prev,
...{
[currentName]: currentCount
}
}
}, {})
const stringToSanitize = Object.entries(itemObj).reduce((prevString, currentKeyValue) => {
return prevString.concat(`${currentKeyValue[0]}(${currentKeyValue[1]}), `)
}, '')
return stringToSanitize.slice(0, -2)
}
return (getCounts(cellInfo.value))
}
"
然后您可以像这样将此渲染提供给 colDef
:
colDef(name = "Transmission", aggregate = "frequency", aggregated = jsRenderer)
在您的 MWE 中,结果显示如下:
Merc(2),
Toyota(2),
Datsun(1),
Fiat(2),
Honda(1),
Porsche(1),
Lotus(1),
Volvo(1)
我正在开发一个闪亮的应用程序,其中 reactable
包由于我的数据的嵌套结构而非常有用。它允许我折叠和汇总更高级别类别中的行,并且仅展开以显示所需的 'subrows' if/when。
对于只有 1 个嵌套级别(例如,品牌中的汽车模型),来自 reactable
的聚合函数(unique
、count
等)开箱即用.然而,当添加额外的嵌套级别时,事情就会崩溃,甚至像 unique
这样的聚合器也会呈现重复的值(!)。我怀疑这是因为子类别并没有全部汇集在一个平面结构中,只对所有子类别执行 1 个聚合步骤,而是唯一值保持子类别特定,然后只是连接在一起,导致重复。此问题也影响其他聚合器,而不仅仅是 unique
。
我在下面的 R 中添加了一个 MWE,因为我一直无法解决这个问题。由于 JS 也远不是我的强项,我一直无法插入任何 JS 来更灵活地解决这个问题,正如建议的那样 here。我怎样才能调整下面的聚合器以获得正确显示的输出(即不重复)?
library(shiny)
library(shinydashboard)
library(reactable)
library(stringr)
if (interactive()) {
ui <- shinyUI(basicPage(
box(title = "mtcars data",
width = 12,
reactableOutput("car_tab")
)
))
server <- function(input, output, session) {
output$car_tab <- renderReactable({
df <- mtcars
df$make <- str_extract(rownames(df), "[[:alpha:]]+")
df$model <- rownames(df)
rownames(df) <- NULL
df <- df[ , c("make", "model", "mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb")]
reactable(df,
groupBy = c("cyl", "am", "make"),
columns = list(
cyl = colDef(name = "Number of cylinders"),
am = colDef(name = "Transmission",
aggregate = "frequency"),
make = colDef(name = "Make",
aggregate = "frequency"),
model = colDef(name = "Model",
aggregate = "unique"),
mpg = colDef(name = "Miles/gallon",
aggregate = "mean",
format = colFormat(digits = 2)),
disp = colDef(name = "Displacement"),
hp = colDef(name = "Horsepower"),
drat = colDef(name = "Rear axle ratio"),
wt = colDef(name = "Weight"),
qsec = colDef(name = "1/4 mile time"),
vs = colDef(name = "Engine",
aggregate = "count"),
gear = colDef(name = "Number of forward gears"),
carb = colDef(name = "Number of carburetors")
)
)
})
}
shinyApp(ui = ui, server = server)
}
要获得0(3), 1(8)
,您需要
groupBy = c("cyl", "am")
我认为这可以通过使用聚合器来解决,因为您刚才正在使用它们,然后通过 colDef
的 aggregated
参数提供自定义 JavaScript 渲染器。这个想法是 reactable
将通过内置运算符进行聚合,并将使用自定义渲染器在聚合单元格中渲染输出。
自定义 JavaScript 渲染器应该采用看起来像 0, 0(2), 1(6), 1(2)
的字符串并进行一些字符串操作以产生像 0(3), 1(8)
.
如果像这样定义JavaScript函数并保存在R变量中,就可以重复使用:
jsRenderer <- "
function(cellInfo) {
const handleBrackets = (item) => {
const currentName = item.replace(/\([0-9]+\)/g, '')
const currentCountsArr = item.match(/\(([0-9]+)\)/)
let currentCount = 1
if (currentCountsArr && currentCountsArr.length === 2) {
currentCount = parseInt(currentCountsArr[1], 10)
}
return {
currentName,
currentCount
}
}
const getCounts = (input) => {
const trimmedInput = input.replace(/\s+/g, '')
const items = trimmedInput.split(',')
const namesWithoutBrackets = trimmedInput.replace(/\(([0-9]+)\)/g, '').split(',')
const itemObj = items.reduce((prev, current, index) => {
const itemWithoutBrackets = handleBrackets(current)
let {
currentName,
currentCount
} = itemWithoutBrackets
if (namesWithoutBrackets.indexOf(currentName) !== index) {
currentCount += prev[currentName]
}
return {
...prev,
...{
[currentName]: currentCount
}
}
}, {})
const stringToSanitize = Object.entries(itemObj).reduce((prevString, currentKeyValue) => {
return prevString.concat(`${currentKeyValue[0]}(${currentKeyValue[1]}), `)
}, '')
return stringToSanitize.slice(0, -2)
}
return (getCounts(cellInfo.value))
}
"
然后您可以像这样将此渲染提供给 colDef
:
colDef(name = "Transmission", aggregate = "frequency", aggregated = jsRenderer)
在您的 MWE 中,结果显示如下:
Merc(2),
Toyota(2),
Datsun(1),
Fiat(2),
Honda(1),
Porsche(1),
Lotus(1),
Volvo(1)