Estou recebendo um erro ao tentar aplicar uma cor a colunas específicas ao criar uma tabela com o reportlab.
Aqui está meu script completo para um exemplo reproduzível (você precisará colocar um caminho de saída na linha 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)
Meu problema surge em algum lugar da df2table2
função com a mensagem de erro completa:
unsupported operand type(s) for -=: 'float' and 'tuple'
Estou olhando para isso há horas e não consigo descobrir o que pode estar errado. Alguma ideia do que pode ser o problema?
O problema está na sua
return
afirmação:Note que você está usando
style
como argumento posicional. Aqui está parte da assinatura deTable
:Significado: aqui
style
está o quarto parâmetro, mas quando você passastyle
como um argumento posicional , ele é passado para o segundo parâmetro, ou seja,colWidths
, que aparentemente espera floats, em vez de tuplas, como('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold')
, etc.Então, a solução é usar argumentos de palavras-chave:
Ou, se você quiser usar argumentos posicionais, para adicionar valores para
colWidths
erowHeights
antes de adicionarstyle
:Trecho do resultado após a correção: