Repository: pandas-dev/pandas

Issue:
Compat with jupyter's new HTML table style
See also discussion in https://github.com/jupyterlab/jupyterlab/issues/961

With the upcoming notebook 5.0 release and in current jupyterlab, jupyter has a new (much better!) default HTML table styling. The only problem is that it fixes the width of columns hardcoded at 150 px.

IMO this is a problem. Not the default as such, but the fact that you cannot easily change this when you *want* to see long table content. The result is that when having cells/column names with long content (and sometimes you deliberately create such cells/column names), it is *impossible* to inspect those data with the default repr.
Only solutions are to inspect specifics such as `df.values` or `df.columns`.

Another consequence is that the `pd.options.display.max_colwidth` option becomes useless. 

Possible way forward:

- override the `max-width` for our html repr's to set it to 'none' (@TomAugspurger shows here that this is possible: https://github.com/jupyterlab/jupyterlab/issues/961#issuecomment-252095223)
- if we like the more narrow default, we can always decrease the default for `pd.options.display.max_colwidth` (currently 50 chars) at the same time. The the default looks similar to jupyter's one, but you have still the ability to adjust it with our options.





Discussion:
The overriding can be done by adding the following style to each `_repr_html_`:

```
<style type="text/css">
  table.dataframe td, table.dataframe th {
    max-width: none;
  }
</style>
```

Other options could be:

- see all data on hover
- wrap longer cells
Another issue is that, IMO, we should keep some kind of wrapping long text in multiple lines, see eg the notebook here: http://nbviewer.jupyter.org/gist/epifanio/e82443b79c24ceb2d59ba49cad9a62d4 (from gitter). 
Even if you can use `pd.options.display.max_colwidth` to set it to a really high number eg 1000, this is only useful if the long text is wrapped over multiple lines, and not on one gigantic line (as you would get with current jupyterlab/notebook master.


In the meantime, my proposal to remove the max-width in jupyter has been accepted, so we can close this issue! 

We can still adjust our `pd.options.display.max_colwidth` default value of 50 if people like the less wide default look of the tables. (cc @TomAugspurger)
Reopening this because there are some other issues we have to think about as well. What I encountered now are the display of MultiIndexes:

Due to the right-alignment instead of left alignment, multi-indexed columns get a bit strange:

![screenshot from 2017-02-28 16-07-58](https://cloud.githubusercontent.com/assets/1020496/23410756/7d64c952-fdd0-11e6-82a1-7b32b1732924.png)

Also multi-indexer rows are not ideal because the distinction between the levels is not always clear (the borders are gone, but the shading does not always differ depending on the number of rows in the level).

In this case, that is OK:

![screenshot from 2017-02-28 16-08-18](https://cloud.githubusercontent.com/assets/1020496/23410793/a22cd428-fdd0-11e6-9cd4-2632c541bcfe.png)

But in the following the shading is the same (and certainly when having more rows in one level label, or different number of rows for different labels, it becomes difficult to see the border between the level labels):

![screenshot from 2017-02-28 16-09-01](https://cloud.githubusercontent.com/assets/1020496/23410876/f017b4c8-fdd0-11e6-982f-05c26e6a72c9.png)


Good catch. Would we be better of vertical-aligning the row MultiIndex? We'll also want to check the interaction with index-level names (I'm getting caught up on things, should have more time to look myself towards the end of the week).
Level names are generally OK I think, see for example:

![screenshot from 2017-02-28 16-55-33](https://cloud.githubusercontent.com/assets/1020496/23412709/c51396d8-fdd6-11e6-898c-ed56b629714d.png)

The question is a bit if there is a general solution that improves this that we can propose to jupyter, or whether we try to solve this on the pandas side with some custom formatting.
For the row multi-index, vertical alignment to the top seems better:

![screenshot from 2017-02-28 17-00-34](https://cloud.githubusercontent.com/assets/1020496/23412934/8215fb86-fdd7-11e6-92f7-e6d014103248.png)

The strange thing is that I see `<th rowspan="3" valign="top">A</th>` for that row header, so we already included the notion that we want it to align at the top, only that does not seem to work. 
-> **EDIT**: "The valign attribute of \<th\> is not supported in HTML5. Use CSS instead." So we could replace that with `<th style="vertical-align:top">` if we only want to fix it specifically for the multi-index

I s there any option to disable the default styling ..  basically i want to roll back to previous default settings..

You can try asking on their mailing list. There's probably a way with custom CSS, but nothing pandas would do. 

> On Apr 13, 2017, at 00:08, Jagdish Chandra Joshi <notifications@github.com> wrote:
> 
> I s there any option to disable the default styling .. basically i want to roll back to previous default settings..
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.
> 

For the MultiIndex issues (from the above comments), I opened an issue on the jupyter side: https://github.com/jupyter/notebook/issues/2408 (and ideas what would be the best solution for the multi-indexed columns is also welcome ..)
@TomAugspurger any ideas what we could do for the multi-index columns? 
Easiest would be to switch back to left-alignment instead of right-alignment, but that would be a pity for normal frames without multi-index
> @TomAugspurger any ideas what we could do for the multi-index columns?

Looking now. I think we'll need to adjust the generated HTML to add some classes to the `th`s in a MI.
I think if we left-align any levels, then we have to do them all.

![screen shot 2017-04-18 at 6 25 58 am](https://cloud.githubusercontent.com/assets/1312546/25129440/b5923d4c-2403-11e7-8357-2a1f27bdac91.png)



This isn't too bad:

<img width="378" alt="screen shot 2017-04-18 at 6 57 23 am" src="https://cloud.githubusercontent.com/assets/1312546/25129595/55fc7310-2404-11e7-8842-fce97a5c8664.png">

I'm going to play around with borders to see how that looks with right-alignment.

OK, putting together a PR now. Here's an example notebook comparing master and the PR https://gist.github.com/79f032afa4e498f86a9d59905d0f6fbd
 You'll have to download it since nbviewer doesn't have the new styling.

Changes:

1. MultiIndex column headers are all left-aligned (this is only for MI, single-level column headers are still right-aligned)
2. MuliIndex row labels are top top-aligned

![screen shot 2017-04-20 at 9 41 53 pm](https://cloud.githubusercontent.com/assets/1312546/25260505/60962c5c-2612-11e7-856d-0bcc6a4dbc3b.png)
![screen shot 2017-04-20 at 9 41 59 pm](https://cloud.githubusercontent.com/assets/1312546/25260506/6287fc02-2612-11e7-8612-c44383ad5c72.png)


Resolving patch:
diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py
--- a/pandas/io/formats/format.py
+++ b/pandas/io/formats/format.py
@@ -9,6 +9,7 @@
 # pylint: disable=W0141
 
 import sys
+from textwrap import dedent
 
 from pandas.core.dtypes.missing import isnull, notnull
 from pandas.core.dtypes.common import (
@@ -1107,6 +1108,24 @@ def write_tr(self, line, indent=0, indent_delta=4, header=False,
         indent -= indent_delta
         self.write('</tr>', indent)
 
+    def write_style(self):
+        template = dedent("""\
+            <style>
+                .dataframe thead tr:only-child th {
+                    text-align: right;
+                }
+
+                .dataframe thead th {
+                    text-align: left;
+                }
+
+                .dataframe tbody tr th {
+                    vertical-align: top;
+                }
+            </style>""")
+        if self.notebook:
+            self.write(template)
+
     def write_result(self, buf):
         indent = 0
         frame = self.frame
@@ -1131,6 +1150,7 @@ def write_result(self, buf):
 
             self.write('<div{0}>'.format(div_style))
 
+        self.write_style()
         self.write('<table border="%s" class="%s">' % (self.border,
                                                        ' '.join(_classes)),
                    indent)


Test patch:
diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py
--- a/pandas/tests/io/formats/test_to_html.py
+++ b/pandas/tests/io/formats/test_to_html.py
@@ -1859,3 +1859,13 @@ def test_to_html_no_index_max_rows(self):
           </tbody>
         </table>""")
         self.assertEqual(result, expected)
+
+    def test_to_html_notebook_has_style(self):
+        df = pd.DataFrame({"A": [1, 2, 3]})
+        result = df.to_html(notebook=True)
+        assert "thead tr:only-child" in result
+
+    def test_to_html_notebook_has_no_style(self):
+        df = pd.DataFrame({"A": [1, 2, 3]})
+        result = df.to_html()
+        assert "thead tr:only-child" not in result