目次
概要
Excel の条件付き書式による色分けを pandas で実現する方法をまとめました。
スタイルを適用する範囲を設定する
特定の列
In [1]:
import numpy as np
import pandas as pd
from IPython.display import display, HTML
df = pd.DataFrame(
[[0, 1, 2], [3, 4, 5], [6, 7, 8]], index=["a", "b", "c"], columns=["A", "B", "C"],
)
css = {"background-color": "green"}
# A 列
display(df.style.set_properties(subset="A", **css))
# A 列と C 列
display(df.style.set_properties(subset=["A", "C"], **css))
# A ~ C 列
display(df.style.set_properties(subset=pd.IndexSlice[:, "A":"B"], **css))
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
特定の行
In [2]:
# a 行
display(df.style.set_properties(subset=pd.IndexSlice["a", :], **css))
# a 行と c 行
display(df.style.set_properties(subset=pd.IndexSlice[["a", "c"], :], **css))
# a 行 ~ c 行
display(df.style.set_properties(subset=pd.IndexSlice["a":"b", :], **css))
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
複数のスタイルを適用する場合
Styler オブジェクトの関数は返り値として Styler オブジェクトを返すので、複数のスタイルを適用する場合は連続して呼び出します。
In [3]:
style = df.style.set_properties(**{"background-color": "gray"})
style = style.set_properties(**{"color": "white"})
style
A | B | C | |
---|---|---|---|
a | 0 | 1 | 2 |
b | 3 | 4 | 5 |
c | 6 | 7 | 8 |
セルの強調表示ルール
値に応じて強調する
内容 | 条件の例 |
---|---|
次の値の間 | lambda x: 3 <= x <= 5 |
次の値の間以外 | lambda x: not 3 <= x <= 5 |
次の値に等しい | lambda x: x == 3 |
次の値に等しくない | lambda x: x != 3 |
次の値より大きい | lambda x: x > 3 |
次の値より小さい | lambda x: x < 3 |
次の値以上 | lambda x: x >= 3 |
次の値以下 | lambda x: x <= 3 |
In [4]:
df = pd.DataFrame(
[
[11, 0, 15, 4, 5],
[2, 12, 10, 8, 16],
[17, 24, 14, 13, 23],
[19, 6, 18, 2, 7],
[3, 9, 20, 21, 1],
],
index=["a", "b", "c", "d", "e"],
columns=["A", "B", "C", "D", "E"],
)
css = "background-color: green"
df.style.where(lambda x: x == 2, css, "")
A | B | C | D | E | |
---|---|---|---|---|---|
a | 11 | 0 | 15 | 4 | 5 |
b | 2 | 12 | 10 | 8 | 16 |
c | 17 | 24 | 14 | 13 | 23 |
d | 19 | 6 | 18 | 2 | 7 |
e | 3 | 9 | 20 | 21 | 1 |
重複する値を強調する
In [5]:
df = pd.DataFrame(
[[1, 2, 1], [1, 3, 2], [2, 3, 3],], index=["a", "b", "c"], columns=["A", "B", "C"]
)
css = "background-color: green"
def highlight_duplicated(s):
return [css if x else "" for x in s.duplicated(keep=False)]
# 列ごと見たとき、重複している値を強調する
display(df.style.apply(highlight_duplicated, axis=0))
# 行ごと見たとき、重複している値を強調する
display(df.style.apply(highlight_duplicated, axis=1))
A | B | C | |
---|---|---|---|
a | 1 | 2 | 1 |
b | 1 | 3 | 2 |
c | 2 | 3 | 3 |
A | B | C | |
---|---|---|---|
a | 1 | 2 | 1 |
b | 1 | 3 | 2 |
c | 2 | 3 | 3 |
一意な値を強調する
In [6]:
df = pd.DataFrame(
[[1, 2, 1], [1, 3, 2], [2, 3, 3],], index=["a", "b", "c"], columns=["A", "B", "C"]
)
css = "background-color: green"
def highlight_unique(s):
return ["" if x else css for x in s.duplicated(keep=False)]
# 列ごと見たとき、一意な値を強調する
display(df.style.apply(highlight_unique, axis=0))
# 行ごと見たとき、重一意な値を強調する
display(df.style.apply(highlight_unique, axis=1))
A | B | C | |
---|---|---|---|
a | 1 | 2 | 1 |
b | 1 | 3 | 2 |
c | 2 | 3 | 3 |
A | B | C | |
---|---|---|---|
a | 1 | 2 | 1 |
b | 1 | 3 | 2 |
c | 2 | 3 | 3 |
値の順位に応じて強調する
In [7]:
df = pd.DataFrame(
[
[11, 0, 15, 4, 5],
[2, 12, 10, 8, 16],
[17, 24, 14, 13, 23],
[19, 6, 18, 2, 7],
[3, 9, 20, 21, 1],
],
index=["a", "b", "c", "d", "e"],
columns=["A", "B", "C", "D", "E"],
)
css = "background-color: green"
def highlight_topk(s, n):
return [css if x <= n else "" for x in s.rank(ascending=False)]
def highlight_bottomk(s, n):
return [css if x <= n else "" for x in s.rank(ascending=True)]
def highlight_topk_percentile(s, q=0.9):
q = s.quantile(q)
return [css if x >= q else "" for x in s]
def highlight_bottomk_percentile(s, q=0.1):
q = s.quantile(q)
return [css if x <= q else "" for x in s]
# 上位2個
display(df.style.apply(highlight_topk, n=2))
# 下位2個
display(df.style.apply(highlight_bottomk, n=2))
# 上位10%
display(df.style.apply(highlight_topk_percentile, axis=0))
# 下位10%
display(df.style.apply(highlight_bottomk_percentile, axis=0))
A | B | C | D | E | |
---|---|---|---|---|---|
a | 11 | 0 | 15 | 4 | 5 |
b | 2 | 12 | 10 | 8 | 16 |
c | 17 | 24 | 14 | 13 | 23 |
d | 19 | 6 | 18 | 2 | 7 |
e | 3 | 9 | 20 | 21 | 1 |
A | B | C | D | E | |
---|---|---|---|---|---|
a | 11 | 0 | 15 | 4 | 5 |
b | 2 | 12 | 10 | 8 | 16 |
c | 17 | 24 | 14 | 13 | 23 |
d | 19 | 6 | 18 | 2 | 7 |
e | 3 | 9 | 20 | 21 | 1 |
A | B | C | D | E | |
---|---|---|---|---|---|
a | 11 | 0 | 15 | 4 | 5 |
b | 2 | 12 | 10 | 8 | 16 |
c | 17 | 24 | 14 | 13 | 23 |
d | 19 | 6 | 18 | 2 | 7 |
e | 3 | 9 | 20 | 21 | 1 |
A | B | C | D | E | |
---|---|---|---|---|---|
a | 11 | 0 | 15 | 4 | 5 |
b | 2 | 12 | 10 | 8 | 16 |
c | 17 | 24 | 14 | 13 | 23 |
d | 19 | 6 | 18 | 2 | 7 |
e | 3 | 9 | 20 | 21 | 1 |
データバー
In [8]:
df = pd.DataFrame(
{
"A": [132, -107, -162, 96, 145, -133, 12, 35, 168, -12],
"B": [0, -143, 21, 10, 105, 56, 120, 103, -132, 63],
"C": [-31, 56, 67, 0, 16, 139, 0, -38, 142, -58],
}
)
df.style.bar(color=["red", "green"], align="mid")
A | B | C | |
---|---|---|---|
0 | 132 | 0 | -31 |
1 | -107 | -143 | 56 |
2 | -162 | 21 | 67 |
3 | 96 | 10 | 0 |
4 | 145 | 105 | 16 |
5 | -133 | 56 | 139 |
6 | 12 | 120 | 0 |
7 | 35 | 103 | -38 |
8 | 168 | -132 | 142 |
9 | -12 | 63 | -58 |
カラースケール
In [9]:
df.style.background_gradient()
A | B | C | |
---|---|---|---|
0 | 132 | 0 | -31 |
1 | -107 | -143 | 56 |
2 | -162 | 21 | 67 |
3 | 96 | 10 | 0 |
4 | 145 | 105 | 16 |
5 | -133 | 56 | 139 |
6 | 12 | 120 | 0 |
7 | 35 | 103 | -38 |
8 | 168 | -132 | 142 |
9 | -12 | 63 | -58 |
書式
小数点以下の桁数
In [10]:
df = pd.DataFrame(
[
[0.38, 0.51, 0.19, 0.79, 0.38],
[0.0, 0.75, 0.85, 0.33, 0.87],
[0.66, 0.76, 0.0, 0.61, 0.5],
[0.94, 0.16, 0.28, 0.07, 0.82],
[0.73, 0.04, 0.14, 0.24, None],
],
index=["a", "b", "c", "d", "e"],
columns=["A", "B", "C", "D", "E"],
)
df.style.set_precision(2)
A | B | C | D | E | |
---|---|---|---|---|---|
a | 0.38 | 0.51 | 0.19 | 0.79 | 0.38 |
b | 0.00 | 0.75 | 0.85 | 0.33 | 0.87 |
c | 0.66 | 0.76 | 0.00 | 0.61 | 0.50 |
d | 0.94 | 0.16 | 0.28 | 0.07 | 0.82 |
e | 0.73 | 0.04 | 0.14 | 0.24 | nan |
パーセント表示
In [11]:
df.style.format("{:.0%}")
A | B | C | D | E | |
---|---|---|---|---|---|
a | 38% | 51% | 19% | 79% | 38% |
b | 0% | 75% | 85% | 33% | 87% |
c | 66% | 76% | 0% | 61% | 50% |
d | 94% | 16% | 28% | 7% | 82% |
e | 73% | 4% | 14% | 24% | 82% |
欠損値の表記
In [12]:
df.style.format("{:.0%}", na_rep="-")
A | B | C | D | E | |
---|---|---|---|---|---|
a | 38% | 51% | 19% | 79% | 38% |
b | 0% | 75% | 85% | 33% | 87% |
c | 66% | 76% | 0% | 61% | 50% |
d | 94% | 16% | 28% | 7% | 82% |
e | 73% | 4% | 14% | 24% | – |
列の幅
In [13]:
df = pd.DataFrame(
[
[0.38, 0.51, 0.19, 0.79, 0.38],
[0.0, 0.75, 0.85, 0.33, 0.87],
[0.66, 0.76, 0.0, 0.61, 0.5],
[0.94, 0.16, 0.28, 0.07, 0.82],
[0.73, 0.04, 0.14, 0.24, None],
],
index=["a", "b", "c", "d", "e"],
columns=["A", "B", "C", "D", "E"],
)
# 各列の幅
widths = {"A": "60px", "B": "70px", "C": "80px", "D": "90px", "E": "100px"}
style = df.style.set_precision(2)
for col, width in widths.items():
style = style.set_properties(subset=col, **{"width": width})
style
A | B | C | D | E | |
---|---|---|---|---|---|
a | 0.38 | 0.51 | 0.19 | 0.79 | 0.38 |
b | 0.00 | 0.75 | 0.85 | 0.33 | 0.87 |
c | 0.66 | 0.76 | 0.00 | 0.61 | 0.50 |
d | 0.94 | 0.16 | 0.28 | 0.07 | 0.82 |
e | 0.73 | 0.04 | 0.14 | 0.24 | nan |
CSS をカスタマイズする
- set_properties(): 各セルに一律に適用する CSS
- set_table_styles(): 各セルに個別に適用する CSS
In [14]:
df = pd.read_csv("https://git.io/JJlHw")
table_css = [
# ヘッダの CSS
{
"selector": "th",
"props": [
("background-color", "#00557F"),
("color", "white"),
("border", "1px solid #006699"),
],
},
# ボディの CSS
{
"selector": "td",
"props": [
("background-color", "#E1EEF4"),
("color", "#00557F"),
("border", "1px solid #006699"),
],
},
]
style = df.head().style
style = style.set_precision(2).set_table_styles(table_css)
style
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 |
1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 |
2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 |
3 | 23.68 | 3.31 | Male | No | Sun | Dinner | 2 |
4 | 24.59 | 3.61 | Female | No | Sun | Dinner | 4 |
コメント