Metadata-Version: 2.4
Name: clean-charts
Version: 0.3.0
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

[![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://raghuramsirigiri.github.io/viz_library/)

`clean-charts` is a lightweight, premium Python library designed to generate beautiful, publication-quality data visualizations inspired by modern editorial styles like *The Economist*.

Featuring right-aligned axes, custom date boundaries, distinct line colors, smooth spline curves, automatic overlap-avoiding label placement, and dynamic scaling for varying canvas resolutions, `clean-charts` makes it effortless to create presentation-ready visualizations — both saved to disk and displayed inline in Jupyter notebooks.

---

## Key Features

- **Premium Aesthetics**: Light cream-gray backgrounds, minimal gridlines, elegant typography, and consistent padding.
- **Jupyter Inline Display**: When `output_path=None`, charts render directly in Jupyter notebooks via `IPython.display`.
- **Time-Series Line Charts (`plot_time_series`)**:
  - Right-aligned Y-axes.
  - Automatically identifies date/time columns and handles multiple value series.
  - Smooth PCHIP spline curves through data points (requires `scipy`).
  - Optional data-point markers with configurable style.
  - Generates custom color gradients or uses a refined default palette.
  - Intelligently spaces dates and years on the X-axis to prevent label overlap.
  - Smart inline label placement at line endpoints (`name`, `value`, `both`, or `none`).
- **Horizontal Bar Charts (`plot_barh_chart`)**:
  - Left-aligned categories, top-aligned numeric ticks with custom suffixes.
  - Values labeled inside/outside the end of each bar.
- **Grouped Horizontal Bar Charts (`plot_grouped_barh_chart`)**:
  - Multi-series bars per category in stacked vertical lanes.
  - Gradient color palette across series, compact legend row above the chart.
  - Optional bar labels (`value`, `name`, `both`, `none`).
- **Responsive Scaling**: All text, line-weights, and paddings scale proportionally based on target width and height.

---

## Installation

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

> **Note**: Smooth curves require `scipy`. Install it alongside the library:
> ```bash
> pip install clean-charts scipy
> ```

---

## 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 Example:
![Grouped Barh Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/main/grouped_barh.png)

---

### 2. Horizontal Bar Chart

Draw a single-series horizontal bar chart with category labels and value annotations. If no data is provided, a built-in survey dataset is used.

```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 Example:
![Barh Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/main/barh.png)

---

### 3. Time-Series Line Chart

Call `plot_time_series` to generate a time-series line chart from a DataFrame. If no `data` argument is provided, the function uses a built-in sample dataset of monthly automobile sales by type from 2020 to 2025.

```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 Example:
![Line Chart](https://raw.githubusercontent.com/raghuramsirigiri/Viz_images/main/time_series_1_1.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. If `None`, uses built-in default data. |
| `output_path` | `str` | `None` | File path to save the image. If `None`, displays inline in Jupyter. |
| `width` | `int` | `1000` | Target image width in pixels. |
| `height` | `int` | `562` | Target image height in pixels. |
| `aspect_ratio` | `str` | `None` | Predefined ratio: `"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` or `str` | `None` | Data-point markers. `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` | `""` | String appended to Y-axis ticks and inline value labels (e.g. `"%"`). |
| `smooth` | `bool` | `True` | Draw smooth PCHIP spline curves. Falls back to straight lines if `scipy` is not installed. |
| `scale_text` | `bool` | `False` | Scale fonts and line weights proportionally to image size. |

---

### `plot_barh_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | DataFrame: column 0 = category strings, column 1 = numeric values. If `None`, uses built-in survey data. |
| `output_path` | `str` | `None` | File path to save the image. If `None`, displays inline in Jupyter. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `None` | Target image height in pixels. Auto-sized by number of categories if `None`. |
| `aspect_ratio` | `str` | `None` | Predefined ratio: `"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 between bars (0.0–1.0). |
| `value_suffix` | `str` | `""` | String appended to value labels and axis ticks (e.g. `"%"`). |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |

---

### `plot_grouped_barh_chart`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `data` | `pd.DataFrame` | `None` | DataFrame: column 0 = category labels, remaining columns = numeric series. If `None`, uses built-in example data. |
| `output_path` | `str` | `None` | File path to save the image. If `None`, displays inline in Jupyter. |
| `width` | `int` | `600` | Target image width in pixels. |
| `height` | `int` | `None` | Target image height in pixels. Auto-sized when `None`. |
| `aspect_ratio` | `str` | `None` | Predefined ratio: `"square"`/`"1:1"`, `"landscape"`/`"2:1"`, `"vertical"`/`"1:2"`. |
| `title` | `str` | `None` | Bold title text. Auto-wraps to 2 lines. |
| `subtitle` | `str` | `None` | Subtitle below the title. Auto-wraps to 3 lines. |
| `start_color` | `str` | `"#000000"` | Hex color for the first series in the gradient. |
| `end_color` | `str` | `"#2323FF"` | Hex color for the last series in the gradient. |
| `bar_padding` | `float` | `0` | Fraction of a single bar slot left as whitespace (0–1). |
| `group_padding` | `float` | `0.45` | Fraction of the group height used as spacing between groups (0–1). |
| `value_suffix` | `str` | `""` | String appended to axis tick labels (e.g. `"%"`). |
| `bar_labels` | `str` | `"none"` | Labels drawn on each bar: `"none"`, `"value"`, `"name"`, or `"both"`. |
| `scale_text` | `bool` | `True` | Scale fonts proportionally to image size. |

---

## Global Customization

Import and modify the 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-line color palette. |

---

## Examples & Development

### Generate Sample Charts

Run the provided script to generate a set of sample charts showcasing aspect ratios, gradient themes, custom label frequencies, and more:

```bash
python generate_sample.py
```

This produces:
- `sales_chart_2_1.png` — Landscape 2:1 time-series
- `sales_chart_1_1.png` — Square 1:1 time-series
- `sales_chart_1_2.png` — Vertical 1:2 time-series
- `sales_chart_gradient.png` — Gradient color demonstration
- `sales_chart_daily.png` — Day-level label frequency
- `sales_chart_long_title.png` — Long title wrapping demo

### Running Tests

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