- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
plottable是一个Python库,用于在matplotlib中绘制精美定制的图形表格。plottable的官方仓库地址为: plottable 。本文主要参考其官方文档,plottable的官方文档地址为: plottable-doc 。plottable安装命令如下:
pip install plottable 。
本文所有代码见: Python-Study-Notes 。
# jupyter notebook环境去除warning
import warnings
warnings.filterwarnings("ignore")
import plottable
# 打印plottable版本
print('plottable version:', plottable.__version__)
# 打印matplotlib版本
import matplotlib as plt
print('matplotlib version:', plt.__version__)
plottable version: 0.1.5
matplotlib version: 3.5.3
下面的代码展示了一个简单的图形表格绘制示例,plottable提供了Table类以创建和展示图形表格.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from plottable import Table
# 生成一个包含随机数据的表格
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# 基于pandas表格数据创建和展示图形表格
tab = Table(d)
# 保存图片
plt.savefig("table.jpg", dpi=300,bbox_inches='tight')
plt.show()
对于plottable的Table类,其构造参数介绍如下:
在这些参数之中,控制表格绘图效果的参数有以下几类:
在这些参数中,最重要的参数是column_definitions,因为column_definitions可以控制几乎所有的绘图效果。接下来本文主要对column_definitions的使用进行具体介绍.
plottable提供了ColumnDefinition类(别名ColDef)来自定义图形表格的单个列的样式。ColumnDefinition类的构造参数如下:
通过ColumnDefinition类来设置Table类的column_definitions参数,可以实现不同表格列样式的效果。如果是同时多个列的绘图效果,则需要使用[ColumnDefinition,ColumnDefinition]列表的形式。一些使用示例如下 。
设置列标题和列宽 。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from plottable import ColumnDefinition, ColDef, Table
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# name表示设置哪个列的样式
tab = Table(d, column_definitions=[ColumnDefinition(name="A", title="Title A"),
ColumnDefinition(name="D", width=2)])
plt.show()
设置列的文字属性和文本格式 。
from plottable.formatters import decimal_to_percent
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# index列的文字居中,加粗
# 列A的文本数值改为百分制
tab = Table(d, column_definitions=[ColumnDefinition(name="index", textprops={"ha": "center", "weight": "bold"}),
ColumnDefinition(name="A", formatter=decimal_to_percent)])
plt.show()
设置列单元格背景色和字体颜色 。
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# cmap设置单元格背景色
tab = Table(d, column_definitions=[ColumnDefinition(name="A", cmap=matplotlib.cm.tab20, text_cmap=matplotlib.cm.Reds),
ColumnDefinition(name="B", cmap=matplotlib.cm.tab20b),
ColumnDefinition(name="C", text_cmap=matplotlib.cm.tab20c)])
plt.show()
设置列的分组名 。
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# 将列B和列C视为同一组,该组命名为group_name
tab = Table(d, column_definitions=[ColumnDefinition(name="B", group="group_name"),
ColumnDefinition(name="C", group="group_name")])
plt.show()
设置列边框 。
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# 将列B和列C视为同一组,该组命名为group_name
tab = Table(d, column_definitions=[ColumnDefinition(name="A", border="l"),
ColumnDefinition(name="C", border="both")])
plt.show()
调用函数的使用 。
ColumnDefinition类的plot_fn和plot_kw参数提供了自定义函数实现表格效果绘制的功能。其中plot_fn表示待调用的函数,plot_kw表示待调用函数的输入参数。此外在plotable.plots预置了一些效果函数,我们可以参考这些效果函数定义自己的绘图函数。预置效果函数如下:
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.colors import LinearSegmentedColormap
from plottable import ColumnDefinition, Table
# 调用预置绘图函数
from plottable.plots import image,monochrome_image,circled_image,bar,percentile_bars,percentile_stars,progress_donut
cmap = matplotlib.cm.tab20
# 柱状图绘制
fig, ax = plt.subplots(figsize=(1, 1))
# 0.7表示数值,lw边框线宽
b = bar(ax, 0.7, plot_bg_bar=True, cmap=cmap, annotate=True, lw=2, height=0.35)
plt.show()
# 星星百分比图
fig, ax = plt.subplots(figsize=(2, 1))
stars = percentile_stars(ax, 85, background_color="#f0f0f0")
# 圆环图
fig, ax = plt.subplots(figsize=(1, 1))
donut = progress_donut(ax, 73, textprops={"fontsize": 14})
plt.show()
对于待调用的函数,可以通过help函数查看这些函数的参数含义.
help(progress_donut)
Help on function progress_donut in module plottable.plots:
progress_donut(ax: matplotlib.axes._axes.Axes, val: float, radius: float = 0.45, color: str = None, background_color: str = None, width: float = 0.05, is_pct: bool = False, textprops: Dict[str, Any] = {}, formatter: Callable = None, **kwargs) -> List[matplotlib.patches.Wedge]
Plots a Progress Donut on the axes.
Args:
ax (matplotlib.axes.Axes): Axes
val (float): value
radius (float, optional):
radius of the progress donut. Defaults to 0.45.
color (str, optional):
color of the progress donut. Defaults to None.
background_color (str, optional):
background_color of the progress donut where the value is not reached. Defaults to None.
width (float, optional):
width of the donut wedge. Defaults to 0.05.
is_pct (bool, optional):
whether the value is given not as a decimal, but as a value between 0 and 100.
Defaults to False.
textprops (Dict[str, Any], optional):
textprops passed to ax.text. Defaults to {}.
formatter (Callable, optional):
a string formatter.
Can either be a string format, ie "{:2f}" for 2 decimal places.
Or a Callable that is applied to the value. Defaults to None.
Returns:
List[matplotlib.patches.Wedge]
通过plot_fn和plot_kw参数设置自定义绘图函数和函数输入参数,可以展示不同的绘图效果,如下所示:
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# plot_fn和plot_kw
tab = Table(d, textprops={"ha": "center"},
column_definitions=[ColumnDefinition(name="B", plot_fn=bar,plot_kw={'plot_bg_bar':True,'cmap':cmap,
'annotate':True, 'height':0.5}),
ColumnDefinition(name="D", plot_fn=progress_donut,plot_kw={'is_pct':True,})])
plt.show()
自定义文字格式 。
plottable提供了以下三个自定义函数来表示不同的文字格式:
我们可以通过ColumnDefinition的formatter来设置文字格式,如下所示:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from plottable import ColumnDefinition, Table
from plottable.formatters import decimal_to_percent,tickcross,signed_integer
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
tab = Table(d, column_definitions=[ColumnDefinition(name="A", formatter=decimal_to_percent),
ColumnDefinition(name="C", formatter=tickcross),
ColumnDefinition(name="D", formatter=signed_integer)])
plt.show()
此外,也可以自定义函数来设置文本格式,如下所示:
def setformat(x):
# 使用format格式函数
return "{:.2e}".format(x)
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
tab = Table(d, textprops={"ha": "center"},column_definitions=[ColumnDefinition(name="B", formatter=setformat),
ColumnDefinition(name="D", formatter=lambda x: round(x, 2))])
plt.show()
访问行列单元格 。
plottable提供了直接访问Table实例的某一行、某一列的方法,如下所示:
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# 实例化Table对象
tab = Table(d)
# 根据列名,提取整列
tab.columns['A']
Column(cells=[TextCell(xy=(1, 0), content=0.0, row_idx=0, col_idx=1), TextCell(xy=(1, 1), content=0.09, row_idx=1, col_idx=1), TextCell(xy=(1, 2), content=0.95, row_idx=2, col_idx=1), TextCell(xy=(1, 3), content=0.08, row_idx=3, col_idx=1), TextCell(xy=(1, 4), content=0.92, row_idx=4, col_idx=1)], index=1)
# 读取某列第1行的内容
tab.columns['B'].cells[1].content
0.04
# 根据行索引,提取整行
tab.rows[1]
Row(cells=[TextCell(xy=(0, 1), content=1, row_idx=1, col_idx=0), TextCell(xy=(1, 1), content=0.09, row_idx=1, col_idx=1), TextCell(xy=(2, 1), content=0.04, row_idx=1, col_idx=2), TextCell(xy=(3, 1), content=0.42, row_idx=1, col_idx=3), TextCell(xy=(4, 1), content=0.64, row_idx=1, col_idx=4), TextCell(xy=(5, 1), content=0.26, row_idx=1, col_idx=5)], index=1)
# 提取表头列名
tab.col_label_row
Row(cells=[TextCell(xy=(0, -1), content=index, row_idx=-1, col_idx=0), TextCell(xy=(1, -1), content=A, row_idx=-1, col_idx=1), TextCell(xy=(2, -1), content=B, row_idx=-1, col_idx=2), TextCell(xy=(3, -1), content=C, row_idx=-1, col_idx=3), TextCell(xy=(4, -1), content=D, row_idx=-1, col_idx=4), TextCell(xy=(5, -1), content=E, row_idx=-1, col_idx=5)], index=-1)
设置单元格样式 。
在上面的例子可以看到plottable直接访问表格行列对象,因此我们可以通过设置这些对象的绘图属性来直接更改其绘图效果或文字效果,所支持更改的属性如下:
示例代码如下:
from plottable.cmap import normed_cmap
import matplotlib.cm
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 5))
# 实例化Table对象
tab = Table(d)
# 设置行号为1的行的背景颜色
tab.rows[1].set_facecolor("grey")
# 设置B列的字体颜色
tab.columns['B'].set_fontcolor("red")
Column(cells=[TextCell(xy=(2, 0), content=0.38, row_idx=0, col_idx=2), TextCell(xy=(2, 1), content=0.69, row_idx=1, col_idx=2), TextCell(xy=(2, 2), content=0.15, row_idx=2, col_idx=2), TextCell(xy=(2, 3), content=0.74, row_idx=3, col_idx=2), TextCell(xy=(2, 4), content=0.41, row_idx=4, col_idx=2)], index=2)
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from plottable import Table
d = pd.DataFrame(np.random.random((5, 5)), columns=["A", "B", "C", "D", "E"]).round(2)
fig, ax = plt.subplots(figsize=(6, 3))
# row_dividers显示行的分割线
# odd_row_color奇数行颜色
# even_row_color偶数行颜色
tab = Table(d, row_dividers=False, odd_row_color="#f0f0f0", even_row_color="#e0f6ff")
plt.show()
fig.savefig("table.jpg",dpi=300,bbox_inches='tight')
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.colors import LinearSegmentedColormap
from plottable import ColumnDefinition, Table
from plottable.formatters import decimal_to_percent
from plottable.plots import bar, percentile_bars, percentile_stars, progress_donut
# 自定义颜色
cmap = LinearSegmentedColormap.from_list(
name="BuYl", colors=["#01a6ff", "#eafedb", "#fffdbb", "#ffc834"], N=256
)
fig, ax = plt.subplots(figsize=(6, 6))
d = pd.DataFrame(np.random.random((5, 4)), columns=["A", "B", "C", "D"]).round(2)
tab = Table(
d,
cell_kw={
"linewidth": 0,
"edgecolor": "k",
},
textprops={"ha": "center"},
column_definitions=[
ColumnDefinition("index", textprops={"ha": "left"}),
ColumnDefinition("A", plot_fn=percentile_bars, plot_kw={"is_pct": True}),
ColumnDefinition(
"B", width=1.5, plot_fn=percentile_stars, plot_kw={"is_pct": True}
),
ColumnDefinition(
"C",
plot_fn=progress_donut,
plot_kw={
"is_pct": True,
"formatter": "{:.0%}"
},
),
ColumnDefinition(
"D",
width=1.25,
plot_fn=bar,
plot_kw={
"cmap": cmap,
"plot_bg_bar": True,
"annotate": True,
"height": 0.5,
"lw": 0.5,
"formatter": decimal_to_percent,
},
),
],
)
plt.show()
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.colors import LinearSegmentedColormap
# ColDef是ColumnDefinition的别名
from plottable import ColDef, Table
# 自定义颜色
cmap = LinearSegmentedColormap.from_list(
name="bugw", colors=["#ffffff", "#f2fbd2", "#c9ecb4", "#93d3ab", "#35b0ab"], N=256
)
# 创建数据
cities = [
"TORONTO",
"VANCOUVER",
"HALIFAX",
"CALGARY",
"OTTAWA",
"MONTREAL",
"WINNIPEG",
"EDMONTON",
"LONDON",
"ST. JONES",
]
months = [
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC",
]
data = np.random.random((10, 12)) + np.abs(np.arange(12) - 5.5)
data = (1 - data / (np.max(data)))
data.shape
(10, 12)
# 绘图
d = pd.DataFrame(data, columns=months, index=cities).round(2)
fig, ax = plt.subplots(figsize=(14, 5))
# 自定义各列的绘图效果
column_definitions = [
ColDef(name, cmap=cmap, formatter=lambda x: "") for name in months
] + [ColDef("index", title="", width=1.5, textprops={"ha": "right"})]
tab = Table(
d,
column_definitions=column_definitions,
row_dividers=False,
col_label_divider=False,
textprops={"ha": "center", "fontname": "Roboto"},
# 设置各个单元格的效果
cell_kw={
"edgecolor": "black",
"linewidth": 0,
},
)
# 设置列标题文字和背景颜色
tab.col_label_row.set_facecolor("white")
tab.col_label_row.set_fontcolor("black")
# 设置行标题文字和背景颜色
tab.columns["index"].set_facecolor("black")
tab.columns["index"].set_fontcolor("white")
tab.columns["index"].set_linewidth(0)
plt.show()
step1 准备数据 。
下载示例数据,所有示例数据在 plottable-example_notebooks .
# 下载数据集
# !wget https://raw.githubusercontent.com/znstrider/plottable/master/docs/example_notebooks/data/wwc_forecasts.csv
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.colors import LinearSegmentedColormap
from plottable import ColumnDefinition, Table
from plottable.cmap import normed_cmap
from plottable.formatters import decimal_to_percent
from plottable.plots import circled_image # image
cols = [
"team",
"points",
"group",
"spi",
"global_o",
"global_d",
"group_1",
"group_2",
"group_3",
"make_round_of_16",
"make_quarters",
"make_semis",
"make_final",
"win_league",
]
# 读取数据
df = pd.read_csv(
"data/wwc_forecasts.csv",
usecols=cols,
)
# 展示数据
df.head()
team | group | spi | global_o | global_d | group_1 | group_2 | group_3 | make_round_of_16 | make_quarters | make_semis | make_final | win_league | points | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | USA | F | 98.32748 | 5.52561 | 0.58179 | 0.82956 | 0.17044 | 0.00000 | 1.0 | 0.78079 | 0.47307 | 0.35076 | 0.23618 | 6 |
1 | France | A | 96.29671 | 4.31375 | 0.52137 | 0.99483 | 0.00515 | 0.00002 | 1.0 | 0.78367 | 0.42052 | 0.30038 | 0.19428 | 6 |
2 | Germany | B | 93.76549 | 3.96791 | 0.67818 | 0.98483 | 0.01517 | 0.00000 | 1.0 | 0.89280 | 0.48039 | 0.27710 | 0.12256 | 6 |
3 | Canada | E | 93.51599 | 3.67537 | 0.56980 | 0.38830 | 0.61170 | 0.00000 | 1.0 | 0.59192 | 0.36140 | 0.20157 | 0.09031 | 6 |
4 | England | D | 91.92311 | 3.51560 | 0.63717 | 0.70570 | 0.29430 | 0.00000 | 1.0 | 0.68510 | 0.43053 | 0.16465 | 0.08003 | 6 |
此外,我们需要准备每个国家对应的国旗图片,该数据也在 plottable-example_notebooks 下.
# 读取图片路径
flag_paths = list(Path("data/country_flags").glob("*.png"))
country_to_flagpath = {p.stem: p for p in flag_paths}
step2 数据处理 。
该步需要合并数据,将其转换为plottable可用的数据结构.
# 重置列名
colnames = [
"Team",
"Points",
"Group",
"SPI",
"OFF",
"DEF",
"1st Place",
"2nd Place",
"3rd Place",
"Make Rd Of 16",
"Make Quarters",
"Make Semis",
"Make Finals",
"Win World Cup",
]
col_to_name = dict(zip(cols, colnames))
col_to_name
{'team': 'Team',
'points': 'Points',
'group': 'Group',
'spi': 'SPI',
'global_o': 'OFF',
'global_d': 'DEF',
'group_1': '1st Place',
'group_2': '2nd Place',
'group_3': '3rd Place',
'make_round_of_16': 'Make Rd Of 16',
'make_quarters': 'Make Quarters',
'make_semis': 'Make Semis',
'make_final': 'Make Finals',
'win_league': 'Win World Cup'}
df[["spi", "global_o", "global_d"]] = df[["spi", "global_o", "global_d"]].round(1)
df = df.rename(col_to_name, axis=1)
# 删除Points列
df = df.drop("Points", axis=1)
# 插入列
df.insert(0, "Flag", df["Team"].apply(lambda x: country_to_flagpath.get(x)))
df = df.set_index("Team")
df.head()
Flag | Group | SPI | OFF | DEF | 1st Place | 2nd Place | 3rd Place | Make Rd Of 16 | Make Quarters | Make Semis | Make Finals | Win World Cup | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Team | |||||||||||||
USA | data/country_flags/USA.png | F | 98.3 | 5.5 | 0.6 | 0.82956 | 0.17044 | 0.00000 | 1.0 | 0.78079 | 0.47307 | 0.35076 | 0.23618 |
France | data/country_flags/France.png | A | 96.3 | 4.3 | 0.5 | 0.99483 | 0.00515 | 0.00002 | 1.0 | 0.78367 | 0.42052 | 0.30038 | 0.19428 |
Germany | data/country_flags/Germany.png | B | 93.8 | 4.0 | 0.7 | 0.98483 | 0.01517 | 0.00000 | 1.0 | 0.89280 | 0.48039 | 0.27710 | 0.12256 |
Canada | data/country_flags/Canada.png | E | 93.5 | 3.7 | 0.6 | 0.38830 | 0.61170 | 0.00000 | 1.0 | 0.59192 | 0.36140 | 0.20157 | 0.09031 |
England | data/country_flags/England.png | D | 91.9 | 3.5 | 0.6 | 0.70570 | 0.29430 | 0.00000 | 1.0 | 0.68510 | 0.43053 | 0.16465 | 0.08003 |
step3 绘图 。
# 设置颜色
cmap = LinearSegmentedColormap.from_list(
name="bugw", colors=["#ffffff", "#f2fbd2", "#c9ecb4", "#93d3ab", "#35b0ab"], N=256
)
team_rating_cols = ["SPI", "OFF", "DEF"]
group_stage_cols = ["1st Place", "2nd Place", "3rd Place"]
knockout_stage_cols = list(df.columns[-5:])
# 单独设置每一列的绘制参数
col_defs = (
# 绘制第一部分效果
[
ColumnDefinition(
name="Flag",
title="",
textprops={"ha": "center"},
width=0.5,
# 设置自定义效果展示函数
plot_fn=circled_image,
),
ColumnDefinition(
name="Team",
textprops={"ha": "left", "weight": "bold"},
width=1.5,
),
ColumnDefinition(
name="Group",
textprops={"ha": "center"},
width=0.75,
),
ColumnDefinition(
name="SPI",
group="Team Rating",
textprops={"ha": "center"},
width=0.75,
),
ColumnDefinition(
name="OFF",
width=0.75,
textprops={
"ha": "center",
# 设置填充方式
"bbox": {"boxstyle": "circle", "pad": 0.35},
},
cmap=normed_cmap(df["OFF"], cmap=matplotlib.cm.PiYG, num_stds=2.5),
group="Team Rating",
),
ColumnDefinition(
name="DEF",
width=0.75,
textprops={
"ha": "center",
"bbox": {"boxstyle": "circle", "pad": 0.35},
},
cmap=normed_cmap(df["DEF"], cmap=matplotlib.cm.PiYG_r, num_stds=2.5),
group="Team Rating",
),
]
# 绘制第二部分效果
+ [
ColumnDefinition(
name=group_stage_cols[0],
title=group_stage_cols[0].replace(" ", "\n", 1),
formatter=decimal_to_percent,
group="Group Stage Chances",
# 设置边框
border="left",
)
]
+ [
ColumnDefinition(
name=col,
title=col.replace(" ", "\n", 1),
formatter=decimal_to_percent,
group="Group Stage Chances",
)
for col in group_stage_cols[1:]
]
# 绘制第三部分效果
+ [
ColumnDefinition(
name=knockout_stage_cols[0],
title=knockout_stage_cols[0].replace(" ", "\n", 1),
formatter=decimal_to_percent,
cmap=cmap,
group="Knockout Stage Chances",
border="left",
)
]
+ [
ColumnDefinition(
name=col,
title=col.replace(" ", "\n", 1),
formatter=decimal_to_percent,
cmap=cmap,
group="Knockout Stage Chances",
)
for col in knockout_stage_cols[1:]
]
)
# 绘图
fig, ax = plt.subplots(figsize=(18, 18))
table = Table(
df,
column_definitions=col_defs,
row_dividers=True,
footer_divider=True,
ax=ax,
textprops={"fontsize": 14},
row_divider_kw={"linewidth": 1, "linestyle": (0, (1, 5))},
col_label_divider_kw={"linewidth": 1, "linestyle": "-"},
column_border_kw={"linewidth": 1, "linestyle": "-"},
).autoset_fontcolors(colnames=["OFF", "DEF"])
plt.show()
# 保存图片
fig.savefig("wwc_table.jpg", facecolor=ax.get_facecolor(), dpi=300,bbox_inches='tight')
step1 准备数据 。
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from plottable import ColDef, Table
from plottable.plots import image
# 下载联赛数据
# !wget https://projects.fivethirtyeight.com/soccer-api/club/spi_matches.csv
# !wget https://projects.fivethirtyeight.com/soccer-api/club/spi_matches_latest.csv
# 数据地址
FIVETHIRTYEIGHT_URLS = {
"SPI_MATCHES": "https://projects.fivethirtyeight.com/soccer-api/club/spi_matches.csv",
"SPI_MATCHES_LATEST": "https://projects.fivethirtyeight.com/soccer-api/club/spi_matches_latest.csv",
}
# 读取数据
# df = pd.read_csv(FIVETHIRTYEIGHT_URLS["SPI_MATCHES_LATEST"])
df = pd.read_csv("data/spi_matches_latest.csv")
df.head()
season | date | league_id | league | team1 | team2 | spi1 | spi2 | prob1 | prob2 | ... | importance1 | importance2 | score1 | score2 | xg1 | xg2 | nsxg1 | nsxg2 | adj_score1 | adj_score2 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2019 | 2019-03-01 | 1979 | Chinese Super League | Shandong Luneng | Guizhou Renhe | 48.22 | 37.83 | 0.5755 | 0.1740 | ... | 45.9 | 22.1 | 1.0 | 0.0 | 1.39 | 0.26 | 2.05 | 0.54 | 1.05 | 0.00 |
1 | 2019 | 2019-03-01 | 1979 | Chinese Super League | Shanghai Greenland | Shanghai SIPG | 39.81 | 60.08 | 0.2387 | 0.5203 | ... | 25.6 | 63.4 | 0.0 | 4.0 | 0.57 | 2.76 | 0.80 | 1.50 | 0.00 | 3.26 |
2 | 2019 | 2019-03-01 | 1979 | Chinese Super League | Guangzhou Evergrande | Tianjin Quanujian | 65.59 | 39.99 | 0.7832 | 0.0673 | ... | 77.1 | 28.8 | 3.0 | 0.0 | 0.49 | 0.45 | 1.05 | 0.75 | 3.15 | 0.00 |
3 | 2019 | 2019-03-01 | 1979 | Chinese Super League | Wuhan Zall | Beijing Guoan | 32.25 | 54.82 | 0.2276 | 0.5226 | ... | 35.8 | 58.9 | 0.0 | 1.0 | 1.12 | 0.97 | 1.51 | 0.94 | 0.00 | 1.05 |
4 | 2019 | 2019-03-01 | 1979 | Chinese Super League | Chongqing Lifan | Guangzhou RF | 38.24 | 40.45 | 0.4403 | 0.2932 | ... | 26.2 | 21.3 | 2.0 | 2.0 | 2.77 | 3.17 | 1.05 | 2.08 | 2.10 | 2.10 |
5 rows × 23 columns 。
# 筛选德甲联赛数据,并删除为空数据
bl = df.loc[df.league == "German Bundesliga"].dropna()
bl.head()
season | date | league_id | league | team1 | team2 | spi1 | spi2 | prob1 | prob2 | ... | importance1 | importance2 | score1 | score2 | xg1 | xg2 | nsxg1 | nsxg2 | adj_score1 | adj_score2 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
497 | 2022 | 2022-08-05 | 1845 | German Bundesliga | Eintracht Frankfurt | Bayern Munich | 68.47 | 91.75 | 0.1350 | 0.6796 | ... | 32.6 | 71.9 | 1.0 | 6.0 | 0.83 | 4.50 | 0.65 | 2.72 | 1.05 | 5.96 |
514 | 2022 | 2022-08-06 | 1845 | German Bundesliga | VfL Bochum | Mainz | 60.73 | 68.88 | 0.3568 | 0.3629 | ... | 33.5 | 34.5 | 1.0 | 2.0 | 1.00 | 1.62 | 0.96 | 0.86 | 1.05 | 2.10 |
515 | 2022 | 2022-08-06 | 1845 | German Bundesliga | Borussia Monchengladbach | TSG Hoffenheim | 69.38 | 66.77 | 0.4872 | 0.2742 | ... | 40.2 | 33.3 | 3.0 | 1.0 | 1.86 | 0.10 | 2.51 | 0.31 | 2.36 | 1.05 |
516 | 2022 | 2022-08-06 | 1845 | German Bundesliga | VfL Wolfsburg | Werder Bremen | 68.18 | 59.82 | 0.5319 | 0.2014 | ... | 30.2 | 33.3 | 2.0 | 2.0 | 0.81 | 0.97 | 1.07 | 1.25 | 2.10 | 2.10 |
517 | 2022 | 2022-08-06 | 1845 | German Bundesliga | 1. FC Union Berlin | Hertha Berlin | 69.98 | 59.70 | 0.5479 | 0.1860 | ... | 34.9 | 33.0 | 3.0 | 1.0 | 1.25 | 0.40 | 1.66 | 0.36 | 3.15 | 1.05 |
5 rows × 23 columns 。
step2 数据处理 。
# 统计得分
def add_points(df: pd.DataFrame) -> pd.DataFrame:
# 三元表达式
# df["score1"] > df["score2"],则返回3
# np.where(df["score1"] == df["score2"],则返回1
# 否则为0
df["pts_home"] = np.where(
df["score1"] > df["score2"], 3, np.where(df["score1"] == df["score2"], 1, 0)
)
df["pts_away"] = np.where(
df["score1"] < df["score2"], 3, np.where(df["score1"] == df["score2"], 1, 0)
)
return df
# 统计得分数据
bl = add_points(bl)
# 总得分、总进球数、总助攻数和总黄牌数
# 以下代码先分别统计team1和team2的得分数据,然后将两组数据相加
perform = (
bl.groupby("team1")[[
"pts_home",
"score1",
"score2",
"xg1",
"xg2",
]]
.sum()
.set_axis(
[
"pts",
"gf",
"ga",
"xgf",
"xga",
],
axis=1,
)
.add(
bl.groupby("team2")[[
"pts_away",
"score2",
"score1",
"xg2",
"xg1",
]]
.sum()
.set_axis(
[
"pts",
"gf",
"ga",
"xgf",
"xga",
],
axis=1,
)
)
)
# 由于python和pandas版本问题,如果上面的代码出问题,则使用下面代码
# t1= bl.groupby("team1")[["pts_home","score1","score2","xg1","xg2", ]]
# t1 = t1.sum()
# t1.set_axis( ["pts","gf","ga","xgf","xga", ], axis=1,)
# t2 = bl.groupby("team1")[["pts_home","score1","score2","xg1","xg2", ]]
# t2 = t2.sum()
# t2.set_axis( ["pts","gf","ga","xgf","xga", ], axis=1,)
# perform = (t1.add(t2))
perform.shape
(18, 5)
# 汇总得分表现数据
perform.index.name = "team"
perform["gd"] = perform["gf"] - perform["ga"]
perform = perform[
[
"pts",
"gd",
"gf",
"ga",
"xgf",
"xga",
]
]
perform["games"] = bl.groupby("team1").size().add(bl.groupby("team2").size())
perform.head()
pts | gd | gf | ga | xgf | xga | games | |
---|---|---|---|---|---|---|---|
team | |||||||
1. FC Union Berlin | 62 | 13.0 | 51.0 | 38.0 | 35.93 | 43.06 | 34 |
Bayer Leverkusen | 50 | 8.0 | 57.0 | 49.0 | 53.62 | 48.20 | 34 |
Bayern Munich | 71 | 54.0 | 92.0 | 38.0 | 84.93 | 40.12 | 34 |
Borussia Dortmund | 71 | 39.0 | 83.0 | 44.0 | 75.96 | 42.69 | 34 |
Borussia Monchengladbach | 43 | -3.0 | 52.0 | 55.0 | 53.05 | 58.88 | 34 |
# 统计各队的胜负数据
def get_wins_draws_losses(games: pd.DataFrame) -> pd.DataFrame:
return (
games.rename({"pts_home": "pts", "team1": "team"}, axis=1)
.groupby("team")["pts"]
.value_counts()
.add(
games.rename({"pts_away": "pts", "team2": "team"}, axis=1)
.groupby("team")["pts"]
.value_counts(),
fill_value=0,
)
.astype(int)
.rename("count")
.reset_index(level=1)
.pivot(columns="pts", values="count")
.rename({0: "L", 1: "D", 3: "W"}, axis=1)[["W", "D", "L"]]
)
wins_draws_losses = get_wins_draws_losses(bl)
wins_draws_losses.head()
pts | W | D | L |
---|---|---|---|
team | |||
1. FC Union Berlin | 18 | 8 | 8 |
Bayer Leverkusen | 14 | 8 | 12 |
Bayern Munich | 21 | 8 | 5 |
Borussia Dortmund | 22 | 5 | 7 |
Borussia Monchengladbach | 11 | 10 | 13 |
# 合并得分和胜负数据
perform = pd.concat([perform, wins_draws_losses], axis=1)
perform.head()
pts | gd | gf | ga | xgf | xga | games | W | D | L | |
---|---|---|---|---|---|---|---|---|---|---|
team | ||||||||||
1. FC Union Berlin | 62 | 13.0 | 51.0 | 38.0 | 35.93 | 43.06 | 34 | 18 | 8 | 8 |
Bayer Leverkusen | 50 | 8.0 | 57.0 | 49.0 | 53.62 | 48.20 | 34 | 14 | 8 | 12 |
Bayern Munich | 71 | 54.0 | 92.0 | 38.0 | 84.93 | 40.12 | 34 | 21 | 8 | 5 |
Borussia Dortmund | 71 | 39.0 | 83.0 | 44.0 | 75.96 | 42.69 | 34 | 22 | 5 | 7 |
Borussia Monchengladbach | 43 | -3.0 | 52.0 | 55.0 | 53.05 | 58.88 | 34 | 11 | 10 | 13 |
step3 映射队标图片 。
队标图片地址为: plottable-example_notebooks 。
# 创建队名和队标的索引数据
club_logo_path = Path("data/bundesliga_crests_22_23")
club_logo_files = list(club_logo_path.glob("*.png"))
club_logos_paths = {f.stem: f for f in club_logo_files}
perform = perform.reset_index()
# 添加新列
perform.insert(0, "crest", perform["team"])
perform["crest"] = perform["crest"].replace(club_logos_paths)
# 数据排序
perform = perform.sort_values(by=["pts", "gd", "gf"], ascending=False)
for colname in ["gd", "gf", "ga"]:
perform[colname] = perform[colname].astype("int32")
perform["goal_difference"] = perform["gf"].astype(str) + ":" + perform["ga"].astype(str)
# 添加排名
perform["rank"] = list(range(1, 19))
perform.head()
crest | team | pts | gd | gf | ga | xgf | xga | games | W | D | L | goal_difference | rank | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2 | data/bundesliga_crests_22_23/Bayern Munich.png | Bayern Munich | 71 | 54 | 92 | 38 | 84.93 | 40.12 | 34 | 21 | 8 | 5 | 92:38 | 1 |
3 | data/bundesliga_crests_22_23/Borussia Dortmund... | Borussia Dortmund | 71 | 39 | 83 | 44 | 75.96 | 42.69 | 34 | 22 | 5 | 7 | 83:44 | 2 |
10 | data/bundesliga_crests_22_23/RB Leipzig.png | RB Leipzig | 66 | 23 | 64 | 41 | 67.01 | 37.48 | 34 | 20 | 6 | 8 | 64:41 | 3 |
0 | data/bundesliga_crests_22_23/1. FC Union Berli... | 1. FC Union Berlin | 62 | 13 | 51 | 38 | 35.93 | 43.06 | 34 | 18 | 8 | 8 | 51:38 | 4 |
11 | data/bundesliga_crests_22_23/SC Freiburg.png | SC Freiburg | 59 | 7 | 51 | 44 | 53.11 | 52.25 | 34 | 17 | 8 | 9 | 51:44 | 5 |
step4 设定绘图数据 。
# 设置颜色
row_colors = {
"top4": "#2d3636",
"top6": "#516362",
"playoffs": "#8d9386",
"relegation": "#c8ab8d",
"even": "#627979",
"odd": "#68817e",
}
bg_color = row_colors["odd"]
text_color = "#e0e8df"
# 确定绘图列
table_cols = ["crest", "team", "games", "W", "D", "L", "goal_difference", "gd", "pts"]
perform[table_cols].head()
crest | team | games | W | D | L | goal_difference | gd | pts | |
---|---|---|---|---|---|---|---|---|---|
2 | data/bundesliga_crests_22_23/Bayern Munich.png | Bayern Munich | 34 | 21 | 8 | 5 | 92:38 | 54 | 71 |
3 | data/bundesliga_crests_22_23/Borussia Dortmund... | Borussia Dortmund | 34 | 22 | 5 | 7 | 83:44 | 39 | 71 |
10 | data/bundesliga_crests_22_23/RB Leipzig.png | RB Leipzig | 34 | 20 | 6 | 8 | 64:41 | 23 | 66 |
0 | data/bundesliga_crests_22_23/1. FC Union Berli... | 1. FC Union Berlin | 34 | 18 | 8 | 8 | 51:38 | 13 | 62 |
11 | data/bundesliga_crests_22_23/SC Freiburg.png | SC Freiburg | 34 | 17 | 8 | 9 | 51:44 | 7 | 59 |
# 定义各列绘图效果
table_col_defs = [
ColDef("rank", width=0.5, title=""),
ColDef("crest", width=0.35, plot_fn=image, title=""),
ColDef("team", width=2.5, title="", textprops={"ha": "left"}),
ColDef("games", width=0.5, title="Games"),
ColDef("W", width=0.5),
ColDef("D", width=0.5),
ColDef("L", width=0.5),
ColDef("goal_difference", title="Goals"),
ColDef("gd", width=0.5, title="", formatter="{:+}"),
ColDef("pts", border="left", title="Points"),
]
step5 绘图 。
fig, ax = plt.subplots(figsize=(14, 12))
plt.rcParams["text.color"] = text_color
# 设置绘图字体
# plt.rcParams["font.family"] = "Roboto"
# 设置背景颜色
fig.set_facecolor(bg_color)
ax.set_facecolor(bg_color)
table = Table(
perform,
column_definitions=table_col_defs,
row_dividers=True,
col_label_divider=False,
footer_divider=True,
index_col="rank",
columns=table_cols,
even_row_color=row_colors["even"],
footer_divider_kw={"color": bg_color, "lw": 2},
row_divider_kw={"color": bg_color, "lw": 2},
column_border_kw={"color": bg_color, "lw": 2},
# 如果设置字体需要添加"fontname": "Roboto"
textprops={"fontsize": 16, "ha": "center"},
)
# 设置不同行的颜色
for idx in [0, 1, 2, 3]:
table.rows[idx].set_facecolor(row_colors["top4"])
for idx in [4, 5]:
table.rows[idx].set_facecolor(row_colors["top6"])
table.rows[15].set_facecolor(row_colors["playoffs"])
for idx in [16, 17]:
table.rows[idx].set_facecolor(row_colors["relegation"])
table.rows[idx].set_fontcolor(row_colors["top4"])
fig.savefig(
"bohndesliga_table_recreation.png",
facecolor=fig.get_facecolor(),
bbox_inches='tight',
dpi=300,
)
最后此篇关于[数据分析与可视化]基于matplotlib和plottable库绘制精美表格的文章就讲到这里了,如果你想了解更多关于[数据分析与可视化]基于matplotlib和plottable库绘制精美表格的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Java 库和 android 库有什么区别,各自有什么优点/缺点? 最佳答案 您可以在 Android 应用程序中包含标准 Java .jar 文件库。它们在 .apk 构建时被翻译成 Dalvik
所以,我现在的代码就像从 Java 层加载库(比如 liba.so),并在内部 liba.so 加载 libb.so。因此,如果我必须将所有库打包到 APK 中并将其安装在没有 root 访问权限的设
我想在我的系统中设置 LEDA 库。 我已经从以下链接下载了 LEDA 库 http://www.algorithmic-solutions.info/free/d5.php Instruct
我想用 autoconf 创建一个共享库。但是,我希望共享库具有“.so”扩展名,而不是以“lib”开头。基本上,我想制作一个加载 dlopen 的插件。 .是否有捷径可寻? 当我尝试使用 autoc
我需要在 Apps 脚本应用程序上修改 PDF。为此,我想使用 JS 库:PDF-LIB 我的代码: eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib
我正在构建一个使用以下 Boost header 的程序(我使用的是 Microsoft Visual C++ 10), #include #include #include #include
当我通过 cygwin 在 hadoop 上运行此命令时: $bin/hadoop jar hadoop-examples-*.jar grep input output 'dfs[a-z.]+' 我
我已经通过 vcpgk 成功安装了一个 C++ 库,名为:lmdb:x64-windows 我还安装了lmdb通过 Cabal 安装的 Haskell 绑定(bind)包 在尝试测试 lmdb 包时:
我该如何解决这个问题? 我刚刚将 javacv jar 文件复制到我的项目 Lib 文件夹下,但出现了这个错误! 我可以找到这个thread来自谷歌,但不幸的是,由于我国的谷歌限制政策,该页面无法打开
我有一个 Android 库项目 FooLib。 FooLib 引用 Android Context 之类的东西,但不需要任何资源文件(res/ 中的东西)所以我目前将其打包为供我的应用使用的 JAR
我正在开发一个 Android 应用程序(使用 Android Studio),它能够通过手势识别算法了解您正在进行的 Activity 。对于我使用 nickgillian ithub 帐户上可用的
关于从 .NET Framework 项目中引用 .NET Standard 类库的问题有很多类似的问题,其中 netstandard 库中的 NuGet 包依赖项不会流向 netframework
我已经从互联网上下载了 jna-4.2.2.jar,现在想将这个 jar 导入到我的项目中。但是当我试图将这个 jar 导入我的项目时,出现以下错误。 [2016-06-20 09:35:01 - F
我正在尝试通过编译在 Mac 上安装 rsync 3.2.3。但是,我想安装所有功能。为此,它需要一些库,此处 ( https://download.samba.org/pub/rsync/INSTA
进入 Web 开发有点困难。过去 5 年我一直致力于 winforms 工作。所以我正在努力从一种切换到另一种。前段时间,我使用过 JavaScript,但现在还没有大量的 JavaScript 库
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我正在寻找一个用Python编写的与logstash(ruby + java)类似的工具/库。 我的目标是: 从 syslog 中解析所有系统日志 解析应用程序特定日志(apache、django、m
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我花了几天时间试图寻找用于 JavaPOS 实现的 .jar 库,但我找不到任何可以工作的东西。我找到了很多像这样的文档:http://jpos.1045706.n5.nabble.com/file/
这个问题在这里已经有了答案: Merge multiple .so shared libraries (2 个答案) 关闭 9 年前。 我有我在代码中使用的第三方库的源代码和对象。该库附带有关如何使
我是一名优秀的程序员,十分优秀!