mwouts / itables

Pandas DataFrames as Interactive DataTables
https://mwouts.github.io/itables/
MIT License
710 stars 53 forks source link

Annoying pop-pup window from datatables.net for tables with multi-index and style #254

Closed Ezibenroc closed 2 months ago

Ezibenroc commented 2 months ago

Hello,

This minimum example reproduces the issue:

from pandas import DataFrame
from itables import show

df = DataFrame({"my_column": {("a1", "b1"): 0.1, ("a1", "b2"): 0.2}})
df = df.style.format()
show(df)

It opens a pop-up window with this message:

DataTables warning: table id=T_63ab3 - Requested unknown parameter '2' for row 1, column 2. For more information about this error, please see https://datatables.net/tn/4

From what I see, it only happens if I have a multi-index and use some style formatting on the table (like in the example). Also, it only seems to happen with itables 2.0.0, I cannot reproduce this with itables 1.7.1. If I export the HTML with to_html_datatable and open it in a browser, the issue also happens (so I guess it is not a jupyter thing).

mwouts commented 2 months ago

Thank you @Ezibenroc for the detailed report. This is very helpful. I will need to check to which extend DataTables support complex rows like the one in your example. One difference between ITables 1.7.1 and 2.0.0 is that we bumped DataTables to version 2.0.1

If we wanted to report this at https://datatables.net/, the below would help:

print(to_html_datatable(df))
<style type="text/css">
</style>
<table id="T_56519" class="display nowrap"style="table-layout:auto;width:auto;margin:auto;caption-side:bottom">
  <thead>
    <tr>
      <th class="blank" >&nbsp;</th>
      <th class="blank level0" >&nbsp;</th>
      <th id="T_56519_level0_col0" class="col_heading level0 col0" >my_column</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="T_56519_level0_row0" class="row_heading level0 row0" rowspan="2">a1</th>
      <th id="T_56519_level1_row0" class="row_heading level1 row0" >b1</th>
      <td id="T_56519_row0_col0" class="data row0 col0" >0.100000</td>
    </tr>
    <tr>
      <th id="T_56519_level1_row1" class="row_heading level1 row1" >b2</th>
      <td id="T_56519_row1_col0" class="data row1 col0" >0.200000</td>
    </tr>
  </tbody>
</table>

<link href="https://www.unpkg.com/dt_for_itables@2.0.1/dt_bundle.css" rel="stylesheet">
<script type="module">
    import {DataTable, jQuery as $} from 'https://www.unpkg.com/dt_for_itables@2.0.1/dt_bundle.js';

    document.querySelectorAll("#T_56519:not(.dataTable)").forEach(table => {
        // Define the table data

        // Define the dt_args
        let dt_args = {"layout": {"topStart": "pageLength", "topEnd": "search", "bottomStart": "info", "bottomEnd": "paging"}, "order": [], "dom": "t"};

        new DataTable(table, dt_args);
    });
</script>
Ezibenroc commented 2 months ago

I found this page with this comment:

There is a colspan or rowspan in the tbody of the table, which is not supported by DataTables.

I think this is exactly what we have in the code you posted:

  <tbody>
    <tr>
      <th id="T_56519_level0_row0" class="row_heading level0 row0" rowspan="2">a1</th>

However, we already had a rowspan in the tbody with itables 1.7.1:

<div class="itables">
<style>.itables table td {
    text-overflow: ellipsis;
    overflow: hidden;
}

.itables table th {
    text-overflow: ellipsis;
    overflow: hidden;
}

.itables thead input {
    width: 100%;
    padding: 3px;
    box-sizing: border-box;
}

.itables tfoot input {
    width: 100%;
    padding: 3px;
    box-sizing: border-box;
}

.itables { font-size: medium; }

.itables select, .itables input {
    color: inherit;
}
</style>
<style type="text/css">
</style>
<table id="T_f599e" class="display nowrap"style="table-layout:auto;width:auto;margin:auto;caption-side:bottom">
  <thead>
    <tr>
      <th class="blank" >&nbsp;</th>
      <th class="blank level0" >&nbsp;</th>
      <th id="T_f599e_level0_col0" class="col_heading level0 col0" >my_column</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="T_f599e_level0_row0" class="row_heading level0 row0" rowspan="2">a1</th>
      <th id="T_f599e_level1_row0" class="row_heading level1 row0" >b1</th>
      <td id="T_f599e_row0_col0" class="data row0 col0" >0.100000</td>
    </tr>
    <tr>
      <th id="T_f599e_level1_row1" class="row_heading level1 row1" >b2</th>
      <td id="T_f599e_row1_col0" class="data row1 col0" >0.200000</td>
    </tr>
  </tbody>
</table>

<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.1/css/jquery.dataTables.min.css">
<script type="module">
    // Import jquery and DataTable
    import 'https://code.jquery.com/jquery-3.6.0.min.js';
    import dt from 'https://cdn.datatables.net/1.12.1/js/jquery.dataTables.mjs';
    dt($);

    $(document).ready(function () {
        document.querySelectorAll("#T_f599e:not(.dataTable)").forEach(table => {
            // Define the table data

            // Define the dt_args
            let dt_args = {"order": [], "dom": "t"};

            new $.fn.dataTable(table, dt_args);
        });
    });
</script>
</div>
mwouts commented 2 months ago

Thanks @Ezibenroc for investigating this. Well what probably happened is that the support for row spans was removed in the latest release of DataTables? Thinking a bit more about this, I think it make sense that they are not supported - sorting must be quite complicated when row spans are present.

I see that Styler.to_html has a sparse_index argument. On your example that fixes the issue. If you confirm that works for your other tables we could set it to False in ITables' call to Styler.to_html - but for now can you try this?

import pandas as pd
pd.options.styler.sparse.index = False
Ezibenroc commented 2 months ago

If you confirm that works for your other tables we could set it to False in ITables' call to Styler.to_html - but for now can you try this?

Yes it seems to fix the problem on my tables, thank you! I find it easier to read when the tables have a sparse index, but I understand how this could make things complicated for sorting.