
要说明白这件事得先亮个底我自己有一套用Python写的疗愈日志分析脚本它不提供诊断、不替代心理咨询只做一件事——把我记录下来的情绪文本转换成可以回看的趋势图、关键词和时间线。这套代码我自己断续用了一年多期间改了很多版从最初单纯的词频统计到后来加入情绪打分、滑窗趋势判断一点点变成了现在这个样子。今天把它整理出来分享主要是给两类人看一类是正在用文字记录自己情绪状态的人想借代码帮自己更客观地回看变化另一类是刚接触文本分析的开发者想找一个轻量、能跑通、又能迁移到其他场景的情感分析模板。先说好这套东西不是玄学也不是诊断工具。它能做的是把你写的字变成数据再把数据变成图。至于怎么理解那些图主动权——也应该——始终在你自己手里。1. 为什么用Python做疗愈日志分析思路与数据边界1.1 疗愈日志到底能量化什么三个可靠维度很多人觉得情绪是没法量化的东西其实这是个误解。文字记录里天然藏着三个可以量化的维度情感倾向、主题分布、时间节奏。情感倾向是最好入手的。你写的每一句话都带有情绪色彩比如“今天睡了个好觉醒过来觉得轻松了些”这句话的得分应该是偏积极而“下午又开始胡思乱想怎么也停不下来”这句话明显偏消极。用代码做这件事的本质就是建立一个情绪词表再设计一套规则把文本里出现的正负词映射成分数。主题分布更偏向“这段时间我到底在反复想什么”。通过分词、过滤掉无意义的虚词之后剩下的高频名词和动词能很直观地反映你当前关注的焦点。比如连续两周的高频词都指向“工作”“会议”“方案”那说明压力源大概率在职场上如果高频词变成了“睡眠”“疲惫”“没力气”那重心就转向了身体状态和精力管理。第三个维度是时间节奏。疗愈从来不是一条直线它有起伏、有反复、有看似倒退但实际在整合的阶段。用滑窗统计的方式画出情绪得分随时间变化的曲线能看见自己在一段较长周期里是整体波动向上还是持续走低这种宏观视角是单靠记忆把握不住的。1.2 工具选型为什么用轻量脚本而不是现成App市面上并不缺情绪记录类的App但我最后还是选择写一套本地脚本核心原因有三个。第一是数据私密性。情绪日志是非常个人的数据放到第三方服务器上我始终不踏实。本地脚本配合本地文件所有分析都在自己电脑上完成数据从不离开硬盘这带来的安全感是云端服务替代不了的。第二是分析透明度。现成App给一个“心情指数87分”你根本不知道分是怎么来的。自己写的脚本每个词的权重、每道规则都摊在眼前清楚知道自己到底是怎么被“打分”的。这种可控感在情绪探索场景里尤其重要——你在分析自己不是被一个黑盒分析。第三是扩展性。App的功能是固定的而我对自己的需求是不断变化的。最初我只想做词频统计后来想加情绪得分再后来想按月对比主题变化。自己写代码需求变化只需要改几行函数的事。提示这套脚本适合“辅助自我理解”不适合也不应该替代专业人员。如果你的情绪状态已经影响睡眠、食欲或正常生活超过两周请优先寻求正规心理服务代码帮不了这个忙。2. 日志数据怎么设计才能让代码有效字段、词典与打分原理2.1 一份能长期坚持的记录格式日期、标签与正文数据是整个项目的地基。我踩过最大的坑就是一开始没有定义清晰的格式想到什么写什么结果后面做分析时光清洗数据就花了一半时间。我自己最终固定下来的格式非常简单每行一条记录用英文逗号分隔长这样2025-01-12,睡眠,昨晚只睡了四个小时中间醒了好几次早上起来头很重。 2025-01-14,情绪,傍晚出门散步风吹过来的时候突然觉得胸口没那么堵了。 2025-01-15,工作,开完会整个人发懵坐在工位上盯着屏幕发呆效率很低。三个字段分别是日期、标签、正文。日期用来做时间序列分析标签是一条记录的大类可以按自己的情况定义——我常用的是“情绪”“睡眠”“工作”“身体”“人际”这几类正文就是完整的情绪叙述分词和情感打分都是在这一栏上进行的。这个格式最大的好处是兼容性好。记事本能写Excel能开Python用一行代码就能读进来。不要用复杂格式越简单越容易坚持。2.2 情感打分原理词典、否定词与程度副词情绪打分是整个脚本的核心它的逻辑其实不复杂准备一个带有权重的正负情绪词表在文本里找到这些词根据上下文调整分值最后加总。正负词典要自己维护。我目前的版本里大概收了一两百个常用词权重按情绪强度来定。举个例子positive_words { 平静: 2.0, 轻松: 2.0, 开心: 3.0, 安心: 2.0, 释然: 3.0, 期待: 1.5, 温暖: 2.5, 恢复: 1.0, 踏实: 2.0, 愉悦: 2.5, 欣慰: 2.0, 放松: 1.5 } negative_words { 烦躁: 2.0, 低落: 2.0, 崩溃: 4.0, 恐慌: 4.0, 无力: 2.0, 压抑: 3.0, 焦虑: 2.5, 疲惫: 1.5, 失眠: 2.0, 痛苦: 3.5, 委屈: 2.5, 麻木: 2.0 }但只有词表远远不够。中文里“不平静”和“平静”完全相反如果只看字面会把意思算反。所以必须有否定词处理——“不”“没”“没有”“无法”“难以”出现时情感词的分数要取反。程度副词也要考虑“很崩溃”和“有点崩溃”强度显然不一样我用倍数来处理比如“非常”“极度”乘1.5倍“有点”“稍微”乘0.7倍。打分的最小单位是句子而不是整段文本因为一段话里可能前半句低落、后半句出现转机拆开算能保留更多细节。2.3 关键词与主题轨迹停用词过滤与高频对比有了情感得分你知道了“好不好”但还不知道“在想什么”。关键词分析解决的就是这个问题。中文和英文不同没有天然空格分词需要先拆词。完整引入大模型分词库会让项目复杂很多一个更轻量的方案是用jieba这个库它体积小、部署快几行代码就能完成分词。如果你连第三方库都不想装也有一个更朴素的降级方案自己维护一个自定义词典用正则在文本里逐个匹配。这个方法召回率不高但做日常主题参考够用。分词后一定要做停用词过滤。把“的”“了”“就是”“还是”“因为”这类纯语法功能的词删掉剩下的词才是真正携带语义的。每次分析时我会输出两个榜单一个是全时间段的关键词Top 20另一个是本月对比上月的“新增高频词”后者能发现议题的迁移。3. 核心代码实现从情感打分到趋势图表的完整脚本3.1 完整脚本可以直接跑起来的版本我尽量把代码写得可以独立运行。下面这个版本做了三件事读取日志、逐句情感打分、绘制趋势图。如果你还没有真实数据文件脚本会自动用内置的两周示例数据演示效果。import os import re from collections import Counter from datetime import datetime import matplotlib.pyplot as plt # ---------- 1. 配置 ---------- # 正负情感词典 positive_words { 平静: 2.0, 轻松: 2.0, 开心: 3.0, 安心: 2.0, 释然: 3.0, 期待: 1.5, 温暖: 2.5, 恢复: 1.0, 踏实: 2.0, 愉悦: 2.5, 欣慰: 2.0, 放松: 1.5 } negative_words { 烦躁: 2.0, 低落: 2.0, 崩溃: 4.0, 恐慌: 4.0, 无力: 2.0, 压抑: 3.0, 焦虑: 2.5, 疲惫: 1.5, 失眠: 2.0, 痛苦: 3.5, 委屈: 2.5, 麻木: 2.0 } negation_words [不, 没, 没有, 无, 无法, 难以, 别, 非] adverb_scales { 非常: 1.5, 极度: 1.5, 特别: 1.5, 很: 1.3, 有点: 0.7, 稍微: 0.7, 有些: 0.8 } stopwords {的, 了, 是, 我, 在, 和, 就, 都, 也, 又, 才, 但, 而, 跟, 被, 把, 让, 这, 那} # ---------- 2. 文本预处理与句子打分 ---------- def split_sentences(text: str) - list: 按常见标点把一段文字拆成句子列表 parts re.split(r[。\n], text) return [p.strip() for p in parts if p.strip()] def score_sentence(sentence: str) - float: 对单个句子打分返回正负分合计 score 0.0 tokens re.findall(r[\u4e00-\u9fa5]|[a-zA-Z], sentence) i 0 while i len(tokens): token tokens[i] # 判断前面是否有否定词 negation False if i 0 and tokens[i-1] in negation_words: negation True # 判断是否有程度副词修饰 adverb_scale 1.0 if i 0 and tokens[i-1] in adverb_scales: adverb_scale adverb_scales[tokens[i-1]] if token in positive_words: base positive_words[token] * adverb_scale score - base if negation else base elif token in negative_words: base negative_words[token] * adverb_scale score base if not negation else -base i 1 return score def score_text(text: str) - float: 对整条日志打分各句子分数求和 total 0.0 for sent in split_sentences(text): total score_sentence(sent) return total # ---------- 3. 加载数据 ---------- def load_logs(path: str healing_log.txt) - list: 读取日志文件返回字典列表。文件不存在时返回内置演示数据 if not os.path.exists(path): print(f[提示] 未找到 {path}使用内置示例数据) return [ {date: 2025-01-05, tag: 情绪, text: 今天睡了个好觉醒过来觉得轻松了些。}, {date: 2025-01-07, tag: 情绪, text: 下午又开始胡思乱想心里烦躁得坐不住。}, {date: 2025-01-09, tag: 睡眠, text: 昨晚睡得不错很久没有这种踏实的感觉。}, {date: 2025-01-11, tag: 工作, text: 会议开完很疲惫感觉脑子里一团浆糊。}, {date: 2025-01-13, tag: 人际, text: 和朋友聊了很久说完之后胸口没那么堵了。}, {date: 2025-01-15, tag: 情绪, text: 傍晚散步时风吹过来突然觉得很平静。}, ] rows [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue parts line.split(,, maxsplit2) if len(parts) 3: continue rows.append({date: parts[0].strip(), tag: parts[1].strip(), text: parts[2].strip()}) return rows # ---------- 4. 关键词提取简化版 ---------- def extract_keywords(texts: list, topk: int 10) - list: 简易分词 停用词过滤 高频统计 custom_words list(positive_words.keys()) list(negative_words.keys()) counter Counter() for text in texts: # 先把词典词和中文连续片段都找出来 for w in custom_words: if w in text: counter[w] 1 # 2字节以上的汉字片段作为候选词 for seq in re.findall(r[\u4e00-\u9fa5]{2,6}, text): if seq not in stopwords and len(seq) 2: counter[seq] 1 return counter.most_common(topk) # ---------- 5. 趋势可视化 ---------- def plot_trend(rows, scores): dates [datetime.strptime(r[date], %Y-%m-%d) for r in rows] plt.figure(figsize(10, 4)) plt.plot(dates, scores, markero, linestyle-, linewidth1.5) plt.axhline(0, colorgray, linestyle--, linewidth0.8) plt.title(情绪得分趋势正值为偏积极负值为偏消极) plt.xlabel(日期) plt.ylabel(情绪得分) plt.grid(True, alpha0.3) # 3点移动平均线更平滑 if len(scores) 3: ma [] for i in range(len(scores)): window scores[max(0, i-1): i2] ma.append(sum(window) / len(window)) plt.plot(dates, ma, colororange, linewidth2, label3期移动平均) plt.legend() # 中文显示调整 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False plt.tight_layout() plt.show() plt.close() # ---------- 6. 汇总主流程 ---------- def main(): rows load_logs() if not rows: print(没有找到有效数据请检查日志文件格式) return scores [score_text(r[text]) for r in rows] print( 日志情绪得分明细 ) for r, s in zip(rows, scores): print(f{r[date]} [{r[tag]}] 得分 {s:.2f} {r[text][:20]}) print(\n 关键词 Top 10 ) for word, cnt in extract_keywords([r[text] for r in rows], topk10): print(f{word}: {cnt}) plot_trend(rows, scores) if __name__ __main__: main()3.2 打分逻辑里那两个“小聪明”否定取反与副词修饰很多第一次接触情感分析的人会问为什么句子里没有词表里的词分数还是不对问题通常出在否定和程度修饰上。我在脚本里处理这两类问题时用了相对轻量的方案在跑通的基础上保证准确率。否定取反的规则是如果情感词的前一个词是否定词那么情感分值就要取反。比如“不烦躁”这句“烦躁”原本是2.0经过否定后变成-2.0。注意这里有个尖角“不烦躁”是一个偏积极的状态分数为负值是正确的因为我的规则是“正值为偏消极负值为偏积极”。为了统一性正文里我主动把正向得分定义为“更平静/更积极”负向得分定义为“更低落/更消极”——这个方向你自己定义就行但全脚本必须保持一致。程度副词的处理更简单取前一个词查表乘系数。“很崩溃”的“很”是1.3倍最终是4.0×1.35.2“稍微平静了一点”里的“稍微”是0.7倍总分2.0×0.71.4。这个线性乘法当然粗糙但做趋势分析足够稳定比不加修饰准确性提高一个档次。3.3 从分数到结论怎么读你的趋势图图跑出来之后重点看三类信息。第一是整体斜率把情绪得分按周做平均看趋势线是抬升还是下降。我自己的记录里经常出现“某几天得分特别低但周平均在持续走高”的情况这说明整体在往好的方向挪短期波动不必恐慌。第二是波动幅度疗愈过程中情绪反弹是正常的模板里“3期移动平均”这条橙线就是为了削弱单日波动的干扰让你把注意力放在中期节奏上。第三是标签分布统计各标签的平均得分和记录频率能定位主要情绪负担来自哪个生活领域。4. 常见问题、避坑清单与心理边界4.1 代码跑不起来十有八九是字体和编码问题这个脚本对Python版本要求不高3.8以上都能跑依赖只有matplotlib。最常见的两类报错都很好解决。一是中文显示成方块。这是matplotlib默认字体不包含中文导致的Windows上把SimHei放到最前面基本能解决macOS用PingFang SCLinux用Noto Sans CJK SC。如果改了还不行可以检查系统是否安装了对应字体或者干脆指定一个绝对存在的字体文件路径。二是读取日志报告UnicodeDecodeError。这通常是Windows记事本默认存成了GBK编码而脚本按UTF-8读取。解决办法是在open时加一个encodingutf-8-sig参数这个变体会自动吃掉BOM头同时兼容常见的中文编码。或者更简单保存文件时在记事本右下角把编码改成UTF-8。4.2 词表不够用打分偏低怎么办情感词典是永远填不满的。我的经验是与其一开始就追求大词表不如每次跑完看结果发现有明显的情绪词没被识别就随手加进字典。词表是在使用中越长越大的别追求一步到位。另外可以考虑用极简加权法来扩展把词典词分成“核心词”和“参考词”参考词权重减半这样既扩大了覆盖范围又不会让不熟悉的高权重词主导结果。比如“失望”我觉得强度中等就给2.5“遗憾”强度较弱给1.0。权重的调整完全靠个人感觉这正是自用工具的优势——不需要迎合别人的标注标准。4.3 最重要的一条别让代码成为新的负担用这套系统最大的风险不是技术问题而是心理上的过度依赖。我踩过的坑是有段时间每天写日志、跑分析、看曲线结果情绪反而被那根橙色的移动平均线控制住了。后来我调整了节奏不是每天都跑而是固定每周日复盘一次只对比本周和上周的周均值、看新增关键词其他时间只是记录不分析。另外务必记住分数波动是一个信号不是判决。某天得分-4.5不代表你“倒退了”它可能只是说明你在某个议题上的情绪能量还是在释放。真正的疗愈曲线绝不是一根向上的直线而是带锯齿、有反复的斜向上的带状区间。代码存在的意义是让你看见这个区间的边界而不是成为评判自己的新标准。4.4 后续可以怎么扩展这套脚本留了几个明显的扩展点。如果做文本分题有需求可以在读取时增加“月份”字段按月输出主题词差异如果希望自动识别情绪周期可以用滑窗标准差检测“波动增大的区间”作为有重要议题触发的提示如果开始对整段文本做回顾可以增加一个“回看模式”随机抽取三个月前的同一天记录配合当时的得分曲线做对照。我在实际使用中的体会是工具的价值不在代码写完那一刻而在你坚持记录两个月、三个月之后回看第一周数据和最近一周数据的对比时才真正显现。那些你以为早就翻篇的情绪其实都在曲线里留下了痕迹。这个脚本帮助你面对的不是别人正是过去某一刻的你自己而这一步往往是疗愈真正开始的地方。