Metadata-Version: 2.4
Name: clean-charts
Version: 0.6.1
Summary: A Python library to generate beautiful, clean charts in premium visual styles
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: matplotlib>=3.5.0
Requires-Dist: pandas>=1.3.0
Requires-Dist: numpy>=1.20.0
Requires-Dist: pillow>=8.0.0
Requires-Dist: scipy>=1.7.0
Dynamic: license-file

# Clean Charts

[![PyPI](https://img.shields.io/pypi/v/clean-charts)](https://pypi.org/project/clean-charts/)
[![Python](https://img.shields.io/badge/python-≥3.8-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://raghuramsirigiri.github.io/viz_library/)

![Dashboard](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/dashboard.png)

**Clean Charts** is a lightweight Python library for creating beautiful, publication-quality data visualizations inspired by modern editorial styles like *The Economist*. Go from raw data to presentation-ready charts in a single function call — no styling boilerplate required.

---

## ✨ Highlights

| Feature | Details |
| :--- | :--- |
| **Publication-ready aesthetics** | Cream-gray backgrounds, minimal gridlines, elegant sans-serif typography, and consistent padding — out of the box. |
| **Jupyter-first workflow** | Set `output_path=None` (the default) and charts render inline instantly. |
| **Six chart types** | Time-series lines, horizontal bars, grouped bars, stacked bars, donut charts, and **multi-chart dashboards**. |
| **Smart defaults** | Auto date parsing, PCHIP spline smoothing, overlap-free label placement, and adaptive scaling for any canvas size. |
| **One-line theming** | Override a handful of `config` variables to restyle every chart in your notebook at once. |

---

## 📦 Installation

```bash
pip install clean-charts
```

**Dependencies** (installed automatically): `matplotlib ≥ 3.5`, `pandas ≥ 1.3`, `numpy ≥ 1.20`, `Pillow ≥ 8.0`, `scipy ≥ 1.7`.

---

## 🚀 Quick Start

### 1. Grouped Horizontal Bar Chart

Display multiple numeric series side-by-side for each category, with an automatic color gradient and legend.

```python
import pandas as pd
from clean_charts import plot_grouped_barh_chart

df = pd.DataFrame({
    'Fruit': ['Apples', 'Bananas', 'Cherries', 'Dates', 'Elderberries', 'Figs', 'Grapes'],
    '2024': [380, 410, 150, 420, 85,  280, 490],
    '2025': [510, 180, 830, 450, 190, 240, 560],
    '2026': [415, 450, 590, 310, 60,  310, 400]
})

plot_grouped_barh_chart(
    data = df,
    title="Regional Supermarket Inventory and Fruit Category Performance Analysis",
    subtitle="Comparative overview of total stock volume across key produce items to optimize supply chain distribution",
    bar_padding=0,
    group_padding=0.3,
    value_suffix = ' Kg'
)
```

#### Output:
![Grouped Barh Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/grouped_barh.png)

---

### 2. Horizontal Bar Chart

Draw a single-series horizontal bar chart with category labels and value annotations.

```python
import pandas as pd
from clean_charts import plot_barh_chart

df = pd.DataFrame({
    'Category': ['Apples', 'Bananas', 'Cherries', 'Dates', 'Elderberries', 'Figs', 'Grapes', 'Honeydew'],
    'Sales':    [400, 350, 300, 450, 120, 210, 520, 180],
})

plot_barh_chart(
    data=df,
    title="Regional Supermarket Inventory and Fruit Category Performance Analysis",
    subtitle="Comparative overview of total stock volume across key produce items to optimize supply chain distribution",
    value_suffix=' Kg',
)
```

#### Output:
![Barh Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/barh.png)

---

### 3. Time-Series Line Chart

Generate a time-series line chart with smooth PCHIP spline curves, right-aligned Y-axis, and smart inline labels.

```python
import pandas as pd
from clean_charts import plot_time_series

df = pd.DataFrame({
    'Dates':        pd.date_range("2026-01-01", periods=12, freq="MS"),
    'Apples':       [500, 596, 590, 523, 582, 515, 501, 551, 494, 467, 548, 490],
    'Bananas':      [350, 339, 349, 382, 328, 359, 403, 390, 459, 390, 373, 437],
    'Elderberries': [160, 118, 118, 124, 179, 126, 117, 115, 157, 114, 120, 127],
})

plot_time_series(
    data=df,
    aspect_ratio='1:1',
    title="Regional Supermarket Inventory and Fruit Category Performance Analysis",
    subtitle="Comparative overview of total stock volume across key produce items to optimize supply chain distribution",
    label_frequency="month",
    line_labels='name',
    value_suffix=' Kg',
)
```

#### Output:
![Line Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/time_series.png)

---

### 4. Donut Chart

Draw a stylized donut chart with automatic label placement that avoids overlaps.

```python
import pandas as pd
from clean_charts import plot_donut_chart

df = pd.DataFrame({
    'Category': ['Apples', 'Bananas', 'Cherries', 'Dates', 'Elderberries', 'Figs', 'Grapes', 'Honeydew'],
    'Sales': [400, 350, 300, 450, 120, 210, 520, 180]
})

plot_donut_chart(
    data=df,
    title="Regional Supermarket Inventory and Fruit Category Performance Analysis",
    subtitle="Comparative overview of total stock volume across key produce items to optimize supply chain distribution",
    value_suffix=' kg',
    start_angle=60,
)
```

#### Output:
![Donut Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/donut_chart.png)

---

### 5. Stacked Horizontal Bar Chart

Visualize part-to-whole relationships across categories. Supports raw values or 100 % stacked mode.

```python
import pandas as pd
from clean_charts import plot_stacked_bar_chart

df = pd.DataFrame({
    'Year': ['2021', '2022', '2023', '2024', '2025', '2026'],
    'Apples': [320, 340, 360, 380, 510, 415],
    'Bananas': [390, 400, 405, 410, 180, 450],
    'Cherries': [120, 135, 140, 150, 830, 590]
})

plot_stacked_bar_chart(
    data = df,
    title="Regional Supermarket Inventory and Fruit Category Performance Analysis",
    subtitle="Comparative overview of total stock volume across key produce items to optimize supply chain distribution",
    bar_padding=0.5,
    value_suffix=' Kg',
    show_percentages=True,
    bar_labels='value',
    aspect_ratio='1:1',
    scale_text=False
)
```

#### Output:
![Stacked Bar Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/stacked_bar_chart.png)

---

### 6. Dashboard (Multi-Chart Layout)

Combine any mix of chart types into a single, cohesive dashboard image using `plot_dashboard`. Each sub-chart is rendered independently and composited onto a unified mosaic — no manual subplot wrangling needed.

```python
import pandas as pd
from clean_charts import (
    plot_time_series,
    plot_barh_chart,
    plot_donut_chart,
    plot_stacked_bar_chart,
    plot_dashboard,
)

# Prepare individual datasets
df_ts = pd.DataFrame({
    'Dates':   pd.date_range("2026-01-01", periods=12, freq="MS"),
    'Apples':  [500, 596, 590, 523, 582, 515, 501, 551, 494, 467, 548, 490],
    'Bananas': [350, 339, 349, 382, 328, 359, 403, 390, 459, 390, 373, 437],
})

df_bar = pd.DataFrame({
    'Category': ['Apples', 'Bananas', 'Cherries', 'Dates', 'Elderberries'],
    'Sales':    [400, 350, 300, 450, 120],
})

df_donut = pd.DataFrame({
    'Category': ['Apples', 'Bananas', 'Cherries', 'Dates'],
    'Sales':    [400, 350, 300, 450],
})

df_stacked = pd.DataFrame({
    'Year':     ['2023', '2024', '2025', '2026'],
    'Apples':   [360, 380, 510, 415],
    'Bananas':  [405, 410, 180, 450],
    'Cherries': [140, 150, 830, 590],
})

# Build the dashboard
plot_dashboard(
    charts=[
        (plot_time_series,       {"data": df_ts,      "title": "Monthly Trend"}),
        (plot_barh_chart,        {"data": df_bar,     "title": "Top Items"}),
        (plot_donut_chart,       {"data": df_donut,   "title": "Market Share"}),
        (plot_stacked_bar_chart, {"data": df_stacked, "title": "Yearly Breakdown"}),
    ],
    layout="AB\nCD",           # 2×2 grid
    title="Fruit Sales Overview — Q1 2026",
    subtitle="A consolidated view of inventory, sales trends, and category distribution",
    width=1400,
)
```

#### How it works

1. **`charts`** — A list of `(plot_function, kwargs_dict)` tuples. You can use any Clean Charts plot function (`plot_time_series`, `plot_barh_chart`, `plot_barv_chart`, `plot_grouped_barh_chart`, `plot_donut_chart`, `plot_stacked_bar_chart`). Do **not** include `output_path` in the kwargs — it is managed automatically.

2. **`layout`** — An ASCII mosaic string where each unique letter maps to one chart in order. Use repeated letters to span cells across rows or columns:

    ```
    "AB\nCD"    →  2×2 grid (default for 4 charts)
    "AA\nBC"    →  chart A spans the entire top row (2 columns)
    "AB\nAC"    →  chart A spans the entire left column (2 rows)
    "AB\nCC"    →  chart C spans the entire bottom row
    "AAB\nCDD"  →  3-column layout with mixed spans
    "ABC"       →  single row, three equal columns
    "AA\nAA"    →  one chart fills the entire canvas
    ```

    If omitted, charts are auto-arranged in a roughly square grid.

3. **Consistent styling** — All sub-charts share a unified scale factor and pixel margins, so titles, subtitles, labels, and margins align perfectly across all charts — even when charts span multiple columns or rows.

#### Multi-span layout example

Charts spanning 2+ columns or rows have the same title size and positioning as 1×1 charts:

```python
# Wide chart on top, two standard charts below
plot_dashboard(
    charts=[
        (plot_time_series, {"data": df_ts, "title": "Revenue Trend", "subtitle": "24-month overview"}),
        (plot_barh_chart,  {"data": df_bar, "title": "Top Items", "subtitle": "By sales volume"}),
        (plot_donut_chart, {"data": df_donut, "title": "Market Share", "subtitle": "By category"}),
    ],
    layout="AA\nBC",
    title="Executive Summary",
    width=1400,
)
```

#### Output:
![Dashboard](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/refs/heads/main/dashboard.png)

---

## 🎨 Customizing Data & Advanced Usage

### Custom Time-Series Data

Supply your own `pandas.DataFrame` containing a date/time column and one or more value columns. The library automatically parses the datetime column and maps all other numeric columns as lines.

```python
import pandas as pd
from clean_charts import plot_time_series

data = pd.DataFrame({
    "Day": pd.date_range("2026-05-01", periods=10, freq="D"),
    "Active Users": [120, 150, 190, 240, 220, 250, 270, 310, 340, 320],
    "Signups":      [15,  22,  35,  40,  28,  30,  32,  45,  52,  48],
})

plot_time_series(
    data=data,
    output_path="daily_stats.png",
    title="Daily Server Growth",
    subtitle="Active users and registrations in May 2026",
    label_frequency="day",   # "year" | "quarter" | "month" | "week" | "day" | "hour" | "minute" | "second"
    start_color="#006400",   # Dark green gradient start
    end_color="#ffd700",     # Gold gradient end
    smooth=True,             # Smooth PCHIP spline curves (default True)
    markers=True,            # Show circle markers on data points
    line_labels="both",      # Show "Series: value" inline labels
    value_suffix="%",        # Append "%" to Y-axis ticks and inline labels
)
```

### Custom Horizontal Bar Chart Data

Pass a `pandas.DataFrame` where the first column contains string labels and the second column contains numeric values.

```python
import pandas as pd
from clean_charts import plot_barh_chart

df = pd.DataFrame({
    "Category":    ["Apples", "Bananas", "Cherries", "Dates", "Elderberries", "Figs", "Grapes", "Honeydew"],
    "Sales (tons)":[400,      350,       300,        450,     120,            210,    520,      180],
})

plot_barh_chart(
    data=df,
    output_path="fruit_sales.png",
    title="Fruit Performance Analysis",
    subtitle="Total sales volume by item",
    value_suffix=" t",
    color="#1f77b4",
)
```

### Custom Grouped Bar Chart Data

Pass a DataFrame whose **first column** contains category labels and each subsequent column represents one series.

```python
import pandas as pd
from clean_charts import plot_grouped_barh_chart

df = pd.DataFrame({
    "Country": ["Germany", "France", "Italy", "Spain", "Poland"],
    "BEV":     [18, 14, 8, 5, 3],
    "PHEV":    [9,  7,  5, 4, 2],
    "Hybrid":  [22, 19, 12, 9, 6],
})

plot_grouped_barh_chart(
    data=df,
    output_path="ev_by_country.png",
    title="EV Adoption by Country",
    subtitle="Share of new car sales by powertrain, %",
    value_suffix="%",
    bar_labels="value",       # "none" | "value" | "name" | "both"
    start_color="#005f73",
    end_color="#94d2bd",
)
```

---

## 📖 API Reference

### `plot_time_series`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | DataFrame with a datetime column and value series. Uses built-in sample data when `None`. |
| `output_path` | `str` | `None` | File path to save the image. Displays inline in Jupyter when `None`. |
| `width` | `int` | `1000` | Target image width in pixels. |
| `height` | `int` | `562` | Target image height in pixels. |
| `aspect_ratio` | `str` | `None` | `"square"` / `"1:1"`, `"landscape"` / `"2:1"`, `"vertical"` / `"1:2"`. Overrides width/height. |
| `title` | `str` | `None` | Bold title text, left-aligned. Auto-wraps to 2 lines. |
| `subtitle` | `str` | `None` | Subtitle below the title. Auto-wraps to 2 lines. |
| `start_color` | `str` | `None` | Hex color for the first series in a gradient. |
| `end_color` | `str` | `None` | Hex color for the last series in a gradient. |
| `label_frequency` | `str` | `"year"` | X-axis tick frequency: `"year"`, `"quarter"`, `"month"`, `"week"`, `"day"`, `"hour"`, `"minute"`, `"second"`. |
| `markers` | `bool` / `str` | `None` | `False`/`None` = none, `True` = circles, or any matplotlib marker string (e.g. `"s"`, `"D"`). |
| `line_labels` | `str` | `"name"` | Inline endpoint labels: `"name"`, `"value"`, `"both"`, or `"none"`. |
| `value_suffix` | `str` | `""` | Appended to Y-axis ticks and inline value labels (e.g. `"%"`). |
| `smooth` | `bool` | `True` | Draw PCHIP spline curves. Falls back to straight lines if `scipy` is missing. |
| `scale_text` | `bool` | `False` | Scale fonts and line weights proportionally to image size. |
| `vlines` | `str`/`dict`/`list` | `None` | Vertical reference lines. Accepts a date string, a styling dict, or a list. |
| `highlight_ranges` | `tuple`/`dict`/`list` | `None` | Shaded time ranges. Accepts a date tuple, a styling dict, or a list. |
| `callouts` | `dict`/`list` | `None` | Point annotations. Snaps to the nearest data point and draws a dot, leader line, and text box. |

---

### `plot_barh_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | Column 0 = category strings, column 1 = numeric values. Uses built-in survey data when `None`. |
| `output_path` | `str` | `None` | File path to save the image. Displays inline when `None`. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `None` | Auto-sized by number of categories when `None`. |
| `aspect_ratio` | `str` | `None` | `"square"` / `"1:1"`, `"landscape"` / `"2:1"`, `"vertical"` / `"1:2"`. |
| `title` | `str` | `None` | Bold title text. |
| `subtitle` | `str` | `None` | Subtitle below the title. |
| `color` | `str` | `"#000000"` | Hex color for the bars. |
| `bar_padding` | `float` | `0.35` | Fraction of bar slot left as gap (0.0–1.0). |
| `value_suffix` | `str` | `""` | Appended to value labels and axis ticks. |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |

---

### `plot_grouped_barh_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | Column 0 = category labels, remaining columns = numeric series. |
| `output_path` | `str` | `None` | File path to save. Displays inline when `None`. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `None` | Auto-sized when `None`. |
| `aspect_ratio` | `str` | `None` | `"square"` / `"1:1"`, `"landscape"` / `"2:1"`, `"vertical"` / `"1:2"`. |
| `title` | `str` | `None` | Bold title text. Auto-wraps to 2 lines. |
| `subtitle` | `str` | `None` | Subtitle. Auto-wraps to 3 lines. |
| `start_color` | `str` | `"#000000"` | Gradient start color. |
| `end_color` | `str` | `"#2323FF"` | Gradient end color. |
| `bar_padding` | `float` | `0` | Whitespace fraction within a single bar slot (0–1). |
| `group_padding` | `float` | `0.45` | Spacing fraction between groups (0–1). |
| `value_suffix` | `str` | `""` | Appended to axis tick labels. |
| `bar_labels` | `str` | `"none"` | `"none"`, `"value"`, `"name"`, or `"both"`. |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |

---

### `plot_donut_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | Two columns: category labels and values. |
| `output_path` | `str` | `None` | File path to save. Renders inline when `None`. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `600` | Target image height. Defaults to `width`. |
| `aspect_ratio` | `str` | `None` | `"square"` / `"1:1"`, `"landscape"` / `"2:1"`, `"vertical"` / `"1:2"`. |
| `title` | `str` | `None` | Bold title text. Auto-wraps to 2 lines. |
| `subtitle` | `str` | `None` | Subtitle. Auto-wraps to 3 lines. |
| `colors` | `list` | `DEFAULT_COLORS` | List of hex colors for slices. |
| `start_color` | `str` | `None` | Gradient start (overrides `colors` when paired with `end_color`). |
| `end_color` | `str` | `None` | Gradient end. |
| `donut_radius` | `float` | `0.4` | Outer radius relative to figure height. |
| `donut_thickness` | `float` | `0.15` | Ring thickness relative to figure height. |
| `value_suffix` | `str` | `""` | Appended to value labels. |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |
| `show_percentages` | `bool` | `False` | Show percentage of total instead of raw value. |
| `start_angle` | `int` | `90` | Starting angle for the first slice (degrees). |

---

### `plot_stacked_bar_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | Column 0 = category strings, columns 1–N = numeric series. |
| `output_path` | `str` | `None` | File path to save. Renders inline when `None`. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `None` | Auto-sized from categories when `None`. |
| `aspect_ratio` | `str` | `None` | `"square"` / `"1:1"`, `"landscape"` / `"2:1"`, `"vertical"` / `"1:2"`. |
| `title` | `str` | `None` | Bold title text. Auto-wraps to 2 lines. |
| `subtitle` | `str` | `None` | Subtitle. Auto-wraps to 3 lines. |
| `colors` | `list` | `DEFAULT_COLORS` | List of hex colors for series. |
| `start_color` | `str` | `None` | Gradient start (overrides `colors` when paired with `end_color`). |
| `end_color` | `str` | `None` | Gradient end. |
| `bar_padding` | `float` | `0.30` | Whitespace fraction within a bar slot (0–1). |
| `value_suffix` | `str` | `""` | Appended to axis tick labels. |
| `bar_labels` | `str` | `"none"` | `"none"`, `"value"`, `"name"`, or `"both"`. |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |
| `show_percentages` | `bool` | `False` | Convert to 100 % stacked bar chart with percentage labels. |

---

### `plot_dashboard`

Combine multiple charts into a single composite image using a mosaic layout. Charts that span multiple columns or rows are rendered with the same title size and margin alignment as single-cell charts.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `charts` | `list[tuple]` | *(required)* | List of `(plot_function, kwargs_dict)` pairs. Accepts any Clean Charts function (`plot_time_series`, `plot_barh_chart`, `plot_barv_chart`, `plot_grouped_barh_chart`, `plot_donut_chart`, `plot_stacked_bar_chart`). `output_path` is managed internally — do **not** include it. |
| `layout` | `str` | `None` | ASCII mosaic string (e.g. `"AB\nCD"`). Each unique letter maps to one chart in order of first appearance. Repeat letters to span columns/rows. Auto-generates a grid when `None`. |
| `title` | `str` | `None` | Dashboard title rendered above the mosaic. |
| `subtitle` | `str` | `None` | Dashboard subtitle rendered below the title. |
| `output_path` | `str` | `None` | File path to save. Displays inline when `None`. |
| `width` | `int` | `1400` | Final image width in pixels. |
| `height` | `int` | `None` | Auto-derived from layout proportions and `width` when `None`. |
| `padding` | `float` | `0.02` | Fractional space between sub-charts (0–0.5). |

**Layout examples:**

```text
"AB\nCD"   →  2×2 grid (default for 4 charts)
"AA\nBC"   →  chart A spans full top row (2 columns)
"AB\nAC"   →  chart A spans full left column (2 rows)
"AB\nCC"   →  chart C spans full bottom row
"AAB\nCDD" →  3-column layout with mixed spans
"ABC"      →  single row, three equal columns
"AA\nAA"   →  one chart fills the entire canvas
```

> **Alignment note:** All sub-charts automatically share a unified scale factor and pixel margins, ensuring titles, subtitles, labels, and axes are perfectly aligned across charts — even when they span different numbers of grid cells.

---

## 🎛️ Global Customization

Import and modify global configuration variables to apply a consistent theme across all charts:

```python
import clean_charts.config as config
from clean_charts import plot_time_series

# Override styling tokens before plotting
config.BACKGROUND_COLOR = "#ffffff"  # Pure white background
config.GRID_COLOR        = "#eaeaea"  # Light gridlines
config.AXIS_COLOR        = "#333333"  # Dark charcoal axes

plot_time_series(
    title="Custom White Theme",
    output_path="white_theme_chart.png"
)
```

### Available Config Variables (`clean_charts.config`)

| Variable | Default | Description |
| :--- | :--- | :--- |
| `BACKGROUND_COLOR` | `"#f4f3f0"` | Chart background (cream-gray). |
| `GRID_COLOR` | `"#dcdbd7"` | Horizontal/vertical gridline color. |
| `AXIS_COLOR` | `"#000000"` | Axis spine and tick color. |
| `TITLE_COLOR` | `"#111111"` | Title text color. |
| `SUBTITLE_COLOR` | `"#444444"` | Subtitle text color. |
| `LINE_COLOR` | `"#000000"` | Default line/bar color. |
| `DEFAULT_COLOR` | `"#000000"` | Fallback single-bar color. |
| `DEFAULT_START_COLOR` | `"#000000"` | Gradient start for grouped bars. |
| `DEFAULT_END_COLOR` | `"#2323FF"` | Gradient end for grouped bars. |
| `DEFAULT_COLORS_LIST` | `['#000000', '#2323FF', ...]` | Default multi-series color palette. |

---

## 🧪 Examples & Development

### Generate Sample Charts

```bash
python generate_sample.py
```

This produces a set of sample images showcasing different aspect ratios, gradient themes, label frequencies, and title-wrapping behavior.

### Running Tests

```bash
python -m unittest tests/test_plot.py
```

---

## 📄 License

[MIT](LICENSE) © Raghuram Sirigiri
