欢迎光临
我们一直在努力

写代码自动统计每月情绪波动,生成心情曲线,颠覆不知道为啥烦。

情绪波动追踪器 – 告别"莫名烦躁"时代

 

📋 项目概述

 

基于自然语言处理和数据分析技术的个人情绪波动监测系统,通过日常记录、语音输入和文字分析,自动识别情绪状态,生成月度心情曲线,帮你找到情绪的真正原因。

 

🎯 实际应用场景

 

场景一:职场人的情绪密码

 

小李最近总觉得心里堵得慌,但又说不出原因。使用情绪追踪器一个月后发现:每周三下午3点情绪最低落,原因是周三例会前的焦虑积累。找到规律后,他调整了会议准备时间,情绪曲线明显改善。

场景二:考研党的压力地图

 

小王备考期间经常莫名烦躁,通过情绪追踪发现:每次刷到同学晒offer时情绪急剧下降,每次完成一套真题后情绪回升。于是他制定了"信息隔离时间",情绪稳定性提升了60%。

场景三:恋爱中的情绪导航

 

小张和女友总是因为小事吵架,通过共同使用情绪追踪器,发现两人都在饭后1小时情绪最敏感。调整沟通时间后,争吵频率降低了70%,感情更加和谐。

场景四:创业者的心理体检

 

创业的小陈经常感到疲惫却找不到原因,情绪追踪显示:每当投资人反馈延迟时情绪下滑,每当产品bug修复时情绪上升。这让他的团队意识到需要建立更好的投资人沟通机制。

 

😫 行业痛点分析

 

痛点 现状 后果

情绪盲视 只知道自己不开心,不知道为什么 情绪积压导致心理问题

归因困难 事后才想起触发事件 无法预防负面情绪爆发

缺乏量化 凭感觉判断情绪状态 无法客观评估心理状态

孤立观察 只看当天情绪,不看趋势 错过周期性情绪规律

表达障碍 不知道如何准确描述感受 难以寻求有效帮助

 

🧠 核心逻辑架构

 

┌─────────────────────────────────────────────────────────────┐

│ 数据采集层 │

│ 文字输入 │ 语音转文字 │ 表情识别 │ 行为数据 │ 生理数据 │

└─────────────────────────────────────────────────────────────┘

                              ↓

┌─────────────────────────────────────────────────────────────┐

│ 预处理层 │

│ 文本清洗 │ 分词处理 │ 去噪降维 │ 标准化 │

└─────────────────────────────────────────────────────────────┘

                              ↓

┌─────────────────────────────────────────────────────────────┐

│ 情绪识别层 │

│ 情感词典匹配 │ ML分类器 │ 深度学习 │ 多模态融合 │

└─────────────────────────────────────────────────────────────┘

                              ↓

┌─────────────────────────────────────────────────────────────┐

│ 分析层 │

│ 情绪分类 │ 强度计算 │ 原因抽取 │ 趋势分析 │ 关联规则 │

└─────────────────────────────────────────────────────────────┘

                              ↓

┌─────────────────────────────────────────────────────────────┐

│ 可视化层 │

│ 心情曲线 │ 热力图 │ 雷达图 │ 报告生成 │ 预警提醒 │

└─────────────────────────────────────────────────────────────┘

 

💻 核心代码实现

 

项目结构

 

mood_tracker/

├── main.py # 主程序入口

├── config.py # 配置文件

├── requirements.txt # 依赖包

├── README.md # 项目说明

├── data/

│ ├── emotion_lexicon.json # 情绪词典

│ ├── user_records.db # SQLite数据库

│ ├── mood_history.json # 历史情绪数据

│ └── templates/ # 报告模板

├── modules/

│ ├── __init__.py

│ ├── data_collector.py # 数据采集模块

│ ├── preprocessor.py # 预处理模块

│ ├── emotion_analyzer.py # 情绪分析模块

│ ├── trend_analyzer.py # 趋势分析模块

│ ├── visualizer.py # 可视化模块

│ └── reporter.py # 报告生成模块

├── utils/

│ ├── __init__.py

│ ├── db_helper.py # 数据库助手

│ ├── nlp_utils.py # NLP工具

│ └── helpers.py # 通用工具

└── tests/ # 测试文件

    └── test_emotion_analyzer.py

 

1. 配置文件 (config.py)

 

"""

配置文件 – 情绪追踪器全局配置

作者: AI Assistant

版本: 1.0.0

"""

 

import os

 

# ==================== 路径配置 ====================

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

DATA_DIR = os.path.join(BASE_DIR, 'data')

TEMPLATES_DIR = os.path.join(DATA_DIR, 'templates')

DB_PATH = os.path.join(DATA_DIR, 'user_records.db')

HISTORY_PATH = os.path.join(DATA_DIR, 'mood_history.json')

 

# ==================== 情绪分类配置 ====================

# 基础情绪类型

EMOTION_CATEGORIES = {

    'joy': '喜悦', # 开心、兴奋、满足

    'sadness': '悲伤', # 难过、失落、沮丧

    'anger': '愤怒', # 生气、恼火、愤慨

    'fear': '恐惧', # 害怕、担心、焦虑

    'surprise': '惊讶', # 意外、震惊、诧异

    'disgust': '厌恶', # 反感、嫌弃、排斥

    'neutral': '平静', # 淡定、无感、平常

    'love': '喜爱', # 爱慕、喜欢、欣赏

    'anxiety': '焦虑', # 紧张、不安、担忧

    'confusion': '困惑' # 迷茫、不解、混乱

}

 

# 情绪强度等级

INTENSITY_LEVELS = {

    1: ('极轻微', '几乎无感,只是隐约有些…'),

    2: ('轻微', '能感觉到,但不影响日常'),

    3: ('中等', '明显感受到,开始影响心情'),

    4: ('强烈', '很强烈,明显影响状态'),

    5: ('极强烈', '非常强烈,难以忽视')

}

 

# 情绪极性

EMOTION_POLARITY = {

    'positive': ['joy', 'love', 'surprise'], # 积极情绪

    'negative': ['sadness', 'anger', 'fear', 'disgust', 'anxiety', 'confusion'], # 消极情绪

    'neutral': ['neutral'] # 中性情绪

}

 

# ==================== 分析配置 ====================

# 情绪分析阈值

SENTIMENT_THRESHOLDS = {

    'positive': 0.6, # 积极情绪阈值

    'negative': -0.3, # 消极情绪阈值

    'neutral_max': 0.3, # 中性区间上限

    'neutral_min': -0.3 # 中性区间下限

}

 

# 滑动窗口大小(用于趋势分析)

MOOD_WINDOW_SIZE = 7 # 7天滑动窗口

MOOD_WINDOW_TYPE = 'simple' # simple/exponential

 

# 周期性分析配置

CYCLE_ANALYSIS = {

    'daily': True, # 日周期

    'weekly': True, # 周周期

    'monthly': True # 月周期

}

 

# ==================== 可视化配置 ====================

CHART_CONFIG = {

    'figure_size': (14, 8),

    'dpi': 100,

    'colors': {

        'joy': '#FFD700', # 金色

        'sadness': '#4169E1', # 皇家蓝

        'anger': '#DC143C', # 猩红

        'fear': '#9370DB', # 中紫色

        'surprise': '#FF69B4', # 热粉色

        'disgust': '#556B2F', # 深橄榄绿

        'neutral': '#808080', # 灰色

        'love': '#FF1493', # 深粉色

        'anxiety': '#FF8C00', # 深橙色

        'confusion': '#00CED1' # 深青色

    },

    'line_style': '-',

    'marker': 'o',

    'grid': True

}

 

# ==================== 预警配置 ====================

ALERT_CONFIG = {

    'low_mood_threshold': 2.0, # 低情绪预警阈值

    'high_anxiety_threshold': 3.5, # 高焦虑预警阈值

    'consecutive_days': 3, # 连续天数触发预警

    'enable_notifications': True, # 启用通知

    'alert_message': '⚠️ 检测到您最近情绪波动较大,建议休息或寻求帮助'

}

 

# ==================== 数据存储配置 ====================

DB_CONFIG = {

    'type': 'sqlite',

    'path': DB_PATH,

    'backup_interval': 7, # 备份间隔(天)

    'retention_period': 365 # 数据保留期(天)

}

 

# ==================== 多模态配置 ====================

MULTIMODAL_CONFIG = {

    'enable_voice': True, # 启用语音输入

    'enable_face': False, # 启用人脸识别(需摄像头)

    'voice_language': 'zh-CN', # 语音识别语言

    'face_cascade_path': 'haarcascade_frontalface_default.xml'

}

 

2. 数据采集模块 (modules/data_collector.py)

 

"""

数据采集模块 – 多源情绪数据收集

支持文字输入、语音转文字、表情识别、行为数据等多种输入方式

"""

 

import json

import sqlite3

import datetime

import hashlib

from dataclasses import dataclass, field, asdict

from typing import Optional, List, Dict, Any, Union

from enum import Enum

import logging

import base64

import io

import wave

import struct

import threading

import time

import uuid

 

# 配置日志

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

 

 

class InputType(Enum):

    """输入类型枚举"""

    TEXT = "text" # 文字输入

    VOICE = "voice" # 语音输入

    FACE = "face" # 面部表情

    BEHAVIOR = "behavior" # 行为数据

    PHYSIOLOGICAL = "physiological" # 生理数据

    MANUAL = "manual" # 手动选择

 

 

class MoodSource(Enum):

    """情绪来源枚举"""

    DAILY_LOG = "daily_log" # 日常记录

    STRESS_EVENT = "stress_event" # 压力事件

    ACHIEVEMENT = "achievement" # 成就事件

    SOCIAL_INTERACTION = "social" # 社交互动

    WORK_RELATED = "work" # 工作相关

    HEALTH_RELATED = "health" # 健康相关

    ENTERTAINMENT = "entertainment"# 娱乐活动

    UNKNOWN = "unknown" # 未知来源

 

 

@dataclass

class EmotionRecord:

    """

    情绪记录数据类

    

    存储单条情绪记录的完整信息

    

    Attributes:

        record_id: 记录唯一ID

        timestamp: 记录时间戳

        input_type: 输入类型

        content: 原始内容(文字/语音转文字/表情描述)

        source: 情绪来源

        primary_emotion: 主要情绪

        secondary_emotions: 次要情绪列表

        intensity: 情绪强度 (1-5)

        polarity: 情绪极性 (positive/negative/neutral)

        context: 上下文信息

        tags: 标签列表

        location: 位置信息(可选)

        weather: 天气信息(可选)

        sleep_hours: 睡眠时长(可选)

        physical_state: 身体状态(可选)

    """

    record_id: str

    timestamp: str

    input_type: str

    content: str

    source: str

    primary_emotion: str

    secondary_emotions: List[str] = field(default_factory=list)

    intensity: int = 3

    polarity: str = "neutral"

    context: Dict[str, Any] = field(default_factory=dict)

    tags: List[str] = field(default_factory=list)

    location: Optional[str] = None

    weather: Optional[str] = None

    sleep_hours: Optional[float] = None

    physical_state: Optional[str] = None

    

    def to_dict(self) -> Dict:

        """转换为字典格式"""

        return asdict(self)

    

    @classmethod

    def from_dict(cls, data: Dict) -> 'EmotionRecord':

        """从字典创建实例"""

        return cls(**data)

 

 

@dataclass

class VoiceData:

    """

    语音数据类

    

    存储语音转文字的相关信息

    """

    audio_data: bytes

    sample_rate: int

    duration: float

    language: str = "zh-CN"

    text: Optional[str] = None

    confidence: float = 0.0

 

 

@dataclass  

class FaceData:

    """

    面部表情数据类

    

    存储人脸识别的情绪分析结果

    """

    image_data: bytes

    detected_emotions: Dict[str, float]

    dominant_emotion: str

    confidence: float

    landmarks: Optional[List[tuple]] = None

 

 

class DatabaseManager:

    """

    数据库管理器

    

    负责情绪数据的持久化存储和检索

    """

    

    def __init__(self, db_path: str = None):

        """

        初始化数据库管理器

        

        Args:

            db_path: 数据库文件路径

        """

        self.db_path = db_path or DB_PATH

        self._init_database()

        logger.info(f"数据库管理器初始化完成: {self.db_path}")

    

    def _init_database(self):

        """初始化数据库表结构"""

        conn = sqlite3.connect(self.db_path)

        cursor = conn.cursor()

        

        # 情绪记录表

        cursor.execute('''

            CREATE TABLE IF NOT EXISTS emotion_records (

                record_id TEXT PRIMARY KEY,

                timestamp TEXT NOT NULL,

                input_type TEXT NOT NULL,

                content TEXT NOT NULL,

                source TEXT DEFAULT 'unknown',

                primary_emotion TEXT NOT NULL,

                secondary_emotions TEXT,

                intensity INTEGER DEFAULT 3,

                polarity TEXT DEFAULT 'neutral',

                context TEXT,

                tags TEXT,

                location TEXT,

                weather TEXT,

                sleep_hours REAL,

                physical_state TEXT,

                created_at TEXT DEFAULT CURRENT_TIMESTAMP

            )

        ''')

        

        # 索引优化查询

        cursor.execute('''

            CREATE INDEX IF NOT EXISTS idx_timestamp ON emotion_records(timestamp)

        ''')

        cursor.execute('''

            CREATE INDEX IF NOT EXISTS idx_primary_emotion ON emotion_records(primary_emotion)

        ''')

        cursor.execute('''

            CREATE INDEX IF NOT EXISTS idx_intensity ON emotion_records(intensity)

        ''')

        

        # 每日汇总表

        cursor.execute('''

            CREATE TABLE IF NOT EXISTS daily_summary (

                date TEXT PRIMARY KEY,

                avg_mood_score REAL,

                dominant_emotion TEXT,

                emotion_distribution TEXT,

                record_count INTEGER,

                updated_at TEXT DEFAULT CURRENT_TIMESTAMP

            )

        ''')

        

        conn.commit()

        conn.close()

        logger.info("数据库表结构初始化完成")

    

    def save_record(self, record: EmotionRecord) -> bool:

        """

        保存情绪记录

        

        Args:

            record: 情绪记录对象

            

        Returns:

            保存是否成功

        """

        try:

            conn = sqlite3.connect(self.db_path)

            cursor = conn.cursor()

            

            cursor.execute('''

                INSERT OR REPLACE INTO emotion_records

                (record_id, timestamp, input_type, content, source, primary_emotion,

                 secondary_emotions, intensity, polarity, context, tags, location,

                 weather, sleep_hours, physical_state)

                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

            ''', (

                record.record_id,

                record.timestamp,

                record.input_type,

                record.content,

                record.source,

                record.primary_emotion,

                json.dumps(record.secondary_emotions, ensure_ascii=False),

                record.intensity,

                record.polarity,

                json.dumps(record.context, ensure_ascii=False),

                json.dumps(record.tags, ensure_ascii=False),

                record.location,

                record.weather,

                record.sleep_hours,

                record.physical_state

            ))

            

            conn.commit()

            conn.close()

            logger.info(f"记录保存成功: {record.record_id}")

            return True

            

        except Exception as e:

            logger.error(f"保存记录失败: {e}")

            return False

    

    def get_records(self, start_date: str = None, end_date: str = None,

                   limit: int = 100) -> List[EmotionRecord]:

        """

        获取情绪记录

        

        Args:

            start_date: 开始日期 (YYYY-MM-DD)

            end_date: 结束日期 (YYYY-MM-DD)

            limit: 返回记录数量限制

            

        Returns:

            情绪记录列表

        """

        try:

            conn = sqlite3.connect(self.db_path)

            cursor = conn.cursor()

            

            query = "SELECT * FROM emotion_records WHERE 1=1"

            params = []

            

            if start_date:

                query += " AND date(timestamp) >= ?"

                params.append(start_date)

            

            if end_date:

                query += " AND date(timestamp) <= ?"

                params.append(end_date)

            

            query += " ORDER BY timestamp DESC LIMIT ?"

            params.append(limit)

            

            cursor.execute(query, params)

            rows = cursor.fetchall()

            conn.close()

            

            records = []

            for row in rows:

                record = EmotionRecord(

                    record_id=row[0],

                    timestamp=row[1],

                    input_type=row[2],

                    content=row[3],

                    source=row[4],

                    primary_emotion=row[5],

                    secondary_emotions=json.loads(row[6]) if row[6] else [],

                    intensity=row[7],

                    polarity=row[8],

                    context=json.loads(row[9]) if row[9] else {},

                    tags=json.loads(row[10]) if row[10] else [],

                    location=row[11],

                    weather=row[12],

                    sleep_hours=row[13],

                    physical_state=row[14]

                )

                records.append(record)

            

            return records

            

        except Exception as e:

            logger.error(f"获取记录失败: {e}")

            return []

    

    def get_daily_stats(self, date: str) -> Optional[Dict]:

        """

        获取某日的情绪统计

        

        Args:

            date: 日期 (YYYY-MM-DD)

            

        Returns:

            统计信息字典

        """

        try:

            conn = sqlite3.connect(self.db_path)

            cursor = conn.cursor()

            

            cursor.execute('''

                SELECT 

                    AVG(CASE WHEN primary_emotion IN ('joy', 'love', 'surprise') THEN 1

                             WHEN primary_emotion = 'neutral' THEN 0

                             ELSE -1 END) as mood_score,

                    primary_emotion,

                    COUNT(*) as count

                FROM emotion_records

                WHERE date(timestamp) = ?

                GROUP BY primary_emotion

                ORDER BY count DESC

            ''', (date,))

            

            rows = cursor.fetchall()

            conn.close()

            

            if not rows:

                return None

            

            # 计算情绪分布

            distribution = {row[1]: row[2] for row in rows}

            dominant = rows[0][1] if rows else 'neutral'

            mood_score = round(rows[0][0] or 0, 2) if rows else 0

            

            return {

                'date': date,

                'mood_score': mood_score,

                'dominant_emotion': dominant,

                'distribution': distribution,

                'total_records': sum(row[2] for row in rows)

            }

            

        except Exception as e:

            logger.error(f"获取日统计失败: {e}")

            return None

    

    def delete_record(self, record_id: str) -> bool:

        """删除指定记录"""

        try:

            conn = sqlite3.connect(self.db_path)

            cursor = conn.cursor()

            cursor.execute("DELETE FROM emotion_records WHERE record_id = ?", (record_id,))

            conn.commit()

            conn.close()

            return True

        except Exception as e:

            logger.error(f"删除记录失败: {e}")

            return False

 

 

class TextInputHandler:

    """

    文字输入处理器

    

    处理用户的文字输入,包括直接输入和从其他来源转换的文字

    """

    

    def __init__(self, nlp_processor=None):

        """

        初始化文字输入处理器

        

        Args:

            nlp_processor: NLP处理器实例

        """

        self.nlp_processor = nlp_processor

        self._init_punctuation_patterns()

        logger.info("文字输入处理器初始化完成")

    

    def _init_punctuation_patterns(self):

        """初始化标点符号和情绪词模式"""

        # 情绪表达模式

        self.emotion_patterns = [

            r'[我|咱|俺]觉得(.*?)[很|挺|非常|特别]?(\\w+)',

            r'(.*?)[让我|使我|令我很](\\w+)',

            r'(.*?),[我|咱|俺](\\w+)了',

            r'[今天|现在|刚才](.*?)[感觉|觉得|状态](\\w+)',

        ]

        

        # 情绪修饰词

        self.intensifiers = {

            'very': ['很', '非常', '特别', '极其', '超级', '巨', '超'],

            'mild': ['有点', '稍微', '略微', '还行', '一般', '马马虎虎'],

            'extreme': ['太', '完全', '彻底', '根本', '绝对', '简直']

        }

    

    def process(self, text: str, context: Dict = None) -> Dict:

        """

        处理文字输入

        

        Args:

            text: 用户输入的文字

            context: 上下文信息

            

        Returns:

            处理后的结构化数据

        """

        if not text or not text.strip():

            return {'success': False, 'error': '输入内容为空'}

        

        # 清理和标准化文本

        cleaned_text = self._clean_text(text)

        

        # 提取关键信息

        extracted_info = self._extract_information(cleaned_text, context)

        

        return {

            'success': True,

            'original_text': text,

            'cleaned_text': cleaned_text,

            'extracted_info': extracted_info,

            'timestamp': datetime.datetime.now().isoformat(),

            'input_type': InputType.TEXT.value

        }

    

    def _clean_text(self, text: str) -> str:

        """清理和标准化文本"""

        # 去除多余空白

        text = ' '.join(text.split())

        

        # 统一标点符号

        text = text.replace(',', ',').replace('。', '.').replace('!', '!')

        text = text.replace('?', '?').replace(':', ':').replace(';', ';')

        

        # 保留中文、英文、数字和基本标点

        allowed_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?;:\\'"-_()[]{},。!?;:""''【】()')

        text = ''.join(c for c in text if c in allowed_chars or '\\u4e00' <= c <= '\\u9fff')

        

        return text.strip()

    

    def _extract_information(self, text: str, context: Dict = None) -> Dict:

        """从文本中提取关键信息"""

        info = {

            'keywords': [],

            'possible_triggers': [],

            'time_references': [],

            'people_mentioned': [],

            'activities': []

        }

        

        # 提取关键词(简单分词)

        words = list(jieba.cut(text))

        info['keywords'] = [w for w in words if len(w) > 1 and w not in ['的', '了', '是', '在', '有', '和', '与']]

        

        # 提取时间引用

        time_patterns = [

            r'今天', r'昨天', r'明天', r'上午', r'下午', r'晚上',

            r'早上', r'中午', r'傍晚', r'深夜', r'凌晨',

            r'\\d+点', r'\\d+:\\d+', r'\\d+-\\d+号', r'\\d+月\\d+日'

        ]

        for pattern in time_patterns:

            matches = re.findall(pattern, text)

            info['time_references'].extend(matches)

        

        # 提取可能的触发因素

        trigger_indicators = ['因为', '由于', '原因', '导致', '所以', '于是']

        for indicator in trigger_indicators:

            if indicator in text:

                parts = text.split(indicator)

                if len(parts) > 1:

                利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

赞(0)
未经允许不得转载:171主机测评 » 写代码自动统计每月情绪波动,生成心情曲线,颠覆不知道为啥烦。
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址