Metadata-Version: 2.5
Name: data-visualiser-package
Version: 0.2.0
Summary: A utility package for data visualization and statistical analysis with Matplotlib and Seaborn
Project-URL: Homepage, https://github.com/jonathan-doenz/data-visualiser-package
Project-URL: Bug Tracker, https://github.com/jonathan-doenz/data-visualiser-package/issues
Author-email: jonathan-doenz <jonathan.doenz@gmail.com>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.9
Requires-Dist: matplotlib>=3.5.0
Requires-Dist: numpy>=1.21.0
Requires-Dist: pandas>=1.5.0
Requires-Dist: seaborn>=0.11.2
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: build>=0.10.0; extra == 'dev'
Requires-Dist: flake8>=5.0.0; extra == 'dev'
Requires-Dist: isort>=5.10.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: twine>=4.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# Data Visualiser Package

A Python utility package for data visualization and statistical analysis with Matplotlib and Seaborn. This package provides a simple interface to create common visualizations and statistical tables for exploratory data analysis and reporting.

## Installation

```bash
pip install data-visualiser-package
```

Requires Python 3.9+ (pandas >= 1.5, seaborn >= 0.11.2, matplotlib >= 3.5).

## Features

- Count plots and distribution plots, plain or stratified by a categorical variable
- Box / violin plots of a numerical variable per category
- Count and distribution statistics tables, including stratified (long or wide) versions
- Save figures as PNG, PDF, SVG, ... and tables as LaTeX (bold headers, escaped special characters), CSV, Markdown or HTML
- Consistent handling of missing values: `None`, `NaN` and `pd.NA` become one `"NaN"` category placed last, or are excluded with `show_nan=False`
- Categories are ordered on their native type (`2 < 5 < 10`), or by an explicit `forced_order`

## Quick Start

```python
import pandas as pd
from data_visualiser_package import DataVisualiser

# Create a DataVisualiser instance
dv = DataVisualiser(
    record_unit_name="patient",   # What a single record represents
    figures_dirpath="./figures",  # Where to save figures
    tables_dirpath="./tables",    # Where to save tables
    create_dirs=True,             # Create directories if they don't exist
    fig_format="png",             # Any format matplotlib supports (png, pdf, svg, ...)
    dpi=150,                      # Resolution of saved figures (None = matplotlib default)
    table_format="latex",         # Default table format: latex, csv, markdown or html
)

# Load your data
df = pd.read_csv("your_data.csv")

# Create a count plot
fig, ax = dv.get_count_plot(
    var="diagnosis",          # Categorical variable to plot
    df=df,                    # DataFrame containing the data
    show_nan=True,            # Show NaN values as a separate (last) category
    save_fig=True,            # Save the figure to disk
)

# Create a distribution plot
fig, ax = dv.get_dist_plot(var="age", df=df, save_fig=True)

# Generate statistics tables
stats_df = dv.get_count_stats_df(var="diagnosis", df=df, save_table=True)

# Create stratified visualizations
fg = dv.get_dist_stratified_plot(var="age", df=df, col="gender", save_fig=True)

# Box plot of a numerical variable per category
fig, ax = dv.get_box_stratified_plot(var="age", df=df, col="diagnosis", save_fig=True)

# Wide crosstab (rows: diagnosis categories, columns: one block per gender), saved as CSV
wide = dv.get_count_stratified_stats_df(
    "diagnosis", df, col="gender", wide=True, save_table=True, table_format="csv"
)
```

## Missing values and category order

All categorical inputs go through the same preprocessing (`prepare_categorical`):

- `None`, `np.nan` and `pd.NA` are treated as missing. With `show_nan=True` (default) they form
  a single category labelled `"NaN"` (configurable through `DataVisualiser(nan_label=...)`) that is
  always placed last. With `show_nan=False` the records are excluded from that plot/table, and
  percentages are computed over the remaining records.
- Categories are sorted on their native values, so numeric codes are ordered numerically.
  Integral floats are labelled without decimals (`10.0` -> `"10"`).
- `forced_order` (and `forced_order_col` for the stratification variable) imposes an explicit
  order and may list categories absent from the data (they appear with a count of 0). It must
  cover every value present, otherwise a `ValueError` is raised.

Distribution tables report `n_nan` and `nan_perc` next to the usual `describe()` statistics.

## Example

Here's a complete example of how to use the DataVisualiser class:

```python
import pandas as pd
import numpy as np
from data_visualiser_package import DataVisualiser

# Create a sample dataset
np.random.seed(42)
n = 1000

data = {
    'age': np.random.normal(50, 15, n),
    'gender': np.random.choice(['Male', 'Female'], n),
    'diagnosis': np.random.choice(['Healthy', 'Condition A', 'Condition B', None], n, p=[0.6, 0.2, 0.15, 0.05]),
    'heart_rate': np.random.normal(80, 10, n),
    'blood_pressure': np.random.normal(120, 15, n)
}
df = pd.DataFrame(data)

dv = DataVisualiser(
    record_unit_name="patient",
    figures_dirpath="./output/figures",
    tables_dirpath="./output/tables",
    create_dirs=True
)

# Plots
dv.get_count_plot('gender', df, save_fig=True)
dv.get_count_plot('diagnosis', df, save_fig=True)
dv.get_count_stratified_plot('diagnosis', df, col='gender', save_fig=True)
dv.get_dist_plot('age', df, save_fig=True)
dv.get_dist_stratified_plot('age', df, col='gender', save_fig=True)
dv.get_dist_hued_plot('age', df, hue='gender', save_fig=True)
dv.get_box_stratified_plot('heart_rate', df, col='diagnosis', kind='violin', save_fig=True)

# Statistics tables
dv.get_count_stats_df('diagnosis', df, save_table=True, add_total=True)
dv.get_count_stratified_stats_df('diagnosis', df, col='gender', wide=True, save_table=True)
dv.get_dist_stats_df('age', df, save_table=True)
dv.get_dist_stratified_stats_df('age', df, col='gender', save_table=True, table_format="markdown")

print("All visualizations and tables have been generated successfully!")
```

See `examples/demo.py` for a runnable version.

## API Reference

### DataVisualiser Class

```python
class DataVisualiser(
    record_unit_name="patient",
    figures_dirpath=None,
    tables_dirpath=None,
    create_dirs=False,
    nan_label="NaN",
    fig_format="png",
    dpi=None,
    table_format="latex",
)
```

`save_fig=True` / `save_table=True` require the corresponding directory to be configured and raise a `ValueError` otherwise. Saved figures are closed after writing.

#### Count Visualizations

- `get_count_plot(var, df, show_nan=True, save_fig=False, plot_kwargs=None, xlabels_rotation=None, forced_order=None)` -> `(fig, ax)`
- `get_count_stratified_plot(var, df, col, show_nan=True, show_nan_col=True, save_fig=False, col_wrap=3, plot_kwargs=None, xlabels_rotation=None, forced_order=None, forced_order_col=None)` -> `FacetGrid`
- `get_count_stats_df(var, df, show_nan=True, save_table=False, percentage=True, add_total=False, round_n_digits=1, forced_order=None, table_format=None)` -> `DataFrame`
- `get_count_stratified_stats_df(var, df, col, show_nan=True, show_nan_col=True, save_table=False, percentage=True, round_n_digits=1, forced_order=None, forced_order_col=None, wide=False, table_format=None)` -> `DataFrame` (percentages are within each stratum; `wide=True` returns strata as columns)

#### Distribution Visualizations

- `get_dist_plot(var, df, save_fig=False, plot_kwargs=None, xlabels_rotation=None)` -> `(fig, ax)` (`plot_kwargs` defaults to `{"kde": True}`)
- `get_dist_stratified_plot(var, df, col, show_nan_col=True, save_fig=False, col_wrap=3, plot_kwargs=None, xlabels_rotation=None, forced_order_col=None)` -> `FacetGrid`
- `get_dist_hued_plot(var, df, hue, show_nan_col=True, save_fig=False, plot_kwargs=None, xlabels_rotation=None, forced_order_col=None)` -> `FacetGrid`
- `get_box_stratified_plot(var, df, col, kind="box", show_nan_col=True, save_fig=False, plot_kwargs=None, xlabels_rotation=None, forced_order_col=None)` -> `(fig, ax)` (`kind` is `"box"` or `"violin"`)
- `get_dist_stats_df(var, df, save_table=False, round_n_digits=None, table_format=None)` -> `DataFrame`
- `get_dist_stratified_stats_df(var, df, col, show_nan_col=True, save_table=False, forced_order_col=None, round_n_digits=None, table_format=None)` -> `DataFrame`

The distribution methods require a numeric `var` (a `TypeError` is raised otherwise). When `round_n_digits` is `None`, the number of decimals written to the table is derived from the standard deviation of the data.

#### Output files

| Method | File name (in `figures_dirpath` / `tables_dirpath`) |
| --- | --- |
| `get_count_plot` | `count_plot_{var}.{fig_format}` |
| `get_count_stratified_plot` | `count_plot_{var}_for_each_{col}.{fig_format}` |
| `get_dist_plot` | `dist_plot_{var}.{fig_format}` |
| `get_dist_stratified_plot` | `dist_plot_{var}_for_each_{col}.{fig_format}` |
| `get_dist_hued_plot` | `dist_plot_{var}_for_hue_{hue}.{fig_format}` |
| `get_box_stratified_plot` | `{kind}_plot_{var}_for_each_{col}.{fig_format}` |
| `get_count_stats_df` | `count_stats_table_{var}.{tex,csv,md,html}` |
| `get_count_stratified_stats_df` | `count_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |
| `get_dist_stats_df` | `dist_stats_table_{var}.{tex,csv,md,html}` |
| `get_dist_stratified_stats_df` | `dist_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |

LaTeX tables use `booktabs` rules (`\toprule`, `\midrule`, `\bottomrule`), bold column titles and escaped special characters (`#`, `%`, `_`, `&`, ...).

### Helper functions

- `prepare_categorical(series, show_nan=True, forced_order=None, nan_label="NaN")` -> ordered categorical `Series` of string labels
- `categorise_variable_in_df(var, df, forced_order=None, nan_replacement_str="NaN")` -> categorises a column in place
- `dataframe_to_latex(df, bold_header=True, round_n_digits=None, na_rep="", column_format=None)` -> `str`
- `dataframe_to_markdown(df, round_n_digits=None, na_rep="")` -> `str`

## Development

```bash
uv sync --group dev        # or: pip install -e ".[dev]"
uv run pytest
uv run flake8 src tests examples --max-line-length=120
```

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
