数据库加载异常导致可视化不完整:仅生成1个饼图且柱状图为空
数据库加载异常导致可视化不完整:仅生成1个饼图且柱状图为空
看起来你遇到了数据查询和可视化的双重问题——只生成了一个饼图,柱状图还空着,这肯定让人头大!咱们一步步拆解问题,先找数据根源,再修复可视化逻辑。
第一步:先确认数据是否真的加载正确
很多时候可视化出问题,本质是数据查询没返回预期结果。先在代码里加几行打印,看看两个DataFrame的内容:
# 执行完查询后添加这几行 print("=== 支出子类别数据 ===") print(df_expenses) print("\n=== 收支对比数据 ===") print(df_income_expenses)
如果df_expenses里只有1个唯一的month值,那自然只会生成1个饼图;如果df_income_expenses是空的,说明你的收支查询根本没拿到数据。
第二步:排查SQL查询的核心问题
1. 日期格式与筛选逻辑
SQLite的date()和strftime()函数只认YYYY-MM-DD格式的日期字符串。如果你的Transactions.date字段是其他格式(比如DD/MM/YYYY或MM-DD-YYYY),那日期比较会完全失效,导致只拿到部分数据甚至空数据。
- 先验证日期格式:运行这条SQL单独查询(可以用SQLiteStudio等工具):
SELECT t.date FROM Transactions LIMIT 5; - 如果日期格式不是YYYY-MM-DD,需要先转换格式再筛选。比如如果是
DD-MM-YYYY,修改WHERE子句:WHERE date(substr(t.date,7,4)||'-'||substr(t.date,4,2)||'-'||substr(t.date,1,2)) >= date('now', '-3 months') - 另外,
date('now', '-3 months')返回的是当前日期往前推3个月的当天,如果你的数据里最近3个月的某几天没有记录,那对应的month可能不会出现在结果里,但如果完全空的话,大概率是日期格式不匹配。
2. JOIN语句的完整性
你的查询用了JOIN(内连接),这意味着只要某条Transaction没有对应的TransactionItems或Subcategories,就会被过滤掉。如果你的数据库里存在这样的记录,会导致数据丢失:
- 改成
LEFT JOIN保留所有Transaction记录,即使关联表没有匹配项:
收支对比查询也做同样的修改。-- 支出查询修改后的JOIN部分 FROM Transactions t LEFT JOIN TransactionItems ti ON t.transactionID = ti.transactionID LEFT JOIN Subcategories s ON t.subcategoryID = s.subcategoryID
3. 收支分类的判断逻辑
确认Subcategories.category里确实有值为'Income'的记录——如果分类名是其他写法(比如'income'小写、'工资收入'等),那CASE WHEN s.category = 'Income'就不会匹配到任何数据,导致total_income全为0,收支对比图也会异常。
第三步:修复可视化代码的小细节
即使数据正确,可视化代码也有小瑕疵可能导致空图:
- 柱状图部分,用pandas的
plot()后,最好显式绑定到指定的figure和axes,避免和其他figure混淆; - 加个判断,避免空DataFrame生成无效图表:
# 替换原来的柱状图代码 if not df_income_expenses.empty: fig, ax = plt.subplots(figsize=(10, 6)) df_income_expenses.plot(x='month', y=['total_income', 'total_expense'], kind='bar', ax=ax) ax.set_title('Income vs Expenses for the Last 3 Months') ax.set_xlabel('Month') ax.set_ylabel('Amount') ax.legend(loc='upper right') pdf_pages.savefig(fig) plt.close(fig)
修复后的完整代码示例
import sqlite3 import pandas as pd import matplotlib.pyplot as plt from matplotlib.backends.backend_pdf import PdfPages from datetime import datetime # Path to SQLite Database db_path = r"D:\Python\pythonProject\Project\Project\98661737.sqlite" # Connect to the SQLite database conn = sqlite3.connect(db_path) # Query to get expenses per subcategory for the last 3 months query_expenses = """ SELECT strftime('%Y-%m', t.date) as month, s.category as subcategory, SUM(COALESCE(ti.baseAmount, 0)) as total_expense FROM Transactions t LEFT JOIN TransactionItems ti ON t.transactionID = ti.transactionID LEFT JOIN Subcategories s ON t.subcategoryID = s.subcategoryID WHERE date(t.date) >= date('now', '-3 months') GROUP BY month, subcategory """ # Query to get income vs expenses for the last 3 months query_income_expenses = """ SELECT strftime('%Y-%m', t.date) as month, SUM(CASE WHEN s.category = 'Income' THEN COALESCE(ti.baseAmount, 0) ELSE 0 END) as total_income, SUM(CASE WHEN s.category != 'Income' THEN COALESCE(ti.baseAmount, 0) ELSE 0 END) as total_expense FROM Transactions t LEFT JOIN TransactionItems ti ON t.transactionID = ti.transactionID LEFT JOIN Subcategories s ON t.subcategoryID = s.subcategoryID WHERE date(t.date) >= date('now', '-3 months') GROUP BY month """ # Execute the queries df_expenses = pd.read_sql_query(query_expenses, conn) df_income_expenses = pd.read_sql_query(query_income_expenses, conn) # 打印数据验证 print("=== 支出子类别数据 ===") print(df_expenses) print("\n=== 收支对比数据 ===") print(df_income_expenses) # Close the database connection conn.close() # Create a PDF file to save the plots date_time = datetime.now().strftime('%Y%m%d_%H%M%S') pdf_filename = f'{date_time}.pdf' pdf_pages = PdfPages(pdf_filename) # Pie charts of expenses per subcategory for the last 3 months months = df_expenses['month'].unique() for month in months: df_month = df_expenses[df_expenses['month'] == month] # 过滤掉总支出为0的子类别 df_month = df_month[df_month['total_expense'] > 0] if not df_month.empty: plt.figure(figsize=(8, 8)) plt.pie(df_month['total_expense'], labels=df_month['subcategory'], autopct='%1.1f%%') plt.title(f'Expenses per Subcategory for {month}') pdf_pages.savefig() plt.close() # Column chart showing income vs expenses for the last 3 months if not df_income_expenses.empty: fig, ax = plt.subplots(figsize=(10, 6)) df_income_expenses.plot(x='month', y=['total_income', 'total_expense'], kind='bar', ax=ax) ax.set_title('Income vs Expenses for the Last 3 Months') ax.set_xlabel('Month') ax.set_ylabel('Amount') ax.legend(loc='upper right') pdf_pages.savefig(fig) plt.close(fig) # Close the PDF file pdf_pages.close() print(f"Report saved as {pdf_filename}")
最后提醒
- 优先看打印出来的DataFrame内容,这是定位问题最快的方式;
- 如果日期格式确实不是YYYY-MM-DD,一定要先调整SQL里的日期转换逻辑,否则所有日期筛选都是无效的;
- 用
COALESCE(ti.baseAmount, 0)可以避免NULL值导致SUM结果为NULL的问题。
备注:内容来源于stack exchange,提问作者Arbab Qaisar
相关产品推荐
相关产品推荐

