Spec-Zone.ru › pandas 0.24

Стиль

Новое в версии 0.17.1

Предварительно: Эта функция новая и всё ещё находится в разработке. В будущих выпусках мы будем добавлять новые функции и, возможно, вносить несовместимые изменения. Мы будем рады вашим отзывам.

Этот документ написан как Jupyter Notebook и может быть просмотрен или скачан здесь.

Вы можете применить условную форматирование, визуальный стиль DataFrame в зависимости от данных внутри, используя свойство DataFrame.style. Это свойство возвращает объект Styler, который имеет полезные методы для форматирования и отображения DataFrame.

Стиль реализуется с помощью CSS. Вы пишете «функции стиля», которые принимают скаляры, DataFrame или Series, и возвращают индексированные DataFrame или Series со строками CSS "attribute: value" для значений. Эти функции могут быть поэтапно переданы в Styler, который собирает стили перед рендерингом.

Создание стилей

Передайте свои функции стиля в один из следующих методов:

  • Styler.applymap: поэлементно
  • Styler.apply: по столбцам/строкам/таблице

Оба метода принимают функцию (и некоторые другие ключевые аргументы) и применяют вашу функцию к DataFrame определённым образом. Styler.applymap обрабатывает DataFrame по элементу. Styler.apply передаёт каждый столбец или строку в ваш DataFrame по одному или всю таблицу сразу, в зависимости от ключевого аргумента axis. Для работы по столбцам используйте axis=0, для работы по строкам используйте axis=1, а для работы со всей таблицей сразу используйте axis=None.

Для Styler.applymap ваша функция должна принимать скаляр и возвращать одну строку с парой атрибут-значение CSS.

Для Styler.apply ваша функция должна принимать Series или DataFrame (в зависимости от параметра оси) и возвращать Series или DataFrame с одинаковой формой, где каждое значение — строка с парой атрибут-значение CSS.

Давайте посмотрим некоторые примеры.

[2]:
import pandas as pd
import numpy as np

np.random.seed(24)
df = pd.DataFrame({'A': np.linspace(1, 10, 10)})
df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4), columns=list('BCDE'))],
               axis=1)
df.iloc[0, 2] = np.nan

Вот пример рендеринга DataFrame, без каких-либо (видимых) стилей:

[3]:
df.style
[3]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Примечание: Атрибут DataFrame.style — это свойство, возвращающее объект Styler. Styler имеет определённый метод _repr_html_, поэтому они рендерятся автоматически. Если вам нужен исходный HTML для дальнейшей обработки или записи в файл, вызовите метод .render(), который вернёт строку.

Вышеприведённый вывод очень похож на стандартное представление DataFrame в HTML. Но мы добавили CSS классы для каждой ячейки. Просмотреть их можно, вызвав метод .render.

[4]:
df.style.highlight_null().render().split('\n')[:10]
[4]:
['<style  type="text/css" >',
 '    #T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4frow0_col2 {',
 '            background-color:  red;',
 '        }</style><table id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4f" ><thead>    <tr>        <th class="blank level0" ></th>        <th class="col_heading level0 col0" >A</th>        <th class="col_heading level0 col1" >B</th>        <th class="col_heading level0 col2" >C</th>        <th class="col_heading level0 col3" >D</th>        <th class="col_heading level0 col4" >E</th>    </tr></thead><tbody>',
 '                <tr>',
 '                        <th id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4flevel0_row0" class="row_heading level0 row0" >0</th>',
 '                        <td id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4frow0_col0" class="data row0 col0" >1</td>',
 '                        <td id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4frow0_col1" class="data row0 col1" >1.32921</td>',
 '                        <td id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4frow0_col2" class="data row0 col2" >nan</td>',
 '                        <td id="T_6acd6e3e_4518_11e9_bee3_b7bf1ae96e4frow0_col3" class="data row0 col3" >-0.31628</td>']

row0_col2 — идентификатор этой конкретной ячейки. Мы также добавили уникальный UUID к каждому идентификатору строки/столбца для каждого DataFrame, чтобы стили одного DataFrame не конфликтовали со стилями другого DataFrame в одном ноутбуке или на одной странице (вы можете установить uuid, чтобы связать стили двух DataFrames).

При написании функций стилей, вы заботитесь о создании пар атрибута/значения CSS, которые вам нужны. Pandas сопоставляет эти пары с CSS классами, идентифицирующими каждую ячейку.

Давайте напишем простую функцию стилей, которая будет окрашивать отрицательные числа красным цветом, а положительные — чёрным.

[5]:
def color_negative_red(val):
    """
    Takes a scalar and returns a string with
    the css property `'color: red'` for negative
    strings, black otherwise.
    """
    color = 'red' if val < 0 else 'black'
    return 'color: %s' % color

В этом случае стиль ячейки зависит только от её значения. Это означает, что мы должны использовать метод Styler.applymap, который работает поэлементно.

[6]:
s = df.style.applymap(color_negative_red)
s
[6]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Обратите внимание на сходство со стандартной df.applymap, которая работает с элементами DataFrames поэлементно. Мы хотим, чтобы вы могли повторно использовать свои существующие знания о взаимодействии с DataFrames.

Обратите также внимание, что наша функция возвращает строку, содержащую атрибут CSS и значение, разделенные двоеточием, как и в теге <style>. Это будет общей темой.

Наконец, формы входных данных совпали. Styler.applymap вызывает функцию для каждого скалярного входа, и функция возвращает скалярный выход.

Теперь предположим, что вы хотите выделить максимальное значение в каждом столбце. Мы больше не можем использовать .applymap , так как она работала по элементу. Вместо этого мы обратимся к .apply, которая работает по столбцам (или по строкам с помощью ключевого слова axis). Позже мы увидим, что что-то вроде highlight_max уже определено в Styler , поэтому вам не нужно будет писать это самостоятельно.

[7]:
def highlight_max(s):
    '''
    highlight the maximum in a Series yellow.
    '''
    is_max = s == s.max()
    return ['background-color: yellow' if v else '' for v in is_max]
[8]:
df.style.apply(highlight_max)
[8]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

В этом случае входные данные являются Series, по одному столбцу за раз. Обратите внимание, что выходная форма highlight_max соответствует входной форме, массиву с len(s) элементами.

Мы рекомендуем использовать цепочки методов для поэтапного построения стиля, прежде чем окончательно выполнить рендеринг в конце цепочки.

[9]:
df.style.\
    applymap(color_negative_red).\
    apply(highlight_max)
[9]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Выше мы использовали Styler.apply для передачи каждого столбца по одному.

Совет по отладке: если у вас проблемы с написанием функции стилей, попробуйте просто передать её в DataFrame.apply. Внутренне Styler.apply использует DataFrame.apply, поэтому результат должен быть одинаковым.

А что, если вы хотите выделить только максимальное значение в всей таблице? Используйте .apply(function, axis=None) для указания того, что ваша функция хочет всю таблицу, а не один столбец или строку за раз. Попробуем это в следующий раз.

Мы перепишем наш highlight-max для обработки либо Series (из .apply(axis=0 or 1)) либо DataFrame (из .apply(axis=None)). Мы также разрешим настройку цвета, чтобы продемонстрировать, что .apply, и .applymap передают ключевые аргументы.

[10]:
def highlight_max(data, color='yellow'):
    '''
    highlight the maximum in a Series or DataFrame
    '''
    attr = 'background-color: {}'.format(color)
    if data.ndim == 1:  # Series from .apply(axis=0) or axis=1
        is_max = data == data.max()
        return [attr if v else '' for v in is_max]
    else:  # from .apply(axis=None)
        is_max = data == data.max().max()
        return pd.DataFrame(np.where(is_max, attr, ''),
                            index=data.index, columns=data.columns)

При использовании Styler.apply(func, axis=None), функция должна возвращать DataFrame с теми же индексами и именами столбцов.

[11]:
df.style.apply(highlight_max, color='darkorange', axis=None)
[11]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Резюме стилей построения

Функции стиля должны возвращать строки с одним или несколькими CSS attribute: value разделенными точкой с запятой. Используйте

  • Styler.applymap(func) для стилей элементов
  • Styler.apply(func, axis=0) для стилей столбцов
  • Styler.apply(func, axis=1) для стилей строк
  • Styler.apply(func, axis=None) для стилей таблиц

И, что очень важно, входные и выходные формы func должны совпадать. Если x является входом, то func(x).shape == x.shape.

Более точный контроль: нарезка

Оба Styler.apply, и Styler.applymap принимают ключевое слово subset. Это позволяет применять стили к определенным строкам или столбцам, не кодируя эту логику в вашей функции style.

Значение, переданное в subset, ведет себя аналогично нарезке DataFrame.

  • Скаляр рассматривается как метка столбца
  • Список (или серия или массив NumPy)
  • Кортеж рассматривается как (row_indexer, column_indexer)

Рассмотрите использование pd.IndexSlice для построения кортежа для последнего.

[12]:
df.style.apply(highlight_max, subset=['B', 'C', 'D'])
[12]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Для срезов строк и столбцов подойдёт любой допустимый индексатор к .loc.

[13]:
df.style.applymap(color_negative_red,
                  subset=pd.IndexSlice[2:5, ['B', 'D']])
[13]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Сейчас поддерживается только срез по меткам, а не по позициям.

Если ваша функция стилей использует ключевое слово subset или axis, рассмотрите возможность обернуть свою функцию в functools.partial, частично исключив это ключевое слово.

my_func2 = functools.partial(my_func, subset=42)

Более тонкий контроль: значения отображения

Мы различаем значение отображения от фактического значения в Styler. Чтобы управлять значением отображения, текст печатается в каждой ячейке, используйте Styler.format. Ячейки могут форматироваться в соответствии со строкой спецификации формата формата строк или вызываемым объектом, который принимает одно значение и возвращает строку.

[14]:
df.style.format("{:.2%}")
[14]:
A B C D E
0 100.00% 132.92% nan% -31.63% -99.08%
1 200.00% -107.08% -143.87% 56.44% 29.57%
2 300.00% -162.64% 21.96% 67.88% 188.93%
3 400.00% 96.15% 10.40% -48.12% 85.02%
4 500.00% 145.34% 105.77% 16.56% 51.50%
5 600.00% -133.69% 56.29% 139.29% -6.33%
6 700.00% 12.17% 120.76% -0.20% 162.78%
7 800.00% 35.45% 103.75% -38.57% 51.98%
8 900.00% 168.66% -132.60% 142.90% -208.94%
9 1000.00% -12.98% 63.15% -58.65% 29.07%

Используйте словарь для форматирования определённых столбцов.

[15]:
df.style.format({'B': "{:0<4.0f}", 'D': '{:+.2f}'})
[15]:
A B C D E
0 1 1000 nan -0.32 -0.99081
1 2 -100 -1.43871 +0.56 0.295722
2 3 -200 0.219565 +0.68 1.88927
3 4 1000 0.104011 -0.48 0.850229
4 5 1000 1.05774 +0.17 0.515018
5 6 -100 0.562861 +1.39 -0.063328
6 7 0000 1.2076 -0.00 1.6278
7 8 0000 1.03753 -0.39 0.519818
8 9 2000 -1.32596 +1.43 -2.08935
9 10 -000 0.631523 -0.59 0.29072

Или передайте вызываемый объект (или словарь вызываемых объектов) для более гибкой обработки.

[16]:
df.style.format({"B": lambda x: "±{:.2f}".format(abs(x))})
[16]:
A B C D E
0 1 ±1.33 nan -0.31628 -0.99081
1 2 ±1.07 -1.43871 0.564417 0.295722
2 3 ±1.63 0.219565 0.678805 1.88927
3 4 ±0.96 0.104011 -0.481165 0.850229
4 5 ±1.45 1.05774 0.165562 0.515018
5 6 ±1.34 0.562861 1.39285 -0.063328
6 7 ±0.12 1.2076 -0.00204021 1.6278
7 8 ±0.35 1.03753 -0.385684 0.519818
8 9 ±1.69 -1.32596 1.42898 -2.08935
9 10 ±0.13 0.631523 -0.586538 0.29072

Встроенные стили

Наконец, мы ожидаем, что определённые функции стилизации будут достаточно распространены, чтобы мы включили несколько «встроенных» в Styler, поэтому вам не придётся писать их самим.

[17]:
df.style.highlight_null(null_color='red')
[17]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Вы можете создать «тепловые карты» с помощью метода background_gradient. Для этого необходимо использовать matplotlib, а для получения красивой цветовой схемы мы будем использовать Seaborn.

[18]:
import seaborn as sns

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

s = df.style.background_gradient(cmap=cm)
s
/opt/conda/envs/pandas/lib/python3.6/site-packages/matplotlib/colors.py:512: RuntimeWarning: invalid value encountered in less
  xa[xa < 0] = -1
[18]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Styler.background_gradient принимает ключевые аргументы low и high. Грубо говоря, они расширяют диапазон ваших данных на low и high процентов, чтобы при преобразовании цветов не использовался весь диапазон цветовой карты. Это полезно для того, чтобы текст по-прежнему был читабельным.

[19]:
# Uses the full color range
df.loc[:4].style.background_gradient(cmap='viridis')
[19]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
[20]:
# Compress the color range
(df.loc[:4]
    .style
    .background_gradient(cmap='viridis', low=.5, high=0)
    .highlight_null('red'))
[20]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018

Также есть .highlight_min и .highlight_max.

[21]:
df.style.highlight_max(axis=0)
[21]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Используйте Styler.set_properties в тех случаях, когда стиль не зависит от значений.

[22]:
df.style.set_properties(**{'background-color': 'black',
                           'color': 'lawngreen',
                           'border-color': 'white'})
[22]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Диаграммы столбчатых графиков

Вы можете включить “диаграммы столбчатых графиков” в свою DataFrame.

[23]:
df.style.bar(subset=['A', 'B'], color='#d65f5f')
[23]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

В версии 0.20.0 добавлена возможность дальнейшей настройки диаграммы в виде столбчатой диаграммы: теперь вы можете центрировать df.style.bar по нулю или по средней величине (в дополнение к уже существующему способу выравнивания минимального значения слева в ячейке), и вы можете передать список [color_negative, color_positive].

Вот как вы можете изменить вышесказанное с помощью нового align='mid' параметра:

[24]:
df.style.bar(subset=['A', 'B'], align='mid', color=['#d65f5f', '#5fba7d'])
[24]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Следующий пример призван продемонстрировать поведение новых опций выравнивания:

[25]:
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,20,50,100], name='All Positive')
test3 = pd.Series([-10,-5,0,90], name='Both Pos and Neg')

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

"""

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

head+= """
</tbody>
</table>"""


HTML(head)
[25]:
Выравнивание Все отрицательные Все положительные И отрицательные, и положительные
слева
0 -100
1 -60
2 -30
3 -20
0 10
1 20
2 50
3 100
0 -10
1 -5
2 0
3 90
ноль
0 -100
1 -60
2 -30
3 -20
0 10
1 20
2 50
3 100
0 -10
1 -5
2 0
3 90
середина
0 -100
1 -60
2 -30
3 -20
0 10
1 20
2 50
3 100
0 -10
1 -5
2 0
3 90

Применение стилей

Предположим, у вас есть стили для DataFrame, и теперь вы хотите применить те же стили к другому DataFrame. Экспортируйте стили с помощью df1.style.export, и импортируйте их во второй DataFrame с помощью df1.style.set

[26]:
df2 = -df
style1 = df.style.applymap(color_negative_red)
style1
[26]:
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072
[27]:
style2 = df2.style
style2.use(style1.export())
style2
[27]:
A B C D E
0 -1 -1.32921 nan 0.31628 0.99081
1 -2 1.07082 1.43871 -0.564417 -0.295722
2 -3 1.6264 -0.219565 -0.678805 -1.88927
3 -4 -0.961538 -0.104011 0.481165 -0.850229
4 -5 -1.45342 -1.05774 -0.165562 -0.515018
5 -6 1.33694 -0.562861 -1.39285 0.063328
6 -7 -0.121668 -1.2076 0.00204021 -1.6278
7 -8 -0.354493 -1.03753 0.385684 -0.519818
8 -9 -1.68658 1.32596 -1.42898 2.08935
9 -10 0.12982 -0.631523 0.586538 -0.29072

Обратите внимание, что вы можете обмениваться стилями, даже если они зависят от данных. Эти стили переоцениваются при применении к новым фреймам данных.

Другие параметры

Вы уже ознакомились с несколькими методами для стилизации данных. Также предоставляются несколько других вариантов стилей, которые не зависят от данных.

  • точность
  • подписи
  • стили таблицы
  • скрытие индекса или столбцов

Каждый из этих пунктов можно указать двумя способами:

  • Аргумент ключевого слова для
  • Вызов одного из методов или методов, например, или

Лучший метод зависит от контекста. Используйте конструктор, когда вы создаете много стилизованных фреймов данных, которые должны иметь общие свойства. Для интерактивного использования методы и более удобны.

Точность

Вы можете контролировать точность чисел с плавающей точкой, используя стандартный параметр pandas.

[28]:
with pd.option_context('display.precision', 2):
    html = (df.style
              .applymap(color_negative_red)
              .apply(highlight_max))
html
[28]:
A B C D E
0 1 1.3 nan -0.32 -0.99
1 2 -1.1 -1.4 0.56 0.3
2 3 -1.6 0.22 0.68 1.9
3 4 0.96 0.1 -0.48 0.85
4 5 1.5 1.1 0.17 0.52
5 6 -1.3 0.56 1.4 -0.063
6 7 0.12 1.2 -0.002 1.6
7 8 0.35 1 -0.39 0.52
8 9 1.7 -1.3 1.4 -2.1
9 10 -0.13 0.63 -0.59 0.29

Или через метод set_precision.

[29]:
df.style\
  .applymap(color_negative_red)\
  .apply(highlight_max)\
  .set_precision(2)
[29]:
A B C D E
0 1 1.3 nan -0.32 -0.99
1 2 -1.1 -1.4 0.56 0.3
2 3 -1.6 0.22 0.68 1.9
3 4 0.96 0.1 -0.48 0.85
4 5 1.5 1.1 0.17 0.52
5 6 -1.3 0.56 1.4 -0.063
6 7 0.12 1.2 -0.002 1.6
7 8 0.35 1 -0.39 0.52
8 9 1.7 -1.3 1.4 -2.1
9 10 -0.13 0.63 -0.59 0.29

Установка точности влияет только на выводимые числа; полные значения с высокой точностью всегда передаются вашим функциям стиля. Вы всегда можете использовать df.round(2).style , если хотите округлить с самого начала.

Подписи

Регулярные подписи таблиц можно добавлять несколькими способами.

[30]:
df.style.set_caption('Colormaps, with a caption.')\
    .background_gradient(cmap=cm)
[30]:
Цветовые карты с подписью.
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Стилевые таблицы

Следующим доступным вариантом являются «стилевые таблицы». Это стили, которые применяются ко всей таблице целиком, но не учитывают данные. Некоторые стили, включая псевдоселекторы, такие как :hover, могут использоваться только таким образом.

[31]:
from IPython.display import HTML

def hover(hover_color="#ffff99"):
    return dict(selector="tr:hover",
                props=[("background-color", "%s" % hover_color)])

styles = [
    hover(),
    dict(selector="th", props=[("font-size", "150%"),
                               ("text-align", "center")]),
    dict(selector="caption", props=[("caption-side", "bottom")])
]
html = (df.style.set_table_styles(styles)
          .set_caption("Hover to highlight."))
html
[31]:
Наведение для выделения.
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

table_styles должно быть списком словарей. Каждый словарь должен иметь ключи selector и props. Значение для selector должно быть допустимым селектором CSS. Помните, что все стили уже прикреплены к id, уникальному для каждого Styler . Этот селектор добавляется к тому id. Значение для props должно быть списком кортежей ('attribute', 'value').

table_styles чрезвычайно гибкие, но не так весело набирать вручную. Мы надеемся собрать некоторые полезные либо в pandas, либо, что предпочтительнее, в новом пакете, который разработан на основе здесь инструментов.

Скрытие индекса или столбцов

Индекс можно скрыть от рендеринга, вызвав Styler.hide_index. Столбцы можно скрыть от рендеринга, вызвав Styler.hide_columns и передав имя столбца или срез столбцов.

[32]:
df.style.hide_index()
[32]:
A B C D E
1 1.32921 nan -0.31628 -0.99081
2 -1.07082 -1.43871 0.564417 0.295722
3 -1.6264 0.219565 0.678805 1.88927
4 0.961538 0.104011 -0.481165 0.850229
5 1.45342 1.05774 0.165562 0.515018
6 -1.33694 0.562861 1.39285 -0.063328
7 0.121668 1.2076 -0.00204021 1.6278
8 0.354493 1.03753 -0.385684 0.519818
9 1.68658 -1.32596 1.42898 -2.08935
10 -0.12982 0.631523 -0.586538 0.29072
[33]:
df.style.hide_columns(['C','D'])
[33]:
A B E
0 1 1.32921 -0.99081
1 2 -1.07082 0.295722
2 3 -1.6264 1.88927
3 4 0.961538 0.850229
4 5 1.45342 0.515018
5 6 -1.33694 -0.063328
6 7 0.121668 1.6278
7 8 0.354493 0.519818
8 9 1.68658 -2.08935
9 10 -0.12982 0.29072

CSS Классы

Некоторые CSS классы прикреплены к ячейкам.

  • Имена индексов и столбцов содержат index_name и level<k>, где k — это его уровень в многоуровневом индексе
  • Ячейки меток индекса содержат
    • row_heading
    • row<n>, где n — это числовой номер строки
    • level<k>, где k — это уровень в многоуровневом индексе
  • Ячейки меток столбцов содержат
    • col_heading
    • col<n>, где n — это числовой номер столбца
    • level<k>, где k — это уровень в многоуровневом индексе
  • Ячейки без значений содержат blank
  • Ячейки данных содержат data

Ограничения

  • Только для DataFrame (use Series.to_frame().style)
  • Индекс и столбцы должны быть уникальными
  • Нет большого представления, и производительность невысока; предназначено для сводных DataFrame
  • Можно стилизовать только значения, а не индекс или столбцы
  • Можно применять стили, но нельзя вставлять новые HTML сущности

Некоторые из этих ограничений будут устранены в будущем.

Термины

  • Функция стиля: функция, которая передается в Styler.apply или Styler.applymap и возвращает значения, такие как 'css attribute: value'
  • Встроенные функции стиля: функции стиля, которые являются методами в Styler
  • Стиль таблицы: словарь с двумя ключами selector и props. selector — это CSS селектор, к которому props будет применяться. props — это список (attribute, value) кортежей. Список стилей таблиц, переданных в Styler.

Интересные примеры

Здесь приведены несколько интересных примеров.

Styler взаимодействует довольно хорошо с виджетами. Если вы просматриваете это онлайн, а не выполняете ноутбук сами, вы упускаете возможность интерактивно настраивать цветовую палитру.

[34]:
from IPython.html import widgets
@widgets.interact
def f(h_neg=(0, 359, 1), h_pos=(0, 359), s=(0., 99.9), l=(0., 99.9)):
    return df.style.background_gradient(
        cmap=sns.palettes.diverging_palette(h_neg=h_neg, h_pos=h_pos, s=s, l=l,
                                            as_cmap=True)
    )
A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072
[35]:
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')])
]
[36]:
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")\
    .set_precision(2)\
    .set_table_styles(magnify())
[36]:
Наведите указатель для увеличения
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
0 0.23 1 -0.84 -0.59 -0.96 -0.22 -0.62 1.8 -2.1 0.87 -0.92 -0.23 2.2 -1.3 0.076 -1.2 1.2 -1 1.1 -0.42 2.3 -2.6 2.8 0.68 -1.6
1 -1.7 1.6 -1.1 -1.1 1 0.0037 -2.5 3.4 -1.7 1.3 -0.52 -0.015 1.5 -1.1 -1.9 -1.1 -0.68 -0.81 0.35 -0.055 1.8 -2.8 2.3 0.78 0.44
2 -0.65 3.2 -1.8 0.52 2.2 -0.37 -3 3.7 -1.9 2.5 0.21 -0.24 -0.1 -0.78 -3 -0.82 -0.21 -0.23 0.86 -0.68 1.4 -4.9 3 1.9 0.61
3 -1.6 3.7 -2.3 0.43 4.2 -0.43 -3.9 4.2 -2.1 1.1 0.12 0.6 -0.89 0.27 -3.7 -2.7 -0.31 -1.6 1.4 -1.8 0.91 -5.8 2.8 2.1 0.28
4 -3.3 4.5 -1.9 -1.7 5.2 -1 -3.8 4.7 -0.72 1.1 -0.18 0.83 -0.22 -1.1 -4.3 -2.9 -0.97 -1.8 1.5 -1.8 2.2 -6.3 3.3 2.5 2.1
5 -0.84 4.2 -1.7 -2 5.3 -0.99 -4.1 3.9 -1.1 -0.94 1.2 0.087 -1.8 -0.11 -4.5 -0.85 -2.1 -1.4 0.8 -1.6 1.5 -6.5 2.8 2.1 3.8
6 -0.74 5.4 -2.1 -1.1 4.2 -1.8 -3.2 3.8 -3.2 -1.2 0.34 0.57 -1.8 0.54 -4.4 -1.8 -4 -2.6 -0.2 -4.7 1.9 -8.5 3.3 2.5 5.8
7 -0.44 4.7 -2.3 -0.21 5.9 -2.6 -1.8 5.5 -4.5 -3.2 -1.7 0.18 0.11 0.036 -6 -0.45 -6.2 -3.9 0.71 -3.9 0.67 -7.3 3 3.4 6.7
8 0.92 5.8 -3.3 -0.65 6 -3.2 -1.8 5.6 -3.5 -1.3 -1.6 0.82 -2.4 -0.4 -6.1 -0.52 -6.6 -3.5 -0.043 -4.6 0.51 -5.8 3.2 2.4 5.1
9 0.38 5.5 -4.5 -0.8 7.1 -2.6 -0.44 5.3 -2 -0.33 -0.8 0.26 -3.4 -0.82 -6.1 -2.6 -8.5 -4.5 0.41 -4.7 1.9 -6.9 2.1 3 5.2
10 2.1 5.8 -3.9 -0.98 7.8 -2.5 -0.59 5.6 -2.2 -0.71 -0.46 1.8 -2.8 0.48 -6 -3.4 -7.8 -5.5 -0.7 -4.6 -0.52 -7.7 1.5 5 5.8
11 1.9 4.5 -2.2 -1.4 5.9 -0.49 0.017 5.8 -1 -0.6 0.49 2 -1.5 1.9 -5.9 -4.5 -8.2 -3.4 -2.2 -4.3 -1.2 -7.9 1.4 5.3 5.8
12 3.2 4.2 -3.1 -2.3 5.9 -2.6 0.33 6.7 -2.8 -0.2 1.9 2.6 -1.5 0.75 -5.3 -4.5 -7.6 -2.9 -2.2 -4.8 -1.1 -9 2.1 6.4 5.6
13 2.3 4.5 -3.9 -2 6.8 -3.3 -2.2 8 -2.6 -0.8 0.71 2.3 -0.16 -0.46 -5.1 -3.8 -7.6 -4 0.33 -3.7 -1 -8.7 2.5 5.9 6.7
14 3.8 4.3 -3.9 -1.6 6.2 -3.2 -1.5 5.6 -2.9 -0.33 -0.97 1.7 3.6 0.29 -4.2 -4.1 -6.7 -4.5 -2.2 -2.4 -1.6 -9.4 3.4 6.1 7.5
15 5.6 5.3 -4 -2.3 5.9 -3.3 -1 5.7 -3.1 -0.33 -1.2 2.2 4.2 1 -3.2 -4.3 -5.7 -4.4 -2.3 -1.4 -1.2 -11 2.6 6.7 5.9
16 4.1 4.3 -2.4 -3.3 6 -2.5 -0.47 5.3 -4.8 1.6 0.23 0.099 5.8 1.8 -3.1 -3.9 -5.5 -3 -2.1 -1.1 -0.56 -13 2.1 6.2 4.9
17 5.6 4.6 -3.5 -3.8 6.6 -2.6 -0.75 6.6 -4.8 3.6 -0.29 0.56 5.8 2 -2.3 -2.3 -5 -3.2 -3.1 -2.4 0.84 -13 3.6 7.4 4.7
18 6 5.8 -2.8 -4.2 7.1 -3.3 -1.2 7.9 -4.9 1.4 -0.63 0.35 7.5 0.87 -1.5 -2.1 -4.2 -2.5 -2.5 -2.9 1.9 -9.7 3.4 7.1 4.4
19 4 6.2 -4.1 -4.1 7.2 -4.1 -1.5 6.5 -5.2 -0.24 0.0072 1.2 6.4 -2 -2.6 -1.7 -5.2 -3.3 -2.9 -1.7 1.6 -11 2.8 7.5 3.9

Экспорт в Excel

Новое в версии 0.20.0

Экспериментально: Эта функция новая и всё ещё находится в разработке. Мы будем добавлять новые функции и, возможно, вносить прерывающие изменения в будущих выпусках. Мы с удовольствием выслушаем ваши отзывы.

Некоторая поддержка доступна для экспорта стилизованных DataFrames в электронные таблицы Excel с использованием OpenPyXL или XlsxWriter движков. Обрабатываются свойства CSS2.2, включая:

  • background-color
  • border-style, border-width, border-color и их {top, right, bottom, left варианты}
  • color
  • font-family
  • font-style
  • font-weight
  • text-align
  • text-decoration
  • vertical-align
  • white-space: nowrap
  • В настоящее время поддерживаются только именованные цвета CSS2 и шестнадцатеричные цвета вида #rgb или #rrggbb.
  • Для установки свойств стиля, специфичных для Excel, также доступны следующие псевдо-свойства CSS:
    • number-format
[37]:
df.style.\
    applymap(color_negative_red).\
    apply(highlight_max).\
    to_excel('styled.xlsx', engine='openpyxl')

Скриншот вывода:

Excel spreadsheet with styled DataFrame

Расширяемость

Ядро pandas — и останется — его «высокопроизводительные, простые в использовании структуры данных». С учётом этого, мы надеемся, что DataFrame.style выполняет две цели

  • Предоставить API, приятный в использовании интерактивно и «достаточно хороший» для многих задач
  • Предоставить основу для специализированных библиотек для дальнейшего построения на ней
END_OF_DOCUMENT_MARKER

Если вы создадите на основе этого отличную библиотеку, сообщите нам, и мы добавим к ней ссылку.

Наследование

Если стандартный шаблон не совсем соответствует вашим потребностям, вы можете создать подкласс Styler и расширить или переопределить шаблон. Мы покажем пример расширения стандартного шаблона для вставки пользовательского заголовка перед каждой таблицей.

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

Мы будем использовать следующий шаблон:

[39]:
with open("templates/myhtml.tpl") as f:
    print(f.read())
{% extends "html.tpl" %}
{% block table %}
<h1>{{ table_title|default("My Table") }}</h1>
{{ super() }}
{% endblock table %}

Теперь, когда мы создали шаблон, нам нужно настроить подкласс Styler, который знает о нём.

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

Обратите внимание, что мы включаем исходный загрузчик в загрузчик нашей среды. Это потому, что мы расширяем исходный шаблон, поэтому среда Jinja должна иметь возможность его найти.

Теперь мы можем использовать этот пользовательский стиль. Это __init__ принимает DataFrame.

[41]:
MyStyler(df)
[41]:

My Table

A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Наш пользовательский шаблон принимает ключевое слово table_title. Мы можем указать значение в методе .render.

[42]:
HTML(MyStyler(df).render(table_title="Extending Example"))
[42]:

Расширение примера

A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Для удобства мы предоставляем метод Styler.from_custom_template, который выполняет ту же функцию, что и пользовательский подкласс.

[43]:
EasyStyler = Styler.from_custom_template("templates", "myhtml.tpl")
EasyStyler(df)
[43]:

Моя таблица

A B C D E
0 1 1.32921 nan -0.31628 -0.99081
1 2 -1.07082 -1.43871 0.564417 0.295722
2 3 -1.6264 0.219565 0.678805 1.88927
3 4 0.961538 0.104011 -0.481165 0.850229
4 5 1.45342 1.05774 0.165562 0.515018
5 6 -1.33694 0.562861 1.39285 -0.063328
6 7 0.121668 1.2076 -0.00204021 1.6278
7 8 0.354493 1.03753 -0.385684 0.519818
8 9 1.68658 -1.32596 1.42898 -2.08935
9 10 -0.12982 0.631523 -0.586538 0.29072

Вот структура шаблона:

[44]:
with open("templates/template_structure.html") as f:
    structure = f.read()

HTML(structure)
[44]:
before_style
style
<style type="text/css">
table_styles
before_cellstyle
cellstyle
</style>
before_table
table
<table ...>
caption
thead
before_head_rows
head_tr (перебор заголовков)
after_head_rows
tbody
before_rows
tr (перебор строк данных)
after_rows
</table>
after_table

См. шаблон в репозитории GitHub для получения более подробной информации.

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.24.2/user_guide/style.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API