data-visualiser-package 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/.github/workflows/python-package.yml +14 -14
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/.gitignore +5 -0
- data_visualiser_package-0.2.0/CHANGELOG.md +60 -0
- data_visualiser_package-0.2.0/PKG-INFO +243 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/PYPI_PUBLISH.md +0 -1
- data_visualiser_package-0.2.0/README.md +208 -0
- data_visualiser_package-0.2.0/examples/demo.py +108 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/pyproject.toml +40 -8
- data_visualiser_package-0.2.0/src/data_visualiser_package/__init__.py +30 -0
- data_visualiser_package-0.2.0/src/data_visualiser_package/data_visualiser.py +801 -0
- data_visualiser_package-0.2.0/src/data_visualiser_package/export_latex_tables.py +215 -0
- data_visualiser_package-0.2.0/tests/test_data_visualiser.py +389 -0
- data_visualiser_package-0.2.0/uv.lock +2809 -0
- data_visualiser_package-0.1.0/CHANGELOG.md +0 -16
- data_visualiser_package-0.1.0/PKG-INFO +0 -181
- data_visualiser_package-0.1.0/README.md +0 -154
- data_visualiser_package-0.1.0/examples/demo.py +0 -95
- data_visualiser_package-0.1.0/setup.py +0 -45
- data_visualiser_package-0.1.0/src/data_visualiser_package/__init__.py +0 -12
- data_visualiser_package-0.1.0/src/data_visualiser_package/data_visualiser.py +0 -643
- data_visualiser_package-0.1.0/src/data_visualiser_package/export_latex_tables.py +0 -102
- data_visualiser_package-0.1.0/tests/test_data_visualiser.py +0 -100
- data_visualiser_package-0.1.0/uv.lock +0 -298
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/.python-version +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/LICENSE +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/MANIFEST.in +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/pytest.ini +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/requirements-dev.txt +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/requirements.txt +0 -0
- {data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/src/data_visualiser_package/py.typed +0 -0
{data_visualiser_package-0.1.0 → data_visualiser_package-0.2.0}/.github/workflows/python-package.yml
RENAMED
|
@@ -2,38 +2,38 @@ name: Python Package
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [ main ]
|
|
5
|
+
branches: [ master, main ]
|
|
6
6
|
tags:
|
|
7
7
|
- 'v*'
|
|
8
8
|
pull_request:
|
|
9
|
-
branches: [ main ]
|
|
9
|
+
branches: [ master, main ]
|
|
10
10
|
|
|
11
11
|
jobs:
|
|
12
12
|
test:
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
python-version: ['3.
|
|
16
|
+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
|
17
17
|
|
|
18
18
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
20
|
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
-
uses: actions/setup-python@
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
22
|
with:
|
|
23
23
|
python-version: ${{ matrix.python-version }}
|
|
24
24
|
- name: Install dependencies
|
|
25
25
|
run: |
|
|
26
26
|
python -m pip install --upgrade pip
|
|
27
|
-
python -m pip install
|
|
28
|
-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
29
|
-
python -m pip install -e .
|
|
27
|
+
python -m pip install -e ".[dev]"
|
|
30
28
|
- name: Lint with flake8
|
|
31
29
|
run: |
|
|
32
30
|
# stop the build if there are Python syntax errors or undefined names
|
|
33
|
-
flake8
|
|
31
|
+
flake8 src tests examples --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
34
32
|
# exit-zero treats all errors as warnings
|
|
35
|
-
flake8
|
|
33
|
+
flake8 src tests examples --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics
|
|
36
34
|
- name: Test with pytest
|
|
35
|
+
env:
|
|
36
|
+
MPLBACKEND: Agg
|
|
37
37
|
run: |
|
|
38
38
|
pytest
|
|
39
39
|
|
|
@@ -41,13 +41,13 @@ jobs:
|
|
|
41
41
|
needs: test
|
|
42
42
|
runs-on: ubuntu-latest
|
|
43
43
|
if: startsWith(github.ref, 'refs/tags/')
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
steps:
|
|
46
|
-
- uses: actions/checkout@
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
47
|
- name: Set up Python
|
|
48
|
-
uses: actions/setup-python@
|
|
48
|
+
uses: actions/setup-python@v5
|
|
49
49
|
with:
|
|
50
|
-
python-version: '3.
|
|
50
|
+
python-version: '3.12'
|
|
51
51
|
- name: Install dependencies
|
|
52
52
|
run: |
|
|
53
53
|
python -m pip install --upgrade pip
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Data Visualiser Package will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-09-11
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Missing values are now handled consistently: `None`, `NaN` and `pd.NA` all become a single
|
|
12
|
+
`"NaN"` category placed last, `show_nan=False` really excludes them, and numeric categories
|
|
13
|
+
are ordered numerically (`2 < 5 < 10`) instead of alphabetically. Previously values were
|
|
14
|
+
converted to strings before being filled, which produced `"None"`/`"nan"` labels and made
|
|
15
|
+
`show_nan` a no-op.
|
|
16
|
+
- Every column title of saved LaTeX tables is now bold, including titles containing `_` or `%`
|
|
17
|
+
(`n_nan`, `nan_perc`, `25%`, ...). Count tables now use the same bold-header writer.
|
|
18
|
+
- LaTeX export no longer depends on pandas' `to_latex`, which required the undeclared
|
|
19
|
+
optional dependency `jinja2` on pandas >= 2.0 and escaped special characters differently
|
|
20
|
+
across versions. Special characters (`#`, `%`, `_`, `&`, ...) are always escaped.
|
|
21
|
+
- Saving a distribution table whose standard deviation is NaN (single value / all missing)
|
|
22
|
+
no longer crashes.
|
|
23
|
+
- `plot_kwargs` dictionaries passed by the caller (and the defaults) are no longer mutated.
|
|
24
|
+
- `create_dirs=True` now creates each configured directory independently.
|
|
25
|
+
- Figures are saved and closed by object rather than through the "current figure".
|
|
26
|
+
- CI runs on the `master` branch and on Python 3.9-3.13.
|
|
27
|
+
|
|
28
|
+
### Changed (breaking)
|
|
29
|
+
- `col` / `hue` are now required arguments of the stratified and hued methods (the previous
|
|
30
|
+
default `"sexe_desc"` was dataset-specific).
|
|
31
|
+
- `save_fig=True` / `save_table=True` raise a `ValueError` when the corresponding directory
|
|
32
|
+
is not configured, instead of silently doing nothing.
|
|
33
|
+
- `get_dist_stats_df` / `get_dist_stratified_stats_df` / `get_box_stratified_plot` raise a
|
|
34
|
+
`TypeError` for non-numeric variables.
|
|
35
|
+
- `categorise_variable_in_df` raises `ValueError` (instead of `AssertionError`) when
|
|
36
|
+
`forced_order` is incomplete.
|
|
37
|
+
- Minimum supported Python is 3.9; minimum pandas is 1.5.
|
|
38
|
+
- The unused low-level helpers of `export_latex_tables` (`format_table`, `boldify_line`, ...)
|
|
39
|
+
were removed. `get_latex_table_bold_col_header` and `get_n_decimals_to_include` remain.
|
|
40
|
+
- `setup.py` was removed; `pyproject.toml` (hatchling) is the single build configuration.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- `DataVisualiser(nan_label=..., fig_format=..., dpi=..., table_format=...)` to customise the
|
|
44
|
+
missing-value label, the figure file format/resolution and the default table format.
|
|
45
|
+
- Tables can be saved as LaTeX, CSV, Markdown or HTML (`table_format` on the constructor or
|
|
46
|
+
per call).
|
|
47
|
+
- `get_box_stratified_plot(var, df, col, kind="box"|"violin")`.
|
|
48
|
+
- `get_count_stratified_stats_df(..., wide=True)` returns a crosstab with strata as columns.
|
|
49
|
+
- `round_n_digits` on the distribution table methods (auto-derived from the spread by default).
|
|
50
|
+
- Public helpers `prepare_categorical`, `dataframe_to_latex`, `dataframe_to_markdown`.
|
|
51
|
+
|
|
52
|
+
## [0.1.0] - 2025-05-14
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
- Initial release of the DataVisualiser package
|
|
56
|
+
- Core functionality for data visualization and statistical analysis
|
|
57
|
+
- Support for count plots and distribution plots
|
|
58
|
+
- Support for stratified visualizations
|
|
59
|
+
- LaTeX table export functionality
|
|
60
|
+
- Comprehensive documentation and examples
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: data-visualiser-package
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A utility package for data visualization and statistical analysis with Matplotlib and Seaborn
|
|
5
|
+
Project-URL: Homepage, https://github.com/jonathan-doenz/data-visualiser-package
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/jonathan-doenz/data-visualiser-package/issues
|
|
7
|
+
Author-email: jonathan-doenz <jonathan.doenz@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
23
|
+
Requires-Dist: numpy>=1.21.0
|
|
24
|
+
Requires-Dist: pandas>=1.5.0
|
|
25
|
+
Requires-Dist: seaborn>=0.11.2
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: build>=0.10.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: flake8>=5.0.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: isort>=5.10.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine>=4.0.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Data Visualiser Package
|
|
37
|
+
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install data-visualiser-package
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Requires Python 3.9+ (pandas >= 1.5, seaborn >= 0.11.2, matplotlib >= 3.5).
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- Count plots and distribution plots, plain or stratified by a categorical variable
|
|
51
|
+
- Box / violin plots of a numerical variable per category
|
|
52
|
+
- Count and distribution statistics tables, including stratified (long or wide) versions
|
|
53
|
+
- Save figures as PNG, PDF, SVG, ... and tables as LaTeX (bold headers, escaped special characters), CSV, Markdown or HTML
|
|
54
|
+
- Consistent handling of missing values: `None`, `NaN` and `pd.NA` become one `"NaN"` category placed last, or are excluded with `show_nan=False`
|
|
55
|
+
- Categories are ordered on their native type (`2 < 5 < 10`), or by an explicit `forced_order`
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import pandas as pd
|
|
61
|
+
from data_visualiser_package import DataVisualiser
|
|
62
|
+
|
|
63
|
+
# Create a DataVisualiser instance
|
|
64
|
+
dv = DataVisualiser(
|
|
65
|
+
record_unit_name="patient", # What a single record represents
|
|
66
|
+
figures_dirpath="./figures", # Where to save figures
|
|
67
|
+
tables_dirpath="./tables", # Where to save tables
|
|
68
|
+
create_dirs=True, # Create directories if they don't exist
|
|
69
|
+
fig_format="png", # Any format matplotlib supports (png, pdf, svg, ...)
|
|
70
|
+
dpi=150, # Resolution of saved figures (None = matplotlib default)
|
|
71
|
+
table_format="latex", # Default table format: latex, csv, markdown or html
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Load your data
|
|
75
|
+
df = pd.read_csv("your_data.csv")
|
|
76
|
+
|
|
77
|
+
# Create a count plot
|
|
78
|
+
fig, ax = dv.get_count_plot(
|
|
79
|
+
var="diagnosis", # Categorical variable to plot
|
|
80
|
+
df=df, # DataFrame containing the data
|
|
81
|
+
show_nan=True, # Show NaN values as a separate (last) category
|
|
82
|
+
save_fig=True, # Save the figure to disk
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Create a distribution plot
|
|
86
|
+
fig, ax = dv.get_dist_plot(var="age", df=df, save_fig=True)
|
|
87
|
+
|
|
88
|
+
# Generate statistics tables
|
|
89
|
+
stats_df = dv.get_count_stats_df(var="diagnosis", df=df, save_table=True)
|
|
90
|
+
|
|
91
|
+
# Create stratified visualizations
|
|
92
|
+
fg = dv.get_dist_stratified_plot(var="age", df=df, col="gender", save_fig=True)
|
|
93
|
+
|
|
94
|
+
# Box plot of a numerical variable per category
|
|
95
|
+
fig, ax = dv.get_box_stratified_plot(var="age", df=df, col="diagnosis", save_fig=True)
|
|
96
|
+
|
|
97
|
+
# Wide crosstab (rows: diagnosis categories, columns: one block per gender), saved as CSV
|
|
98
|
+
wide = dv.get_count_stratified_stats_df(
|
|
99
|
+
"diagnosis", df, col="gender", wide=True, save_table=True, table_format="csv"
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Missing values and category order
|
|
104
|
+
|
|
105
|
+
All categorical inputs go through the same preprocessing (`prepare_categorical`):
|
|
106
|
+
|
|
107
|
+
- `None`, `np.nan` and `pd.NA` are treated as missing. With `show_nan=True` (default) they form
|
|
108
|
+
a single category labelled `"NaN"` (configurable through `DataVisualiser(nan_label=...)`) that is
|
|
109
|
+
always placed last. With `show_nan=False` the records are excluded from that plot/table, and
|
|
110
|
+
percentages are computed over the remaining records.
|
|
111
|
+
- Categories are sorted on their native values, so numeric codes are ordered numerically.
|
|
112
|
+
Integral floats are labelled without decimals (`10.0` -> `"10"`).
|
|
113
|
+
- `forced_order` (and `forced_order_col` for the stratification variable) imposes an explicit
|
|
114
|
+
order and may list categories absent from the data (they appear with a count of 0). It must
|
|
115
|
+
cover every value present, otherwise a `ValueError` is raised.
|
|
116
|
+
|
|
117
|
+
Distribution tables report `n_nan` and `nan_perc` next to the usual `describe()` statistics.
|
|
118
|
+
|
|
119
|
+
## Example
|
|
120
|
+
|
|
121
|
+
Here's a complete example of how to use the DataVisualiser class:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import pandas as pd
|
|
125
|
+
import numpy as np
|
|
126
|
+
from data_visualiser_package import DataVisualiser
|
|
127
|
+
|
|
128
|
+
# Create a sample dataset
|
|
129
|
+
np.random.seed(42)
|
|
130
|
+
n = 1000
|
|
131
|
+
|
|
132
|
+
data = {
|
|
133
|
+
'age': np.random.normal(50, 15, n),
|
|
134
|
+
'gender': np.random.choice(['Male', 'Female'], n),
|
|
135
|
+
'diagnosis': np.random.choice(['Healthy', 'Condition A', 'Condition B', None], n, p=[0.6, 0.2, 0.15, 0.05]),
|
|
136
|
+
'heart_rate': np.random.normal(80, 10, n),
|
|
137
|
+
'blood_pressure': np.random.normal(120, 15, n)
|
|
138
|
+
}
|
|
139
|
+
df = pd.DataFrame(data)
|
|
140
|
+
|
|
141
|
+
dv = DataVisualiser(
|
|
142
|
+
record_unit_name="patient",
|
|
143
|
+
figures_dirpath="./output/figures",
|
|
144
|
+
tables_dirpath="./output/tables",
|
|
145
|
+
create_dirs=True
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Plots
|
|
149
|
+
dv.get_count_plot('gender', df, save_fig=True)
|
|
150
|
+
dv.get_count_plot('diagnosis', df, save_fig=True)
|
|
151
|
+
dv.get_count_stratified_plot('diagnosis', df, col='gender', save_fig=True)
|
|
152
|
+
dv.get_dist_plot('age', df, save_fig=True)
|
|
153
|
+
dv.get_dist_stratified_plot('age', df, col='gender', save_fig=True)
|
|
154
|
+
dv.get_dist_hued_plot('age', df, hue='gender', save_fig=True)
|
|
155
|
+
dv.get_box_stratified_plot('heart_rate', df, col='diagnosis', kind='violin', save_fig=True)
|
|
156
|
+
|
|
157
|
+
# Statistics tables
|
|
158
|
+
dv.get_count_stats_df('diagnosis', df, save_table=True, add_total=True)
|
|
159
|
+
dv.get_count_stratified_stats_df('diagnosis', df, col='gender', wide=True, save_table=True)
|
|
160
|
+
dv.get_dist_stats_df('age', df, save_table=True)
|
|
161
|
+
dv.get_dist_stratified_stats_df('age', df, col='gender', save_table=True, table_format="markdown")
|
|
162
|
+
|
|
163
|
+
print("All visualizations and tables have been generated successfully!")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
See `examples/demo.py` for a runnable version.
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### DataVisualiser Class
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
class DataVisualiser(
|
|
174
|
+
record_unit_name="patient",
|
|
175
|
+
figures_dirpath=None,
|
|
176
|
+
tables_dirpath=None,
|
|
177
|
+
create_dirs=False,
|
|
178
|
+
nan_label="NaN",
|
|
179
|
+
fig_format="png",
|
|
180
|
+
dpi=None,
|
|
181
|
+
table_format="latex",
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`save_fig=True` / `save_table=True` require the corresponding directory to be configured and raise a `ValueError` otherwise. Saved figures are closed after writing.
|
|
186
|
+
|
|
187
|
+
#### Count Visualizations
|
|
188
|
+
|
|
189
|
+
- `get_count_plot(var, df, show_nan=True, save_fig=False, plot_kwargs=None, xlabels_rotation=None, forced_order=None)` -> `(fig, ax)`
|
|
190
|
+
- `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`
|
|
191
|
+
- `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`
|
|
192
|
+
- `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)
|
|
193
|
+
|
|
194
|
+
#### Distribution Visualizations
|
|
195
|
+
|
|
196
|
+
- `get_dist_plot(var, df, save_fig=False, plot_kwargs=None, xlabels_rotation=None)` -> `(fig, ax)` (`plot_kwargs` defaults to `{"kde": True}`)
|
|
197
|
+
- `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`
|
|
198
|
+
- `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`
|
|
199
|
+
- `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"`)
|
|
200
|
+
- `get_dist_stats_df(var, df, save_table=False, round_n_digits=None, table_format=None)` -> `DataFrame`
|
|
201
|
+
- `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`
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
#### Output files
|
|
206
|
+
|
|
207
|
+
| Method | File name (in `figures_dirpath` / `tables_dirpath`) |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| `get_count_plot` | `count_plot_{var}.{fig_format}` |
|
|
210
|
+
| `get_count_stratified_plot` | `count_plot_{var}_for_each_{col}.{fig_format}` |
|
|
211
|
+
| `get_dist_plot` | `dist_plot_{var}.{fig_format}` |
|
|
212
|
+
| `get_dist_stratified_plot` | `dist_plot_{var}_for_each_{col}.{fig_format}` |
|
|
213
|
+
| `get_dist_hued_plot` | `dist_plot_{var}_for_hue_{hue}.{fig_format}` |
|
|
214
|
+
| `get_box_stratified_plot` | `{kind}_plot_{var}_for_each_{col}.{fig_format}` |
|
|
215
|
+
| `get_count_stats_df` | `count_stats_table_{var}.{tex,csv,md,html}` |
|
|
216
|
+
| `get_count_stratified_stats_df` | `count_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |
|
|
217
|
+
| `get_dist_stats_df` | `dist_stats_table_{var}.{tex,csv,md,html}` |
|
|
218
|
+
| `get_dist_stratified_stats_df` | `dist_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |
|
|
219
|
+
|
|
220
|
+
LaTeX tables use `booktabs` rules (`\toprule`, `\midrule`, `\bottomrule`), bold column titles and escaped special characters (`#`, `%`, `_`, `&`, ...).
|
|
221
|
+
|
|
222
|
+
### Helper functions
|
|
223
|
+
|
|
224
|
+
- `prepare_categorical(series, show_nan=True, forced_order=None, nan_label="NaN")` -> ordered categorical `Series` of string labels
|
|
225
|
+
- `categorise_variable_in_df(var, df, forced_order=None, nan_replacement_str="NaN")` -> categorises a column in place
|
|
226
|
+
- `dataframe_to_latex(df, bold_header=True, round_n_digits=None, na_rep="", column_format=None)` -> `str`
|
|
227
|
+
- `dataframe_to_markdown(df, round_n_digits=None, na_rep="")` -> `str`
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
uv sync --group dev # or: pip install -e ".[dev]"
|
|
233
|
+
uv run pytest
|
|
234
|
+
uv run flake8 src tests examples --max-line-length=120
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|
|
240
|
+
|
|
241
|
+
## Contributing
|
|
242
|
+
|
|
243
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Data Visualiser Package
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install data-visualiser-package
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.9+ (pandas >= 1.5, seaborn >= 0.11.2, matplotlib >= 3.5).
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Count plots and distribution plots, plain or stratified by a categorical variable
|
|
16
|
+
- Box / violin plots of a numerical variable per category
|
|
17
|
+
- Count and distribution statistics tables, including stratified (long or wide) versions
|
|
18
|
+
- Save figures as PNG, PDF, SVG, ... and tables as LaTeX (bold headers, escaped special characters), CSV, Markdown or HTML
|
|
19
|
+
- Consistent handling of missing values: `None`, `NaN` and `pd.NA` become one `"NaN"` category placed last, or are excluded with `show_nan=False`
|
|
20
|
+
- Categories are ordered on their native type (`2 < 5 < 10`), or by an explicit `forced_order`
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import pandas as pd
|
|
26
|
+
from data_visualiser_package import DataVisualiser
|
|
27
|
+
|
|
28
|
+
# Create a DataVisualiser instance
|
|
29
|
+
dv = DataVisualiser(
|
|
30
|
+
record_unit_name="patient", # What a single record represents
|
|
31
|
+
figures_dirpath="./figures", # Where to save figures
|
|
32
|
+
tables_dirpath="./tables", # Where to save tables
|
|
33
|
+
create_dirs=True, # Create directories if they don't exist
|
|
34
|
+
fig_format="png", # Any format matplotlib supports (png, pdf, svg, ...)
|
|
35
|
+
dpi=150, # Resolution of saved figures (None = matplotlib default)
|
|
36
|
+
table_format="latex", # Default table format: latex, csv, markdown or html
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Load your data
|
|
40
|
+
df = pd.read_csv("your_data.csv")
|
|
41
|
+
|
|
42
|
+
# Create a count plot
|
|
43
|
+
fig, ax = dv.get_count_plot(
|
|
44
|
+
var="diagnosis", # Categorical variable to plot
|
|
45
|
+
df=df, # DataFrame containing the data
|
|
46
|
+
show_nan=True, # Show NaN values as a separate (last) category
|
|
47
|
+
save_fig=True, # Save the figure to disk
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Create a distribution plot
|
|
51
|
+
fig, ax = dv.get_dist_plot(var="age", df=df, save_fig=True)
|
|
52
|
+
|
|
53
|
+
# Generate statistics tables
|
|
54
|
+
stats_df = dv.get_count_stats_df(var="diagnosis", df=df, save_table=True)
|
|
55
|
+
|
|
56
|
+
# Create stratified visualizations
|
|
57
|
+
fg = dv.get_dist_stratified_plot(var="age", df=df, col="gender", save_fig=True)
|
|
58
|
+
|
|
59
|
+
# Box plot of a numerical variable per category
|
|
60
|
+
fig, ax = dv.get_box_stratified_plot(var="age", df=df, col="diagnosis", save_fig=True)
|
|
61
|
+
|
|
62
|
+
# Wide crosstab (rows: diagnosis categories, columns: one block per gender), saved as CSV
|
|
63
|
+
wide = dv.get_count_stratified_stats_df(
|
|
64
|
+
"diagnosis", df, col="gender", wide=True, save_table=True, table_format="csv"
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Missing values and category order
|
|
69
|
+
|
|
70
|
+
All categorical inputs go through the same preprocessing (`prepare_categorical`):
|
|
71
|
+
|
|
72
|
+
- `None`, `np.nan` and `pd.NA` are treated as missing. With `show_nan=True` (default) they form
|
|
73
|
+
a single category labelled `"NaN"` (configurable through `DataVisualiser(nan_label=...)`) that is
|
|
74
|
+
always placed last. With `show_nan=False` the records are excluded from that plot/table, and
|
|
75
|
+
percentages are computed over the remaining records.
|
|
76
|
+
- Categories are sorted on their native values, so numeric codes are ordered numerically.
|
|
77
|
+
Integral floats are labelled without decimals (`10.0` -> `"10"`).
|
|
78
|
+
- `forced_order` (and `forced_order_col` for the stratification variable) imposes an explicit
|
|
79
|
+
order and may list categories absent from the data (they appear with a count of 0). It must
|
|
80
|
+
cover every value present, otherwise a `ValueError` is raised.
|
|
81
|
+
|
|
82
|
+
Distribution tables report `n_nan` and `nan_perc` next to the usual `describe()` statistics.
|
|
83
|
+
|
|
84
|
+
## Example
|
|
85
|
+
|
|
86
|
+
Here's a complete example of how to use the DataVisualiser class:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import pandas as pd
|
|
90
|
+
import numpy as np
|
|
91
|
+
from data_visualiser_package import DataVisualiser
|
|
92
|
+
|
|
93
|
+
# Create a sample dataset
|
|
94
|
+
np.random.seed(42)
|
|
95
|
+
n = 1000
|
|
96
|
+
|
|
97
|
+
data = {
|
|
98
|
+
'age': np.random.normal(50, 15, n),
|
|
99
|
+
'gender': np.random.choice(['Male', 'Female'], n),
|
|
100
|
+
'diagnosis': np.random.choice(['Healthy', 'Condition A', 'Condition B', None], n, p=[0.6, 0.2, 0.15, 0.05]),
|
|
101
|
+
'heart_rate': np.random.normal(80, 10, n),
|
|
102
|
+
'blood_pressure': np.random.normal(120, 15, n)
|
|
103
|
+
}
|
|
104
|
+
df = pd.DataFrame(data)
|
|
105
|
+
|
|
106
|
+
dv = DataVisualiser(
|
|
107
|
+
record_unit_name="patient",
|
|
108
|
+
figures_dirpath="./output/figures",
|
|
109
|
+
tables_dirpath="./output/tables",
|
|
110
|
+
create_dirs=True
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Plots
|
|
114
|
+
dv.get_count_plot('gender', df, save_fig=True)
|
|
115
|
+
dv.get_count_plot('diagnosis', df, save_fig=True)
|
|
116
|
+
dv.get_count_stratified_plot('diagnosis', df, col='gender', save_fig=True)
|
|
117
|
+
dv.get_dist_plot('age', df, save_fig=True)
|
|
118
|
+
dv.get_dist_stratified_plot('age', df, col='gender', save_fig=True)
|
|
119
|
+
dv.get_dist_hued_plot('age', df, hue='gender', save_fig=True)
|
|
120
|
+
dv.get_box_stratified_plot('heart_rate', df, col='diagnosis', kind='violin', save_fig=True)
|
|
121
|
+
|
|
122
|
+
# Statistics tables
|
|
123
|
+
dv.get_count_stats_df('diagnosis', df, save_table=True, add_total=True)
|
|
124
|
+
dv.get_count_stratified_stats_df('diagnosis', df, col='gender', wide=True, save_table=True)
|
|
125
|
+
dv.get_dist_stats_df('age', df, save_table=True)
|
|
126
|
+
dv.get_dist_stratified_stats_df('age', df, col='gender', save_table=True, table_format="markdown")
|
|
127
|
+
|
|
128
|
+
print("All visualizations and tables have been generated successfully!")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See `examples/demo.py` for a runnable version.
|
|
132
|
+
|
|
133
|
+
## API Reference
|
|
134
|
+
|
|
135
|
+
### DataVisualiser Class
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
class DataVisualiser(
|
|
139
|
+
record_unit_name="patient",
|
|
140
|
+
figures_dirpath=None,
|
|
141
|
+
tables_dirpath=None,
|
|
142
|
+
create_dirs=False,
|
|
143
|
+
nan_label="NaN",
|
|
144
|
+
fig_format="png",
|
|
145
|
+
dpi=None,
|
|
146
|
+
table_format="latex",
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`save_fig=True` / `save_table=True` require the corresponding directory to be configured and raise a `ValueError` otherwise. Saved figures are closed after writing.
|
|
151
|
+
|
|
152
|
+
#### Count Visualizations
|
|
153
|
+
|
|
154
|
+
- `get_count_plot(var, df, show_nan=True, save_fig=False, plot_kwargs=None, xlabels_rotation=None, forced_order=None)` -> `(fig, ax)`
|
|
155
|
+
- `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`
|
|
156
|
+
- `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`
|
|
157
|
+
- `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)
|
|
158
|
+
|
|
159
|
+
#### Distribution Visualizations
|
|
160
|
+
|
|
161
|
+
- `get_dist_plot(var, df, save_fig=False, plot_kwargs=None, xlabels_rotation=None)` -> `(fig, ax)` (`plot_kwargs` defaults to `{"kde": True}`)
|
|
162
|
+
- `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`
|
|
163
|
+
- `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`
|
|
164
|
+
- `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"`)
|
|
165
|
+
- `get_dist_stats_df(var, df, save_table=False, round_n_digits=None, table_format=None)` -> `DataFrame`
|
|
166
|
+
- `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`
|
|
167
|
+
|
|
168
|
+
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.
|
|
169
|
+
|
|
170
|
+
#### Output files
|
|
171
|
+
|
|
172
|
+
| Method | File name (in `figures_dirpath` / `tables_dirpath`) |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `get_count_plot` | `count_plot_{var}.{fig_format}` |
|
|
175
|
+
| `get_count_stratified_plot` | `count_plot_{var}_for_each_{col}.{fig_format}` |
|
|
176
|
+
| `get_dist_plot` | `dist_plot_{var}.{fig_format}` |
|
|
177
|
+
| `get_dist_stratified_plot` | `dist_plot_{var}_for_each_{col}.{fig_format}` |
|
|
178
|
+
| `get_dist_hued_plot` | `dist_plot_{var}_for_hue_{hue}.{fig_format}` |
|
|
179
|
+
| `get_box_stratified_plot` | `{kind}_plot_{var}_for_each_{col}.{fig_format}` |
|
|
180
|
+
| `get_count_stats_df` | `count_stats_table_{var}.{tex,csv,md,html}` |
|
|
181
|
+
| `get_count_stratified_stats_df` | `count_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |
|
|
182
|
+
| `get_dist_stats_df` | `dist_stats_table_{var}.{tex,csv,md,html}` |
|
|
183
|
+
| `get_dist_stratified_stats_df` | `dist_stats_table_{var}_for_each_{col}.{tex,csv,md,html}` |
|
|
184
|
+
|
|
185
|
+
LaTeX tables use `booktabs` rules (`\toprule`, `\midrule`, `\bottomrule`), bold column titles and escaped special characters (`#`, `%`, `_`, `&`, ...).
|
|
186
|
+
|
|
187
|
+
### Helper functions
|
|
188
|
+
|
|
189
|
+
- `prepare_categorical(series, show_nan=True, forced_order=None, nan_label="NaN")` -> ordered categorical `Series` of string labels
|
|
190
|
+
- `categorise_variable_in_df(var, df, forced_order=None, nan_replacement_str="NaN")` -> categorises a column in place
|
|
191
|
+
- `dataframe_to_latex(df, bold_header=True, round_n_digits=None, na_rep="", column_format=None)` -> `str`
|
|
192
|
+
- `dataframe_to_markdown(df, round_n_digits=None, na_rep="")` -> `str`
|
|
193
|
+
|
|
194
|
+
## Development
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
uv sync --group dev # or: pip install -e ".[dev]"
|
|
198
|
+
uv run pytest
|
|
199
|
+
uv run flake8 src tests examples --max-line-length=120
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
205
|
+
|
|
206
|
+
## Contributing
|
|
207
|
+
|
|
208
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|