R中的父/子行 [英] Parent/Child Rows in R
问题描述
我正在尝试使用一些JavaScript创建带有父母/孩子嵌套的漂亮表格布局。每个父母我只需要一个孩子。我有两个数据框。这里的目标是创建一个将这两个数据帧组合在一起的表。到目前为止,我能够做到。但是,这里的问题是我只能让代码在df1中为一行工作。当我向df1中添加另一行时,我在data.frame中得到 Error:参数暗示行数不同:1、2
。例如,当我在df1中只有一行时,但是在一行以上时,我可以得到上面的错误。
I'm trying to use some JavaScript to create a nice table layout with parents/child nesting. I only need one child per parent. I have two data frames. The goal here is to make a table that combines these two data frames. So far I am able to do that. However, the problem here is that I can only get the code to work for one row in df1. When I go to add in another row to df1 I get Error in data.frame: arguments imply differing number of rows: 1, 2
. For example, the desired result is achieved when I have only one row in df1 but when there is more than one row I can the error above.
df# 1
structure(list(Market = c("ALBANY-SCHENECTADY-TROY, NY", "ALBANY, GA",
"ALBUQUERQUE-SANTA FE"), Gross = c("$0", "$0", "$0"), Net = c("$0",
"$0", "$0"), GRP = c(100, 100, 100), `Demo Impressions` = c("957,776",
"238,792", "1,259,307"), `Gross CPP` = c("$0", "$0", "$0"), `Gross CPM` = c("$0",
"$0", "$0")), .Names = c("Market", "Gross", "Net", "GRP", "Demo Impressions",
"Gross CPP", "Gross CPM"), row.names = c(NA, -3L), class = "data.frame")
Market Gross Net GRP Demo Impressions Gross CPP Gross CPM
1 ALBANY-SCHENECTADY-TROY, NY $0 $0 100 957,776 $0 $0
2 ALBANY, GA $0 $0 100 238,792 $0 $0
3 ALBUQUERQUE-SANTA FE $0 $0 100 1,259,307 $0 $0
df#2
structure(list(Daypart = c("Daytime", "Early Fringe", "Early Morning",
"Early News", "Late Fringe", "Late News", "Prime Access", "Prime Time",
"Total"), `Share (%)` = c(15L, 10L, 15L, 10L, 10L, 10L, 15L,
15L, 100L), `Spot:30 (%)` = c(0, 0, 0, 0, 0, 0, 0, 0, 0), `Spot:15 (%)` = c(0,
0, 0, 0, 0, 0, 0, 0, 0), `Demo Impressions` = c("368,381", "245,588",
"368,381", "245,588", "245,588", "245,588", "368,381", "368,381",
"2,455,876"), Gross = c("$0", "$0", "$0", "$0", "$0", "$0", "$0",
"$0", "$0"), Net = c("$0", "$0", "$0", "$0", "$0", "$0", "$0",
"$0", "$0"), `Gross CPM` = c("$0", "$0", "$0", "$0", "$0", "$0",
"$0", "$0", "$-")), .Names = c("Daypart", "Share (%)", "Spot:30 (%)",
"Spot:15 (%)", "Demo Impressions", "Gross", "Net", "Gross CPM"
), row.names = c(NA, -9L), class = "data.frame")
Daypart Share (%) Spot:30 (%) Spot:15 (%) Demo Impressions Gross Net Gross CPM
1 Daytime 15 0 0 368,381 $0 $0 $0
2 Early Fringe 10 0 0 245,588 $0 $0 $0
3 Early Morning 15 0 0 368,381 $0 $0 $0
4 Early News 10 0 0 245,588 $0 $0 $0
5 Late Fringe 10 0 0 245,588 $0 $0 $0
6 Late News 10 0 0 245,588 $0 $0 $0
7 Prime Access 15 0 0 368,381 $0 $0 $0
8 Prime Time 15 0 0 368,381 $0 $0 $0
9 Total 100 0 0 2,455,876 $0 $0 $-
# Merge the row details
subdats <- lapply(
list(df2),
purrr::transpose
)
# Dataframe for the datatable
Dat <- cbind(
" " = "⊕",
df1,
details = I(subdats)
)
callback_js = JS(
"table.column(1).nodes().to$().css({cursor: 'pointer'});",
"",
"// make the table header of the nested table",
"var format = function(d, childId){",
" if(d != null){",
" var html = ",
" '<table class=\"display compact hover\" id=\"' + childId + '\"><thead><tr>';",
" for (var key in d[d.length-1][0]) {",
" html += '<th>' + key + '</th>';",
" }",
" html += '</tr></thead></table>'",
" return html;",
" } else {",
" return '';",
" }",
"};",
"",
"// row callback to style the rows of the child tables",
"var rowCallback = function(row, dat, displayNum, index){",
" if($(row).hasClass('odd')){",
" $(row).css('background-color', 'white');",
" $(row).hover(function(){",
" $(this).css('background-color', 'white');",
" }, function() {",
" $(this).css('background-color', 'white');",
" });",
" } else {",
" $(row).css('background-color', 'white');",
" $(row).hover(function(){",
" $(this).css('background-color', 'white');",
" }, function() {",
" $(this).css('background-color', 'white');",
" });",
" }",
"};",
"",
"// header callback to style the header of the child tables",
"var headerCallback = function(thead, data, start, end, display){",
" $('th', thead).css({",
" 'border-top': '3px solid indigo',",
" 'color': 'white',",
" 'background-color': 'white'",
" });",
"};",
"",
"// make the datatable",
"var format_datatable = function(d, childId){",
" var dataset = [];",
" var n = d.length - 1;",
" for(var i = 0; i < d[n].length; i++){",
" var datarow = $.map(d[n][i], function (value, index) {",
" return [value];",
" });",
" dataset.push(datarow);",
" }",
" var id = 'table#' + childId;",
" var subtable = $(id).DataTable({",
" 'data': dataset,",
" 'autoWidth': true,",
" 'deferRender': true,",
" 'info': false,",
" 'lengthChange': false,",
" 'ordering': d[n].length > 1,",
" 'order': [],",
" 'paging': false,",
" 'scrollX': false,",
" 'scrollY': false,",
" 'searching': false,",
" 'sortClasses': false,",
" 'rowCallback': rowCallback,",
" 'headerCallback': headerCallback,",
" 'columnDefs': [",
" {targets: -1, visible: false},",
" {targets: 0, orderable: false, className: 'details-control'},",
" {targets: '_all', className: 'dt-center'}",
" ]",
" }).column(0).nodes().to$().css({cursor: 'pointer'});",
" }",
"",
"// display the child table on click",
"table.on('click', 'td.details-control', function(){",
" var tbl = $(this).closest('table'),",
" tblId = tbl.attr('id'),",
" td = $(this),",
" row = $(tbl).DataTable().row(td.closest('tr')),",
" rowIdx = row.index();",
" if(row.child.isShown()){",
" row.child.hide();",
" td.html('⊕');",
" } else {",
" var childId = tblId + '-child-' + rowIdx;",
" row.child(format(row.data(), childId)).show();",
" td.html('⊖');",
" format_datatable(row.data(), childId);",
" }",
"});")
# Render the table
output$daypartTable <- DT::renderDataTable({
Dat <- Dat
DT::datatable(Dat, callback = callback_js, escape = -2, editable = TRUE,
options = list(
columnDefs = list(
list(visible = FALSE, targets = ncol(Dat)),
list(orderable = FALSE, className = 'details-control', targets = 1),
list(className = "dt-center", targets = "_all")
)
)
)
})
结果应如下所示,但有多个父行,每行来自df1,子行来自df2。
The result should look like below but has multiple parent rows, each row from df1, with child rows from df2.
推荐答案
请在下面找到完整的代码。
Please find the full code below.
我制作了一个函数 NestedData
,该函数为带有子行的数据表构造所需的数据框。
I made a function NestedData
which constructs the required dataframe for the datatable with child rows.
在您的情况下,主表 df1
有三行,每行 df1
的孩子有 df2
,因此您必须这样做:
In your case, the main table df1
has three rows, and each row of df1
has df2
as child, therefore you have to do:
Dat <- NestedData(
dat = df1,
children = list(df2, df2, df2)
)
函数 NestedData
也可以在需要任意嵌套深度时使用:子行,子行,子行子级的行,子级的行,等等。此外,当需要一些没有子级的行时可以使用它。以下是用法示例:
The function NestedData
can also be used when one wants an arbitrary depth of nesting: children rows of the children rows, children rows of the children rows of the children rows, etc. Moreover it can be used when one wants some rows without children. Here is an example of usage:
Dat <- NestedData(
dat = dat0, # dat0 has three rows
children = list(
dat01, # child of first row
list( # child of second row, which has children itself
dat02, # dat02 has two rows
children = list(dat021, dat022)
),
data.frame(NULL) # no child for the third row
)
)
以下是应用于您的示例的代码:
Here is the code applied to your example:
# function to make the required dataframe
NestedData <- function(dat, children){
stopifnot(length(children) == nrow(dat))
g <- function(d){
if(is.data.frame(d)){
purrr::transpose(d)
}else{
purrr::transpose(NestedData(d[[1]], children = d$children))
}
}
subdats <- lapply(children, g)
oplus <- sapply(subdats, function(x) if(length(x)) "⊕" else "")
cbind(" " = oplus, dat, "_details" = I(subdats), stringsAsFactors = FALSE)
}
# make the required dataframe
# one must have: length(children) == nrow(dat)
# EDIT: need to use replicate() on df2 for cases when there is an arbitrary
# number of rows in df1
n <- nrow(df1)
children_list <- replicate(n, df2, simplify = FALSE)
Dat <- NestedData(
dat = df1,
children = children_list
)
## whether to show row names (set TRUE or FALSE)
rowNames <- FALSE
colIdx <- as.integer(rowNames)
## make the callback
parentRows <- which(Dat[,1] != "")
callback = JS(
sprintf("var parentRows = [%s];", toString(parentRows-1)),
sprintf("var j0 = %d;", colIdx),
"var nrows = table.rows().count();",
"for(var i=0; i < nrows; ++i){",
" if(parentRows.indexOf(i) > -1){",
" table.cell(i,j0).nodes().to$().css({cursor: 'pointer'});",
" }else{",
" table.cell(i,j0).nodes().to$().removeClass('details-control');",
" }",
"}",
"",
"// make the table header of the nested table",
"var format = function(d, childId){",
" if(d != null){",
" var html = ",
" '<table class=\"display compact hover\" ' + ",
" 'style=\"padding-left: 30px;\" id=\"' + childId + '\"><thead><tr>';",
" for(var key in d[d.length-1][0]){",
" html += '<th>' + key + '</th>';",
" }",
" html += '</tr></thead></table>'",
" return html;",
" } else {",
" return '';",
" }",
"};",
"",
"// row callback to style the rows of the child tables",
"var rowCallback = function(row, dat, displayNum, index){",
" if($(row).hasClass('odd')){",
" $(row).css('background-color', 'papayawhip');",
" $(row).hover(function(){",
" $(this).css('background-color', '#E6FF99');",
" }, function() {",
" $(this).css('background-color', 'papayawhip');",
" });",
" } else {",
" $(row).css('background-color', 'lemonchiffon');",
" $(row).hover(function(){",
" $(this).css('background-color', '#DDFF75');",
" }, function() {",
" $(this).css('background-color', 'lemonchiffon');",
" });",
" }",
"};",
"",
"// header callback to style the header of the child tables",
"var headerCallback = function(thead, data, start, end, display){",
" $('th', thead).css({",
" 'border-top': '3px solid indigo',",
" 'color': 'indigo',",
" 'background-color': '#fadadd'",
" });",
"};",
"",
"// make the datatable",
"var format_datatable = function(d, childId){",
" var dataset = [];",
" var n = d.length - 1;",
" for(var i = 0; i < d[n].length; i++){",
" var datarow = $.map(d[n][i], function (value, index) {",
" return [value];",
" });",
" dataset.push(datarow);",
" }",
" var id = 'table#' + childId;",
" if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
" var subtable = $(id).DataTable({",
" 'data': dataset,",
" 'autoWidth': true,",
" 'deferRender': true,",
" 'info': false,",
" 'lengthChange': false,",
" 'ordering': d[n].length > 1,",
" 'order': [],",
" 'paging': false,",
" 'scrollX': false,",
" 'scrollY': false,",
" 'searching': false,",
" 'sortClasses': false,",
" 'rowCallback': rowCallback,",
" 'headerCallback': headerCallback,",
" 'columnDefs': [{targets: '_all', className: 'dt-center'}]",
" });",
" } else {",
" var subtable = $(id).DataTable({",
" 'data': dataset,",
" 'autoWidth': true,",
" 'deferRender': true,",
" 'info': false,",
" 'lengthChange': false,",
" 'ordering': d[n].length > 1,",
" 'order': [],",
" 'paging': false,",
" 'scrollX': false,",
" 'scrollY': false,",
" 'searching': false,",
" 'sortClasses': false,",
" 'rowCallback': rowCallback,",
" 'headerCallback': headerCallback,",
" 'columnDefs': [",
" {targets: -1, visible: false},",
" {targets: 0, orderable: false, className: 'details-control'},",
" {targets: '_all', className: 'dt-center'}",
" ]",
" }).column(0).nodes().to$().css({cursor: 'pointer'});",
" }",
"};",
"",
"// display the child table on click",
"table.on('click', 'td.details-control', function(){",
" var tbl = $(this).closest('table'),",
" tblId = tbl.attr('id'),",
" td = $(this),",
" row = $(tbl).DataTable().row(td.closest('tr')),",
" rowIdx = row.index();",
" if(row.child.isShown()){",
" row.child.hide();",
" td.html('⊕');",
" } else {",
" var childId = tblId + '-child-' + rowIdx;",
" row.child(format(row.data(), childId)).show();",
" td.html('⊖');",
" format_datatable(row.data(), childId);",
" }",
"});")
## the datatable
datatable(
Dat, callback = callback, rownames = rowNames, escape = -colIdx-1,
options = list(
columnDefs = list(
list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
list(orderable = FALSE, className = 'details-control', targets = colIdx),
list(className = "dt-center", targets = "_all")
)
)
)
这篇关于R中的父/子行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!