使用 Pandas Excelwriter 写入 StringIO 对象?

Posted

技术标签:

【中文标题】使用 Pandas Excelwriter 写入 StringIO 对象?【英文标题】:Write to StringIO object using Pandas Excelwriter? 【发布时间】:2015-03-19 11:04:39 【问题描述】:

我可以将 StringIO 对象传递给 pd.to_csv() 就好了:

io = StringIO.StringIO()
pd.DataFrame().to_csv(io)

但是在使用 excel writer 时,我遇到了很多麻烦。

io = StringIO.StringIO()
writer = pd.ExcelWriter(io)
pd.DataFrame().to_excel(writer,"sheet name")
writer.save()   

返回一个

AttributeError: StringIO instance has no attribute 'rfind'

我正在尝试创建一个ExcelWriter 对象而不调用pd.ExcelWriter(),但我遇到了一些麻烦。这是我迄今为止尝试过的:

from xlsxwriter.workbook import Workbook
writer = Workbook(io)
pd.DataFrame().to_excel(writer,"sheet name")
writer.save()

但现在我收到了AttributeError: 'Workbook' object has no attribute 'write_cells'

如何将 excel 格式的 pandas 数据框保存到 StringIO 对象?

【问题讨论】:

我不确定你能做到,至少不容易。 to_excel 的参数是 Excel 文件的路径,而不是实际的文件对象。为什么还要在内存中创建 Excel 文件的表示形式? 使用 Flask 制作可下载的报告。 在 Python 3 中,您应该使用 io.BytesIO,因为写入 Excel 文件的输出是一系列字节,而不是(unicode)字符串。 【参考方案1】:

如果您不介意使用 xlwt 作为您的编写器,浏览 pandas.io.excel 源代码看起来应该不是什么大问题。其他引擎可能也不是那么困难,但 xlwt 跳出来很容易,因为它的保存方法需要一个流或文件路径。

您最初需要传入一个文件名,只是为了让 pandas 满意,因为它会根据引擎检查文件扩展名以确保它是受支持的格式。但在 xlwt 引擎的情况下,它只是将文件名填充到对象的路径属性中,然后在保存方法中使用它。如果您将路径属性更改为流,当您调用 save 方法时,它会很高兴地保存到该流。

这是一个例子:

import pandas as pd
import StringIO
import base64

df = pd.DataFrame.from_csv('http://moz.com/top500/domains/csv')
xlwt_writer = pd.io.excel.get_writer('xlwt')
my_writer = xlwt_writer('whatever.xls')  #make pandas happy 
xl_out = StringIO.StringIO()
my_writer.path = xl_out  
df.to_excel(my_writer)
my_writer.save()
print base64.b64encode(xl_out.getvalue())

这是一种快速、简单且略显肮脏的方法。顺便说一句...一种更简洁的方法是将 ExcelWriter 子类化(或其中一个现有的子类,例如 _XlwtWriter)——但老实说,更新路径属性很少涉及,我投票向您展示简单的方法而不是去稍长的路线。

【讨论】:

【参考方案2】:

Pandas 期望 ExcelWriter 构造函数的文件名路径,尽管每个编写器引擎都支持StringIO。也许这应该作为 Pandas 中的错误/功能请求提出。

同时,这里有一个使用 Pandas xlsxwriter 引擎的解决方法示例:

import pandas as pd
import StringIO

io = StringIO.StringIO()

# Use a temp filename to keep pandas happy.
writer = pd.ExcelWriter('temp.xlsx', engine='xlsxwriter')

# Set the filename/file handle in the xlsxwriter.workbook object.
writer.book.filename = io

# Write the data frame to the StringIO object.
pd.DataFrame().to_excel(writer, sheet_name='Sheet1')
writer.save()
xlsx_data = io.getvalue()

更新:从 Pandas 0.17 开始,现在可以更直接地执行此操作:

# Note, Python 2 example. For Python 3 use: output = io.BytesIO().
output = StringIO.StringIO()

# Use the StringIO object as the filehandle.
writer = pd.ExcelWriter(output, engine='xlsxwriter')

另请参阅 XlsxWriter 文档中的 Saving the Dataframe output to a string。

【讨论】:

谢谢——单行解决方法效果很好! 这是刚刚在 pandas 中添加的,请参见此处:github.com/pydata/pandas/pull/10376。将在 0.17.0 版本中发布(可能 7 月底) 对我来说帮助很大——但在将输出提供给 Flask 时缺少一件关键的事情:output.seek(0)【参考方案3】:

对于那些不使用xlsxwriter 作为engine=to_excel 的人,这是在内存中使用openpyxl 的解决方案:

in_memory_file = StringIO.StringIO()
xlw = pd.ExcelWriter('temp.xlsx', engine='openpyxl')
# ... do many .to_excel() thingies
xlw.book.save(in_memory_file)
# if you want to read it or stream to a client, don't forget this
in_memory_file.seek(0)

说明:ExcelWriter 包装类通过 .book 属性公开引擎的各个工作簿。对于openpyxl,您可以照常使用Workbook.save 方法!

【讨论】:

【参考方案4】:

这些都不是我的工作。我有一个观点,我想从 Django 中返回一个 excel 工作簿。我找到了我的解决方案from the pandas documentation。

import io
bio = io.BytesIO()
writer = pd.ExcelWriter(bio, engine='xlsxwriter')
df.to_excel(writer, sheet_name='Sheet1')
writer.save()
bio.seek(0)

# BONUS CONTENT
# .. because I wanted to return from an api
response = HttpResponse(bio, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=myfile.xlsx'
return response # returned from a view here

注意,我使用该值作为内容类型,因为根据 mozzilla 文档,它是 mime 类型。来自以下链接中的“.xlsx”。根据需要更换。 https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types

【讨论】:

以上是关于使用 Pandas Excelwriter 写入 StringIO 对象?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Pandas 写入时文件损坏

pandas写入数据

Pandas ExcelWriter .save() 出错;权限错误 [WinError 32]

Python:在多张工作表上将 Pandas DataFrame 写入 Excel 的最快方法

Python 如何使用 ExcelWriter 写入现有工作表

有没有办法在使用 pandas Excelwriter 时为某些特定行设置列的宽度?