よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Pandas Excel】データフレームのテーブルに色をつける方法

今回は、Pandasのデータフレーム・テーブルに色をつけるなど、styleをいじります。



参考リンク

Pandas公式ページのStyleに関するドキュメントを参考にしています。
Styling — pandas 1.2.2 documentation

はてなブログに色付きテーブルを貼る方法
【Pandasデータフレーム】色付きテーブルをはてなブログに貼るテスト - よちよちpython



目次



実行環境




できること


  • Jupyter Notebookでデータフレームをそのまま出力させると自動的にテーブルとして表示されます。
  • styleの様々なメソッドを使うとデータフレームの任意の値や背景色に色付けできます。
  • 条件に当てはまる値や背景の色付け等ができます。
  • to_excel()メソッドで色がついたままExcelに書き出しできます。


特に最後の色付きのままExcelファイルに出力できるのは重宝するかもしれない。
上記について実行方法を確認したいと思います。



1. DataFrameの用意


まずはじめに、実験用に使う適当なデータフレームを生成します。

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[3, 3] = np.nan
df.iloc[0, 2] = np.nan

公式ページのまんまです。
Aカラムを生成し、B~Eカラムをランダム生成してAに横に結合したデータフレームを作っています。
最後にnp.nanで欠損値を作って2ヶ所置き換えてあります。



2. データフレームのテーブル表示


# 表示
df
A B C D E
0 1.0 1.329212 NaN -0.316280 -0.990810
1 2.0 -1.070816 -1.438713 0.564417 0.295722
2 3.0 -1.626404 0.219565 0.678805 1.889273
3 4.0 0.961538 0.104011 NaN 0.850229
4 5.0 1.453425 1.057737 0.165562 0.515018
5 6.0 -1.336936 0.562861 1.392855 -0.063328
6 7.0 0.121668 1.207603 -0.002040 1.627796
7 8.0 0.354493 1.037528 -0.385684 0.519818
8 9.0 1.686583 -1.325963 1.428984 -2.089354
9 10.0 -0.129820 0.631523 -0.586538 0.290720

次のようにprint(df)とやってしまうとテーブルで表示されません。

print(df)
      A         B         C         D         E
0   1.0  1.329212       NaN -0.316280 -0.990810
1   2.0 -1.070816 -1.438713  0.564417  0.295722
2   3.0 -1.626404  0.219565  0.678805  1.889273
3   4.0  0.961538  0.104011       NaN  0.850229
4   5.0  1.453425  1.057737  0.165562  0.515018
5   6.0 -1.336936  0.562861  1.392855 -0.063328
6   7.0  0.121668  1.207603 -0.002040  1.627796
7   8.0  0.354493  1.037528 -0.385684  0.519818
8   9.0  1.686583 -1.325963  1.428984 -2.089354
9  10.0 -0.129820  0.631523 -0.586538  0.290720



3. データフレームテーブルのhtmlを出力


テーブルのhtmlの中身を表示させてみます。

df.style.render()
'<style  type="text/css" >\n</style><table id="T_8994f_" ><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>\n                <tr>\n                        <th id="T_8994f_level0_row0" class="row_heading level0 row0" >0</th>\n                        <td id="T_8994f_row0_col0" class="data row0 col0" >1.000000</td>\n                        <td id="T_8994f_row0_col1" class="data row0 col1" >1.329212</td>\n                        <td id="T_8994f_row0_col2" class="data row0 col2" >nan</td>\n                        <td id="T_8994f_row0_col3" class="data row0 col3" >-0.316280</td>\n                        <td id="T_8994f_row0_col4" class="data row0 col4" >-0.990810</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row1" class="row_heading level0 row1" >1</th>\n                        <td id="T_8994f_row1_col0" class="data row1 col0" >2.000000</td>\n                        <td id="T_8994f_row1_col1" class="data row1 col1" >-1.070816</td>\n                        <td id="T_8994f_row1_col2" class="data row1 col2" >-1.438713</td>\n                        <td id="T_8994f_row1_col3" class="data row1 col3" >0.564417</td>\n                        <td id="T_8994f_row1_col4" class="data row1 col4" >0.295722</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row2" class="row_heading level0 row2" >2</th>\n                        <td id="T_8994f_row2_col0" class="data row2 col0" >3.000000</td>\n                        <td id="T_8994f_row2_col1" class="data row2 col1" >-1.626404</td>\n                        <td id="T_8994f_row2_col2" class="data row2 col2" >0.219565</td>\n                        <td id="T_8994f_row2_col3" class="data row2 col3" >0.678805</td>\n                        <td id="T_8994f_row2_col4" class="data row2 col4" >1.889273</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row3" class="row_heading level0 row3" >3</th>\n                        <td id="T_8994f_row3_col0" class="data row3 col0" >4.000000</td>\n                        <td id="T_8994f_row3_col1" class="data row3 col1" >0.961538</td>\n                        <td id="T_8994f_row3_col2" class="data row3 col2" >0.104011</td>\n                        <td id="T_8994f_row3_col3" class="data row3 col3" >nan</td>\n                        <td id="T_8994f_row3_col4" class="data row3 col4" >0.850229</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row4" class="row_heading level0 row4" >4</th>\n                        <td id="T_8994f_row4_col0" class="data row4 col0" >5.000000</td>\n                        <td id="T_8994f_row4_col1" class="data row4 col1" >1.453425</td>\n                        <td id="T_8994f_row4_col2" class="data row4 col2" >1.057737</td>\n                        <td id="T_8994f_row4_col3" class="data row4 col3" >0.165562</td>\n                        <td id="T_8994f_row4_col4" class="data row4 col4" >0.515018</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row5" class="row_heading level0 row5" >5</th>\n                        <td id="T_8994f_row5_col0" class="data row5 col0" >6.000000</td>\n                        <td id="T_8994f_row5_col1" class="data row5 col1" >-1.336936</td>\n                        <td id="T_8994f_row5_col2" class="data row5 col2" >0.562861</td>\n                        <td id="T_8994f_row5_col3" class="data row5 col3" >1.392855</td>\n                        <td id="T_8994f_row5_col4" class="data row5 col4" >-0.063328</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row6" class="row_heading level0 row6" >6</th>\n                        <td id="T_8994f_row6_col0" class="data row6 col0" >7.000000</td>\n                        <td id="T_8994f_row6_col1" class="data row6 col1" >0.121668</td>\n                        <td id="T_8994f_row6_col2" class="data row6 col2" >1.207603</td>\n                        <td id="T_8994f_row6_col3" class="data row6 col3" >-0.002040</td>\n                        <td id="T_8994f_row6_col4" class="data row6 col4" >1.627796</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row7" class="row_heading level0 row7" >7</th>\n                        <td id="T_8994f_row7_col0" class="data row7 col0" >8.000000</td>\n                        <td id="T_8994f_row7_col1" class="data row7 col1" >0.354493</td>\n                        <td id="T_8994f_row7_col2" class="data row7 col2" >1.037528</td>\n                        <td id="T_8994f_row7_col3" class="data row7 col3" >-0.385684</td>\n                        <td id="T_8994f_row7_col4" class="data row7 col4" >0.519818</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row8" class="row_heading level0 row8" >8</th>\n                        <td id="T_8994f_row8_col0" class="data row8 col0" >9.000000</td>\n                        <td id="T_8994f_row8_col1" class="data row8 col1" >1.686583</td>\n                        <td id="T_8994f_row8_col2" class="data row8 col2" >-1.325963</td>\n                        <td id="T_8994f_row8_col3" class="data row8 col3" >1.428984</td>\n                        <td id="T_8994f_row8_col4" class="data row8 col4" >-2.089354</td>\n            </tr>\n            <tr>\n                        <th id="T_8994f_level0_row9" class="row_heading level0 row9" >9</th>\n                        <td id="T_8994f_row9_col0" class="data row9 col0" >10.000000</td>\n                        <td id="T_8994f_row9_col1" class="data row9 col1" >-0.129820</td>\n                        <td id="T_8994f_row9_col2" class="data row9 col2" >0.631523</td>\n                        <td id="T_8994f_row9_col3" class="data row9 col3" >-0.586538</td>\n                        <td id="T_8994f_row9_col4" class="data row9 col4" >0.290720</td>\n            </tr>\n    </tbody></table>'

styletableのタグが並んでおります。
データフレームのオブジェクトにstyle.render()をつけるとこのようにhtmlが出力されます。
先頭にシングルクオートがついていますのでstring型です。

# 型の確認
type(df.style.render())
str



4. 欠損値nanの背景色に色付け


4.1 style.highlight_null()を使う

生成したデータフレームには二ヶ所の欠損値が入っていましたが、style.highlight_null() を使うとその部分に自動的に赤で背景に色付けしてくれます。

# 欠損値に色付け
df.style.highlight_null()

f:id:chayarokurokuro:20210225173620j:plain


4.2 highlight_null()のhtmlを確認


先頭から10個だけリストで取り出してみます。

df.style.highlight_null().render().split('\n')[:10]
['<style  type="text/css" >',
 '#T_8fd3c_row0_col2,#T_8fd3c_row3_col3{',
 '            background-color:  red;',
 '        }</style><table id="T_8fd3c_" ><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_8fd3c_level0_row0" class="row_heading level0 row0" >0</th>',
 '                        <td id="T_8fd3c_row0_col0" class="data row0 col0" >1.000000</td>',
 '                        <td id="T_8fd3c_row0_col1" class="data row0 col1" >1.329212</td>',
 '                        <td id="T_8fd3c_row0_col2" class="data row0 col2" >nan</td>',
 '                        <td id="T_8fd3c_row0_col3" class="data row0 col3" >-0.316280</td>']

background-color: redと書いた部分があることが分かります。





5. style.applymap(関数) データフレームの全部の値に対して関数を適用する


上記4のhighlight_null()ではデータフレームの全ての値から欠損値を探して背景色を変えました。
欠損値ではなく、別の条件に当てはまる値に対しても処理できます。

まずCSSを返す自作関数を作って、df.style.applymap(関数)の引数に渡すことで、そのデータフレームの全ての値に対して関数処理を適用します。

5.1 条件にはまる文字の色を変える関数を作る

マイナスの値なら赤色、そうでなければ黒色の文字にするcssのプロパティを返す関数を用意します。

# マイナスの値を赤くする
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

引数valの値に対して、0未満なら赤、それ以外は黒の色にするCSSの文字列を返しています。



5.2 applymap(関数)で色を変更



上で定義した「マイナスの値を赤く変える関数」をdf.style.applymap(関数)に入れてやれば、データフレームの全ての値に対して関数処理が行われます。

df.style.applymap(color_negative_red)

f:id:chayarokurokuro:20210225173723j:plain



5.3 文字の太さを変えるCSS

CSSで文字の色を変えられるなら、太さも変えられるだろう。試してみます。(Pandasの公式ドキュメントには載ってません。)

def font_weight_bold(val):
    font = "bold" if val<0 else ""
    return "font-weight:%s" % font

df.style.applymap(font_weight_bold)
A B C D E
0 1.000000 1.329212 nan -0.316280 -0.990810
1 2.000000 -1.070816 -1.438713 0.564417 0.295722
2 3.000000 -1.626404 0.219565 0.678805 1.889273
3 4.000000 0.961538 0.104011 nan 0.850229
4 5.000000 1.453425 1.057737 0.165562 0.515018
5 6.000000 -1.336936 0.562861 1.392855 -0.063328
6 7.000000 0.121668 1.207603 -0.002040 1.627796
7 8.000000 0.354493 1.037528 -0.385684 0.519818
8 9.000000 1.686583 -1.325963 1.428984 -2.089354
9 10.000000 -0.129820 0.631523 -0.586538 0.290720

※注)上のテーブルでは太字に変えた部分が変わっておりませんが、ブログ表示の問題です。実際は変わっております。



htmlやCSSに明るい方なら他にも自在に出来そうです。次は背景色を変えます。





6. 背景色を変える


今度は背景色を変えてみます。
やってることはこれまでと同じです。

6.1 背景色を変える関数を用意し出力

先ほどの関数の戻り値はcolor: %sにしていたので文字の色が変わりましたが、background-color : %sに変更すれば背景色が変えられます。

1より大きい値の背景色を薄緑に変えてみます。

def color_background_lightgreen(val):
    color = 'lightgreen' if val > 1 else '' #1より大なら薄緑、その他は白
    return 'background-color: %s' % color

#表示
df.style.applymap(color_background_lightgreen)

f:id:chayarokurokuro:20210225173824j:plain



7. subset=[列のリスト]で列を絞る

"A"列には関数を適用したくないなぁ。除外したい。
そんな時は、applymap()の引数にsubsetを与えて、関数の及ぶ範囲を限定できます。

def color_background_lightgreen(val):
    color = 'lightgreen' if val > 1 else '' #1より大なら薄緑、その他は白
    return 'background-color: %s' % color

#表示
df.style.applymap(color_background_lightgreen, subset=["B","C","D","E"])

f:id:chayarokurokuro:20210225173904j:plain





8. style.apply(関数) 行/列単位で関数を適用する


先ほどまではdf.style.applymap()を使ってましたが、今度のはapply()です。
違いは、applymap()は値ごとを調べますが、今度のapplyは行か列単位で調べます。

8.1 Seriesデータの最大値を探し、背景色を変える関数を用意

関数の引数で受けたSeriesデータからそれぞれの最大値を探し、リスト化して、背景色を黄色に変える関数を作ってみます。

def highlight_max_x(s):
    '''
    highlight the maximum in a Series yellow.
    '''
    is_max = s == s.max()
    
    # 中身の確認
    print("【is_max】\n",is_max)
    print("-"*20)
    print("【s】\n",s)
    print("-"*20)
    print("【s.max()】\n",s.max())
    print("="*20)
    
    return ['background-color: yellow' if v else '' for v in is_max]

8.1.1 a=b=="c"という書き方について

関数内にis_max = s == s.max()と書いた部分があります。Pandas公式ドキュメントのままです。
あまり見かけない書き方なので内容確認の為のprint()を付け加えました。どういう意味でしょうか。



参考


is_max = s == s.max()は、

比較演算で s == s.max() をして返ったTrueかFalseの値をis_maxに代入する

というような意味になる。

sは関数の引数で、データフレームの行か列から取り出したSeriesデータです。

たとえば
A列Seriesデータ

# タイプの確認
print(type(df["A"]))

# 表示
df["A"]
<class 'pandas.core.series.Series'>





0     1.0
1     2.0
2     3.0
3     4.0
4     5.0
5     6.0
6     7.0
7     8.0
8     9.0
9    10.0
Name: A, dtype: float64

A列Seriesデータの最大値

# Seriesデータの最大値
df["A"].max()
10.0

A列Seriesデータとその最大値の比較

df["A"] == df["A"].max()
0    False
1    False
2    False
3    False
4    False
5    False
6    False
7    False
8    False
9     True
Name: A, dtype: bool

これをis_maxに代入する、という意味。

実際に実行すれば分かる!次いきます。

8.2 列単位での最大値の背景色を変える

apply()メソッドの引数にaxis=0か又は何も書かなければデフォルトで列単位の処理が行われます。

# 表示
df.style.apply(highlight_max_x)
【is_max】
 0    False
1    False
2    False
3    False
4    False
5    False
6    False
7    False
8    False
9     True
Name: A, dtype: bool
--------------------
【s】
 0     1.0
1     2.0
2     3.0
3     4.0
4     5.0
5     6.0
6     7.0
7     8.0
8     9.0
9    10.0
Name: A, dtype: float64
--------------------
【s.max()】
 10.0
====================
【is_max】
 0    False
1    False
2    False
3    False
4    False
5    False
6    False
7    False
8     True
9    False
Name: B, dtype: bool
--------------------
【s】
 0    1.329212
1   -1.070816
2   -1.626404
3    0.961538
4    1.453425
5   -1.336936
6    0.121668
7    0.354493
8    1.686583
9   -0.129820
Name: B, dtype: float64
--------------------
【s.max()】
 1.6865828874516027
====================
【is_max】
 0    False
1    False
2    False
3    False
4    False
5    False
6     True
7    False
8    False
9    False
Name: C, dtype: bool
--------------------
【s】
 0         NaN
1   -1.438713
2    0.219565
3    0.104011
4    1.057737
5    0.562861
6    1.207603
7    1.037528
8   -1.325963
9    0.631523
Name: C, dtype: float64
--------------------
【s.max()】
 1.2076025381991033
====================
【is_max】
 0    False
1    False
2    False
3    False
4    False
5    False
6    False
7    False
8     True
9    False
Name: D, dtype: bool
--------------------
【s】
 0   -0.316280
1    0.564417
2    0.678805
3         NaN
4    0.165562
5    1.392855
6   -0.002040
7   -0.385684
8    1.428984
9   -0.586538
Name: D, dtype: float64
--------------------
【s.max()】
 1.4289837002109638
====================
【is_max】
 0    False
1    False
2     True
3    False
4    False
5    False
6    False
7    False
8    False
9    False
Name: E, dtype: bool
--------------------
【s】
 0   -0.990810
1    0.295722
2    1.889273
3    0.850229
4    0.515018
5   -0.063328
6    1.627796
7    0.519818
8   -2.089354
9    0.290720
Name: E, dtype: float64
--------------------
【s.max()】
 1.8892727314152815
====================


20210225174042


【is_max】で列ごとのSeriesの最大値はTrueになっている。そこの背景色が変わっているのが分かる。

列ごとの処理が行われたのはaxis=0省略形がデフォルトだからです。
行ごとの処理は次。

8.3 行ごとで背景色を変える

apply()メソッドの引数にaxis=1を書けば、行単位で処理されます。

df.style.apply(highlight_max_x, axis=1)
【is_max】
 A    False
B     True
C    False
D    False
E    False
Name: 0, dtype: bool
--------------------
【s】
 A    1.000000
B    1.329212
C         NaN
D   -0.316280
E   -0.990810
Name: 0, dtype: float64
--------------------
【s.max()】
 1.3292121726491863
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 1, dtype: bool
--------------------
【s】
 A    2.000000
B   -1.070816
C   -1.438713
D    0.564417
E    0.295722
Name: 1, dtype: float64
--------------------
【s.max()】
 2.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 2, dtype: bool
--------------------
【s】
 A    3.000000
B   -1.626404
C    0.219565
D    0.678805
E    1.889273
Name: 2, dtype: float64
--------------------
【s.max()】
 3.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 3, dtype: bool
--------------------
【s】
 A    4.000000
B    0.961538
C    0.104011
D         NaN
E    0.850229
Name: 3, dtype: float64
--------------------
【s.max()】
 4.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 4, dtype: bool
--------------------
【s】
 A    5.000000
B    1.453425
C    1.057737
D    0.165562
E    0.515018
Name: 4, dtype: float64
--------------------
【s.max()】
 5.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 5, dtype: bool
--------------------
【s】
 A    6.000000
B   -1.336936
C    0.562861
D    1.392855
E   -0.063328
Name: 5, dtype: float64
--------------------
【s.max()】
 6.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 6, dtype: bool
--------------------
【s】
 A    7.000000
B    0.121668
C    1.207603
D   -0.002040
E    1.627796
Name: 6, dtype: float64
--------------------
【s.max()】
 7.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 7, dtype: bool
--------------------
【s】
 A    8.000000
B    0.354493
C    1.037528
D   -0.385684
E    0.519818
Name: 7, dtype: float64
--------------------
【s.max()】
 8.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 8, dtype: bool
--------------------
【s】
 A    9.000000
B    1.686583
C   -1.325963
D    1.428984
E   -2.089354
Name: 8, dtype: float64
--------------------
【s.max()】
 9.0
====================
【is_max】
 A     True
B    False
C    False
D    False
E    False
Name: 9, dtype: bool
--------------------
【s】
 A    10.000000
B    -0.129820
C     0.631523
D    -0.586538
E     0.290720
Name: 9, dtype: float64
--------------------
【s.max()】
 10.0
====================


20210225174323


それぞれの行における最大値の背景色が変わりました。





9. 複数の色付けを複合する


メソッドをドットで数珠繋ぎにすれば複数のスタイルを同時に組み合わせて表示できます。

# マイナス値を赤く、欠損値の背景色を赤くする
df.style.applymap(color_negative_red).highlight_null()

f:id:chayarokurokuro:20210225174458j:plain





10. テーブル全体にプロパティをセットする


テーブル全体にCSSプロパティをセットできます。

df.style.set_properties(**{'background-color': 'black',
                           'color': 'lawngreen',
                           'border-color': 'white'})

f:id:chayarokurokuro:20210225174526j:plain



10.2 列の背景色を変える


set_properties()の引数にsubsetで行を限定させます。

df.style.set_properties(**{"background-color":"orange"}, subset=["A","C"]).set_properties(**{"background-color":"purple"}, subset=["E"])

f:id:chayarokurokuro:20210225174614j:plain



10.3 行の背景色を変える


set_properties()の引数にsubset=pd.IndexSliceで範囲を限定させました。

【参考リンク】
Jupyter NotebookでpandasのMultiIndexに色を付ける - Qiita

df.style.set_properties(**{"background-color":"lightgreen"}, subset=pd.IndexSlice[0:2]).set_properties(**{"background-color":"yellow"}, subset=pd.IndexSlice[3:])

f:id:chayarokurokuro:20210225174645j:plain





11. 任意の範囲で色を変える

もう少し範囲を限定させたい。
set_properties()の引数subsetだと行か列を全部変えてしまう。
関数を作ってapply(関数)で適用させればできる。

def color(df):
    styles = df.copy()
    styles.iloc[:,:] = ''
    styles.iloc[0:4,0:1] = "background-color:green"
    styles.iloc[7:9,1] = "color:purple"
    styles.iloc[-1,0:-1] = "font-weight:bold"
    styles.iloc[4:7,2:] = "background-color:pink" 
    return styles

df.style.apply(color, axis=None)
    

f:id:chayarokurokuro:20210225174709j:plain



範囲が重なると後で書いた方のスタイルに上書きされます。





12. 値によって棒グラフ背景色


テーブルの値によって棒グラフで背景色をつけることができる。見易いかどうかは疑問。

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

f:id:chayarokurokuro:20210225174733j:plain



背景色の長さは列単位での値の相対的なものになっているようです。
たとえば2行目のA列とB列のグラフの長さはほぼ同じだが、値は2と-1.07で同じではない。



13. その他フォーマット


色付けではありません。値の小数点以下を丸めたり、パーセントで表したりするフォーマット変換用のメソッド。

ざっと流します。

◆ 小数第二位までパーセント表示

# 小数第二位までパーセント表示
df.style.format("{:.2%}")
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% nan% 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%



◆ A列以外で、少数第二位までパーセント表示で、欠損値を置換

# A列以外で、少数第二位までパーセント表示で、欠損値を置換
df.style.format("{:.2%}", na_rep="-",subset=["B","C","D","E"])
A B C D E
0 1.000000 132.92% - -31.63% -99.08%
1 2.000000 -107.08% -143.87% 56.44% 29.57%
2 3.000000 -162.64% 21.96% 67.88% 188.93%
3 4.000000 96.15% 10.40% - 85.02%
4 5.000000 145.34% 105.77% 16.56% 51.50%
5 6.000000 -133.69% 56.29% 139.29% -6.33%
6 7.000000 12.17% 120.76% -0.20% 162.78%
7 8.000000 35.45% 103.75% -38.57% 51.98%
8 9.000000 168.66% -132.60% 142.90% -208.94%
9 10.000000 -12.98% 63.15% -58.65% 29.07%



◆ A列以外、小数2位まで%表示、欠損値置換。A列は整数。A列以外列の最大値背景色を黄色。

# Series単位で最大値背景色を黄色に変える
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]

# 表示
## A列以外、小数2位まで%表示、欠損値置換。A列は整数。A列以外列の最大値背景色を黄色。
df.style.format("{:.2%}", na_rep="-",subset=["B","C","D","E"]).format("{:.0f}",subset=["A"]).apply(highlight_max,subset=["B","C","D","E"])

f:id:chayarokurokuro:20210225174841j:plain



◆ BとD列の2~5行目内のマイナス値を赤色に変える

# BとD列の2~5行目内のマイナス値を赤色に変える
df.style.applymap(color_negative_red,
                  subset=pd.IndexSlice[2:5, ['B', 'D']])

f:id:chayarokurokuro:20210225174912j:plain



◆ インデックスを隠す

df.style.hide_index()
A B C D E
1.000000 1.329212 nan -0.316280 -0.990810
2.000000 -1.070816 -1.438713 0.564417 0.295722
3.000000 -1.626404 0.219565 0.678805 1.889273
4.000000 0.961538 0.104011 nan 0.850229
5.000000 1.453425 1.057737 0.165562 0.515018
6.000000 -1.336936 0.562861 1.392855 -0.063328
7.000000 0.121668 1.207603 -0.002040 1.627796
8.000000 0.354493 1.037528 -0.385684 0.519818
9.000000 1.686583 -1.325963 1.428984 -2.089354
10.000000 -0.129820 0.631523 -0.586538 0.290720



◆ CとD行のカラムを隠す

df.style.hide_columns(['C','D'])
A B E
0 1.000000 1.329212 -0.990810
1 2.000000 -1.070816 0.295722
2 3.000000 -1.626404 1.889273
3 4.000000 0.961538 0.850229
4 5.000000 1.453425 0.515018
5 6.000000 -1.336936 -0.063328
6 7.000000 0.121668 1.627796
7 8.000000 0.354493 0.519818
8 9.000000 1.686583 -2.089354
9 10.000000 -0.129820 0.290720





14. テーブルをファイルに変換出力する


14.1 htmlファイルに出力

df.style.render()でデータフレームテーブルのhtml文字列に変換できます。それをファイルに拡張子htmlで書き込み保存すればOK。



欠損値の背景色を赤くしたテーブルをhtmlファイルに出力する例。

with open("highlight_null.html", "w") as f:
    f.write(df.style.highlight_null().render())

実行するとカレントディレクトリにhighlight_null.htmlという名前のファイルが出来ている。ブラウザかHTMLビューワーで開けばテーブルが表示される。(画像省略)



14.2 テーブルをExcelファイルに変換出力

背景色などを変えたデータフレームのテーブルを通常通りdf.to_excel(保存名)メソッドでExcelファイルにすれば、色付きのままのExcelシートができる。

set_properties()で全体を黒背景色の緑文字にしたものをExcelに変換出力させてみる。

df_EX = df.style.set_properties(**{'background-color': 'black',
                           'color': 'lawngreen',
                           'border-color': 'white'})

# Excelに変換出力
df_EX.to_excel("black_green.xlsx")

実行するとカレントディレクトリにblack_green.xlsxなるExcelファイルができる。

Excelで開くと、

f:id:chayarokurokuro:20210225175012j:plain



Googleスプレッドシートで開くと、

f:id:chayarokurokuro:20210225175030j:plain



以上です。