Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Demo] Add lollipop chart to ViViVo #874

Merged
merged 23 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f28ca8c
Add original example
huong-li-nguyen Nov 14, 2024
bb0537a
Add lollipop refactored code
huong-li-nguyen Nov 14, 2024
b90d126
Add factory function and add to groups
huong-li-nguyen Nov 14, 2024
7d2f165
Update README.md
huong-li-nguyen Nov 14, 2024
644a9c7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 14, 2024
fa2f8eb
Declutter some more
huong-li-nguyen Nov 14, 2024
04c6790
Merge branch 'demo/add-lollipop-chart' of https://github.com/mckinsey…
huong-li-nguyen Nov 14, 2024
143a4af
Lint
huong-li-nguyen Nov 14, 2024
3cd607d
Add author
huong-li-nguyen Nov 14, 2024
d2e5190
Merge branch 'main' into demo/add-lollipop-chart
huong-li-nguyen Nov 14, 2024
7f447da
Merge branch 'main' into demo/add-lollipop-chart
huong-li-nguyen Nov 15, 2024
8545946
Fix lollipop
huong-li-nguyen Nov 15, 2024
c58aeb1
Merge branch 'main' into demo/add-lollipop-chart
huong-li-nguyen Nov 15, 2024
eebd889
Dynamic layout updates
huong-li-nguyen Nov 15, 2024
c5994db
Add argument
huong-li-nguyen Nov 15, 2024
1980b53
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2024
4bcc3ea
Update vizro-core/examples/visual-vocabulary/custom_charts.py
huong-li-nguyen Nov 15, 2024
6ca75dc
Refactor
huong-li-nguyen Nov 15, 2024
5dda70d
Merge branch 'demo/add-lollipop-chart' of https://github.com/mckinsey…
huong-li-nguyen Nov 15, 2024
407421a
Merge branch 'main' into demo/add-lollipop-chart
huong-li-nguyen Nov 19, 2024
5f00159
Tidy README
huong-li-nguyen Nov 19, 2024
74d9bce
Tidy chart
huong-li-nguyen Nov 19, 2024
6dc9368
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 19, 2024
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
1 change: 1 addition & 0 deletions vizro-core/docs/pages/explanation/authors.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Natalia Kurakina,
[Hilary Ivy](https://github.com/hxe00570),
[Jasmine Wu](https://github.com/jazwu),
[njmcgrat](https://github.com/njmcgrat),
[Jenelle Yonkman](https://github.com/yonkmanjl),
[ataraexia](https://github.com/ataraexia)

with thanks to Sam Bourton and Kevin Staight for sponsorship, inspiration and guidance,
Expand Down
78 changes: 46 additions & 32 deletions vizro-core/examples/scratch_dev/app.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
"""Dev app to try things out."""

import pandas as pd
import plotly.graph_objects as go
import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro._themes._color_values import COLORS

pastry = pd.DataFrame(
{
"pastry": [
"Scones",
"Bagels",
"Muffins",
"Cakes",
"Donuts",
"Cookies",
"Croissants",
"Eclairs",
"Brownies",
"Tarts",
"Macarons",
"Pies",
],
"Profit Ratio": [-0.10, -0.15, -0.05, 0.10, 0.05, 0.20, 0.15, -0.08, 0.08, -0.12, 0.02, -0.07],
}
)
from vizro.models.types import capture


@capture("graph")
def lollipop(data_frame: pd.DataFrame, x: str, y: str):
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
"""Creates a lollipop chart using Plotly.

This function generates a scatter chart and then draws lines extending from each point to the x-axis.

Args:
data_frame (pd.DataFrame): The data source for the chart.
x (str): The column name to be used for the x-axis.
y (str): The column name to be used for the y-axis.

Returns:
go.Figure: : A Plotly Figure object representing the lollipop chart.
"""
fig = go.Figure()

# Draw points
fig.add_trace(
go.Scatter(
x=data_frame[x],
y=data_frame[y],
mode="markers",
marker=dict(color="#00b4ff", size=12),
)
)

for i in range(len(data_frame)):
fig.add_trace(
go.Scatter(
x=[0, data_frame[x].iloc[i]],
y=[data_frame[y].iloc[i], data_frame[y].iloc[i]],
mode="lines",
line=dict(color="#00b4ff", width=3),
)
)
fig.update_layout(showlegend=False)
return fig


gapminder = px.data.gapminder()


page = vm.Page(
title="Charts UI",
title="Lollipop",
components=[
vm.Graph(
figure=px.bar(
pastry.sort_values("Profit Ratio"),
orientation="h",
x="Profit Ratio",
y="pastry",
color="Profit Ratio",
color_continuous_scale=COLORS["DIVERGING_RED_CYAN"],
),
),
vm.Graph(figure=lollipop(gapminder.query("year == 2007 and gdpPercap > 36000"), y="country", x="gdpPercap"))
],
)

Expand Down
2 changes: 1 addition & 1 deletion vizro-core/examples/visual-vocabulary/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The dashboard is still in development. Below is an overview of the chart types f
| Correlation matrix | ❌ | Correlation | | |
| Histogram | ✅ | Distribution | [Histograms with px](https://plotly.com/python/histograms/) | [px.histogram](https://plotly.github.io/plotly.py-docs/generated/plotly.express.histogram) |
| Line | ✅ | Time | [Line plot with px](https://plotly.com/python/line-charts/) | [px.line](https://plotly.com/python-api-reference/generated/plotly.express.line) |
| Lollipop | | Ranking, Magnitude | | |
| Lollipop | | Ranking, Magnitude | [Lollipop & Dumbbell Charts with Plotly](https://towardsdatascience.com/lollipop-dumbbell-charts-with-plotly-696039d5f85) | [px.scatter](https://plotly.com/python-api-reference/generated/plotly.express.scatter) |
| Marimekko | ❌ | Magnitude, Part-to-whole | | |
| Network | ❌ | Flow | | |
| Ordered bar | ✅ | Ranking | [Bar chart with px](https://plotly.com/python/bar-charts/) | [px.bar](https://plotly.com/python-api-reference/generated/plotly.express.bar.html) |
Expand Down
2 changes: 0 additions & 2 deletions vizro-core/examples/visual-vocabulary/chart_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ class ChartGroup:
incomplete_pages=[
IncompletePage("Ordered bubble"),
IncompletePage("Slope"),
IncompletePage("Lollipop"),
IncompletePage("Bump"),
],
icon="Stacked Bar Chart",
Expand Down Expand Up @@ -117,7 +116,6 @@ class ChartGroup:
pages=pages.magnitude.pages,
incomplete_pages=[
IncompletePage("Marimekko"),
IncompletePage("Lollipop"),
IncompletePage("Pictogram"),
IncompletePage("Bullet"),
IncompletePage("Radial"),
Expand Down
48 changes: 48 additions & 0 deletions vizro-core/examples/visual-vocabulary/custom_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,51 @@ def diverging_stacked_bar(data_frame: pd.DataFrame, **kwargs) -> go.Figure:
fig.add_hline(y=0, line_width=2, line_color="grey")

return fig


@capture("graph")
def lollipop(data_frame: pd.DataFrame, **kwargs):
"""Creates a lollipop based on px.scatter.

A lollipop chart is a variation of a bar chart where each data point is represented by a line and a dot at the end
to mark the value.

Inspired by: https://towardsdatascience.com/lollipop-dumbbell-charts-with-plotly-696039d5f85

Args:
data_frame: DataFrame for the chart. Can be long form or wide form.
See https://plotly.com/python/wide-form/.
**kwargs: Keyword arguments to pass into px.scatter (e.g. x, y, labels).
See https://plotly.com/python-api-reference/generated/plotly.scatter.html.

Returns:
go.Figure: Lollipop chart.
"""
# Plots the dots of the lollipop chart
fig = px.scatter(data_frame, **kwargs)

# Enables the orientation of the chart to be either horizontal or vertical
orientation = fig.data[0].orientation
x_or_y = "x" if orientation == "h" else "y"
y_or_x = "y" if orientation == "h" else "x"

# Plots the lines of the lollipop chart
for x_or_y_value, y_or_x_value in zip(fig.data[0][x_or_y], fig.data[0][y_or_x]):
fig.add_trace(go.Scatter({x_or_y: [0, x_or_y_value], y_or_x: [y_or_x_value, y_or_x_value], "mode": "lines"}))

# Styles the lollipop chart and makes it uni-colored
fig.update_traces(
marker_size=12,
line_width=3,
line_color=fig.layout.template.layout.colorway[0],
)

fig.update_layout(
{
"showlegend": False,
f"{x_or_y}axis_showgrid": True,
f"{y_or_x}axis_showgrid": False,
f"{x_or_y}axis_rangemode": "tozero",
},
)
return fig
47 changes: 46 additions & 1 deletion vizro-core/examples/visual-vocabulary/pages/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import vizro.models as vm

from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file
from pages.examples import butterfly, column_and_line, connected_scatter, waterfall
from pages.examples import butterfly, column_and_line, connected_scatter, lollipop, waterfall


def butterfly_factory(group: str):
Expand Down Expand Up @@ -179,3 +179,48 @@ def waterfall_factory(group: str):
),
],
)


def lollipop_factory(group: str):
"""Reusable function to create the page content for the lollipop chart with a unique ID."""
return vm.Page(
id=f"{group}-lollipop",
path=f"{group}/lollipop",
title="Lollipop",
layout=vm.Layout(grid=PAGE_GRID),
components=[
vm.Card(
text="""

#### What is a lollipop chart?

A lollipop chart is a variation of a bar chart where each data point is represented by a line and a
dot at the end to mark the value. It functions like a bar chart but offers a cleaner visual,
especially useful when dealing with a large number of high values, to avoid the clutter of tall columns.
However, it can be less precise due to the difficulty in judging the exact center of the circle.

 

#### When should I use it?

Use a lollipop chart to compare values across categories, especially when dealing with many high values.
It highlights differences and trends clearly without the visual bulk of a bar chart. Ensure clarity by
limiting categories, using consistent scales, and clearly labeling axes. Consider alternatives if
precise value representation is crucial.
"""
),
vm.Graph(figure=lollipop.fig),
vm.Tabs(
tabs=[
vm.Container(
title="Vizro dashboard",
components=[make_code_clipboard_from_py_file("lollipop.py", mode="vizro")],
),
vm.Container(
title="Plotly figure",
components=[make_code_clipboard_from_py_file("lollipop.py", mode="plotly")],
),
]
),
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from vizro.models.types import capture


@capture("graph")
def lollipop(data_frame: pd.DataFrame, **kwargs):
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
"""Creates a lollipop chart using Plotly."""
fig = px.scatter(data_frame, **kwargs)

orientation = fig.data[0].orientation
x_or_y = "x" if orientation == "h" else "y"
y_or_x = "y" if orientation == "h" else "x"

for x_or_y_value, y_or_x_value in zip(fig.data[0][x_or_y], fig.data[0][y_or_x]):
fig.add_trace(go.Scatter({x_or_y: [0, x_or_y_value], y_or_x: [y_or_x_value, y_or_x_value], "mode": "lines"}))

fig.update_traces(
marker_size=12,
line_width=3,
line_color=fig.layout.template.layout.colorway[0],
)

fig.update_layout(
{
"showlegend": False,
f"{x_or_y}axis_showgrid": True,
f"{y_or_x}axis_showgrid": False,
f"{x_or_y}axis_rangemode": "tozero",
},
)
return fig


gapminder = (
px.data.gapminder()
.query("year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])")
.sort_values("pop")
)

fig = lollipop(gapminder, y="country", x="pop")
12 changes: 11 additions & 1 deletion vizro-core/examples/visual-vocabulary/pages/magnitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import vizro.models as vm

from pages._factories import lollipop_factory
from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file
from pages.examples import bar, magnitude_column, paired_bar, paired_column, parallel_coordinates, radar

Expand Down Expand Up @@ -238,4 +239,13 @@
],
)

pages = [bar_page, column_page, paired_bar_page, paired_column_page, parallel_coordinates_page, radar_page]
lollipop_page = lollipop_factory("magnitude")
pages = [
bar_page,
column_page,
paired_bar_page,
paired_column_page,
parallel_coordinates_page,
radar_page,
lollipop_page,
]
5 changes: 4 additions & 1 deletion vizro-core/examples/visual-vocabulary/pages/ranking.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import vizro.models as vm

from pages._factories import lollipop_factory
from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file
from pages.examples import ordered_bar, ordered_column

Expand Down Expand Up @@ -85,4 +86,6 @@
)


pages = [ordered_bar_page, ordered_column_page]
lollipop_page = lollipop_factory("deviation")

pages = [ordered_bar_page, ordered_column_page, lollipop_page]