import matplotlib.pyplot
# We have this here to trigger matplotlib's font cache stuff.
# This cell is hidden from the output

import pandas as pd
import numpy as np

df = pd.DataFrame([[38.0, 2.0, 18.0, 22.0, 21, np.nan],[19, 439, 6, 452, 226,232]], 
                  index=pd.Index(['Tumour (Positive)', 'Non-Tumour (Negative)'], name='Actual Label:'), 
                  columns=pd.MultiIndex.from_product([['Decision Tree', 'Regression', 'Random'],['Tumour', 'Non-Tumour']], names=['Model:', 'Predicted:']))
df.style

"""
Explanation: Table Visualization
This section demonstrates visualization of tabular data using the Styler
class. For information on visualization with charting please see Chart Visualization. This document is written as a Jupyter Notebook, and can be viewed or downloaded here.
Styler Object and HTML
Styling should be performed after the data in a DataFrame has been processed. The Styler creates an HTML &lt;table&gt; and leverages CSS styling language to manipulate many parameters including colors, fonts, borders, background, etc. See here for more information on styling HTML tables. This allows a lot of flexibility out of the box, and even enables web developers to integrate DataFrames into their exiting user interface designs.
The DataFrame.style attribute is a property that returns a Styler object. It has a _repr_html_ method defined on it so they are rendered automatically in Jupyter Notebook.
End of explanation
"""


# Hidden cell to just create the below example: code is covered throughout the guide.
s = df.style\
      .hide_columns([('Random', 'Tumour'), ('Random', 'Non-Tumour')])\
      .format('{:.0f}')\
      .set_table_styles([{
        'selector': '',
        'props':  'border-collapse: separate;'
      },{
        'selector': 'caption',
        'props': 'caption-side: bottom; font-size:1.3em;'
      },{
        'selector': '.index_name',
        'props': 'font-style: italic; color: darkgrey; font-weight:normal;'
      },{
        'selector': 'th:not(.index_name)',
        'props': 'background-color: #000066; color: white;'
      },{
        'selector': 'th.col_heading',
        'props': 'text-align: center;'
      },{
        'selector': 'th.col_heading.level0',
        'props': 'font-size: 1.5em;'
      },{
        'selector': 'th.col2',
        'props': 'border-left: 1px solid white;'
      },{
        'selector': '.col2',
        'props': 'border-left: 1px solid #000066;'
      },{
        'selector': 'td',
        'props': 'text-align: center; font-weight:bold;'
      },{
        'selector': '.true',
        'props': 'background-color: #e6ffe6;'
      },{
        'selector': '.false',
        'props': 'background-color: #ffe6e6;'
      },{
        'selector': '.border-red',
        'props': 'border: 2px dashed red;'
      },{
        'selector': '.border-green',
        'props': 'border: 2px dashed green;'
      },{
        'selector': 'td:hover',
        'props': 'background-color: #ffffb3;'
      }])\
      .set_td_classes(pd.DataFrame([['true border-green', 'false', 'true', 'false border-red', '', ''],
                                    ['false', 'true', 'false', 'true', '', '']], 
                                    index=df.index, columns=df.columns))\
      .set_caption("Confusion matrix for multiple cancer prediction models.")\
      .set_tooltips(pd.DataFrame([['This model has a very strong true positive rate', '', '', "This model's total number of false negatives is too high", '', ''],
                                    ['', '', '', '', '', '']], 
                                    index=df.index, columns=df.columns),
                   css_class='pd-tt', props=
    'visibility: hidden; position: absolute; z-index: 1; border: 1px solid #000066;'
    'background-color: white; color: #000066; font-size: 0.8em;' 
    'transform: translate(0px, -24px); padding: 0.6em; border-radius: 0.5em;')


s

"""
Explanation: The above output looks very similar to the standard DataFrame HTML representation. But the HTML here has already attached some CSS classes to each cell, even if we haven't yet created any styles. We can view these by calling the  .render() method, which returns the raw HTML as string, which is useful for further processing or adding to a file - read on in More about CSS and HTML. Below we will show how we can use these to format the DataFrame to be more communicative. For example how we can build s:
End of explanation
"""


df.style.format(precision=0, na_rep='MISSING', thousands=" ",
                formatter={('Decision Tree', 'Tumour'): "{:.2f}",
                           ('Regression', 'Non-Tumour'): lambda x: "$ {:,.1f}".format(x*-1e6)
                          })

"""
Explanation: Formatting the Display
Formatting Values
Before adding styles it is useful to show that the Styler can distinguish the display value from the actual value. To control the display value, the text is printed in each cell, and we can use the .format() method to manipulate this according to a format spec string or a callable that takes a single value and returns a string. It is possible to define this for the whole table or for individual columns. 
Additionally, the format function has a precision argument to specifically help formatting floats, as well as decimal and thousands separators to support other locales, an na_rep argument to display missing data, and an escape argument to help displaying safe-HTML or safe-LaTeX. The default formatter is configured to adopt pandas' regular display.precision option, controllable using with pd.option_context('display.precision', 2):
Here is an example of using the multiple options to control the formatting generally and with specific column formatters.
End of explanation
"""


s = df.style.format('{:.0f}').hide_columns([('Random', 'Tumour'), ('Random', 'Non-Tumour')])
s

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_hide')

"""
Explanation: Hiding Data
The index and column headers can be completely hidden, as well subselecting rows or columns that one wishes to exclude. Both these options are performed using the same methods.
The index can be hidden from rendering by calling .hide_index() without any arguments, which might be useful if your index is integer based. Similarly column headers can be hidden by calling .hide_columns() without any arguments.
Specific rows or columns can be hidden from rendering by calling the same .hide_index() or .hide_columns() methods and passing in a row/column label, a list-like or a slice of row/column labels to for the subset argument.
Hiding does not change the integer arrangement of CSS classes, e.g. hiding the first two columns of a DataFrame means the column class indexing will start at col2, since col0 and col1 are simply ignored.
We can update our Styler object to hide some data and format the values.
End of explanation
"""


cell_hover = {  # for row hover use <tr> instead of <td>
    'selector': 'td:hover',
    'props': [('background-color', '#ffffb3')]
}
index_names = {
    'selector': '.index_name',
    'props': 'font-style: italic; color: darkgrey; font-weight:normal;'
}
headers = {
    'selector': 'th:not(.index_name)',
    'props': 'background-color: #000066; color: white;'
}
s.set_table_styles([cell_hover, index_names, headers])

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_tab_styles1')

"""
Explanation: Methods to Add Styles
There are 3 primary methods of adding custom CSS styles to Styler:

Using .set_table_styles() to control broader areas of the table with specified internal CSS. Although table styles allow the flexibility to add CSS selectors and properties controlling all individual parts of the table, they are unwieldy for individual cell specifications. Also, note that table styles cannot be exported to Excel. 
Using .set_td_classes() to directly link either external CSS classes to your data cells or link the internal CSS classes created by .set_table_styles(). See here. These cannot be used on column header rows or indexes, and also won't export to Excel. 
Using the .apply() and .applymap() functions to add direct internal CSS to specific data cells. See here. These cannot be used on column header rows or indexes, but only these methods add styles that will export to Excel. These methods work in a similar way to DataFrame.apply() and DataFrame.applymap().

Table Styles
Table styles are flexible enough to control all individual parts of the table, including column headers and indexes. 
However, they can be unwieldy to type for individual data cells or for any kind of conditional formatting, so we recommend that table styles are used for broad styling, such as entire rows or columns at a time.
Table styles are also used to control features which can apply to the whole table at once such as creating a generic hover functionality. The :hover pseudo-selector, as well as other pseudo-selectors, can only be used this way.
To replicate the normal format of CSS selectors and properties (attribute value pairs), e.g. 
tr:hover {
  background-color: #ffff99;
}
the necessary format to pass styles to .set_table_styles() is as a list of dicts, each with a CSS-selector tag and CSS-properties. Properties can either be a list of 2-tuples, or a regular CSS-string, for example:
End of explanation
"""


s.set_table_styles([
    {'selector': 'th.col_heading', 'props': 'text-align: center;'},
    {'selector': 'th.col_heading.level0', 'props': 'font-size: 1.5em;'},
    {'selector': 'td', 'props': 'text-align: center; font-weight: bold;'},
], overwrite=False)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_tab_styles2')

"""
Explanation: Next we just add a couple more styling artifacts targeting specific parts of the table. Be careful here, since we are chaining methods we need to explicitly instruct the method not to overwrite the existing styles.
End of explanation
"""


s.set_table_styles({
    ('Regression', 'Tumour'): [{'selector': 'th', 'props': 'border-left: 1px solid white'},
                               {'selector': 'td', 'props': 'border-left: 1px solid #000066'}]
}, overwrite=False, axis=0)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('xyz01')

"""
Explanation: As a convenience method (since version 1.2.0) we can also pass a dict to .set_table_styles() which contains row or column keys. Behind the scenes Styler just indexes the keys and adds relevant .col&lt;m&gt; or .row&lt;n&gt; classes as necessary to the given CSS selectors.
End of explanation
"""


out = s.set_table_attributes('class="my-table-cls"').render()
print(out[out.find('<table'):][:109])

"""
Explanation: Setting Classes and Linking to External CSS
If you have designed a website then it is likely you will already have an external CSS file that controls the styling of table and cell objects within it. You may want to use these native files rather than duplicate all the CSS in python (and duplicate any maintenance work).
Table Attributes
It is very easy to add a class to the main &lt;table&gt; using .set_table_attributes(). This method can also attach inline styles - read more in CSS Hierarchies.
End of explanation
"""


s.set_table_styles([  # create internal CSS classes
    {'selector': '.true', 'props': 'background-color: #e6ffe6;'},
    {'selector': '.false', 'props': 'background-color: #ffe6e6;'},
], overwrite=False)
cell_color = pd.DataFrame([['true ', 'false ', 'true ', 'false '], 
                           ['false ', 'true ', 'false ', 'true ']], 
                          index=df.index, 
                          columns=df.columns[:4])
s.set_td_classes(cell_color)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_classes')

"""
Explanation: Data Cell CSS Classes
New in version 1.2.0
The .set_td_classes() method accepts a DataFrame with matching indices and columns to the underlying Styler's DataFrame. That DataFrame will contain strings as css-classes to add to individual data cells: the &lt;td&gt; elements of the &lt;table&gt;. Rather than use external CSS we will create our classes internally and add them to table style. We will save adding the borders until the section on tooltips.
End of explanation
"""


np.random.seed(0)
df2 = pd.DataFrame(np.random.randn(10,4), columns=['A','B','C','D'])
df2.style

"""
Explanation: Styler Functions
We use the following methods to pass your style functions. Both of those methods take a function (and some other keyword arguments) and apply it to the DataFrame in a certain way, rendering CSS styles.

.applymap() (elementwise): accepts a function that takes a single value and returns a string with the CSS attribute-value pair.
.apply() (column-/row-/table-wise): accepts a function that takes a Series or DataFrame and returns a Series, DataFrame, or numpy array with an identical shape where each element is a string with a CSS attribute-value pair. This method passes each column or row of your DataFrame one-at-a-time or the entire table at once, depending on the axis keyword argument. For columnwise use axis=0, rowwise use axis=1, and for the entire table at once use axis=None.

This method is powerful for applying multiple, complex logic to data cells. We create a new DataFrame to demonstrate this.
End of explanation
"""


def style_negative(v, props=''):
    return props if v < 0 else None
s2 = df2.style.applymap(style_negative, props='color:red;')\
              .applymap(lambda v: 'opacity: 20%;' if (v < 0.3) and (v > -0.3) else None)
s2

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s2.set_uuid('after_applymap')

"""
Explanation: For example we can build a function that colors text if it is negative, and chain this with a function that partially fades cells of negligible value. Since this looks at each element in turn we use applymap.
End of explanation
"""


def highlight_max(s, props=''):
    return np.where(s == np.nanmax(s.values), props, '')
s2.apply(highlight_max, props='color:white;background-color:darkblue', axis=0)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s2.set_uuid('after_apply')

"""
Explanation: We can also build a function that highlights the maximum value across rows, cols, and the DataFrame all at once. In this case we use apply. Below we highlight the maximum in a column.
End of explanation
"""


s2.apply(highlight_max, props='color:white;background-color:pink;', axis=1)\
  .apply(highlight_max, props='color:white;background-color:purple', axis=None)

"""
Explanation: We can use the same function across the different axes, highlighting here the DataFrame maximum in purple, and row maximums in pink.
End of explanation
"""


s.set_caption("Confusion matrix for multiple cancer prediction models.")\
 .set_table_styles([{
     'selector': 'caption',
     'props': 'caption-side: bottom; font-size:1.25em;'
 }], overwrite=False)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_caption')

"""
Explanation: This last example shows how some styles have been overwritten by others. In general the most recent style applied is active but you can read more in the section on CSS hierarchies. You can also apply these styles to more granular parts of the DataFrame - read more in section on subset slicing.
It is possible to replicate some of this functionality using just classes but it can be more cumbersome. See item 3)  of Optimization
<div class="alert alert-info">

*Debugging Tip*: If you're having trouble writing your style function, try just passing it into ``DataFrame.apply``. Internally, ``Styler.apply`` uses ``DataFrame.apply`` so the result should be the same, and with ``DataFrame.apply`` you will be able to inspect the CSS string output of your intended function in each cell.

</div>

Tooltips and Captions
Table captions can be added with the .set_caption() method. You can use table styles to control the CSS relevant to the caption.
End of explanation
"""


tt = pd.DataFrame([['This model has a very strong true positive rate', 
                    "This model's total number of false negatives is too high"]], 
                  index=['Tumour (Positive)'], columns=df.columns[[0,3]])
s.set_tooltips(tt, props='visibility: hidden; position: absolute; z-index: 1; border: 1px solid #000066;'
                         'background-color: white; color: #000066; font-size: 0.8em;' 
                         'transform: translate(0px, -24px); padding: 0.6em; border-radius: 0.5em;')

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_tooltips')

"""
Explanation: Adding tooltips (since version 1.3.0) can be done using the .set_tooltips() method in the same way you can add CSS classes to data cells by providing a string based DataFrame with intersecting indices and columns. You don't have to specify a css_class name or any css props for the tooltips, since there are standard defaults, but the option is there if you want more visual control.
End of explanation
"""


s.set_table_styles([  # create internal CSS classes
    {'selector': '.border-red', 'props': 'border: 2px dashed red;'},
    {'selector': '.border-green', 'props': 'border: 2px dashed green;'},
], overwrite=False)
cell_border = pd.DataFrame([['border-green ', ' ', ' ', 'border-red '], 
                           [' ', ' ', ' ', ' ']], 
                          index=df.index, 
                          columns=df.columns[:4])
s.set_td_classes(cell_color + cell_border)

# Hidden cell to avoid CSS clashes and latter code upcoding previous formatting 
s.set_uuid('after_borders')

"""
Explanation: The only thing left to do for our table is to add the highlighting borders to draw the audience attention to the tooltips. We will create internal CSS classes as before using table styles. Setting classes always overwrites so we need to make sure we add the previous classes.
End of explanation
"""


df3 = pd.DataFrame(np.random.randn(4,4), 
                   pd.MultiIndex.from_product([['A', 'B'], ['r1', 'r2']]),
                   columns=['c1','c2','c3','c4'])
df3

"""
Explanation: Finer Control with Slicing
The examples we have shown so far for the Styler.apply and Styler.applymap functions have not demonstrated the use of the subset argument. This is a useful argument which permits a lot of flexibility: it allows you to apply styles to specific rows or columns, without having to code that logic into your style function.
The value passed to subset behaves similar to slicing a DataFrame;

A scalar is treated as a column label
A list (or Series or NumPy array) is treated as multiple column labels
A tuple is treated as (row_indexer, column_indexer)

Consider using pd.IndexSlice to construct the tuple for the last one. We will create a MultiIndexed DataFrame to demonstrate the functionality.
End of explanation
"""


slice_ = ['c3', 'c4']
df3.style.apply(highlight_max, props='color:red;', axis=0, subset=slice_)\
         .set_properties(**{'background-color': '#ffffb3'}, subset=slice_)

"""
Explanation: We will use subset to highlight the maximum in the third and fourth columns with red text. We will highlight the subset sliced region in yellow.
End of explanation
"""


idx = pd.IndexSlice
slice_ = idx[idx[:,'r1'], idx['c2':'c4']]
df3.style.apply(highlight_max, props='color:red;', axis=0, subset=slice_)\
         .set_properties(**{'background-color': '#ffffb3'}, subset=slice_)

"""
Explanation: If combined with the IndexSlice as suggested then it can index across both dimensions with greater flexibility.
End of explanation
"""


slice_ = idx[idx[:,'r2'], :]
df3.style.apply(highlight_max, props='color:red;', axis=1, subset=slice_)\
         .set_properties(**{'background-color': '#ffffb3'}, subset=slice_)

"""
Explanation: This also provides the flexibility to sub select rows when used with the axis=1.
End of explanation
"""


slice_ = idx[idx[(df3['c1'] + df3['c3']) < -2.0], ['c2', 'c4']]
df3.style.apply(highlight_max, props='color:red;', axis=1, subset=slice_)\
         .set_properties(**{'background-color': '#ffffb3'}, subset=slice_)

"""
Explanation: There is also scope to provide conditional filtering. 
Suppose we want to highlight the maximum across columns 2 and 4 only in the case that the sum of columns 1 and 3 is less than -2.0 (essentially excluding rows (:,'r2')).
End of explanation
"""


df4 = pd.DataFrame([[1,2],[3,4]])
s4 = df4.style

"""
Explanation: Only label-based slicing is supported right now, not positional, and not callables.
If your style function uses a subset or axis keyword argument, consider wrapping your function in a functools.partial, partialing out that keyword.
python
my_func2 = functools.partial(my_func, subset=42)
Optimization
Generally, for smaller tables and most cases, the rendered HTML does not need to be optimized, and we don't really recommend it. There are two cases where it is worth considering:

If you are rendering and styling a very large HTML table, certain browsers have performance issues.
If you are using Styler to dynamically create part of online user interfaces and want to improve network performance.

Here we recommend the following steps to implement:
1. Remove UUID and cell_ids
Ignore the uuid and set cell_ids to False. This will prevent unnecessary HTML.
<div class="alert alert-warning">

<font color=red>This is sub-optimal:</font>

</div>
End of explanation
"""


from pandas.io.formats.style import Styler
s4 = Styler(df4, uuid_len=0, cell_ids=False)

"""
Explanation: <div class="alert alert-info">

<font color=green>This is better:</font>

</div>
End of explanation
"""


props = 'font-family: "Times New Roman", Times, serif; color: #e83e8c; font-size:1.3em;'
df4.style.applymap(lambda x: props, subset=[1])

"""
Explanation: 2. Use table styles
Use table styles where possible (e.g. for all cells or rows or columns at a time) since the CSS is nearly always more efficient than other formats.
<div class="alert alert-warning">

<font color=red>This is sub-optimal:</font>

</div>
End of explanation
"""


df4.style.set_table_styles([{'selector': 'td.col1', 'props': props}])

"""
Explanation: <div class="alert alert-info">

<font color=green>This is better:</font>

</div>
End of explanation
"""


df2.style.apply(highlight_max, props='color:white;background-color:darkblue;', axis=0)\
         .apply(highlight_max, props='color:white;background-color:pink;', axis=1)\
         .apply(highlight_max, props='color:white;background-color:purple', axis=None)

"""
Explanation: 3. Set classes instead of using Styler functions
For large DataFrames where the same style is applied to many cells it can be more efficient to declare the styles as classes and then apply those classes to data cells, rather than directly applying styles to cells. It is, however, probably still easier to use the Styler function api when you are not concerned about optimization.
<div class="alert alert-warning">

<font color=red>This is sub-optimal:</font>

</div>
End of explanation
"""


build = lambda x: pd.DataFrame(x, index=df2.index, columns=df2.columns)
cls1 = build(df2.apply(highlight_max, props='cls-1 ', axis=0))
cls2 = build(df2.apply(highlight_max, props='cls-2 ', axis=1, result_type='expand').values)
cls3 = build(highlight_max(df2, props='cls-3 '))
df2.style.set_table_styles([
    {'selector': '.cls-1', 'props': 'color:white;background-color:darkblue;'},
    {'selector': '.cls-2', 'props': 'color:white;background-color:pink;'},
    {'selector': '.cls-3', 'props': 'color:white;background-color:purple;'}
]).set_td_classes(cls1 + cls2 + cls3)

"""
Explanation: <div class="alert alert-info">

<font color=green>This is better:</font>

</div>
End of explanation
"""


html = Styler(df4, uuid_len=0, cell_ids=False)\
      .set_table_styles([{'selector': 'td', 'props': props},
                         {'selector': '.col1', 'props': 'color:green;'},
                         {'selector': '.level0', 'props': 'color:blue;'}])\
      .render()\
      .replace('blank', '')\
      .replace('data', '')\
      .replace('level0', 'l0')\
      .replace('col_heading', '')\
      .replace('row_heading', '')

import re
html = re.sub(r'col[0-9]+', lambda x: x.group().replace('col', 'c'), html)
html = re.sub(r'row[0-9]+', lambda x: x.group().replace('row', 'r'), html)
print(html)

from IPython.display import HTML
HTML(html)

"""
Explanation: 4. Don't use tooltips
Tooltips require cell_ids to work and they generate extra HTML elements for every data cell.
5. If every byte counts use string replacement
You can remove unnecessary HTML, or shorten the default class names with string replace functions.
End of explanation
"""


df2.iloc[0,2] = np.nan
df2.iloc[4,3] = np.nan
df2.loc[:4].style.highlight_null(null_color='yellow')

"""
Explanation: Builtin Styles
Some styling functions are common enough that we've "built them in" to the Styler, so you don't have to write them and apply them yourself. The current list of such functions is:

.highlight_null: for use with identifying missing data. 
.highlight_min and .highlight_max: for use with identifying extremeties in data.
.highlight_between and .highlight_quantile: for use with identifying classes within data.
.background_gradient: a flexible method for highlighting cells based or their, or other, values on a numeric scale.
.text_gradient: similar method for highlighting text based on their, or other, values on a numeric scale.
.bar: to display mini-charts within cell backgrounds.

The individual documentation on each function often gives more examples of their arguments.
Highlight Null
End of explanation
"""


df2.loc[:4].style.highlight_max(axis=1, props='color:white; font-weight:bold; background-color:darkblue;')

"""
Explanation: Highlight Min or Max
End of explanation
"""


left = pd.Series([1.0, 0.0, 1.0], index=["A", "B", "D"])
df2.loc[:4].style.highlight_between(left=left, right=1.5, axis=1, props='color:white; background-color:purple;')

"""
Explanation: Highlight Between
This method accepts ranges as float, or NumPy arrays or Series provided the indexes match.
End of explanation
"""


df2.loc[:4].style.highlight_quantile(q_left=0.85, axis=None, color='yellow')

"""
Explanation: Highlight Quantile
Useful for detecting the highest or lowest percentile values
End of explanation
"""


import seaborn as sns
cm = sns.light_palette("green", as_cmap=True)

df2.style.background_gradient(cmap=cm)

df2.style.text_gradient(cmap=cm)

"""
Explanation: Background Gradient and Text Gradient
You can create "heatmaps" with the background_gradient and text_gradient methods. These require matplotlib, and we'll use Seaborn to get a nice colormap.
End of explanation
"""


df2.loc[:4].style.set_properties(**{'background-color': 'black',
                           'color': 'lawngreen',
                           'border-color': 'white'})

"""
Explanation: .background_gradient and .text_gradient have a number of keyword arguments to customise the gradients and colors. See the documentation.
Set properties
Use Styler.set_properties when the style doesn't actually depend on the values. This is just a simple wrapper for .applymap where the function returns the same properties for all cells.
End of explanation
"""


df2.style.bar(subset=['A', 'B'], color='#d65f5f')

"""
Explanation: Bar charts
You can include "bar charts" in your DataFrame.
End of explanation
"""


df2.style.bar(align=0, vmin=-2.5, vmax=2.5, color=['#d65f5f', '#5fba7d'],
              width=60, props="width: 120px; border-right: 1px solid black;").format('{:.3f}', na_rep="")

"""
Explanation: Additional keyword arguments give more control on centering and positioning, and you can pass a list of [color_negative, color_positive] to highlight lower and higher values.
Here's how you can change the above with the new align option, combined with setting vmin and vmax limits, the width of the figure, and underlying css props of cells, leaving space to display the text and the bars:
End of explanation
"""


# Hide the construction of the display chart from the user
import pandas as pd
from IPython.display import HTML

# Test series
test1 = pd.Series([-100,-60,-30,-20], name='All Negative')
test2 = pd.Series([-10,-5,0,90], name='Both Pos and Neg')
test3 = pd.Series([10,20,50,100], name='All Positive')
test4 = pd.Series([100, 103, 101, 102], name='Large Positive')


head = """
<table>
    <thead>
        <th>Align</th>
        <th>All Negative</th>
        <th>Both Neg and Pos</th>
        <th>All Positive</th>
        <th>Large Positive</th>
    </thead>
    </tbody>

"""

aligns = ['left', 'right', 'zero', 'mid', 'mean', 99]
for align in aligns:
    row = "<tr><th>{}</th>".format(align)
    for series in [test1,test2,test3, test4]:
        s = series.copy()
        s.name=''
        row += "<td>{}</td>".format(s.to_frame().style.hide_index().bar(align=align, 
                                                           color=['#d65f5f', '#5fba7d'], 
                                                           width=100).render()) #testn['width']
    row += '</tr>'
    head += row
    
head+= """
</tbody>
</table>"""

HTML(head)

"""
Explanation: The following example aims to give a highlight of the behavior of the new align options:
End of explanation
"""


style1 = df2.style.applymap(style_negative, props='color:red;')\
         .applymap(lambda v: 'opacity: 20%;' if (v < 0.3) and (v > -0.3) else None)

style2 = df3.style
style2.use(style1.export())
style2

"""
Explanation: Sharing styles
Say you have a lovely style built up for a DataFrame, and now you want to apply the same style to a second DataFrame. Export the style with df1.style.export, and import it on the second DataFrame with df1.style.set
End of explanation
"""


from ipywidgets import widgets
@widgets.interact
def f(h_neg=(0, 359, 1), h_pos=(0, 359), s=(0., 99.9), l=(0., 99.9)):
    return df2.style.background_gradient(
        cmap=sns.palettes.diverging_palette(h_neg=h_neg, h_pos=h_pos, s=s, l=l,
                                            as_cmap=True)
    )

"""
Explanation: Notice that you're able to share the styles even though they're data aware. The styles are re-evaluated on the new DataFrame they've been used upon.
Limitations

DataFrame only (use Series.to_frame().style)
The index and columns must be unique
No large repr, and construction performance isn't great; although we have some HTML optimizations
You can only style the values, not the index or columns (except with table_styles above)
You can only apply styles, you can't insert new HTML entities

Some of these might be addressed in the future. 
Other Fun and Useful Stuff
Here are a few interesting examples.
Widgets
Styler interacts pretty well with widgets. If you're viewing this online instead of running the notebook yourself, you're missing out on interactively adjusting the color palette.
End of explanation
"""


def magnify():
    return [dict(selector="th",
                 props=[("font-size", "4pt")]),
            dict(selector="td",
                 props=[('padding', "0em 0em")]),
            dict(selector="th:hover",
                 props=[("font-size", "12pt")]),
            dict(selector="tr:hover td:hover",
                 props=[('max-width', '200px'),
                        ('font-size', '12pt')])
]

np.random.seed(25)
cmap = cmap=sns.diverging_palette(5, 250, as_cmap=True)
bigdf = pd.DataFrame(np.random.randn(20, 25)).cumsum()

bigdf.style.background_gradient(cmap, axis=1)\
    .set_properties(**{'max-width': '80px', 'font-size': '1pt'})\
    .set_caption("Hover to magnify")\
    .format(precision=2)\
    .set_table_styles(magnify())

"""
Explanation: Magnify
End of explanation
"""


bigdf = pd.DataFrame(np.random.randn(16, 100))
bigdf.style.set_sticky(axis="index")

"""
Explanation: Sticky Headers
If you display a large matrix or DataFrame in a notebook, but you want to always see the column and row headers you can use the .set_sticky method which manipulates the table styles CSS.
End of explanation
"""


bigdf.index = pd.MultiIndex.from_product([["A","B"],[0,1],[0,1,2,3]])
bigdf.style.set_sticky(axis="index", pixel_size=18, levels=[1,2])

"""
Explanation: It is also possible to stick MultiIndexes and even only specific levels.
End of explanation
"""


df4 = pd.DataFrame([['<div></div>', '"&other"', '<span></span>']])
df4.style

df4.style.format(escape="html")

df4.style.format('<a href="https://pandas.pydata.org" target="_blank">{}</a>', escape="html")

"""
Explanation: HTML Escaping
Suppose you have to display HTML within HTML, that can be a bit of pain when the renderer can't distinguish. You can use the escape formatting option to handle this, and even use it within a formatter that contains HTML itself.
End of explanation
"""


df2.style.\
    applymap(style_negative, props='color:red;').\
    highlight_max(axis=0).\
    to_excel('styled.xlsx', engine='openpyxl')

"""
Explanation: Export to Excel
Some support (since version 0.20.0) is available for exporting styled DataFrames to Excel worksheets using the OpenPyXL or XlsxWriter engines. CSS2.2 properties handled include:

background-color
color
font-family
font-style
font-weight
text-align
text-decoration
vertical-align

white-space: nowrap


Currently broken: border-style, border-width, border-color and their {top, right, bottom, left variants}


Only CSS2 named colors and hex colors of the form #rgb or #rrggbb are currently supported.

The following pseudo CSS properties are also available to set excel specific style properties:
number-format



Table level styles, and data cell CSS-classes are not included in the export to Excel: individual cells must have their properties mapped by the Styler.apply and/or Styler.applymap methods.
End of explanation
"""


print(pd.DataFrame([[1,2],[3,4]], index=['i1', 'i2'], columns=['c1', 'c2']).style.render())

"""
Explanation: A screenshot of the output:

Export to LaTeX
There is support (since version 1.3.0) to export Styler to LaTeX. The documentation for the .to_latex method gives further detail and numerous examples.
More About CSS and HTML
Cascading Style Sheet (CSS) language, which is designed to influence how a browser renders HTML elements, has its own peculiarities. It never reports errors: it just silently ignores them and doesn't render your objects how you intend so can sometimes be frustrating. Here is a very brief primer on how Styler creates HTML and interacts with CSS, with advice on common pitfalls to avoid.
CSS Classes and Ids
The precise structure of the CSS class attached to each cell is as follows.

Cells with Index and Column names include index_name and level&lt;k&gt; where k is its level in a MultiIndex
Index label cells include
row_heading
level&lt;k&gt; where k is the level in a MultiIndex
row&lt;m&gt; where m is the numeric position of the row
Column label cells include
col_heading
level&lt;k&gt; where k is the level in a MultiIndex
col&lt;n&gt; where n is the numeric position of the column
Data cells include
data
row&lt;m&gt;, where m is the numeric position of the cell.
col&lt;n&gt;, where n is the numeric position of the cell.
Blank cells include blank

The structure of the id is T_uuid_level&lt;k&gt;_row&lt;m&gt;_col&lt;n&gt; where level&lt;k&gt; is used only on headings, and headings will only have either row&lt;m&gt; or col&lt;n&gt; whichever is needed. By default we've also prepended each row/column identifier with a UUID unique to each DataFrame so that the style from one doesn't collide with the styling from another within the same notebook or page. You can read more about the use of UUIDs in Optimization.
We can see example of the HTML by calling the .render() method.
End of explanation
"""


df4 = pd.DataFrame([['text']])
df4.style.applymap(lambda x: 'color:green;')\
         .applymap(lambda x: 'color:red;')

df4.style.applymap(lambda x: 'color:red;')\
         .applymap(lambda x: 'color:green;')

"""
Explanation: CSS Hierarchies
The examples have shown that when CSS styles overlap, the one that comes last in the HTML render, takes precedence. So the following yield different results:
End of explanation
"""


df4.style.set_uuid('a_')\
         .set_table_styles([{'selector': 'td', 'props': 'color:red;'}])\
         .applymap(lambda x: 'color:green;')

"""
Explanation: This is only true for CSS rules that are equivalent in hierarchy, or importance. You can read more about CSS specificity here but for our purposes it suffices to summarize the key points:
A CSS importance score for each HTML element is derived by starting at zero and adding:

1000 for an inline style attribute
100 for each ID
10 for each attribute, class or pseudo-class
1 for each element name or pseudo-element

Let's use this to describe the action of the following configurations
End of explanation
"""


df4.style.set_uuid('b_')\
         .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
                            {'selector': '.cls-1', 'props': 'color:blue;'}])\
         .applymap(lambda x: 'color:green;')\
         .set_td_classes(pd.DataFrame([['cls-1']]))

"""
Explanation: This text is red because the generated selector #T_a_ td is worth 101 (ID plus element), whereas #T_a_row0_col0 is only worth 100 (ID), so is considered inferior even though in the HTML it comes after the previous.
End of explanation
"""


df4.style.set_uuid('c_')\
         .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
                            {'selector': '.cls-1', 'props': 'color:blue;'},
                            {'selector': 'td.data', 'props': 'color:yellow;'}])\
         .applymap(lambda x: 'color:green;')\
         .set_td_classes(pd.DataFrame([['cls-1']]))

"""
Explanation: In the above case the text is blue because the selector #T_b_ .cls-1 is worth 110 (ID plus class), which takes precendence.
End of explanation
"""


df4.style.set_uuid('d_')\
         .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
                            {'selector': '.cls-1', 'props': 'color:blue;'},
                            {'selector': 'td.data', 'props': 'color:yellow;'}])\
         .applymap(lambda x: 'color:green !important;')\
         .set_td_classes(pd.DataFrame([['cls-1']]))

"""
Explanation: Now we have created another table style this time the selector T_c_ td.data (ID plus element plus class) gets bumped up to 111. 
If your style fails to be applied, and its really frustrating, try the !important trump card.
End of explanation
"""


from jinja2 import Environment, ChoiceLoader, FileSystemLoader
from IPython.display import HTML
from pandas.io.formats.style import Styler

"""
Explanation: Finally got that green text after all!
Extensibility
The core of pandas is, and will remain, its "high-performance, easy-to-use data structures".
With that in mind, we hope that DataFrame.style accomplishes two goals

Provide an API that is pleasing to use interactively and is "good enough" for many tasks
Provide the foundations for dedicated libraries to build on

If you build a great library on top of this, let us know and we'll link to it.
Subclassing
If the default template doesn't quite suit your needs, you can subclass Styler and extend or override the template.
We'll show an example of extending the default template to insert a custom header before each table.
End of explanation
"""


with open("templates/myhtml.tpl") as f:
    print(f.read())

"""
Explanation: We'll use the following template:
End of explanation
"""


class MyStyler(Styler):
    env = Environment(
        loader=ChoiceLoader([
            FileSystemLoader("templates"),  # contains ours
            Styler.loader,  # the default
        ])
    )
    template_html_table = env.get_template("myhtml.tpl")

"""
Explanation: Now that we've created a template, we need to set up a subclass of Styler that
knows about it.
End of explanation
"""


MyStyler(df3)

"""
Explanation: Notice that we include the original loader in our environment's loader.
That's because we extend the original template, so the Jinja environment needs
to be able to find it.
Now we can use that custom styler. It's __init__ takes a DataFrame.
End of explanation
"""


HTML(MyStyler(df3).render(table_title="Extending Example"))

"""
Explanation: Our custom template accepts a table_title keyword. We can provide the value in the .render method.
End of explanation
"""


EasyStyler = Styler.from_custom_template("templates", "myhtml.tpl")
HTML(EasyStyler(df3).render(table_title="Another Title"))

"""
Explanation: For convenience, we provide the Styler.from_custom_template method that does the same as the custom subclass.
End of explanation
"""


with open("templates/html_style_structure.html") as f:
    style_structure = f.read()

HTML(style_structure)

"""
Explanation: Template Structure
Here's the template structure for the both the style generation template and the table generation template:
Style template:
End of explanation
"""


with open("templates/html_table_structure.html") as f:
    table_structure = f.read()

HTML(table_structure)

"""
Explanation: Table template:
End of explanation
"""


# # Hack to get the same style in the notebook as the
# # main site. This is hidden in the docs.
# from IPython.display import HTML
# with open("themes/nature_with_gtoc/static/nature.css_t") as f:
#     css = f.read()
    
# HTML('<style>{}</style>'.format(css))

"""
Explanation: See the template in the GitHub repo for more details.
End of explanation
"""


