如何将 pandas DataFrame 表保存为 png
- 2025-01-09 08:47:00
- admin 原创
- 111
问题描述:
我构建了一个结果的 pandas 数据框。此数据框充当表格。有 MultiIndexed 列,每行代表一个名称,即index=['name1','name2',...]
在创建 DataFrame 时。我想显示此表并将其保存为 png(或任何图形格式)。目前,我能得到的最接近的方法是将其转换为 html,但我想要一个 png。看起来有人问过类似的问题,例如如何将 Pandas 数据框/系列数据保存为图形?
但是,标记的解决方案将数据框转换为线图(而不是表格),而另一个解决方案依赖于 PySide,我想远离它,因为我无法在 Linux 上 pip 安装它。我希望此代码易于移植。我真的希望使用 python 可以轻松创建 png 表格。感谢所有帮助。
解决方案 1:
Pandas 允许您使用 matplotlib 绘制表格(详情请见此处)。通常,这会将表格直接绘制到图上(带有轴和所有内容),这不是您想要的。但是,可以先删除这些:
import matplotlib.pyplot as plt
import pandas as pd
from pandas.table.plotting import table # EDIT: see deprecation warnings below
ax = plt.subplot(111, frame_on=False) # no visible frame
ax.xaxis.set_visible(False) # hide the x axis
ax.yaxis.set_visible(False) # hide the y axis
table(ax, df) # where df is your data frame
plt.savefig('mytable.png')
输出可能不是最漂亮的,但您可以在这里找到 table() 函数的其他参数。
编辑:
这是使用上述方法绘图时模拟多索引的一种(诚然,相当古怪)方法。如果您有一个名为 df 的多索引数据框,如下所示:
first second
bar one 1.991802
two 0.403415
baz one -1.024986
two -0.522366
foo one 0.350297
two -0.444106
qux one -0.472536
two 0.999393
dtype: float64
首先重置索引,使它们成为正常列
df = df.reset_index()
df
first second 0
0 bar one 1.991802
1 bar two 0.403415
2 baz one -1.024986
3 baz two -0.522366
4 foo one 0.350297
5 foo two -0.444106
6 qux one -0.472536
7 qux two 0.999393
通过将高阶多索引列中的所有重复项设置为空字符串来删除它们(在我的示例中,我只有在“first”中才有重复的索引):
df.ix[df.duplicated('first') , 'first'] = '' # see deprecation warnings below
df
first second 0
0 bar one 1.991802
1 two 0.403415
2 baz one -1.024986
3 two -0.522366
4 foo one 0.350297
5 two -0.444106
6 qux one -0.472536
7 two 0.999393
将“索引”上的列名称更改为空字符串
new_cols = df.columns.values
new_cols[:2] = '','' # since my index columns are the two left-most on the table
df.columns = new_cols
现在调用表函数,但将表中的所有行标签设置为空字符串(这确保不显示图的实际索引):
table(ax, df, rowLabels=['']*df.shape[0], loc='center')
瞧:
虽然不太漂亮,但功能齐全的多索引表。
编辑:弃用警告
正如评论中指出的那样,导入声明table
:
from pandas.tools.plotting import table
在较新版本的 Pandas 中已被弃用,取而代之的是:
from pandas.plotting import table
编辑:弃用警告 2
索引器ix
现已完全弃用,因此我们应该改用loc
索引器。替换:
df.ix[df.duplicated('first') , 'first'] = ''
和
df.loc[df.duplicated('first') , 'first'] = ''
解决方案 2:
实际上有一个名为 dataframe_image 的 Python 库,只需执行
pip install dataframe_image
进行导入
import pandas as pd
import numpy as np
import dataframe_image as dfi
df = pd.DataFrame(np.random.randn(6, 6), columns=list('ABCDEF'))
并根据需要设置表格样式:
df_styled = df.style.background_gradient() #adding a gradient based on values in cell
最后:
dfi.export(df_styled,"mytable.png")
解决方案 3:
解决您的问题的最佳方法可能是先将数据框导出为 HTML,然后使用 HTML 转图像工具进行转换。最终外观可以通过 CSS 进行调整。
HTML 到图像渲染的热门选项包括:
WeasyPrint
wkhtmltopdf
/wkhtmltoimage
假设我们有一个名为的数据框df
。我们可以使用以下代码生成一个:
import string
import numpy as np
import pandas as pd
np.random.seed(0) # just to get reproducible results from `np.random`
rows, cols = 5, 10
labels = list(string.ascii_uppercase[:cols])
df = pd.DataFrame(np.random.randint(0, 100, size=(5, 10)), columns=labels)
print(df)
# A B C D E F G H I J
# 0 44 47 64 67 67 9 83 21 36 87
# 1 70 88 88 12 58 65 39 87 46 88
# 2 81 37 25 77 72 9 20 80 69 79
# 3 47 64 82 99 88 49 29 19 19 14
# 4 39 32 65 9 57 32 31 74 23 35
使用WeasyPrint
这种方法使用了一个pip
-installable 包,它允许您使用 Python 生态系统完成所有操作。 的一个缺点是它似乎没有提供根据内容调整图像大小weasyprint
的方法。 无论如何,在 Python/PIL 中从图像中删除一些背景相对容易,它是在下面的功能中实现的(改编自此处)。 还需要确保图像足够大,这可以通过 CSS 的属性来实现。trim()
`@page size`
代码如下:
import weasyprint as wsp
import PIL as pil
def trim(source_filepath, target_filepath=None, background=None):
if not target_filepath:
target_filepath = source_filepath
img = pil.Image.open(source_filepath)
if background is None:
background = img.getpixel((0, 0))
border = pil.Image.new(img.mode, img.size, background)
diff = pil.ImageChops.difference(img, border)
bbox = diff.getbbox()
img = img.crop(bbox) if bbox else img
img.save(target_filepath)
img_filepath = 'table1.png'
css = wsp.CSS(string='''
@page { size: 2048px 2048px; padding: 0px; margin: 0px; }
table, td, tr, th { border: 1px solid black; }
td, th { padding: 4px 8px; }
''')
html = wsp.HTML(string=df.to_html())
html.write_png(img_filepath, stylesheets=[css])
trim(img_filepath)
使用wkhtmltopdf
/wkhtmltoimage
此方法使用外部开源工具,需要在生成映像之前安装。还有一个 Python 包,pdfkit
可作为其前端(它不会免除您自行安装核心软件的义务),但我不会使用它。
wkhtmltoimage
可以简单地使用subprocess
(或任何其他类似的在 Python 中运行外部程序的方法)进行调用。还需要将 HTML 文件输出到磁盘。
代码如下:
import subprocess
df.to_html('table2.html')
subprocess.call(
'wkhtmltoimage -f png --width 0 table2.html table2.png', shell=True)
并且其外观可以像其他方法一样使用 CSS 进行进一步调整。
解决方案 4:
虽然我不确定这是否是你期望的结果,但是你可以通过使用 Seaborn Heatmap 绘制带有注释的 DataFrame 来将 DataFrame 保存为 png 格式,如下所示:
http://stanford.edu/~mwaskom/software/seaborn/ generated/seaborn.heatmap.html#seaborn.heatmap
它可立即与 Pandas Dataframe 配合使用。您可以查看此示例:使用 Python 高效绘制 csv 格式的表格
您可能想要更改颜色图以便它仅显示白色背景。
希望这有帮助。
编辑:这是一个执行此操作的片段:
import matplotlib
import seaborn as sns
def save_df_as_image(df, path):
# Set background to white
norm = matplotlib.colors.Normalize(-1,1)
colors = [[norm(-1.0), "white"],
[norm( 1.0), "white"]]
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", colors)
# Make plot
plot = sns.heatmap(df, annot=True, cmap=cmap, cbar=False)
fig = plot.get_figure()
fig.savefig(path)
解决方案 5:
@bunji 的解决方案对我有用,但默认选项并不总是能产生良好的结果。我添加了一些有用的参数来调整表格的外观。
import pandas as pd
import matplotlib.pyplot as plt
from pandas.plotting import table
import numpy as np
dates = pd.date_range('20130101',periods=6)
df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=list('ABCD'))
df.index = [item.strftime('%Y-%m-%d') for item in df.index] # Format date
fig, ax = plt.subplots(figsize=(12, 2)) # set size frame
ax.xaxis.set_visible(False) # hide the x axis
ax.yaxis.set_visible(False) # hide the y axis
ax.set_frame_on(False) # no visible frame, uncomment if size is ok
tabla = table(ax, df, loc='upper right', colWidths=[0.17]*len(df.columns)) # where df is your data frame
tabla.auto_set_font_size(False) # Activate set fontsize manually
tabla.set_fontsize(12) # if ++fontsize is necessary ++colWidths
tabla.scale(1.2, 1.2) # change size table
plt.savefig('table.png', transparent=True)
结果:
解决方案 6:
有一个名为https://pypi.org/project/df2img/df2img
的 Python 库可用(免责声明:我是作者)。它是一个用作后端的包装器/便利函数。plotly
您可以在https://df2img.dev找到文档。
import pandas as pd
import df2img
df = pd.DataFrame(
data=dict(
float_col=[1.4, float("NaN"), 250, 24.65],
str_col=("string1", "string2", float("NaN"), "string4"),
),
index=["row1", "row2", "row3", "row4"],
)
保存pd.DataFrame
为 .png 文件非常快捷。您可以应用格式,例如背景颜色或交替行颜色以提高可读性。
fig = df2img.plot_dataframe(
df,
title=dict(
font_color="darkred",
font_family="Times New Roman",
font_size=16,
text="This is a title",
),
tbl_header=dict(
align="right",
fill_color="blue",
font_color="white",
font_size=10,
line_color="darkslategray",
),
tbl_cells=dict(
align="right",
line_color="darkslategray",
),
row_fill_color=("#ffffff", "#d7d8d6"),
fig_size=(300, 160),
)
df2img.save_dataframe(fig=fig, filename="plot.png")
解决方案 7:
我正在进行的项目也有同样的要求。但没有一个答案能满足我的要求。下面这个终于对我有帮助,可能对这个案例有用:
from bokeh.io import export_png, export_svgs
from bokeh.models import ColumnDataSource, DataTable, TableColumn
def save_df_as_image(df, path):
source = ColumnDataSource(df)
df_columns = [df.index.name]
df_columns.extend(df.columns.values)
columns_for_table=[]
for column in df_columns:
columns_for_table.append(TableColumn(field=column, title=column))
data_table = DataTable(source=source, columns=columns_for_table,height_policy="auto",width_policy="auto",index_position=None)
export_png(data_table, filename = path)
解决方案 8:
如果您对在编码环境中调用 DataFrame 时出现的格式感到满意,那么最简单的方法就是使用打印屏幕并使用基本的图像编辑软件裁剪图像。
以下是我使用 Jupyter Notebook 和 Pinta Image Editor(Ubuntu 免费软件)的结果。
解决方案 9:
以下内容需要大量定制才能正确格式化表格,但其基本原理是可行的:
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import pandas as pd
df = pd.DataFrame({ 'A' : 1.,
'B' : pd.Series(1,index=list(range(4)),dtype='float32'),
'C' : np.array([3] * 4,dtype='int32'),
'D' : pd.Categorical(["test","train","test","train"]),
'E' : 'foo' })
class DrawTable():
def __init__(self,_df):
self.rows,self.cols = _df.shape
img_size = (300,200)
self.border = 50
self.bg_col = (255,255,255)
self.div_w = 1
self.div_col = (128,128,128)
self.head_w = 2
self.head_col = (0,0,0)
self.image = Image.new("RGBA", img_size,self.bg_col)
self.draw = ImageDraw.Draw(self.image)
self.draw_grid()
self.populate(_df)
self.image.show()
def draw_grid(self):
width,height = self.image.size
row_step = (height-self.border*2)/(self.rows)
col_step = (width-self.border*2)/(self.cols)
for row in range(1,self.rows+1):
self.draw.line((self.border-row_step//2,self.border+row_step*row,width-self.border,self.border+row_step*row),fill=self.div_col,width=self.div_w)
for col in range(1,self.cols+1):
self.draw.line((self.border+col_step*col,self.border-col_step//2,self.border+col_step*col,height-self.border),fill=self.div_col,width=self.div_w)
self.draw.line((self.border-row_step//2,self.border,width-self.border,self.border),fill=self.head_col,width=self.head_w)
self.draw.line((self.border,self.border-col_step//2,self.border,height-self.border),fill=self.head_col,width=self.head_w)
self.row_step = row_step
self.col_step = col_step
def populate(self,_df2):
font = ImageFont.load_default().font
for row in range(self.rows):
print(_df2.iloc[row,0])
self.draw.text((self.border-self.row_step//2,self.border+self.row_step*row),str(_df2.index[row]),font=font,fill=(0,0,128))
for col in range(self.cols):
text = str(_df2.iloc[row,col])
text_w, text_h = font.getsize(text)
x_pos = self.border+self.col_step*(col+1)-text_w
y_pos = self.border+self.row_step*row
self.draw.text((x_pos,y_pos),text,font=font,fill=(0,0,128))
for col in range(self.cols):
text = str(_df2.columns[col])
text_w, text_h = font.getsize(text)
x_pos = self.border+self.col_step*(col+1)-text_w
y_pos = self.border - self.row_step//2
self.draw.text((x_pos,y_pos),text,font=font,fill=(0,0,128))
def save(self,filename):
try:
self.image.save(filename,mode='RGBA')
print(filename," Saved.")
except:
print("Error saving:",filename)
table1 = DrawTable(df)
table1.save('C:/Users/user/Pictures/table1.png')
输出如下所示:
解决方案 10:
正如jcdoming所建议的,使用 Seaborn heatmap()
:
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure(facecolor='w', edgecolor='k')
sns.heatmap(df.head(), annot=True, cmap='viridis', cbar=False)
plt.savefig('DataFrame.png')
解决方案 11:
使用 Anaconda Spyder IDE 将 Pandas 数据框转换为 png 图像的最简单、最快捷的方法 - 只需双击变量资源管理器中的数据框,IDE 表就会出现,并带有自动格式和配色方案。只需使用截图工具捕获表格以用于报告,然后保存为 png:
这让我节省了很多时间,而且依然优雅而专业。
解决方案 12:
用于数据可视化的人员Plotly
:
您可以轻松地将数据框转换为go.Table。
您可以使用列名保存数据框。
您可以通过 来格式化数据框
go.Table
。您可以将数据框保存为具有不同比例和高分辨率的 pdf、jpg 或 png。
import plotly.express as px
df = px.data.medals_long()
fig = go.Figure(data=[
go.Table(
header=dict(values=list(df.columns),align='center'),
cells=dict(values=df.values.transpose(),
fill_color = [["white","lightgrey"]*df.shape[0]],
align='center'
)
)
])
fig.write_image('image.png',scale=6)
注意:镜像下载到当前python文件运行的同一目录中。
输出:
解决方案 13:
我真的很喜欢 Jupyter 笔记本格式化 DataFrame 的方式,并且这个库以相同的格式导出它:
import dataframe_image as dfi
dfi.export(df, "df.png")
还有一个 dpi 参数,以防您想提高图像质量。我建议 300 以获得还行的质量,600 以获得极好的质量,1200 以获得完美质量,如果超过这个值,可能就太多了。
import dataframe_image as dfi
dfi.export(df, "df.png", dpi = 600)