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
159 changes: 92 additions & 67 deletions plots/slope-basic/implementations/python/seaborn.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,141 @@
""" pyplots.ai
""" anyplot.ai
slope-basic: Basic Slope Chart (Slopegraph)
Library: seaborn 0.13.2 | Python 3.13.11
Quality: 91/100 | Created: 2025-12-23
Library: seaborn 0.13.2 | Python 3.13.13
Quality: 83/100 | Updated: 2026-04-30
"""

import os

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from matplotlib.lines import Line2D


# Data - Tech company revenue growth comparison Q1 vs Q4
# Values spaced to avoid label overlap
# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

COLOR_INCREASE = "#009E73" # Okabe-Ito position 1
COLOR_DECREASE = "#D55E00" # Okabe-Ito position 2

sns.set_theme(
style="ticks",
rc={
"figure.facecolor": PAGE_BG,
"axes.facecolor": PAGE_BG,
"axes.edgecolor": INK_SOFT,
"axes.labelcolor": INK,
"text.color": INK,
"xtick.color": INK_SOFT,
"ytick.color": INK_SOFT,
"grid.color": INK,
"grid.alpha": 0.10,
"legend.facecolor": ELEVATED_BG,
"legend.edgecolor": INK_SOFT,
},
)

# Data — tech company revenue comparison Q1 vs Q4 (four rank crossings)
data = {
"entity": ["TechCorp", "DataFlow", "CloudNine", "NetWorks", "CodeBase", "ByteSize", "LogicPro", "SoftEdge"],
"Q1 Revenue ($M)": [145, 72, 215, 52, 178, 98, 125, 162],
"Q4 Revenue ($M)": [192, 108, 178, 72, 225, 82, 155, 138],
"entity": ["StreamPeak", "DataCore", "CloudSync", "NetPulse", "CodeBase", "ByteFlow", "LogicGrid", "TechVault"],
"Q1 ($M)": [50, 110, 165, 220, 275, 325, 378, 430],
"Q4 ($M)": [95, 60, 230, 178, 335, 268, 415, 368],
}

df = pd.DataFrame(data)

# Calculate change for color coding
df["change"] = df["Q4 Revenue ($M)"] - df["Q1 Revenue ($M)"]
df["change"] = df["Q4 ($M)"] - df["Q1 ($M)"]
df["direction"] = df["change"].apply(lambda x: "Increase" if x > 0 else "Decrease")
df = df.sort_values("Q1 ($M)").reset_index(drop=True)

# Sort by Q1 value for better visual layout
df = df.sort_values("Q1 Revenue ($M)").reset_index(drop=True)

# Create figure
fig, ax = plt.subplots(figsize=(16, 9))
sns.set_style("whitegrid")

# Define colors - Python palette
colors = {"Increase": "#306998", "Decrease": "#FFD43B"}

# Reshape data for seaborn
df_melted = df.melt(
id_vars=["entity", "direction"],
value_vars=["Q1 Revenue ($M)", "Q4 Revenue ($M)"],
var_name="Period",
value_name="Revenue",
id_vars=["entity", "direction"], value_vars=["Q1 ($M)", "Q4 ($M)"], var_name="Period", value_name="Revenue ($M)"
)
df_melted["period_num"] = df_melted["Period"].map({"Q1 ($M)": 0, "Q4 ($M)": 1})

# Plot
fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)
ax.set_facecolor(PAGE_BG)

palette = {"Increase": COLOR_INCREASE, "Decrease": COLOR_DECREASE}

sns.lineplot(
data=df_melted,
x="period_num",
y="Revenue ($M)",
hue="direction",
units="entity",
estimator=None,
palette=palette,
linewidth=3.5,
marker="o",
markersize=12,
alpha=0.85,
legend=False,
ax=ax,
)

# Plot lines for each entity using seaborn
for entity in df["entity"]:
entity_data = df_melted[df_melted["entity"] == entity]
direction = entity_data["direction"].iloc[0]
sns.lineplot(
data=entity_data,
x="Period",
y="Revenue",
color=colors[direction],
linewidth=3.5,
alpha=0.85,
marker="o",
markersize=14,
ax=ax,
)

# Add entity labels at both ends
for _idx, row in df.iterrows():
color = colors[row["direction"]]
q1_val = row["Q1 Revenue ($M)"]
q4_val = row["Q4 Revenue ($M)"]
# Endpoint labels at both axes
for _, row in df.iterrows():
color = palette[row["direction"]]
q1_val = int(row["Q1 ($M)"])
q4_val = int(row["Q4 ($M)"])

# Left labels (Q1)
ax.annotate(
f"{row['entity']} ({q1_val})",
xy=(0, q1_val),
xytext=(-12, 0),
xytext=(-14, 0),
textcoords="offset points",
fontsize=15,
fontsize=14,
color=color,
ha="right",
va="center",
fontweight="bold",
)
# Right labels (Q4)
ax.annotate(
f"({q4_val}) {row['entity']}",
xy=(1, q4_val),
xytext=(12, 0),
xytext=(14, 0),
textcoords="offset points",
fontsize=15,
fontsize=14,
color=color,
ha="left",
va="center",
fontweight="bold",
)

# Style adjustments
# Style
ax.set_xticks([0, 1])
ax.set_xticklabels(["Q1 Revenue ($M)", "Q4 Revenue ($M)"], fontsize=16, color=INK_SOFT)
ax.set_xlabel("")
ax.set_ylabel("Revenue ($M)", fontsize=20)
ax.set_title("slope-basic · seaborn · pyplots.ai", fontsize=24, fontweight="bold", pad=20)
ax.tick_params(axis="both", labelsize=16)
ax.set_ylabel("Revenue ($M)", fontsize=20, color=INK)
ax.set_title("slope-basic · seaborn · anyplot.ai", fontsize=24, fontweight="bold", pad=20, color=INK)
ax.tick_params(axis="both", labelsize=16, colors=INK_SOFT)
ax.set_xlim(-0.15, 1.15)

# Add padding to y-axis for labels
y_min = min(df["Q1 Revenue ($M)"].min(), df["Q4 Revenue ($M)"].min())
y_max = max(df["Q1 Revenue ($M)"].max(), df["Q4 Revenue ($M)"].max())
y_padding = (y_max - y_min) * 0.08
y_min = min(df["Q1 ($M)"].min(), df["Q4 ($M)"].min())
y_max = max(df["Q1 ($M)"].max(), df["Q4 ($M)"].max())
y_padding = (y_max - y_min) * 0.10
ax.set_ylim(y_min - y_padding, y_max + y_padding)

ax.grid(True, alpha=0.3, linestyle="--")
ax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
for spine in ("left", "bottom"):
ax.spines[spine].set_color(INK_SOFT)

# Add legend for direction
# Legend
legend_elements = [
Line2D([0], [0], color="#306998", linewidth=3.5, marker="o", markersize=10, label="Increase"),
Line2D([0], [0], color="#FFD43B", linewidth=3.5, marker="o", markersize=10, label="Decrease"),
Line2D([0], [0], color=COLOR_INCREASE, linewidth=3.5, marker="o", markersize=10, label="Increase"),
Line2D([0], [0], color=COLOR_DECREASE, linewidth=3.5, marker="o", markersize=10, label="Decrease"),
]
ax.legend(handles=legend_elements, loc="lower right", fontsize=16, framealpha=0.9)
ax.legend(
handles=legend_elements, loc="lower right", fontsize=16, framealpha=0.9, facecolor=ELEVATED_BG, edgecolor=INK_SOFT
)

plt.tight_layout()
plt.subplots_adjust(left=0.22, right=0.78)
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG)
Loading
Loading