当我使用 reportlab 创建表格时尝试将颜色应用于特定列时出现错误。
下面是我为可重现示例编写的完整脚本(您需要在第 109 行输入输出路径):
import pandas as pd
from datetime import datetime
from reportlab.platypus import Frame
from reportlab.lib.pagesizes import A4, landscape
from reportlab.platypus import PageTemplate
from reportlab.platypus import BaseDocTemplate
from reportlab.platypus import Image
from reportlab.lib.units import inch
from reportlab.platypus import Table, Paragraph
from reportlab.lib import colors
from reportlab.platypus import NextPageTemplate, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
def on_page(canvas, doc, pagesize=A4):
page_num = canvas.getPageNumber()
canvas.drawCentredString(pagesize[0]/2, 50, str(page_num))
now = datetime.now()
today = now.strftime("%B %d, %Y")
current_time = now.strftime("%I:%M %p")
canvas.drawString(50, pagesize[1] - 50, f"{today}")
canvas.drawString(50, pagesize[1] - 70, f"{current_time}")
def on_page_landscape(canvas, doc):
return on_page(canvas, doc, pagesize=landscape(A4))
def format_nums(num):
if num < 0:
x = '(${:.2f})'.format(abs(num))
elif num > 0:
x = '${:.2f}'.format(num)
else:
x = '${:.0f}'.format(num)
return x
def df2table(df, custom_style):
for col in df.columns:
if df[col].dtype == 'float64':
df[col] = df[col].apply(lambda x: format_nums(x))
#('BACKGROUND', (start_col, start_row), (end_col, end_row), color)
return Table(
[[Paragraph(col, custom_style) for col in df.columns]] + df.values.tolist(),
style = [
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), # Header font
('FONTSIZE', (0, 1), (-1, -1), 8), # Body font size
('TEXTCOLOR', (0, 0), (-1, 0), colors.white), # Header text color
('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey), # Header background color
('BACKGROUND', (0, 0), (0, 0), colors.lightblue), # first Header background color
('BACKGROUND', (0, 1), (0, -1), colors.lightblue), # First column background color
('LINEBELOW', (0, 0), (-1, 0), 1, colors.black), # Line below header
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black), # Inner grid lines
('BOX', (0, 0), (-1, -1), 1, colors.black)], # Outer box
hAlign = 'LEFT')
def df2table2(df, custom_style, commodity_cols):
for col in df.columns:
if df[col].dtype == 'float64':
df[col] = df[col].apply(lambda x: format_nums(x))
data = [[Paragraph(col, custom_style) for col in df.columns]] + df.values.tolist()
#('BACKGROUND', (start_col, start_row), (end_col, end_row), color)
style = [
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), # Header font
('FONTSIZE', (0, 1), (-1, -1), 8), # Body font size
('TEXTCOLOR', (0, 0), (-1, 0), colors.white), # Header text color
('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey), # Header background color
('LINEBELOW', (0, 0), (-1, 0), 1, colors.black), # Line below header
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black), # Inner grid lines
('BOX', (0, 0), (-1, -1), 1, colors.black)] # Outer box
# Apply colors based on column types
for i, col in enumerate(df.columns):
if col in commodity_cols:
style.append(('BACKGROUND', (i, 1), (i, -1), colors.lightgrey)) # Commodity column
else:
if col != 'Counterparty':
style.append(('BACKGROUND', (i, 1), (i, -1), colors.lightgreen)) # Aggregation column
return Table(data, style, hAlign='LEFT')
df = pd.DataFrame({
'Counterparty': ['foo', 'fizz', 'fizz', 'fizz','fizz', 'foo'],
'Commodity': ['bar', 'bar', 'bar', 'bar','bar', 'ab cred'],
'DealType': ['Buy', 'Buy', 'Buy', 'Buy', 'Buy', 'Buy'],
'StartDate': ['07/01/2024', '09/01/2024', '10/01/2024', '11/01/2024', '12/01/2024', '01/01/2025'],
'FloatPrice': [18.73, 17.12, 17.76, 18.72, 19.47, 20.26],
'MTMValue':[10, 10, 10, 10, 10, 10]
})
commodity_cols = df['Commodity'].unique()
out = pd.pivot_table(df, values = 'MTMValue', index='Counterparty', columns = 'Commodity', aggfunc='sum').reset_index().rename_axis(None, axis=1).fillna(0)
out['Cumulative Exposure'] = out[out.columns[1:]].sum(axis = 1)
path = <INSERT PDF OUTPUT FILE HERE>
padding = dict(
leftPadding=72,
rightPadding=72,
topPadding=72,
bottomPadding=18)
portrait_frame = Frame(0, 0, *A4, **padding)
landscape_frame = Frame(0, 0, *landscape(A4), **padding)
portrait_template = PageTemplate(
id='portrait',
frames=portrait_frame,
onPage=on_page,
pagesize=A4)
landscape_template = PageTemplate(
id='landscape',
frames=landscape_frame,
onPage=on_page_landscape,
pagesize=landscape(A4))
doc = BaseDocTemplate(
path,
pageTemplates=[
#portrait_template
landscape_template
]
)
styles = getSampleStyleSheet()
custom_style = ParagraphStyle(name='CustomStyle', fontSize=8)
# NOT WOKRING
#story = [
# Paragraph('Title 1', styles['Title']),
# Paragraph('Title 2', styles['Title']),
# Paragraph("<br/><br/>", styles['Normal']),
# Paragraph('Current Total Positions', styles['Heading2']),
# df2table2(out, custom_style, commodity_cols)
#]
# WORKING
story = [
Paragraph('Title 1', styles['Title']),
Paragraph('Tilte 2', styles['Title']),
Paragraph("<br/><br/>", styles['Normal']),
Paragraph('Current Total Positions', styles['Heading2']),
df2table(out, custom_style)
]
doc.build(story)
我的问题出现在df2table2
函数中的某个地方,并带有完整的错误消息:
unsupported operand type(s) for -=: 'float' and 'tuple'
我已经看了好几个小时了,但无论如何也想不出到底出了什么问题。有什么想法可以解决吗?
问题就出在你的
return
表述上:请注意,您正在使用
style
作为位置参数。以下是 签名的一部分Table
:含义:这里
style
是第四个参数,但是当你style
作为位置参数传递时,它会传递给第二个参数,即,colWidths
它显然需要浮点数,而不是元组,例如('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold')
,等等。因此,解决方法是使用关键字参数:
或者,如果您想使用位置参数,则在添加之前添加
colWidths
和的值:rowHeights
style
修复后结果片段: