data-export-pdf について
reportlabを使ってローカルでPDFレポートを作成するスキルです。解析の概要、整形した表、図の埋め込み、文書出力に対応し、ページ構成や各要素の配置例も示します。
主な機能
- 01テキスト入りPDFの作成
- 02表や図の追加
- 03ヘッダー・フッター・段組みの設定
- 04ページと画像配置の案内
活用シーン
- 01QC指標のレポートを出力
- 02差次的発現結果を要約
- 03解析図と説明をPDFにまとめる
- 04論文用の補足資料を作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ omicverse/ omicverse --skill data-export-pdfインストール後、Claude Code で/data-export-pdfを入力して読み込まれたか確認します。
詳細
PDF レポートの生成(ユニバーサル)
概要
このスキルを使うと、分析の概要、書式設定された表、埋め込みの可視化を含む、プロフェッショナルな PDF レポートを作成できます。クラウドホスト型のソリューションとは異なり、このスキルは Python ライブラリの reportlab を使って環境内で ローカルに実行されるため、GPT、Gemini、Claude、DeepSeek、Qwen など、すべての LLM プロバイダーと互換性があります。
このスキルを使う場面
- テキストと表を含む分析レポートを生成する
- プロットを埋め込んだ概要 PDF を作成する
- 書式設定されたドキュメントを書き出す
- 出版可能な品質の補足資料を作成する
- 複数の分析結果を 1 つのドキュメントにまとめる
使用方法
ステップ 1:必要なライブラリをインポートする
from reportlab.lib.pagesizes import letter, A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from datetime import datetime
import matplotlib.pyplot as plt
ステップ 2:基本的な PDF ドキュメントを作成する
# Create PDF file
pdf_filename = "analysis_report.pdf"
doc = SimpleDocTemplate(pdf_filename, pagesize=letter)
story = [] # Container for PDF elements
# Get default styles
styles = getSampleStyleSheet()
title_style = styles['Title']
heading_style = styles['Heading1']
normal_style = styles['Normal']
# Add title
story.append(Paragraph("Analysis Report", title_style))
story.append(Spacer(1, 0.2*inch))
# Add date
date_text = f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}"
story.append(Paragraph(date_text, normal_style))
story.append(Spacer(1, 0.3*inch))
# Build PDF
doc.build(story)
print(f"✅ PDF saved to: {pdf_filename}")
ステップ 3:テキストコンテンツを追加する
story = []
# Title
story.append(Paragraph("Single-Cell RNA-seq Analysis Report", title_style))
story.append(Spacer(1, 0.2*inch))
# Section heading
story.append(Paragraph("1. Overview", heading_style))
story.append(Spacer(1, 0.1*inch))
# Paragraph text
overview_text = """
This report summarizes the single-cell RNA-seq analysis performed on the dataset.
The analysis includes quality control, normalization, dimensionality reduction,
clustering, and cell type annotation.
"""
story.append(Paragraph(overview_text, normal_style))
story.append(Spacer(1, 0.2*inch))
ステップ 4:表を追加する
# Prepare table data
table_data = [
['Metric', 'Value'], # Header
['Total Cells', '5,000'],
['Total Genes', '20,000'],
['Mean Genes/Cell', '2,500'],
['Median UMIs/Cell', '10,000']
]
# Create table
table = Table(table_data, colWidths=[2.5*inch, 2*inch])
# Style table
table.setStyle(TableStyle([
# Header styling
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 12),
# Body styling
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 1), (-1, -1), 10),
]))
story.append(table)
story.append(Spacer(1, 0.3*inch))
ステップ 5:画像/プロットを埋め込む
# Save matplotlib figure first
fig, ax = plt.subplots(figsize=(6, 4))
# ... create your plot ...
plot_filename = "temp_plot.png"
fig.savefig(plot_filename, dpi=150, bbox_inches='tight')
plt.close(fig)
# Add image to PDF
story.append(Paragraph("2. UMAP Visualization", heading_style))
story.append(Spacer(1, 0.1*inch))
img = Image(plot_filename, width=4*inch, height=3*inch)
story.append(img)
story.append(Spacer(1, 0.2*inch))
完全な例:分析レポート
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
from datetime import datetime
import matplotlib.pyplot as plt
import pandas as pd
def create_analysis_report(adata, output_path="analysis_report.pdf"):
"""Create comprehensive PDF analysis report"""
# Initialize PDF
doc = SimpleDocTemplate(output_path, pagesize=letter)
story = []
styles = getSampleStyleSheet()
# Title
story.append(Paragraph("Single-Cell RNA-seq Analysis Report", styles['Title']))
story.append(Spacer(1, 0.2*inch))
story.append(Paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", styles['Normal']))
story.append(Spacer(1, 0.3*inch))
# Overview
story.append(Paragraph("1. Dataset Overview", styles['Heading1']))
story.append(Spacer(1, 0.1*inch))
overview_data = [
['Metric', 'Value'],
['Total Cells', f'{adata.n_obs:,}'],
['Total Genes', f'{adata.n_vars:,}'],
['Observations', ', '.join(adata.obs.columns[:5].tolist())],
]
table = Table(overview_data, colWidths=[2.5*inch, 3.5*inch])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
]))
story.append(table)
story.append(Spacer(1, 0.3*inch))
# Cluster distribution
if 'clusters' in adata.obs:
story.append(Paragraph("2. Cluster Distribution", styles['Heading1']))
story.append(Spacer(1, 0.1*inch))
cluster_counts = adata.obs['clusters'].value_counts().sort_index()
cluster_data = [['Cluster', 'Cell Count', 'Percentage']]
total_cells = adata.n_obs
for cluster, count in cluster_counts.items():
percentage = (count / total_cells) * 100
cluster_data.append([str(cluster), str(count), f'{percentage:.1f}%'])
table = Table(cluster_data, colWidths=[1.5*inch, 1.5*inch, 1.5*inch])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, 1), (-1, -1), colors.lightblue),
]))
story.append(table)
story.append(Spacer(1, 0.3*inch))
# Visualization (if UMAP exists)
if 'X_umap' in adata.obsm:
story.append(Paragraph("3. UMAP Visualization", styles['Heading1']))
story.append(Spacer(1, 0.1*inch))
# Create UMAP plot
fig, ax = plt.subplots(figsize=(6, 5))
scatter = ax.scatter(
adata.obsm['X_umap'][:, 0],
adata.obsm['X_umap'][:, 1],
c=adata.obs['clusters'].astype('category').cat.codes if 'clusters' in adata.obs else 'blue',
s=5, alpha=0.5
)
ax.set_xlabel('UMAP1')
ax.set_ylabel('UMAP2')
ax.set_title('UMAP Projection')
plot_path = 'temp_umap.png'
fig.savefig(plot_path, dpi=150, bbox_inches='tight')
plt.close(fig)
img = Image(plot_path, width=5*inch, height=4*inch)
story.append(img)
# Build PDF
doc.build(story)
print(f"✅ PDF report saved to: {output_path}")
return output_path
# Usage
create_analysis_report(adata, "my_analysis_report.pdf")
ベストプラクティス
- ページサイズ:標準的なドキュメントには、
letter(米国)またはA4(国際標準)を使います - 余白:SimpleDocTemplate にはデフォルトの余白(1 インチ)が設定されています。
leftMargin、rightMarginなどで調整します - 画像:品質を保つため、matplotlib の図は 150~300 DPI で保存します
- 表:読みやすさを保てるよう、列数は適切な範囲(最大 4~6 列)にします
- ファイルのクリーンアップ:PDF の作成後に一時画像ファイルを削除します
- メモリ:大きなドキュメントでは、メモリを管理できるようセクションごとに作成します
高度な機能
カスタムのページヘッダー/フッター
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
def add_header_footer(canvas_obj, doc):
canvas_obj.saveState()
# Header
canvas_obj.setFont('Helvetica', 9)
canvas_obj.drawString(inch, letter[1] - 0.5*inch, "Analysis Report")
# Footer
canvas_obj.drawString(inch, 0.5*inch, f"Page {doc.page}")
canvas_obj.restoreState()
doc = SimpleDocTemplate(pdf_filename, pagesize=letter)
doc.build(story, onFirstPage=add_header_footer, onLaterPages=add_header_footer)
複数列レイアウト
from reportlab.platypus import Frame, PageTemplate
frame1 = Frame(doc.leftMargin, doc.bottomMargin, doc.width/2-6, doc.height, id='col1')
frame2 = Frame(doc.leftMargin+doc.width/2+6, doc.bottomMargin, doc.width/2-6, doc.height, id='col2')
doc.addPageTemplates([PageTemplate(id='TwoCol', frames=[frame1, frame2])])
色分けされた表
# Highlight significant results
for i, row in enumerate(deg_results):
if row['qvalue'] < 0.05:
table.setStyle(TableStyle([
('BACKGROUND', (0, i+1), (-1, i+1), colors.yellow)
]))
よくある使用例
QC レポート
qc_metrics = {
'Total Cells': adata.n_obs,
'Median Genes/Cell': int(adata.obs['n_genes'].median()),
'Median UMIs/Cell': int(adata.obs['n_counts'].median()),
'Mean Mito %': f"{adata.obs['percent_mito'].mean():.2f}%"
}
table_data = [['Metric', 'Value']] + [[k, str(v)] for k, v in qc_metrics.items()]
# ... create table as shown above
DEG 概要表
# Top 10 upregulated genes
top_genes = deg_df.nlargest(10, 'log2FC')[['gene', 'log2FC', 'qvalue']]
table_data = [['Gene', 'log2FC', 'Q-value']]
for _, row in top_genes.iterrows():
table_data.append([row['gene'], f"{row['log2FC']:.2f}", f"{row['qvalue']:.2e}"])
トラブルシューティング
問題:「reportlab が見つかりません」
解決方法:
import subprocess
subprocess.check_call(['pip', 'install', 'reportlab'])
問題:「画像が見つかりません」
解決方法:PDF に追加する前に、画像のパスが正しく、ファイルが存在することを確認します:
import os
if os.path.exists(plot_filename):
img = Image(plot_filename, width=4*inch, height=3*inch)
story.append(img)
問題:「表がページ幅を超えています」
解決方法:列幅またはフォントサイズを小さくします:
table = Table(data, colWidths=[1.5*inch, 1.5*inch, 2*inch])
table.setStyle(TableStyle([('FONTSIZE', (0, 0), (-1, -1), 8)]))
技術メモ
- ライブラリ:
reportlabを使用します(純粋な Python で、幅広くサポートされています) - 実行:エージェントのサンドボックス内でローカルに実行されます
- 互換性:GPT、Gemini、Claude、DeepSeek、Qwen など、すべての LLM プロバイダーで動作します
- ファイルサイズ:テキスト中心の PDF は小さく(1 MB 未満)、画像中心の PDF は 5~20 MB になることがあります
- パフォーマンス:一般的なレポートの生成には 1~3 秒かかります
参考資料
- reportlab のドキュメント:https://www.reportlab.com/docs/reportlab-userguide.pdf
- reportlab の platypus ガイド:https://www.reportlab.com/software/opensource/rl-toolkit/guide/
コメント
Markdown 対応