|
| 1 | +from functools import reduce |
| 2 | +from itertools import groupby |
| 3 | +from operator import add |
| 4 | + |
1 | 5 | import dash_html_components as html
|
2 | 6 |
|
3 | 7 |
|
@@ -70,15 +74,69 @@ def _generate_table_from_df(
|
70 | 74 | )
|
71 | 75 | elif isinstance(header, dict):
|
72 | 76 | df = df.rename(columns=header)
|
| 77 | + |
| 78 | + # Get the actual headers |
| 79 | + n_levels = df.columns.nlevels |
| 80 | + header_values = [ |
| 81 | + list(df.columns.get_level_values(level)) |
| 82 | + for level in range(n_levels) |
| 83 | + ] |
| 84 | + |
| 85 | + # The sizes of consecutive header groups at each level |
| 86 | + header_spans = [ |
| 87 | + [len(list(group)) for _, group in groupby(level_values)] |
| 88 | + for level_values in header_values |
| 89 | + ] |
| 90 | + |
| 91 | + # The positions of header changes for each level as an integer |
| 92 | + header_breaks = [ |
| 93 | + [sum(level_spans[:i]) for i in range(1, len(level_spans) + 1)] |
| 94 | + for level_spans in header_spans |
| 95 | + ] |
| 96 | + |
| 97 | + # Include breaks from higher levels |
| 98 | + header_breaks = [ |
| 99 | + sorted(set(reduce(add, header_breaks[:level])).union({0})) |
| 100 | + for level in range(1, n_levels + 1) |
| 101 | + ] |
| 102 | + |
| 103 | + # Go from header break positions back to cell spans |
| 104 | + header_spans = [ |
| 105 | + reversed( |
| 106 | + [ |
| 107 | + level_breaks[i] - level_breaks[i - 1] |
| 108 | + for i in range(len(level_breaks) - 1, 0, -1) |
| 109 | + ] |
| 110 | + ) |
| 111 | + for level_breaks in header_breaks |
| 112 | + ] |
| 113 | + |
73 | 114 | table = [
|
74 |
| - html.Thead(html.Tr(children=[html.Th(col) for col in df.columns])) |
| 115 | + html.Thead( |
| 116 | + [ |
| 117 | + html.Tr( |
| 118 | + children=[ |
| 119 | + html.Th( |
| 120 | + header_values[level][pos], |
| 121 | + colSpan=span, |
| 122 | + ) |
| 123 | + for pos, span in zip( |
| 124 | + header_breaks[level], header_spans[level] |
| 125 | + ) |
| 126 | + ] |
| 127 | + ) |
| 128 | + for level in range(n_levels) |
| 129 | + ] |
| 130 | + ) |
75 | 131 | ]
|
76 | 132 | else:
|
77 | 133 | table = []
|
78 | 134 | table.append(
|
79 | 135 | html.Tbody(
|
80 | 136 | [
|
81 |
| - html.Tr([html.Td(df.iloc[i][col]) for col in df.columns]) |
| 137 | + html.Tr( |
| 138 | + [html.Td(df.iloc[i, j]) for j in range(len(df.columns))] |
| 139 | + ) |
82 | 140 | for i in range(len(df))
|
83 | 141 | ]
|
84 | 142 | )
|
|
0 commit comments