Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,490 changes: 4,490 additions & 0 deletions docs/api_examples/line_plot.ipynb

Large diffs are not rendered by default.

194 changes: 194 additions & 0 deletions docs/api_examples/line_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: percent
# format_version: '1.3'
# jupytext_version: 1.17.2
# kernelspec:
# display_name: vuecore-dev
# language: python
# name: python3
# ---

# %% [markdown]
# # Line Plot
#
# ![VueCore logo][vuecore_logo]
#
# [![Open In Colab][colab_badge]][colab_link]
#
# [VueCore][vuecore_repo] is a Python package for creating interactive and static visualizations of multi-omics data.
# 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.
#
# This notebook demonstrates how to generate line 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.
#
# ## Notebook structure
#
# 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 line plots.
#
# 0. [Work environment setup](#0-work-environment-setup)
# 1. [Basic line plot](#1-basic-line-plot)
# 2. [Advanced line plot](#2-advanced-line-plot)
#
# ## Credits and Contributors
# - 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].
# - You can find more details about the project in this [GitHub repository][vuecore_repo].
#
# [colab_badge]: https://colab.research.google.com/assets/colab-badge.svg
# [colab_link]: https://colab.research.google.com/github/Multiomics-Analytics-Group/vuecore/blob/main/docs/api_examples/line_plot.ipynb
# [vuecore_logo]: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuecore/main/docs/images/logo/vuecore_logo.svg
# [Mona]: https://multiomics-analytics-group.github.io/
# [Biosustain]: https://www.biosustain.dtu.dk/
# [vuecore_repo]: https://github.com/Multiomics-Analytics-Group/vuecore
# [vuegen_repo]: https://github.com/Multiomics-Analytics-Group/vuegen
# [acore_repo]: https://github.com/Multiomics-Analytics-Group/acore

# %% [markdown]
# ## 0. Work environment setup

# %% [markdown]
# ### 0.1. Installing libraries and creating global variables for platform and working directory
#
# 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.

# %% tags=["hide-output"]
# VueCore library
# %pip install vuecore

# %% tags=["hide-cell"]
import os

IN_COLAB = "COLAB_GPU" in os.environ

# %% tags=["hide-cell"]
# Create a directory for outputs
output_dir = "./outputs"
os.makedirs(output_dir, exist_ok=True)

# %% [markdown]
# ### 0.2. Importing libraries

# %%
# Imports
import pandas as pd
import plotly.io as pio

from vuecore.plots.basic.line import create_line_plot

# Set the Plotly renderer based on the environment
pio.renderers.default = "notebook"

# %% [markdown]
# ### 0.3. Create sample data
# We create a synthetic dataset showing measurements over five days for two experiments (A, B), each tested under Control and Treatment conditions, with associated measurement errors.

# %%
sample_df = pd.DataFrame(
{
"day": list(range(1, 6)) * 4, # 5 days
"experiment": ["A"] * 10 + ["B"] * 10, # 2 experiments
"condition": (["Control"] * 5 + ["Treatment"] * 5) * 2, # 2 conditions
"value": [
11,
13,
15,
17,
18, # A - Control
10,
12,
14,
15,
16, # A - Treatment
19,
20,
21,
22,
23, # B - Control
20,
22,
21,
23,
22, # B - Treatment
],
"value_error": [
1,
1.2,
0.9,
1.1,
1.0,
1.3,
1.0,
1.2,
1.4,
1.1,
2.0,
1.8,
2.1,
1.5,
2.3,
1.7,
2.0,
1.8,
2.1,
2.2,
],
}
)

sample_df.head()

# %% [markdown]
# ## 1. Basic Line Plot
# A basic line plot can be created by simply providing the `x`, `y`, and `color` columns from the DataFrame.

# %%
# Define output path
file_path_basic_png = os.path.join(output_dir, "line_plot_basic.png")

# Generate basic plot
fig = create_line_plot(
data=sample_df,
x="day",
y="value",
color="experiment",
line_dash="condition",
file_path=file_path_basic_png,
)

fig.show()

# %% [markdown]
# ## 2. Advanced Line Plot
# Here is an example of an advanced line plot with more descriptive parameters, including error bars, line styles, markers, and custom colors.

# %%
# Define output path
file_path_adv_html = os.path.join(output_dir, "line_plot_advanced.html")

# Generate advanced plot
fig_advanced = create_line_plot(
data=sample_df,
x="day",
y="value",
color="experiment",
line_dash="condition",
error_y="value_error",
title="Experiment & Condition Trends",
subtitle="Measurements over 5 days for two experiments (A, B) under Control and Treatment conditions.",
labels={
"day": "Day",
"value": "Response",
"condition": "Condition",
"experiment": "Experiment",
},
color_discrete_map={"A": "#508AA8", "B": "#A8505E"},
line_dash_map={"Control": "solid", "Treatment": "dot"},
markers=True,
line_shape="spline",
file_path=file_path_adv_html,
)

fig_advanced.show()
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
:caption: API Usage Examples

api_examples/scatter_plot
api_examples/line_plot
```

```{toctree}
Expand Down
3 changes: 3 additions & 0 deletions src/vuecore/engines/plotly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
from vuecore import PlotType, EngineType

from .scatter import build as build_scatter
from .line import build as build_line
from .saver import save

# Register the functions with the central dispatcher
register_builder(
plot_type=PlotType.SCATTER, engine=EngineType.PLOTLY, func=build_scatter
)
register_builder(plot_type=PlotType.LINE, engine=EngineType.PLOTLY, func=build_line)

register_saver(engine=EngineType.PLOTLY, func=save)
67 changes: 67 additions & 0 deletions src/vuecore/engines/plotly/line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# vuecore/engines/plotly/line.py

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

from vuecore.schemas.basic.line import LineConfig
from .theming import apply_line_theme


def build(data: pd.DataFrame, config: LineConfig) -> go.Figure:
"""
Creates a Plotly line plot figure from a DataFrame and a Pydantic configuration.

This function acts as a bridge between the abstract plot definition and the
Plotly Express implementation. It translates the validated `LineConfig`
into the arguments for `plotly.express.line` and also forwards any
additional, unvalidated keyword arguments form plotly. The resulting figure
is then customized with layout and theme settings using `plotly.graph_objects`.
(https://plotly.com/python-api-reference/generated/plotly.express.line.html).

Parameters
----------
data : pd.DataFrame
The DataFrame containing the plot data.
config : LineConfig
The validated Pydantic model with all plot configurations.

Returns
-------
go.Figure
A `plotly.graph_objects.Figure` object representing the line plot.
"""
# Get all parameters from the config model, including extras
all_config_params = config.model_dump()

# Define parameters handled by the theme script
theming_params = [
"markers",
"log_x",
"log_y",
"range_x",
"range_y",
"line_shape",
"title",
"x_title",
"y_title",
"subtitle",
"template",
"width",
"height",
]

# Create the dictionary of arguments for px.line
plot_args = {
k: v
for k, v in all_config_params.items()
if k not in theming_params and v is not None
}

# Create the base figure using only the arguments for px.line
fig = px.line(data, **plot_args)

# Apply theme and additional styling
fig = apply_line_theme(fig, config)

return fig
53 changes: 53 additions & 0 deletions src/vuecore/engines/plotly/theming.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import plotly.graph_objects as go

from vuecore.schemas.basic.scatter import ScatterConfig
from vuecore.schemas.basic.line import LineConfig


def apply_scatter_theme(fig: go.Figure, config: ScatterConfig) -> go.Figure:
Expand Down Expand Up @@ -42,3 +43,55 @@ def apply_scatter_theme(fig: go.Figure, config: ScatterConfig) -> go.Figure:
hovermode="closest",
)
return fig


def apply_line_theme(fig: go.Figure, config: LineConfig) -> go.Figure:
"""
Applies a consistent layout and theme to a Plotly line plot.

This function handles all styling and layout adjustments, such as titles,
dimensions, templates, and trace properties, separating these concerns
from the initial data mapping.

Parameters
----------
fig : go.Figure
The Plotly figure object to be styled.
config : LineConfig
The configuration object containing all styling and layout info.

Returns
-------
go.Figure
The styled Plotly figure object.
"""
# Apply trace-specific updates
fig.update_traces(
mode="lines+markers" if config.markers else "lines",
line_shape=config.line_shape,
)

# Use the labels dictionary to set axis titles, falling back to defaults
x_title = config.x_title or (
config.labels.get(config.x) if config.labels else None or config.x.title()
)
y_title = config.y_title or (
config.labels.get(config.y) if config.labels else None or config.y.title()
)

# Apply layout updates
fig.update_layout(
title_text=config.title,
title_subtitle_text=config.subtitle,
xaxis_title=x_title,
yaxis_title=y_title,
height=config.height,
width=config.width,
template=config.template,
xaxis_type="log" if config.log_x else "linear",
yaxis_type="log" if config.log_y else "linear",
xaxis_range=config.range_x,
yaxis_range=config.range_y,
hovermode="x unified",
)
return fig
Loading