# 打造个人阅读时长统计与可视化工具:从数据记录到趋势分析


背景介绍

在碎片化阅读时代,我们常常难以直观感知自己的阅读习惯——每月读了多久?哪本书最常读?阅读时间是否有规律?为了解决这些问题,我开发了一个本地阅读时长统计工具,通过CSV存储数据、Pandas分析、Matplotlib可视化,帮助用户轻松跟踪阅读趋势。工具无需外部服务,完全本地运行,适合数据分析初学者练习核心技能。

思路分析

工具分为四个核心模块,每个模块职责明确:
1. 数据存储:用CSV文件保存阅读记录(日期、书籍、时长),结构简单易维护。
2. 记录管理:实现添加记录和初始化CSV的功能,确保数据格式规范。
3. 统计分析:利用Pandas的时间筛选、分组聚合,计算总时长、平均时长和最常阅读书籍。
4. 可视化:通过Matplotlib生成柱状图(每日时长)和折线图(每月趋势),直观展示数据。
5. 交互逻辑:命令行菜单引导用户操作,降低使用门槛。

代码实现

1. 环境准备

首先安装依赖库:

pip install pandas matplotlib

2. 完整代码

import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import os

# -------------------------- 数据初始化与存储 --------------------------
def init_csv():
    """初始化CSV文件(若不存在则创建带表头的空文件)"""
    if not os.path.exists('reading_records.csv'):
        df = pd.DataFrame(columns=['date', 'book', 'duration'])
        df.to_csv('reading_records.csv', index=False)
        print("✅ 初始化成功:创建reading_records.csv")

def add_reading_record(date_str, book_name, duration):
    """添加阅读记录到CSV文件"""
    try:
        # 验证日期格式
        datetime.strptime(date_str, '%Y-%m-%d')
        # 验证时长为正整数
        if duration <= 0:
            raise ValueError("时长必须大于0")

        # 构造新记录
        new_record = pd.DataFrame([[date_str, book_name, duration]], 
                                 columns=['date', 'book', 'duration'])
        # 追加到CSV(不写索引和表头)
        new_record.to_csv('reading_records.csv', mode='a', header=False, index=False)
        print("✅ 记录保存成功!")
    except ValueError as e:
        print(f"❌ 输入错误:{e}")
    except Exception as e:
        print(f"❌ 保存失败:{str(e)}")

# -------------------------- 统计分析模块 --------------------------
def analyze_reading_data(time_range_days):
    """分析指定时间范围内的阅读数据(总时长、平均、最常读书籍)"""
    try:
        # 读取CSV并解析日期列
        df = pd.read_csv('reading_records.csv', parse_dates=['date'])
        if df.empty:
            return None

        # 筛选时间范围
        end_date = datetime.now()
        start_date = end_date - timedelta(days=time_range_days)
        filtered_df = df[df['date'] >= start_date]

        if filtered_df.empty:
            return None

        # 计算统计指标
        total_duration = filtered_df['duration'].sum()
        avg_duration = total_duration / time_range_days

        # 最常阅读书籍(按总时长排序)
        book_stats = filtered_df.groupby('book')['duration'].sum().sort_values(ascending=False)
        top_book = book_stats.index[0]
        top_book_duration = book_stats.iloc[0]

        return {
            'total': total_duration,
            'avg': round(avg_duration, 2),
            'top_book': top_book,
            'top_book_duration': top_book_duration
        }
    except Exception as e:
        print(f"❌ 分析失败:{str(e)}")
        return None

# -------------------------- 可视化模块 --------------------------
def plot_daily_bar():
    """生成最近14天每日阅读时长柱状图"""
    try:
        df = pd.read_csv('reading_records.csv', parse_dates=['date'])
        if df.empty:
            print("❌ 无数据可可视化")
            return

        # 筛选最近14天数据
        start_date = datetime.now() - timedelta(days=14)
        daily_data = df[df['date'] >= start_date].groupby('date')['duration'].sum().reset_index()

        if daily_data.empty:
            print("❌ 最近14天无阅读记录")
            return

        # 绘制柱状图
        plt.figure(figsize=(10,5))
        plt.bar(daily_data['date'].dt.strftime('%Y-%m-%d'), daily_data['duration'], color='#3498db')
        plt.title('最近14天每日阅读时长', fontsize=14)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('时长(分钟)', fontsize=12)
        plt.xticks(rotation=45)
        plt.tight_layout()  # 自动调整布局避免重叠
        plt.savefig('daily_bar.png', dpi=100)
        print("✅ 柱状图保存:daily_bar.png")
    except Exception as e:
        print(f"❌ 绘图失败:{str(e)}")

def plot_monthly_line():
    """生成最近3个月每月阅读时长折线图"""
    try:
        df = pd.read_csv('reading_records.csv', parse_dates=['date'])
        if df.empty:
            print("❌ 无数据可可视化")
            return

        # 筛选最近3个月数据
        start_date = datetime.now() - timedelta(days=90)
        monthly_data = df[df['date'] >= start_date].copy()

        # 按月份分组(格式:YYYY-MM)
        monthly_data['month'] = monthly_data['date'].dt.strftime('%Y-%m')
        monthly_total = monthly_data.groupby('month')['duration'].sum().reset_index()

        if monthly_total.empty:
            print("❌ 最近3个月无阅读记录")
            return

        # 绘制折线图
        plt.figure(figsize=(10,5))  
        plt.plot(monthly_total['month'], monthly_total['duration'], marker='o', color='#e74c3c', linewidth=2)
        plt.title('最近3个月阅读时长趋势', fontsize=14)
        plt.xlabel('月份', fontsize=12)
        plt.ylabel('总时长(分钟)', fontsize=12)
        plt.grid(alpha=0.3)  # 添加网格线
        plt.tight_layout()
        plt.savefig('monthly_line.png', dpi=100)
        print("✅ 折线图保存:monthly_line.png")
    except Exception as e:
        print(f"❌ 绘图失败:{str(e)}")

# -------------------------- 主交互逻辑 --------------------------
def main():
    init_csv()  # 启动时初始化CSV

    while True:
        print("\n=== 阅读时长统计工具 ===")
        print("1. 添加阅读记录")
        print("2. 查看统计报告(最近7/30天)")
        print("3. 生成可视化图表")
        print("4. 退出")

        choice = input("请输入操作编号:")

        if choice == '1':
            date = input("日期(YYYY-MM-DD):")
            book = input("书籍名称:")
            try:
                duration = int(input("阅读时长(分钟):"))
                add_reading_record(date, book, duration)
            except ValueError:
                print("❌ 时长必须是整数!")

        elif choice == '2':
            range_choice = input("统计范围:1.最近7天 2.最近30天:")
            days = 7 if range_choice == '1' else 30 if range_choice == '2' else None

            if days:
                stats = analyze_reading_data(days)
                if stats:
                    print(f"\n--- 最近{days}天统计 ---")
                    print(f"总时长:{stats['total']}分钟")
                    print(f"平均每日:{stats['avg']}分钟")
                    print(f"最常阅读:《{stats['top_book']}》({stats['top_book_duration']}分钟)")
                else:
                    print(f"❌ 最近{days}天无有效记录")

        elif choice == '3':
            plot_daily_bar()
            plot_monthly_line()

        elif choice == '4':
            print("再见!")
            break

        else:
            print("❌ 无效操作,请重新输入!")

if __name__ == "__main__":
    main()

功能演示

运行代码后,通过命令行交互:
1. 添加记录:输入日期、书籍、时长,自动保存到CSV。
2. 统计报告:选择时间范围,查看总时长、平均时长和最常读书籍。
3. 可视化:生成两张图表,分别保存为daily_bar.pngmonthly_line.png

总结

这个工具覆盖了数据分析的核心流程:数据收集→存储→分析→可视化。通过实践,我巩固了以下技能:
– CSV文件的读写与初始化。
– Pandas的时间筛选、分组聚合(groupby)。
– Matplotlib的图表定制(标题、标签、布局)。
– 命令行交互的错误处理与用户引导。

扩展方向
– 支持多用户(添加user列)。
– 导出PDF报告(结合ReportLab)。
– 添加周度统计(比如每周哪一天阅读最多)。

如果你也想跟踪阅读习惯,不妨试试这个工具——它不仅能帮你优化阅读,还能提升数据分析能力!


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注