热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为个人理财编程语言开发者设计,提供专业的数据可视化功能开发指导。通过系统化的任务拆解,帮助开发者快速构建图表生成和趋势分析模块,支持多种数据类型和图表形式,确保输出内容直观准确且具备良好的可扩展性。适用于月度收支分析、资产分布展示、消费趋势追踪等常见理财场景,有效提升开发效率和可视化效果。
本方案面向“近三个月的月度收支”数据,提供一套可扩展的堆叠面积图可视化模块,支持:
注意:所有图表仅在传入真实的用户数据时生成,示例中不包含虚构数据;请按示例接口传入您自己的CSV/DataFrame数据。
from typing import List, Dict, Optional, Tuple, Union
import pandas as pd
import numpy as np
try:
import plotly.graph_objects as go
except ImportError:
go = None
def detect_anomalies(
series: pd.Series,
method: str = "pct_change",
threshold: float = 0.25
) -> List[int]:
"""
异常检测:
- method='pct_change': 月环比变化绝对值 >= threshold(如0.25代表±25%)标记为异常
- method='zscore': 计算z分数,|z| >= threshold(如1.5)标记为异常
返回异常点的索引列表(相对series的顺序索引)
"""
series = series.astype(float)
if method == "pct_change":
pct = series.pct_change()
return [i for i, v in enumerate(pct) if i > 0 and pd.notnull(v) and abs(v) >= threshold]
elif method == "zscore":
mu = series.mean()
sigma = series.std(ddof=1)
if sigma == 0 or np.isnan(sigma):
return []
z = (series - mu) / sigma
return [i for i, v in enumerate(z) if abs(v) >= threshold]
else:
raise ValueError("Unsupported anomaly method. Use 'pct_change' or 'zscore'.")
def monthly_cashflow_area(
df: pd.DataFrame,
income_cols: List[str],
expense_cols: List[str],
month_col: str = "month",
salary_paydays: Optional[Dict[str, str]] = None,
anomaly_on: str = "total", # 'total' 或 'surplus_rate'
anomaly_method: str = "pct_change", # 'pct_change' 或 'zscore'
anomaly_threshold: float = 0.25,
colors: Optional[Dict[str, str]] = None,
library: str = "plotly", # 'plotly' 或 'matplotlib'
title: Optional[str] = None,
return_components: bool = False
) -> Union["go.Figure", "matplotlib.figure.Figure", Tuple[object, pd.DataFrame, Dict]]:
"""
生成“近三个月月度收支”堆叠面积图,并计算结余与结余率、标注异常与工资发放日。
参数:
- df: 包含月份与各项收支的DataFrame。必须包含month_col、income_cols、expense_cols对应的列。
- income_cols: 收入列名列表,例如 ['income_salary', 'income_side']。
- expense_cols: 支出列名列表,例如 ['expense_rent', 'expense_dining', 'expense_transport']。
- month_col: 月份列,建议为可解析为日期的字符串(如'2025-09-01')或datetime。
- salary_paydays: 可选,字典 { 'YYYY-MM': 'YYYY-MM-DD' },用于在图中标记工资发放日。
- anomaly_on: 异常检测维度,'total'在总收入与总支出上检测,'surplus_rate'在结余率上检测。
- anomaly_method: 异常检测方法,'pct_change'(环比百分比变化)或 'zscore'(z分数)。
- anomaly_threshold: 异常阈值,pct_change时如0.25=±25%,zscore时如1.5=|z|≥1.5。
- colors: 可选,颜色映射字典。未提供时使用推荐配色。
- library: 可视化库,'plotly'(默认,交互)或 'matplotlib'(静态)。
- title: 图表标题。
- return_components: True时返回(figure, metrics_df, diagnostics)。
返回:
- figure: Plotly或Matplotlib图对象。
- (可选)metrics_df: 含 total_income、total_expense、surplus、surplus_rate 的DataFrame。
- (可选)diagnostics: 包含异常索引与文本的字典。
"""
# 校验列存在
for col in [month_col] + income_cols + expense_cols:
if col not in df.columns:
raise ValueError(f"DataFrame缺少必要列: {col}")
# 复制并预处理
work = df.copy()
# 规范月份为datetime,便于排序和轴显示
work[month_col] = pd.to_datetime(work[month_col])
work.sort_values(by=month_col, inplace=True)
work.reset_index(drop=True, inplace=True)
# 计算总计、结余与结余率
work["total_income"] = work[income_cols].astype(float).sum(axis=1)
work["total_expense"] = work[expense_cols].astype(float).sum(axis=1)
work["surplus"] = work["total_income"] - work["total_expense"]
work["surplus_rate"] = np.where(
work["total_income"] > 0,
work["surplus"] / work["total_income"],
np.nan
)
# 异常检测
if anomaly_on == "total":
inc_anom_idx = detect_anomalies(work["total_income"], method=anomaly_method, threshold=anomaly_threshold)
exp_anom_idx = detect_anomalies(work["total_expense"], method=anomaly_method, threshold=anomaly_threshold)
anomalies = {"income": inc_anom_idx, "expense": exp_anom_idx}
elif anomaly_on == "surplus_rate":
sr_anom_idx = detect_anomalies(work["surplus_rate"], method=anomaly_method, threshold=anomaly_threshold)
anomalies = {"surplus_rate": sr_anom_idx}
else:
raise ValueError("anomaly_on 必须为 'total' 或 'surplus_rate'")
# 推荐配色(可覆盖)
default_colors = {
# 收入
"income_salary": "#2ecc71", # 绿 - 工资
"income_side": "#27ae60", # 深绿 - 副业
# 支出
"expense_rent": "#e74c3c", # 红 - 房租
"expense_dining": "#f39c12", # 橙 - 餐饮
"expense_transport": "#e67e22", # 橘 - 出行
# 结余
"surplus": "#34495e" # 蓝灰 - 结余线
}
# 合并用户配色
palette = {**default_colors, **(colors or {})}
# 工资发放日解析为datetime
payday_map = {}
if salary_paydays:
for k, v in salary_paydays.items():
try:
payday_map[pd.to_datetime(k).strftime("%Y-%m")] = pd.to_datetime(v)
except Exception as e:
raise ValueError(f"工资发放日解析失败: {k} -> {v}. {e}")
# 构图
if library == "plotly":
if go is None:
raise ImportError("未安装 plotly,请选择 library='matplotlib' 或安装 plotly")
fig = go.Figure()
x = work[month_col]
# 收入堆叠(正向)
for col in income_cols:
fig.add_trace(go.Scatter(
x=x, y=work[col], name=col,
stackgroup="income", mode="lines",
line={"width": 1, "color": palette.get(col, "#2ecc71")},
fill="tonexty",
hovertemplate=f"%{{x|%Y-%m}}<br>{col}: %{{y:,.2f}}<extra></extra>"
))
# 支出堆叠(负向)
for col in expense_cols:
fig.add_trace(go.Scatter(
x=x, y=-work[col], name=col,
stackgroup="expense", mode="lines",
line={"width": 1, "color": palette.get(col, "#e74c3c")},
fill="tonexty",
hovertemplate=f"%{{x|%Y-%m}}<br>{col}: -%{{y:,.2f}}<extra></extra>"
))
# 结余线
fig.add_trace(go.Scatter(
x=x, y=work["surplus"], name="结余",
mode="lines+markers",
line={"width": 2, "color": palette.get("surplus", "#34495e")},
hovertemplate="%{x|%Y-%m}<br>结余: %{y:,.2f}<br>结余率: %{customdata:.1%}<extra></extra>",
customdata=work["surplus_rate"]
))
# 工资发放日标注(垂直虚线 + 文本)
if payday_map:
xmin, xmax = x.min(), x.max()
for m, d in payday_map.items():
if d >= xmin and d <= xmax:
fig.add_vline(
x=d, line_width=1, line_dash="dot", line_color="#7f8c8d"
)
fig.add_annotation(
x=d, y=max(work["total_income"].max(), work["surplus"].max()),
text="工资日", showarrow=False, yshift=10,
font={"size": 10, "color": "#7f8c8d"}
)
# 异常标注
def _annotate(idx: int, text: str, color: str = "#c0392b"):
fig.add_annotation(
x=work.loc[idx, month_col],
y=work.loc[idx, "surplus"],
text=text,
showarrow=True, arrowhead=2, ax=0, ay=-30,
font={"size": 10, "color": color},
bgcolor="rgba(255,255,255,0.7)"
)
if "income" in anomalies:
for i in anomalies["income"]:
_annotate(i, "收入异常")
if "expense" in anomalies:
for i in anomalies["expense"]:
_annotate(i, "支出异常")
if "surplus_rate" in anomalies:
for i in anomalies["surplus_rate"]:
_annotate(i, "结余率异常")
fig.update_layout(
title=title or "近三个月收支堆叠面积图(含结余与标注)",
hovermode="x unified",
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
margin=dict(l=40, r=20, t=60, b=40),
template="plotly_white"
)
fig.update_yaxes(
title="金额(收入为正,支出为负)",
zeroline=True, zerolinewidth=1, zerolinecolor="#95a5a6"
)
fig.update_xaxes(
dtick="M1", tickformat="%Y-%m", showgrid=True
)
elif library == "matplotlib":
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(9, 5))
x = work[month_col].dt.to_pydatetime()
# 收入堆叠
income_data = [work[col].values for col in income_cols]
ax.stackplot(x, *income_data, labels=income_cols,
colors=[palette.get(c, "#2ecc71") for c in income_cols], alpha=0.85)
# 支出堆叠(负值)
expense_data = [(-work[col].values) for col in expense_cols]
ax.stackplot(x, *expense_data, labels=expense_cols,
colors=[palette.get(c, "#e74c3c") for c in expense_cols], alpha=0.85)
# 结余线
ax.plot(x, work["surplus"], label="结余", color=palette.get("surplus", "#34495e"), linewidth=2, marker="o")
# 工资日
if payday_map:
for m, d in payday_map.items():
if d >= work[month_col].min() and d <= work[month_col].max():
ax.axvline(d, color="#7f8c8d", linestyle=":", linewidth=1)
ax.text(d, work["surplus"].max(), "工资日", color="#7f8c8d", fontsize=9, va="bottom")
# 异常标注
def _annotate(idx: int, text: str, color: str = "#c0392b"):
ax.annotate(text, xy=(x[idx], work.loc[idx, "surplus"]),
xytext=(0, -25), textcoords="offset points",
arrowprops=dict(arrowstyle="->", color=color),
fontsize=9, color=color,
bbox=dict(boxstyle="round,pad=0.2", fc="white", alpha=0.7))
if "income" in anomalies:
for i in anomalies["income"]:
_annotate(i, "收入异常")
if "expense" in anomalies:
for i in anomalies["expense"]:
_annotate(i, "支出异常")
if "surplus_rate" in anomalies:
for i in anomalies["surplus_rate"]:
_annotate(i, "结余率异常")
ax.set_title(title or "近三个月收支堆叠面积图(含结余与标注)")
ax.set_ylabel("金额(收入为正,支出为负)")
ax.grid(True, axis="y", linestyle="--", alpha=0.3)
ax.legend(loc="upper center", ncol=3, bbox_to_anchor=(0.5, 1.12))
else:
raise ValueError("library 必须为 'plotly' 或 'matplotlib'")
diagnostics = {
"anomalies": anomalies,
"paydays": payday_map
}
metrics_df = work[[month_col, "total_income", "total_expense", "surplus", "surplus_rate"]].copy()
if return_components:
return fig, metrics_df, diagnostics
return fig
注意:为遵循“所有图表必须基于真实数据”的规则,以下示例只展示如何加载与调用。请用您真实的近三个月数据文件或DataFrame替换示例中的路径或变量。
示例一:基础用法(Plotly,CSV加载)
import pandas as pd
# 请准备真实数据CSV,列至少包含:
# month, income_salary, income_side, expense_rent, expense_dining, expense_transport
df = pd.read_csv("your_finance_3m.csv")
fig = monthly_cashflow_area(
df=df,
income_cols=["income_salary", "income_side"],
expense_cols=["expense_rent", "expense_dining", "expense_transport"],
month_col="month",
library="plotly",
title="近三个月收支堆叠面积图"
)
fig.show()
示例二:标注工资发放日 + 自定义异常检测与配色
import pandas as pd
df = pd.read_csv("your_finance_3m.csv")
custom_colors = {
"income_salary": "#2ecc71",
"income_side": "#1abc9c",
"expense_rent": "#e74c3c",
"expense_dining": "#f39c12",
"expense_transport": "#d35400",
"surplus": "#34495e"
}
# 将每个月映射到实际工资发放日(请填入真实日期)
salary_paydays = {
"2025-08": "2025-08-25",
"2025-09": "2025-09-25",
"2025-10": "2025-10-25"
}
fig, metrics, diag = monthly_cashflow_area(
df=df,
income_cols=["income_salary", "income_side"],
expense_cols=["expense_rent", "expense_dining", "expense_transport"],
month_col="month",
salary_paydays=salary_paydays,
anomaly_on="total",
anomaly_method="pct_change",
anomaly_threshold=0.25, # 环比变化超过±25%标记为异常
colors=custom_colors,
library="plotly",
title="近三个月收支堆叠面积图(工资日与异常标注)",
return_components=True
)
print("指标:")
print(metrics) # 查看每月总收入/总支出/结余/结余率
print("诊断:", diag) # 查看异常索引、工资日字典
fig.show()
示例三:Matplotlib静态图(适合离线报告)
import pandas as pd
df = pd.read_csv("your_finance_3m.csv")
fig = monthly_cashflow_area(
df=df,
income_cols=["income_salary", "income_side"],
expense_cols=["expense_rent", "expense_dining", "expense_transport"],
month_col="month",
library="matplotlib",
title="近三个月收支堆叠面积图(静态)"
)
import matplotlib.pyplot as plt
plt.show()
如需,我可以将以上模块封装为独立Python包,附带更详细的文档与单元测试模板,方便在您的个人理财应用中直接集成。
本方案面向“消费类别按周跟踪+月度环比+7日平滑”的多序列折线可视化需求,适配餐饮/购物/出行/数码等常见类别。核心特点:
注意:出图严格基于用户提供的真实数据;示例仅展示调用方法,不包含虚构数据或图像。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from dataclasses import dataclass
from typing import List, Optional, Dict, Tuple
@dataclass
class TrendOutputs:
fig: plt.Figure
ax: plt.Axes
daily: pd.DataFrame # 每日各类别支出(原始聚合)
daily_ma: pd.DataFrame # 每日各类别7日移动平均
weekly: pd.DataFrame # 每周各类别支出
monthly: pd.DataFrame # 每月各类别支出
monthly_mom: pd.Series # 总支出月环比(近六个月范围内)
anomalies: Dict[str, pd.DataFrame] # 各类别异常点明细
def prepare_category_timeseries(
df: pd.DataFrame,
date_col: str = "date",
category_col: str = "category",
amount_col: str = "amount",
categories: Optional[List[str]] = None,
start: Optional[pd.Timestamp] = None,
end: Optional[pd.Timestamp] = None,
tz: Optional[str] = None
) -> Tuple[pd.DataFrame, pd.DatetimeIndex, List[str]]:
"""
将原始流水标准化为每日x类别矩阵,自动裁剪近6个月范围。
参数:
- df: 包含日期、类别、金额的流水数据(真实数据,金额支出为正值)
- date_col/category_col/amount_col: 列名
- categories: 指定仅分析的类别列表(缺省为数据内出现的类别)
- start/end: 时间范围;若缺省,默认 end=数据最大日期,start=end-6个月
- tz: 可选时区名称,将日期转为该时区的日期(日粒度聚合不受时区细节影响)
返回:
- daily_df: 行为日期、列为类别的每日支出矩阵(缺失填0)
- idx: 日索引
- cats: 实际使用的类别列表(按传入或数据内出现顺序)
"""
if df.empty:
raise ValueError("输入数据为空。请提供真实交易流水。")
data = df.copy()
if date_col not in data.columns or category_col not in data.columns or amount_col not in data.columns:
raise ValueError("缺失必要列,需包含: date, category, amount。")
data[date_col] = pd.to_datetime(data[date_col], errors="coerce")
data = data.dropna(subset=[date_col, category_col, amount_col])
if tz:
data[date_col] = data[date_col].dt.tz_localize(tz) if data[date_col].dt.tz is None else data[date_col].dt.tz_convert(tz)
data[date_col] = data[date_col].dt.tz_localize(None) # 移除时区以稳定重采样
data[amount_col] = pd.to_numeric(data[amount_col], errors="coerce").fillna(0.0)
# 仅保留指定类别
if categories is not None:
data = data[data[category_col].isin(categories)]
cats = list(categories)
else:
cats = list(data[category_col].dropna().unique())
if data.empty:
raise ValueError("过滤类别后数据为空。请检查类别参数或原始数据。")
# 自动时间范围:近6个月
max_date = data[date_col].max().normalize()
if end is None:
end = max_date
else:
end = pd.to_datetime(end).normalize()
if start is None:
start = end - pd.DateOffset(months=6) + pd.Timedelta(days=1) # 包含端点
else:
start = pd.to_datetime(start).normalize()
# 过滤时间窗
data = data[(data[date_col] >= start) & (data[date_col] <= end)]
if data.empty:
raise ValueError("选定时间范围内无数据。请调整起止日期或确认流水。")
# 构造每日x类别矩阵
g = data.groupby([data[date_col].dt.normalize(), category_col])[amount_col].sum()
daily_df = g.unstack(fill_value=0.0)
# 补齐日期
idx = pd.date_range(start=daily_df.index.min(), end=daily_df.index.max(), freq="D")
daily_df = daily_df.reindex(idx, fill_value=0.0)
# 仅保留目标列,并保证顺序
for c in cats:
if c not in daily_df.columns:
daily_df[c] = 0.0
daily_df = daily_df[cats]
return daily_df, idx, cats
def compute_aggregates(
daily_df: pd.DataFrame,
ma_window: int = 7,
week_anchor: str = "W-SUN" # 一周结束于周日
) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
"""
计算7日移动平均、周聚合、月聚合。
返回:
- daily_ma: 7日移动平均
- weekly: 周总额
- monthly: 月总额
"""
daily_ma = daily_df.rolling(window=ma_window, min_periods=1).mean()
weekly = daily_df.resample(week_anchor).sum()
monthly = daily_df.resample("MS").sum() # 月初对齐的每月总额
return daily_ma, weekly, monthly
def compute_monthly_mom(monthly: pd.DataFrame) -> pd.Series:
"""
计算总支出的月环比(所有类别合计)。
返回:
- monthly_mom: 按月索引的环比百分比(Series)
"""
total = monthly.sum(axis=1)
mom = total.pct_change().replace([np.inf, -np.inf], np.nan)
return mom
def detect_anomalies(
daily_df: pd.DataFrame,
method: str = "iqr",
iqr_k: float = 1.5,
z_sigma: float = 3.0,
top_per_cat: int = 3
) -> Dict[str, pd.DataFrame]:
"""
基于每日金额检测各类别异常点,并返回前top_per_cat个最显著点。
- method='iqr': 值 > Q3 + k*IQR 或 < Q1 - k*IQR 判为异常(稳健)
- method='zscore': 均值±z_sigma*std
返回:
{category: DataFrame(index=日期, columns=['value','score'])}
"""
out = {}
for c in daily_df.columns:
s = daily_df[c]
if method == "iqr":
q1, q3 = s.quantile(0.25), s.quantile(0.75)
iqr = q3 - q1
upper = q3 + iqr_k * iqr
lower = max(0.0, q1 - iqr_k * iqr) # 支出不应为负,取0作为下限
mask = (s > upper) | (s < lower)
score = (s - q3) / (iqr + 1e-9) # 简易强度分数(上侧为正)
else:
mu, sd = s.mean(), s.std(ddof=0)
sd = sd if sd > 1e-9 else 1e-9
upper = mu + z_sigma * sd
lower = max(0.0, mu - z_sigma * sd)
mask = (s > upper) | (s < lower)
score = (s - mu) / sd
cand = s[mask]
if cand.empty:
out[c] = pd.DataFrame(columns=["value", "score"])
continue
dfc = pd.DataFrame({"value": cand, "score": score.loc[cand.index]})
# 选择绝对分数最大的前N个
dfc = dfc.reindex(dfc["score"].abs().sort_values(ascending=False).index)[:top_per_cat]
out[c] = dfc.sort_index()
return out
def plot_category_trends(
daily_df: pd.DataFrame,
daily_ma: pd.DataFrame,
weekly: pd.DataFrame,
monthly_mom: pd.Series,
categories: List[str],
highlight_weekends: bool = True,
show_weekly_markers: bool = True,
show_month_boundary: bool = True,
annotate_mom: bool = True,
anomalies: Optional[Dict[str, pd.DataFrame]] = None,
title: str = "消费类别趋势(近六个月)",
currency: Optional[str] = None,
figsize: Tuple[int, int] = (12, 7),
output_path: Optional[str] = None,
style: str = "seaborn-v0_8"
) -> Tuple[plt.Figure, plt.Axes]:
"""
绘制多序列折线图(7日平滑),可叠加周标记、周末阴影、月环比与异常点注释。
"""
plt.style.use(style)
fig, ax = plt.subplots(figsize=figsize)
# 颜色循环
prop_cycle = plt.rcParams['axes.prop_cycle']
colors = prop_cycle.by_key()['color']
color_map = {c: colors[i % len(colors)] for i, c in enumerate(categories)}
# 绘制平滑曲线
for c in categories:
ax.plot(daily_ma.index, daily_ma[c], label=c, color=color_map[c], linewidth=2)
# 可选:每周总额标记(在周末或周末后一日放点)
if show_weekly_markers and not weekly.empty:
wpos = weekly.index
for c in categories:
ax.scatter(wpos, weekly[c], color=color_map[c], s=20, alpha=0.7, marker="o", edgecolor="white", linewidths=0.5, zorder=3)
# 周末高亮
if highlight_weekends:
start = daily_ma.index.min().normalize()
end = daily_ma.index.max().normalize()
# 找到每个周六,阴影覆盖周六->下周一
saturdays = pd.date_range(start, end, freq="W-SAT")
for sat in saturdays:
ax.axvspan(sat, sat + pd.Timedelta(days=2), color="#f0f0f0", alpha=0.5, lw=0)
# 月边界与环比标注
if show_month_boundary:
months = pd.date_range(daily_ma.index.min().normalize(), daily_ma.index.max().normalize(), freq="MS")
for m in months:
ax.axvline(m, color="#cccccc", linestyle="--", linewidth=1, alpha=0.7)
if annotate_mom and not monthly_mom.empty:
for m, v in monthly_mom.dropna().items():
# 仅在可见范围内标注
if m >= daily_ma.index.min() and m <= daily_ma.index.max():
txt = f"MoM {v:+.1%}"
ax.annotate(txt, xy=(m, ax.get_ylim()[1]), xytext=(0, -18),
textcoords="offset points", ha="left", va="top",
fontsize=9, color="#555555", bbox=dict(boxstyle="round,pad=0.2", fc="white", ec="#bbbbbb", alpha=0.8))
# 异常点注释
if anomalies:
for c, dfc in anomalies.items():
if dfc is None or dfc.empty:
continue
for dt, row in dfc.iterrows():
ax.scatter(dt, row["value"], color=color_map[c], s=36, marker="D", edgecolor="black", linewidths=0.5, zorder=4)
ax.annotate(f"{c} 异常: {row['value']:.0f}", xy=(dt, row["value"]),
xytext=(6, -10), textcoords="offset points", fontsize=8,
bbox=dict(boxstyle="round,pad=0.2", fc="white", ec="#bbbbbb", alpha=0.9))
# 轴与标题
ax.set_title(title, fontsize=14, pad=10)
ax.set_xlim(daily_ma.index.min(), daily_ma.index.max())
ax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekday=mdates.MO, interval=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))
plt.setp(ax.get_xticklabels(), rotation=30, ha="right")
ylab = "支出金额"
if currency:
ylab += f"({currency})"
ax.set_ylabel(ylab)
ax.grid(True, axis="y", alpha=0.2)
ax.legend(ncol=2, frameon=False)
plt.tight_layout()
if output_path:
fig.savefig(output_path, dpi=150, bbox_inches="tight")
return fig, ax
def create_category_trend_chart(
df: pd.DataFrame,
date_col: str = "date",
category_col: str = "category",
amount_col: str = "amount",
categories: Optional[List[str]] = None, # 例如 ["餐饮","购物","出行","数码"]
start: Optional[str] = None,
end: Optional[str] = None,
tz: Optional[str] = None,
# 分析参数
ma_window: int = 7,
anomaly_method: str = "iqr",
anomaly_iqr_k: float = 1.5,
anomaly_sigma: float = 3.0,
max_annotations_per_cat: int = 3,
# 显示参数
highlight_weekends: bool = True,
show_weekly_markers: bool = True,
show_month_boundary: bool = True,
annotate_mom: bool = True,
currency: Optional[str] = None,
figsize=(12,7),
style: str = "seaborn-v0_8",
output_path: Optional[str] = None
) -> TrendOutputs:
"""
一站式生成“近六个月、按周跟踪、7日平滑、月环比、周末高亮、异常注释”的多序列折线图。
返回 TrendOutputs,包含图表对象与聚合数据,方便二次处理或导出。
"""
daily_df, idx, cats = prepare_category_timeseries(
df=df,
date_col=date_col,
category_col=category_col,
amount_col=amount_col,
categories=categories,
start=start,
end=end,
tz=tz
)
daily_ma, weekly, monthly = compute_aggregates(daily_df, ma_window=ma_window)
monthly_mom = compute_monthly_mom(monthly)
anomalies = detect_anomalies(
daily_df=daily_df,
method=anomaly_method,
iqr_k=anomaly_iqr_k,
z_sigma=anomaly_sigma,
top_per_cat=max_annotations_per_cat
)
fig, ax = plot_category_trends(
daily_df=daily_df,
daily_ma=daily_ma,
weekly=weekly,
monthly_mom=monthly_mom,
categories=cats,
highlight_weekends=highlight_weekends,
show_weekly_markers=show_weekly_markers,
show_month_boundary=show_month_boundary,
annotate_mom=annotate_mom,
anomalies=anomalies,
title="消费类别趋势(近六个月,周跟踪 + 7日平滑)",
currency=currency,
figsize=figsize,
output_path=output_path,
style=style
)
return TrendOutputs(
fig=fig,
ax=ax,
daily=daily_df,
daily_ma=daily_ma,
weekly=weekly,
monthly=monthly,
monthly_mom=monthly_mom,
anomalies=anomalies
)
参数说明要点:
以下示例仅演示如何调用接口,不会生成任何基于虚构数据的图表。请替换为您自己的真实流水数据后再运行。
示例1:最简用法(默认近6个月、自动类别、周末高亮、7日平滑、异常点注释)
import pandas as pd
# 读取您自己的流水数据(需包含 date, category, amount 列)
df = pd.read_csv("transactions.csv") # 例如:date=2025-06-01, category=餐饮, amount=58.0
out = create_category_trend_chart(
df=df,
date_col="date",
category_col="category",
amount_col="amount",
currency="CNY"
)
# 可选:查看衍生数据
print(out.weekly.tail())
print(out.monthly_mom.dropna())
示例2:限定类别为餐饮/购物/出行/数码,设定时间范围,使用IQR异常检测并导出图片
selected = ["餐饮","购物","出行","数码"]
out = create_category_trend_chart(
df=df,
categories=selected,
start="2025-05-01",
end="2025-10-31",
anomaly_method="iqr",
anomaly_iqr_k=1.5,
max_annotations_per_cat=2,
output_path="category_trend.png",
currency="¥"
)
示例3:关闭周末高亮,仅展示平滑曲线并保留月环比
out = create_category_trend_chart(
df=df,
categories=["餐饮","购物","出行","数码"],
highlight_weekends=False,
show_weekly_markers=False,
annotate_mom=True,
currency="CNY"
)
准确性与避免误导的设计:
使用须知与数据规范:
把 AI 变成你的“理财数据可视化解决方案设计师”,围绕月度收支、资产分布、消费趋势等核心场景,快速产出清晰、准确且可扩展的图表方案与落地示例。通过一次输入(数据类型、图表类型、时间范围、分析维度),即可获得可直接应用于产品的模块化设计、场景化案例和效果说明,显著缩短开发周期,减少沟通成本与试错时间,提升用户体验与留存,并为后续功能扩展打下坚实基础。
快速搭建收支趋势、资产分布与消费分析模块,缩短首版上线周期,形成可复用的可视化框架。
基于模板配置生成稳定图表方案,敏捷定位异常数据并优化展示逻辑,提高交付质量与效率。
无需深度编码即可规划可视化路线,验证用户理解路径,优化图表呈现以提升转化与留存。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
免费获取高级提示词-优惠即将到期