Amit-kr26's picture
HF Spaces deployment
c9f187d
Raw
History Blame Contribute Delete
6.34 kB
"""Reusable Plotly chart builders."""
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
_COLORS = [
"#3B82F6", "#10B981", "#F59E0B", "#EF4444",
"#8B5CF6", "#06B6D4", "#F97316", "#84CC16",
]
_TEMPLATE = "plotly_white"
def make_line_chart(
df: pd.DataFrame,
x: str,
y: str,
title: str = "",
color: str = "#3B82F6",
add_ma: int | None = 30,
) -> go.Figure:
fig = px.line(df, x=x, y=y, title=title, color_discrete_sequence=[color])
if add_ma and len(df) > add_ma:
ma = df[y].rolling(add_ma).mean()
fig.add_scatter(x=df[x], y=ma, mode="lines", name=f"{add_ma}-day MA",
line=dict(dash="dash", color="#F59E0B"))
fig.update_layout(height=350, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_bar_chart(
df: pd.DataFrame,
x: str,
y: str,
title: str = "",
orientation: str = "v",
color_col: str | None = None,
) -> go.Figure:
fig = px.bar(df, x=x, y=y, title=title, orientation=orientation,
color=color_col, text_auto=".2s",
color_discrete_sequence=_COLORS)
fig.update_layout(height=350, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_treemap(
df: pd.DataFrame,
path: list[str],
values: str,
title: str = "",
) -> go.Figure:
fig = px.treemap(df, path=path, values=values, title=title,
color_discrete_sequence=_COLORS)
fig.update_layout(height=400, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_donut(df: pd.DataFrame, names: str, values: str, title: str = "") -> go.Figure:
fig = px.pie(df, names=names, values=values, title=title, hole=0.4,
color_discrete_sequence=_COLORS)
fig.update_layout(height=350, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_histogram(df: pd.DataFrame, x: str, title: str = "", nbins: int = 40) -> go.Figure:
fig = px.histogram(df, x=x, title=title, nbins=nbins,
color_discrete_sequence=_COLORS)
fig.update_layout(height=350, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_scatter(
df: pd.DataFrame,
x: str,
y: str,
color: str | None = None,
title: str = "",
opacity: float = 0.5,
) -> go.Figure:
fig = px.scatter(df, x=x, y=y, color=color, title=title, opacity=opacity,
color_discrete_sequence=_COLORS)
fig.update_layout(height=400, margin=dict(t=40, b=20), template=_TEMPLATE)
return fig
def make_map_scatter(
df: pd.DataFrame,
lat: str,
lon: str,
color: str | None = None,
size: str | None = None,
title: str = "",
hover_name: str | None = None,
) -> go.Figure:
fig = px.scatter_mapbox(
df, lat=lat, lon=lon, color=color, size=size, title=title,
hover_name=hover_name, mapbox_style="open-street-map", zoom=3,
center={"lat": -14.2, "lon": -51.9}, opacity=0.6,
color_discrete_sequence=_COLORS,
)
fig.update_layout(height=450, margin=dict(t=40, b=0))
return fig
def make_gauge(
value: float,
title: str = "",
max_val: float = 1.0,
good_high: bool = False,
) -> go.Figure:
lo, hi = ("salmon", "lightgreen") if good_high else ("lightgreen", "salmon")
fig = go.Figure(go.Indicator(
mode="gauge+number",
value=value * 100,
title={"text": title, "font": {"size": 16}},
gauge={
"axis": {"range": [0, max_val * 100]},
"bar": {"color": "#3B82F6"},
"steps": [
{"range": [0, 30], "color": lo},
{"range": [30, 60], "color": "khaki"},
{"range": [60, 100], "color": hi},
],
"threshold": {
"line": {"color": "#1e293b", "width": 3},
"thickness": 0.8,
"value": value * 100,
},
},
number={"suffix": "%", "font": {"size": 36}},
))
fig.update_layout(
height=320, margin=dict(t=50, b=30, l=30, r=30), template=_TEMPLATE
)
return fig
def make_heatmap(df: pd.DataFrame, title: str = "") -> go.Figure:
"""Render a retention heatmap. Rows = cohort, columns = month offset.
NaN cells (cohort too new to have data for that month) are rendered in
light grey so they're visually distinct from real 0 % retention cells.
The colorscale uses -1 as the NaN sentinel: values in [-1, 0) β†’ grey,
values in [0, 100] β†’ Blues gradient.
"""
import math
raw = df.values.tolist()
# Replace NaN with -1 (sentinel for "no data yet")
z = [
[-1 if (v is None or (isinstance(v, float) and math.isnan(v))) else v for v in row]
for row in raw
]
text_vals = [
[f"{v:.1f}%" if v >= 0 else "" for v in row]
for row in z
]
# Custom colorscale: grey for -1 (no data), Blues for 0–100
# Positions are normalised over the range [-1, 100] β†’ span of 101
grey = "#D1D5DB"
colorscale = [
[0.0, grey], # -1 β†’ grey
[0.99 / 101, grey], # ~0 β†’ grey boundary
[1.0 / 101, "#EFF6FF"], # 0 % β†’ lightest blue
[1.0, "#1E3A8A"], # 100 % β†’ darkest blue
]
fig = go.Figure(go.Heatmap(
z=z,
x=df.columns.tolist(),
y=df.index.tolist(),
text=text_vals,
texttemplate="%{text}",
textfont={"size": 11},
colorscale=colorscale,
zmin=-1,
zmax=100,
colorbar={
"title": "Retention %",
"ticksuffix": "%",
"tickvals": [0, 25, 50, 75, 100],
},
))
fig.update_layout(
title=title,
height=max(300, len(df) * 28 + 80),
margin=dict(t=50, b=20, l=80, r=40),
template=_TEMPLATE,
xaxis_title="Months Since First Purchase",
yaxis_title="Acquisition Cohort",
yaxis={"autorange": "reversed"},
)
return fig
def make_sunburst(df: pd.DataFrame, path: list[str], values: str, title: str = "") -> go.Figure:
fig = px.sunburst(df, path=path, values=values, title=title,
color_discrete_sequence=_COLORS)
fig.update_layout(height=420, margin=dict(t=40, b=0), template=_TEMPLATE)
return fig