Skip to content

Commit 3776564

Browse files
authored
✨ Feat: Add scripts, test, and example for histogram plot (#41)
* ✨ Feat(vuecore/schemas/basic/histogram.py): Create Pydantic schema of the histogram plot * ♻️ Refactor(vuecore/schemas/basic/histogram.py): Update parameters from the schema * ✨ Feat(vuecore/engines/plotly/histogram.py): Create script with build function for histogram plot * ✨ Feat(vuecore/engines/plotly/theming.py): Add apply_histogram_theme function to the script * ✨ Feat(vuecore/engines/plotly/__init__.py): Register histogram plot builder and add it to the PlotType StrEnum from the constants,py script * ✨ Feat(vuecore/plots/histogram.py): Create script with the user-facing function for the histogram plot * ✏️ Fix(vuecore/engines/plotly/theming.py): Correct import typo and add vilion and histogram function on the plots/__init__.py script * ✨ Feat(docs/api_examples/histogram.ipynb): Create notebook api example for histogram plot and sync it with a python script * 📝 Docs: update index.md to add histogram plot example * ✅ Feat(tests/test_histogramplot.py): Create histogram plot test based on the api example code
1 parent 6e19f17 commit 3776564

File tree

11 files changed

+4820
-0
lines changed

11 files changed

+4820
-0
lines changed

docs/api_examples/histogram_plot.ipynb

Lines changed: 4318 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# ---
2+
# jupyter:
3+
# jupytext:
4+
# text_representation:
5+
# extension: .py
6+
# format_name: percent
7+
# format_version: '1.3'
8+
# jupytext_version: 1.17.2
9+
# kernelspec:
10+
# display_name: vuecore-dev
11+
# language: python
12+
# name: python3
13+
# ---
14+
15+
# %% [markdown]
16+
# # Histogram Plot
17+
#
18+
# ![VueCore logo][vuecore_logo]
19+
#
20+
# [![Open In Colab][colab_badge]][colab_link]
21+
#
22+
# [VueCore][vuecore_repo] is a Python package for creating interactive and static visualizations of multi-omics data.
23+
# It is part of a broader ecosystem of tools—including [ACore][acore_repo] for data processing and [VueGen][vuegen_repo] for automated reporting—that together enable end-to-end workflows for omics analysis.
24+
#
25+
# This notebook demonstrates how to generate histogram plots using plotting functions from VueCore. We showcase basic and advanced plot configurations, highlighting key customization options such as grouping, color mapping, text annotations, and export to multiple file formats.
26+
#
27+
# ## Notebook structure
28+
#
29+
# First, we will set up the work environment by installing the necessary packages and importing the required libraries. Next, we will create basic and advanced histogram plots.
30+
#
31+
# 0. [Work environment setup](#0-work-environment-setup)
32+
# 1. [Basic histogram plot](#1-basic-histogram-plot)
33+
# 2. [Advanced histogram plot](#2-advanced-histogram-plot)
34+
#
35+
# ## Credits and Contributors
36+
#
37+
# - This notebook was created by Sebastián Ayala-Ruano under the supervision of Henry Webel and Alberto Santos, head of the [Multiomics Network Analytics Group (MoNA)][Mona] at the [Novo Nordisk Foundation Center for Biosustainability (DTU Biosustain)][Biosustain].
38+
# - You can find more details about the project in this [GitHub repository][vuecore_repo].
39+
#
40+
# [colab_badge]: https://colab.research.google.com/assets/colab-badge.svg
41+
# [colab_link]: https://colab.research.google.com/github/Multiomics-Analytics-Group/vuecore/blob/main/docs/api_examples/bar_plot.ipynb
42+
# [vuecore_logo]: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuecore/main/docs/images/logo/vuecore_logo.svg
43+
# [Mona]: https://multiomics-analytics-group.github.io/
44+
# [Biosustain]: https://www.biosustain.dtu.dk/
45+
# [vuecore_repo]: https://github.com/Multiomics-Analytics-Group/vuecore
46+
# [vuegen_repo]: https://github.com/Multiomics-Analytics-Group/vuegen
47+
# [acore_repo]: https://github.com/Multiomics-Analytics-Group/acore
48+
49+
# %% [markdown]
50+
# ## 0. Work environment setup
51+
52+
# %% [markdown]
53+
# ### 0.1. Installing libraries and creating global variables for platform and working directory
54+
#
55+
# To run this notebook locally, you should create a virtual environment with the required libraries. If you are running this notebook on Google Colab, everything should be set.
56+
57+
# %% tags=["hide-output"]
58+
# VueCore library
59+
# %pip install vuecore
60+
61+
# %% tags=["hide-cell"]
62+
import os
63+
64+
IN_COLAB = "COLAB_GPU" in os.environ
65+
66+
# %% tags=["hide-cell"]
67+
# Create a directory for outputs
68+
output_dir = "./outputs"
69+
os.makedirs(output_dir, exist_ok=True)
70+
71+
# %% [markdown]
72+
# ### 0.2. Importing libraries
73+
74+
# %%
75+
# Imports
76+
import pandas as pd
77+
import numpy as np
78+
from pathlib import Path
79+
import plotly.io as pio
80+
81+
from vuecore.plots.basic.histogram import create_histogram_plot
82+
83+
# Set the Plotly renderer based on the environment
84+
pio.renderers.default = "notebook"
85+
86+
# %% [markdown]
87+
# ### 0.3. Create sample data
88+
# We create a synthetic dataset simulating gene expression data across two experimental conditions to demonstrate how histograms can visualize data distribution.
89+
90+
# %%
91+
# Set a random seed for reproducibility of the synthetic data
92+
np.random.seed(42)
93+
94+
# Define parameters for synthetic gene expression data
95+
num_genes = 1000
96+
conditions = ["Control", "Treated"]
97+
gene_names = [f"Gene_{i}" for i in range(num_genes)]
98+
99+
# Simulate expression data with a slight shift in the "Treated" group
100+
expression_values = np.concatenate(
101+
[
102+
np.random.normal(loc=10, scale=2, size=num_genes // 2),
103+
np.random.normal(loc=12, scale=2, size=num_genes // 2),
104+
]
105+
)
106+
condition_values = np.concatenate(
107+
[["Control"] * (num_genes // 2), ["Treated"] * (num_genes // 2)]
108+
)
109+
110+
# Create the DataFrame
111+
gene_exp_df = pd.DataFrame(
112+
{
113+
"Gene_ID": gene_names,
114+
"Expression": expression_values,
115+
"Condition": condition_values,
116+
}
117+
)
118+
119+
gene_exp_df.head()
120+
121+
# %% [markdown]
122+
# ## 1. Basic Histogram Plot
123+
# A basic histogram plot can be created by simply providing the `x` and `y` columns from the DataFrame, along with style options like `title`.
124+
125+
# %%
126+
# Define output file path for the PNG basic histogram
127+
file_path_basic_hist_png = Path(output_dir) / "histogram_plot_basic.png"
128+
129+
# Generate the basic histogram plot
130+
histogram_plot_basic = create_histogram_plot(
131+
data=gene_exp_df,
132+
x="Expression",
133+
title="Distribution of Gene Expression Levels",
134+
file_path=file_path_basic_hist_png,
135+
)
136+
137+
histogram_plot_basic.show()
138+
139+
# %% [markdown]
140+
# ## 2. Advanced Histogram Plot
141+
# Here is an example of an advanced histogram plot with more descriptive parameters, including `color grouping`, `overlay barmode`, `probability density normalization`, `hover tooltips`, and export to `HTML`.
142+
143+
# %%
144+
# Define the output file path for the advanced HTML histogram
145+
file_path_adv_hist_html = Path(output_dir) / "histogram_plot_advanced.html"
146+
147+
# Generate the advanced histogram plot
148+
histogram_plot_adv = create_histogram_plot(
149+
data=gene_exp_df,
150+
x="Expression",
151+
color="Condition",
152+
barmode="overlay",
153+
histnorm="probability density",
154+
title="Gene Expression Distribution by Treatment Condition",
155+
subtitle="Histogram with probability density normalized",
156+
labels={"Expression": "Gene Expression", "Condition": "Treatment Condition"},
157+
hover_data=["Gene_ID"],
158+
opacity=0.75,
159+
file_path=file_path_adv_hist_html,
160+
)
161+
162+
histogram_plot_adv.show()

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ api_examples/scatter_plot
2424
api_examples/line_plot
2525
api_examples/bar_plot
2626
api_examples/box_violin_plot
27+
api_examples/histogram_plot
2728
```
2829

2930
```{toctree}

src/vuecore/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class PlotType(StrEnum):
1515
BAR = auto()
1616
BOX = auto()
1717
VIOLIN = auto()
18+
HISTOGRAM = auto()
1819

1920

2021
class EngineType(StrEnum):

src/vuecore/engines/plotly/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .bar import build as build_bar
77
from .box import build as build_box
88
from .violin import build as build_violin
9+
from .histogram import build as build_histogram
910
from .saver import save
1011

1112
# Import build_utils to ensure it's available
@@ -19,5 +20,8 @@
1920
register_builder(plot_type=PlotType.BAR, engine=EngineType.PLOTLY, func=build_bar)
2021
register_builder(plot_type=PlotType.BOX, engine=EngineType.PLOTLY, func=build_box)
2122
register_builder(plot_type=PlotType.VIOLIN, engine=EngineType.PLOTLY, func=build_violin)
23+
register_builder(
24+
plot_type=PlotType.HISTOGRAM, engine=EngineType.PLOTLY, func=build_histogram
25+
)
2226

2327
register_saver(engine=EngineType.PLOTLY, func=save)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import pandas as pd
2+
import plotly.express as px
3+
import plotly.graph_objects as go
4+
5+
from vuecore.schemas.basic.histogram import HistogramConfig
6+
from .theming import apply_histogram_theme
7+
from .plot_builder import build_plot
8+
9+
# Define parameters handled by the theme script
10+
THEMING_PARAMS = [
11+
"opacity",
12+
"barmode",
13+
"barnorm",
14+
"histnorm",
15+
"log_x",
16+
"log_y",
17+
"range_x",
18+
"range_y",
19+
"title",
20+
"x_title",
21+
"y_title",
22+
"subtitle",
23+
"template",
24+
"width",
25+
"height",
26+
]
27+
28+
29+
def build(data: pd.DataFrame, config: HistogramConfig) -> go.Figure:
30+
"""
31+
Creates a Plotly histogram figure from a DataFrame and a Pydantic configuration.
32+
33+
This function acts as a bridge between the abstract plot definition and the
34+
Plotly Express implementation. It translates the validated `HistogramConfig`
35+
into the arguments for `plotly.express.histogram` and also forwards any
36+
additional, unvalidated keyword arguments from Plotly. The resulting figure
37+
is then customized with layout and theme settings using `plotly.graph_objects`.
38+
(https://plotly.com/python-api-reference/generated/plotly.express.histogram.html).
39+
40+
Parameters
41+
----------
42+
data : pd.DataFrame
43+
The DataFrame containing the plot data.
44+
config : HistogramConfig
45+
The validated Pydantic model with all plot configurations.
46+
47+
Returns
48+
-------
49+
go.Figure
50+
A `plotly.graph_objects.Figure` object representing the histogram.
51+
"""
52+
return build_plot(
53+
data=data,
54+
config=config,
55+
px_function=px.histogram,
56+
theming_function=apply_histogram_theme,
57+
theming_params=THEMING_PARAMS,
58+
)

src/vuecore/engines/plotly/theming.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from vuecore.schemas.basic.bar import BarConfig
66
from vuecore.schemas.basic.box import BoxConfig
77
from vuecore.schemas.basic.violin import ViolinConfig
8+
from vuecore.schemas.basic.histogram import HistogramConfig
89

910

1011
def _get_axis_title(config, axis: str) -> str:
@@ -246,3 +247,36 @@ def apply_violin_theme(fig: go.Figure, config: ViolinConfig) -> go.Figure:
246247
fig = _apply_common_layout(fig, config)
247248

248249
return fig
250+
251+
252+
def apply_histogram_theme(fig: go.Figure, config: HistogramConfig) -> go.Figure:
253+
"""
254+
Applies a consistent layout and theme to a Plotly histogram plot.
255+
256+
This function handles all styling and layout adjustments, such as titles,
257+
dimensions, templates, and trace properties, separating these concerns
258+
from the initial data mapping.
259+
260+
Parameters
261+
----------
262+
fig : go.Figure
263+
The Plotly figure object to be styled.
264+
config : HistogramConfig
265+
The configuration object containing all styling and layout info.
266+
267+
Returns
268+
-------
269+
go.Figure
270+
The styled Plotly figure object.
271+
"""
272+
# Apply trace-specific updates for histogram
273+
fig.update_traces(
274+
opacity=config.opacity,
275+
orientation=config.orientation,
276+
selector=dict(type="histogram"),
277+
)
278+
279+
# Apply common layout
280+
fig = _apply_common_layout(fig, config)
281+
282+
return fig
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# vuecore/plots/basic/__init__.py
22
from .bar import create_bar_plot
33
from .box import create_box_plot
4+
from .histogram import create_histogram_plot
45
from .line import create_line_plot
56
from .scatter import create_scatter_plot
7+
from .violin import create_violin_plot
68

79
__all__ = [
810
"create_bar_plot",
911
"create_box_plot",
1012
"create_line_plot",
1113
"create_scatter_plot",
14+
"create_histogram_plot",
15+
"create_violin_plot",
1216
]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from typing import Any
2+
3+
import pandas as pd
4+
5+
from vuecore import EngineType, PlotType
6+
from vuecore.schemas.basic.histogram import HistogramConfig
7+
from vuecore.plots.plot_factory import create_plot
8+
from vuecore.utils.docs_utils import document_pydant_params
9+
10+
11+
@document_pydant_params(HistogramConfig)
12+
def create_histogram_plot(
13+
data: pd.DataFrame,
14+
engine: EngineType = EngineType.PLOTLY,
15+
file_path: str = None,
16+
**kwargs,
17+
) -> Any:
18+
"""
19+
Creates, styles, and optionally saves a histogram using the specified engine.
20+
21+
This function serves as the main entry point for users to generate histograms.
22+
It validates the provided configuration against the `HistogramConfig` schema,
23+
retrieves the appropriate plotting builder and saver functions based on the
24+
selected engine, builds the plot, and optionally saves it to a file.
25+
26+
Parameters
27+
----------
28+
data : pd.DataFrame
29+
The DataFrame containing the data to be plotted. Each row represents
30+
an observation, and columns correspond to variables.
31+
engine : EngineType, optional
32+
The plotting engine to use for rendering the plot.
33+
Defaults to `EngineType.PLOTLY`.
34+
file_path : str, optional
35+
If provided, the path where the final plot will be saved.
36+
The file format is automatically inferred from the file extension
37+
(e.g., '.html', '.png', '.jpeg', '.svg'). Defaults to None, meaning
38+
the plot will not be saved.
39+
40+
Returns
41+
-------
42+
Any
43+
The final plot object returned by the selected engine.
44+
For Plotly, this will typically be a `plotly.graph_objects.Figure`.
45+
The exact type depends on the chosen engine.
46+
47+
Raises
48+
------
49+
pydantic.ValidationError
50+
If the provided keyword arguments do not conform to the `HistogramConfig` schema.
51+
e.g., a required parameter is missing or a value has an incorrect type.
52+
ValueError
53+
Raised by the plotting engine (e.g., Plotly Express) if a
54+
column specified in the configuration (e.g., 'x', 'y', 'color') is
55+
not found in the provided DataFrame.
56+
57+
Examples
58+
--------
59+
For detailed examples and usage, please refer to the documentation:
60+
61+
* **Jupyter Notebook:** `docs/api_examples/histogram_plot.ipynb` -
62+
https://vuecore.readthedocs.io/en/latest/api_examples/histogram_plot.html
63+
* **Python Script:** `docs/api_examples/histogram_plot.py` -
64+
https://github.com/Multiomics-Analytics-Group/vuecore/blob/main/docs/api_examples/histogram_plot.py
65+
"""
66+
return create_plot(
67+
data=data,
68+
config=HistogramConfig,
69+
plot_type=PlotType.HISTOGRAM,
70+
engine=engine,
71+
file_path=file_path,
72+
**kwargs,
73+
)

0 commit comments

Comments
 (0)