agentsclimarketplace

Report generator

Skill tathadn/data-quality-skills-pipeline/skills/report-generator

Automated data quality auditing pipeline using Claude Skills — generates multi-page PDF reports with charts and Excel workbooks from any CSV dataset.

Install
npx -y skills add tathadn/data-quality-skills-pipeline --skill report-generator

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Generates a professional PDF summary report with charts, figures, and written analysis from a clean CSV or Excel dataset, plus a companion Excel workbook with detailed statistics. Produces bar charts, pie charts, histograms, trend lines, and correlation analysis. Use this skill when the user asks for a data summary report, dataset overview, data profile report, analytics report, or wants a professional document summarizing their data — even if they just say 'summarize this data', 'create a report', or 'analyze this file'.

SKILL.md

14.6 KB, ~3.8k tokens by cl100k_base, as published. Nobody here has run it

Report Generator Skill

Purpose

Transform a dataset into two professional deliverables:

  1. A PDF report with charts, figures, tables, and written analysis — ready for stakeholders
  2. An Excel workbook with detailed statistics and raw profile data — for analysts who want to dig deeper

Both outputs are generated every time.

Dependencies

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('Agg')  # REQUIRED — no display server
import matplotlib.pyplot as plt
import seaborn as sns
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.colors import HexColor
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, PageBreak,
    Table, TableStyle, Image as RLImage, HRFlowable
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment
from datetime import datetime
import os
import tempfile

Color Palette

Same palette as the Data Auditor for visual consistency:

COLORS = {
    'primary': '#1a56db',
    'primary_light': '#e8eefb',
    'accent': '#10b981',
    'accent_light': '#d1fae5',
    'warning': '#f59e0b',
    'danger': '#ef4444',
    'purple': '#8b5cf6',
    'gray_dark': '#374151',
    'gray_medium': '#6b7285',
    'gray_light': '#f3f4f6',
    'white': '#ffffff',
}
CHART_PALETTE = ['#1a56db', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16']

plt.rcParams.update({
    'figure.facecolor': 'white',
    'axes.facecolor': 'white',
    'axes.edgecolor': '#e5e7eb',
    'axes.grid': True,
    'grid.alpha': 0.3,
    'font.family': 'sans-serif',
    'font.size': 10,
    'figure.dpi': 150,
})

When to Use

Activate this skill when the user:

  • Uploads a CSV or Excel file and asks for a "report", "summary", or "overview"
  • Says "summarize this data", "create a report", or "analyze this file"
  • Wants a professional document to share with stakeholders or include in a presentation
  • Has already audited/cleaned data and wants the final analysis deliverable
  • Asks for "insights", "findings", or "what does this data tell us"

Report Generation Procedure

Step 1: Load and Analyze

df = pd.read_csv(filepath)  # or pd.read_excel()
  • Identify column types: numeric, categorical, datetime, boolean
  • Compute summary statistics for all columns
  • Detect date columns (try pd.to_datetime on string columns)
  • Calculate completeness rate: (non_null_values / total_values) * 100
  • Identify potential ID columns (all unique values) and exclude from analysis
  • Detect correlations between numeric columns

Step 2: Generate All Charts

Save every chart as a temporary PNG. Always use plt.savefig(path, bbox_inches='tight', dpi=150) then plt.close().

Figure 1: Dataset Overview Bar Chart

A horizontal bar showing key metrics:

fig, ax = plt.subplots(figsize=(8, 3))
metrics = ['Total Records', 'Total Fields', 'Numeric Fields', 'Categorical Fields', 'Completeness %']
values = [total_rows, total_cols, n_numeric, n_categorical, completeness_pct]
colors = [COLORS['primary'], COLORS['primary'], COLORS['accent'], COLORS['purple'], COLORS['warning']]
bars = ax.barh(metrics, values, color=colors, edgecolor='white', height=0.6)
# Add value labels on bars
for bar, val in zip(bars, values):
    ax.text(bar.get_width() + max(values)*0.02, bar.get_y() + bar.get_height()/2,
            f'{val:,.0f}' if val > 1 else f'{val:.1f}%', va='center', fontsize=10, fontweight='bold')
ax.set_title('Dataset Overview', fontsize=14, fontweight='bold')
ax.set_xlim(0, max(values) * 1.2)
plt.savefig(overview_path, bbox_inches='tight', dpi=150)
plt.close()

Figure 2: Data Composition Pie Chart

Pie chart showing the proportion of numeric vs categorical vs datetime columns:

fig, ax = plt.subplots(figsize=(5, 5))
type_counts = [n_numeric, n_categorical, n_datetime, n_other]
type_labels = ['Numeric', 'Categorical', 'Datetime', 'Other']
# Filter out zero-count types
non_zero = [(l, c) for l, c in zip(type_labels, type_counts) if c > 0]
labels, counts = zip(*non_zero)
colors_pie = [COLORS['primary'], COLORS['accent'], COLORS['warning'], COLORS['purple']][:len(labels)]
wedges, texts, autotexts = ax.pie(counts, labels=labels, colors=colors_pie,
    autopct='%1.0f%%', startangle=90, textprops={'fontsize': 10},
    wedgeprops={'edgecolor': 'white', 'linewidth': 2})
ax.set_title('Column Type Distribution', fontsize=14, fontweight='bold')
plt.savefig(composition_path, bbox_inches='tight', dpi=150)
plt.close()

Figure 3: Distribution Histograms (Numeric Columns)

For up to 6 numeric columns, create a grid of histograms:

numeric_cols = df.select_dtypes(include=[np.number]).columns
# Exclude likely ID columns (all unique values)
numeric_cols = [c for c in numeric_cols if df[c].nunique() < len(df) * 0.9]
plot_cols = numeric_cols[:6]
if len(plot_cols) > 0:
    n_rows = (len(plot_cols) + 2) // 3
    fig, axes = plt.subplots(nrows=n_rows, ncols=3, figsize=(12, 3.5 * n_rows))
    axes = np.array(axes).flatten()
    for i, col in enumerate(plot_cols):
        ax = axes[i]
        data = df[col].dropna()
        ax.hist(data, bins=min(30, max(10, len(data)//50)), 
                color=CHART_PALETTE[i % len(CHART_PALETTE)], edgecolor='white', alpha=0.85)
        # Add median line
        median_val = data.median()
        ax.axvline(median_val, color=COLORS['danger'], linestyle='--', linewidth=1.5)
        ax.set_title(f'{col}', fontsize=11, fontweight='bold')
        # Add stats annotation
        skew = data.skew()
        skew_label = 'Left-skewed' if skew < -0.5 else 'Right-skewed' if skew > 0.5 else 'Symmetric'
        ax.text(0.97, 0.95, f'Median: {median_val:,.1f}\n{skew_label}',
                transform=ax.transAxes, ha='right', va='top', fontsize=7,
                bbox=dict(boxstyle='round,pad=0.3', facecolor='white', edgecolor='#e5e7eb', alpha=0.9))
    for j in range(len(plot_cols), len(axes)):
        axes[j].set_visible(False)
    fig.suptitle('Value Distributions', fontsize=14, fontweight='bold', y=1.02)
    plt.tight_layout()
    plt.savefig(hist_path, bbox_inches='tight', dpi=150)
    plt.close()

Figure 4: Top Categories Bar Charts (Categorical Columns)

For up to 4 categorical columns, show the top 8 most frequent values:

cat_cols = df.select_dtypes(include=['object', 'category']).columns
# Exclude likely ID or high-cardinality columns
cat_cols = [c for c in cat_cols if 2 < df[c].nunique() < 50][:4]
if len(cat_cols) > 0:
    fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
    axes = axes.flatten()
    for i, col in enumerate(cat_cols):
        ax = axes[i]
        top_values = df[col].value_counts().head(8)
        bars = ax.barh(top_values.index.astype(str), top_values.values,
                       color=CHART_PALETTE[i % len(CHART_PALETTE)], edgecolor='white')
        ax.set_title(f'{col} — Top Values', fontsize=11, fontweight='bold')
        ax.invert_yaxis()
        # Add count labels
        for bar, val in zip(bars, top_values.values):
            ax.text(bar.get_width() + max(top_values.values)*0.02, 
                    bar.get_y() + bar.get_height()/2, f'{val:,}', va='center', fontsize=8)
    for j in range(len(cat_cols), len(axes)):
        axes[j].set_visible(False)
    fig.suptitle('Top Categories', fontsize=14, fontweight='bold', y=1.02)
    plt.tight_layout()
    plt.savefig(categories_path, bbox_inches='tight', dpi=150)
    plt.close()

Figure 5: Correlation Heatmap (If 3+ Numeric Columns)

numeric_for_corr = df[numeric_analysis_cols].dropna()
if len(numeric_analysis_cols) >= 3:
    corr_matrix = numeric_for_corr.corr()
    fig, ax = plt.subplots(figsize=(max(6, len(numeric_analysis_cols)*0.8), 
                                     max(5, len(numeric_analysis_cols)*0.7)))
    mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
    sns.heatmap(corr_matrix, mask=mask, annot=True, fmt='.2f', cmap='RdBu_r',
                center=0, vmin=-1, vmax=1, ax=ax, linewidths=0.5,
                cbar_kws={'shrink': 0.8, 'label': 'Correlation'},
                annot_kws={'fontsize': 8})
    ax.set_title('Correlation Matrix', fontsize=14, fontweight='bold')
    plt.xticks(rotation=45, ha='right', fontsize=9)
    plt.yticks(fontsize=9)
    plt.savefig(corr_path, bbox_inches='tight', dpi=150)
    plt.close()

Figure 6: Trend Lines (Only If Date Column Exists)

if date_col is not None:
    trend_cols = numeric_analysis_cols[:3]
    if len(trend_cols) > 0:
        fig, axes = plt.subplots(nrows=len(trend_cols), ncols=1,
                                  figsize=(10, 3.5 * len(trend_cols)), sharex=True)
        if len(trend_cols) == 1:
            axes = [axes]
        sorted_df = df.sort_values(date_col)
        for i, col in enumerate(trend_cols):
            ax = axes[i]
            ax.plot(sorted_df[date_col], sorted_df[col], 
                    color=CHART_PALETTE[i], alpha=0.3, linewidth=0.8)
            # Rolling average
            window = max(7, len(sorted_df) // 20)
            rolling = sorted_df[col].rolling(window=window, center=True).mean()
            ax.plot(sorted_df[date_col], rolling, color=CHART_PALETTE[i], 
                    linewidth=2.5, label=f'{window}-period moving avg')
            ax.set_ylabel(col, fontsize=10)
            ax.legend(fontsize=8, loc='upper left')
            # Shade min/max range
            ax.fill_between(sorted_df[date_col], sorted_df[col].min(), sorted_df[col],
                           alpha=0.05, color=CHART_PALETTE[i])
        axes[-1].set_xlabel('Date')
        fig.suptitle('Trends Over Time', fontsize=14, fontweight='bold', y=1.01)
        plt.tight_layout()
        plt.savefig(trend_path, bbox_inches='tight', dpi=150)
        plt.close()

Step 3: Generate PDF Report

Structure the PDF as follows:

Page 1: Cover Page

  • Report title: "Data Summary Report"
  • Subtitle: Dataset filename
  • Date generated
  • Overview bar chart (Figure 1)
  • Key stats: rows, columns, completeness rate

Page 2: Executive Summary & Composition

  • 3-4 paragraph plain-language summary answering:
    • What is this dataset about? (infer from column names)
    • How large is it and what time period does it cover?
    • What are the most important findings?
  • Data Composition pie chart (Figure 2)
  • Key Metrics table

Page 3: Distribution Analysis

  • Histogram grid (Figure 3)
  • Written interpretation for each column:
    • Is the distribution skewed? Normal? Bimodal?
    • What are the key statistics (mean, median, std)?
    • Any notable outliers?

Page 4: Categorical Analysis

  • Top Categories bar charts (Figure 4)
  • Written notes:
    • Does any single value dominate?
    • How many unique values per column?

Page 5: Correlation Analysis (only if 3+ numeric columns)

  • Correlation heatmap (Figure 5)
  • Written notes on strong correlations (|r| > 0.7)
  • Warn about potential multicollinearity

Page 6: Trend Analysis (only if date column exists)

  • Trend line charts (Figure 6)
  • Written interpretation:
    • Is the trend increasing, decreasing, or stable?
    • Any seasonal patterns?
    • Any sudden spikes or drops?

Page 7: Detailed Column Profiles

  • Table with one row per column:
    • Column name, type, non-null count, unique values
    • For numeric: mean, median, std, min, max
    • For categorical: top value, top value frequency

Page 8: Notable Findings & Methodology

  • 3-5 key insights with specific numbers
  • Brief methodology paragraph
  • Suggestions for further analysis

PDF Formatting Rules:

  • Page size: US Letter (8.5" x 11")
  • Margins: 0.75 inches
  • Title: Helvetica-Bold, 24pt, #1a56db
  • Heading 1: Helvetica-Bold, 16pt, #374151
  • Heading 2: Helvetica-Bold, 13pt, #374151
  • Body: Helvetica, 10pt, #4b5563, justified
  • Tables: #1a56db header with white text, alternating row colors
  • Page numbers: bottom center
  • Header line: thin #1a56db line below "Data Summary Report" on every page

Step 4: Generate Excel Workbook

Create a companion Excel file with four sheets:

Sheet 1: "Overview"

Key metrics in a clean two-column layout: Dataset Name, Total Rows, Total Columns, Completeness Rate, Numeric Columns, Categorical Columns, Date Range (if applicable), Generated On.

Sheet 2: "Column Profiles"

One row per column with: Column Name, Data Type, Non-Null Count, Null Count, Null %, Unique Values, Top Value, Top Value %, Mean (numeric), Median (numeric), Std Dev (numeric), Min (numeric), Max (numeric).

Sheet 3: "Correlations"

The full correlation matrix for numeric columns. Apply conditional formatting: strong positive (> 0.7) in green, strong negative (< -0.7) in red.

Sheet 4: "Top Categories"

For each categorical column: Column Name, Value, Count, Percentage. Include top 10 values per column.

Excel formatting: Same as Data Auditor (bold headers, alternating rows, auto-width, filters enabled).

Step 5: Deliver Both Files

  • Save PDF as: report_[dataset_name]_[YYYY-MM-DD].pdf
  • Save Excel as: report_data_[dataset_name]_[YYYY-MM-DD].xlsx
  • Present both files to the user
  • Provide a verbal summary: the 2-3 most interesting findings

Edge Cases

  • Empty dataset: Return error, no report
  • Single column: Generate report but simplify layout (no correlation, no multi-chart pages)
  • All categorical: Skip histograms, correlation, and trend pages
  • All numeric: Skip categorical analysis page
  • No date column: Skip trend analysis page, note in report
  • Very wide (> 50 columns): Profile first 20 in detail, summarize rest
  • Very large (> 100k rows): Sample 10k rows for charts, use full data for stats where feasible
  • Fewer than 3 numeric columns: Skip correlation heatmap

What NOT To Do

  • Do NOT make causal claims — describe correlations and patterns only
  • Do NOT skip any applicable chart — if the data supports it, include it
  • Do NOT use plt.show() — always plt.savefig() then plt.close()
  • Do NOT generate only text — PDF with figures + Excel are always required
  • Do NOT forget matplotlib.use('Agg') at the top
  • Do NOT use jargon in the Executive Summary without explanation
  • Do NOT include raw data dumps — summarize and visualize instead

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.