├── README.md ├── stopwords_hit.txt └── data_analysis.py /README.md: -------------------------------------------------------------------------------- 1 | # Love_data 2 | 微信聊天记录可视化教程 3 | 4 | 所用编程语言与软件 python+pycharm 5 | 6 | 环境配置 7 | python 3.10.9 8 | matplotlib 由于包不兼容问题,使用两个版本 3.7.0/3.8.2 代码中有解释 9 | 下列包下载最新版即可 10 | numpy 11 | pandas 12 | seaborn 13 | jieba 14 | july 15 | wordcloud 16 | 17 | 步骤一 18 | 通过github开源项目“留痕”解包聊天记录,得到聊天记录csv文件,详细教程参考https://github.com/LC044/WeChatMsg 19 | 该项目也可进行简单数据分析,无代码能力可直接使用 20 | 21 | 步骤二 22 | 编写python代码进行数据可视化 23 | 24 | 25 | file 26 | data_analysis.py 为数据可视化代码 27 | stopwords_hit.txt 为哈工大停词表 28 | reference 29 | https://github.com/LC044/WeChatMsg 30 | -------------------------------------------------------------------------------- /stopwords_hit.txt: -------------------------------------------------------------------------------- 1 | ——— 2 | 》), 3 | )÷(1- 4 | ”, 5 | )、 6 | =( 7 | : 8 | → 9 | ℃ 10 | & 11 | * 12 | 一一 13 | ~~~~ 14 | ’ 15 | . 16 | 『 17 | .一 18 | ./ 19 | -- 20 | 』 21 | =″ 22 | 【 23 | [*] 24 | }> 25 | [⑤]] 26 | [①D] 27 | c] 28 | ng昉 29 | * 30 | // 31 | [ 32 | ] 33 | [②e] 34 | [②g] 35 | ={ 36 | } 37 | ,也 38 | ‘ 39 | A 40 | [①⑥] 41 | [②B] 42 | [①a] 43 | [④a] 44 | [①③] 45 | [③h] 46 | ③] 47 | 1. 48 | -- 49 | [②b] 50 | ’‘ 51 | ××× 52 | [①⑧] 53 | 0:2 54 | =[ 55 | [⑤b] 56 | [②c] 57 | [④b] 58 | [②③] 59 | [③a] 60 | [④c] 61 | [①⑤] 62 | [①⑦] 63 | [①g] 64 | ∈[ 65 | [①⑨] 66 | [①④] 67 | [①c] 68 | [②f] 69 | [②⑧] 70 | [②①] 71 | [①C] 72 | [③c] 73 | [③g] 74 | [②⑤] 75 | [②②] 76 | 一. 77 | [①h] 78 | .数 79 | [] 80 | [①B] 81 | 数/ 82 | [①i] 83 | [③e] 84 | [①①] 85 | [④d] 86 | [④e] 87 | [③b] 88 | [⑤a] 89 | [①A] 90 | [②⑧] 91 | [②⑦] 92 | [①d] 93 | [②j] 94 | 〕〔 95 | ][ 96 | :// 97 | ′∈ 98 | [②④ 99 | [⑤e] 100 | 12% 101 | b] 102 | ... 103 | ................... 104 | …………………………………………………③ 105 | ZXFITL 106 | [③F] 107 | 」 108 | [①o] 109 | ]∧′=[ 110 | ∪φ∈ 111 | ′| 112 | {- 113 | ②c 114 | } 115 | [③①] 116 | R.L. 117 | [①E] 118 | Ψ 119 | -[*]- 120 | ↑ 121 | .日 122 | [②d] 123 | [② 124 | [②⑦] 125 | [②②] 126 | [③e] 127 | [①i] 128 | [①B] 129 | [①h] 130 | [①d] 131 | [①g] 132 | [①②] 133 | [②a] 134 | f] 135 | [⑩] 136 | a] 137 | [①e] 138 | [②h] 139 | [②⑥] 140 | [③d] 141 | [②⑩] 142 | e] 143 | 〉 144 | 】 145 | 元/吨 146 | [②⑩] 147 | 2.3% 148 | 5:0 149 | [①] 150 | :: 151 | [②] 152 | [③] 153 | [④] 154 | [⑤] 155 | [⑥] 156 | [⑦] 157 | [⑧] 158 | [⑨] 159 | …… 160 | —— 161 | ? 162 | 、 163 | 。 164 | “ 165 | ” 166 | 《 167 | 》 168 | ! 169 | , 170 | : 171 | ; 172 | ? 173 | . 174 | , 175 | . 176 | ' 177 | ? 178 | · 179 | ——— 180 | ── 181 | ? 182 | — 183 | < 184 | > 185 | ( 186 | ) 187 | 〔 188 | 〕 189 | [ 190 | ] 191 | ( 192 | ) 193 | - 194 | + 195 | ~ 196 | × 197 | / 198 | / 199 | ① 200 | ② 201 | ③ 202 | ④ 203 | ⑤ 204 | ⑥ 205 | ⑦ 206 | ⑧ 207 | ⑨ 208 | ⑩ 209 | Ⅲ 210 | В 211 | " 212 | ; 213 | # 214 | @ 215 | γ 216 | μ 217 | φ 218 | φ. 219 | × 220 | Δ 221 | ■ 222 | ▲ 223 | sub 224 | exp 225 | sup 226 | sub 227 | Lex 228 | # 229 | % 230 | & 231 | ' 232 | + 233 | +ξ 234 | ++ 235 | - 236 | -β 237 | < 238 | <± 239 | <Δ 240 | <λ 241 | <φ 242 | << 243 | = 244 | = 245 | =☆ 246 | =- 247 | > 248 | >λ 249 | _ 250 | ~± 251 | ~+ 252 | [⑤f] 253 | [⑤d] 254 | [②i] 255 | ≈ 256 | [②G] 257 | [①f] 258 | LI 259 | ㈧ 260 | [- 261 | ...... 262 | 〉 263 | [③⑩] 264 | 第二 265 | 一番 266 | 一直 267 | 一个 268 | 一些 269 | 许多 270 | 种 271 | 有的是 272 | 也就是说 273 | 末##末 274 | 啊 275 | 阿 276 | 哎 277 | 哎呀 278 | 哎哟 279 | 唉 280 | 俺 281 | 俺们 282 | 按 283 | 按照 284 | 吧 285 | 吧哒 286 | 把 287 | 罢了 288 | 被 289 | 本 290 | 本着 291 | 比 292 | 比方 293 | 比如 294 | 鄙人 295 | 彼 296 | 彼此 297 | 边 298 | 别 299 | 别的 300 | 别说 301 | 并 302 | 并且 303 | 不比 304 | 不成 305 | 不单 306 | 不但 307 | 不独 308 | 不管 309 | 不光 310 | 不过 311 | 不仅 312 | 不拘 313 | 不论 314 | 不怕 315 | 不然 316 | 不如 317 | 不特 318 | 不惟 319 | 不问 320 | 不只 321 | 朝 322 | 朝着 323 | 趁 324 | 趁着 325 | 乘 326 | 冲 327 | 除 328 | 除此之外 329 | 除非 330 | 除了 331 | 此 332 | 此间 333 | 此外 334 | 从 335 | 从而 336 | 打 337 | 待 338 | 但 339 | 但是 340 | 当 341 | 当着 342 | 到 343 | 得 344 | 的 345 | 的话 346 | 等 347 | 等等 348 | 地 349 | 第 350 | 叮咚 351 | 对 352 | 对于 353 | 多 354 | 多少 355 | 而 356 | 而况 357 | 而且 358 | 而是 359 | 而外 360 | 而言 361 | 而已 362 | 尔后 363 | 反过来 364 | 反过来说 365 | 反之 366 | 非但 367 | 非徒 368 | 否则 369 | 嘎 370 | 嘎登 371 | 该 372 | 赶 373 | 个 374 | 各 375 | 各个 376 | 各位 377 | 各种 378 | 各自 379 | 给 380 | 根据 381 | 跟 382 | 故 383 | 故此 384 | 固然 385 | 关于 386 | 管 387 | 归 388 | 果然 389 | 果真 390 | 过 391 | 哈 392 | 哈哈 393 | 呵 394 | 和 395 | 何 396 | 何处 397 | 何况 398 | 何时 399 | 嘿 400 | 哼 401 | 哼唷 402 | 呼哧 403 | 乎 404 | 哗 405 | 还是 406 | 还有 407 | 换句话说 408 | 换言之 409 | 或 410 | 或是 411 | 或者 412 | 极了 413 | 及 414 | 及其 415 | 及至 416 | 即 417 | 即便 418 | 即或 419 | 即令 420 | 即若 421 | 即使 422 | 几 423 | 几时 424 | 己 425 | 既 426 | 既然 427 | 既是 428 | 继而 429 | 加之 430 | 假如 431 | 假若 432 | 假使 433 | 鉴于 434 | 将 435 | 较 436 | 较之 437 | 叫 438 | 接着 439 | 结果 440 | 借 441 | 紧接着 442 | 进而 443 | 尽 444 | 尽管 445 | 经 446 | 经过 447 | 就 448 | 就是 449 | 就是说 450 | 据 451 | 具体地说 452 | 具体说来 453 | 开始 454 | 开外 455 | 靠 456 | 咳 457 | 可 458 | 可见 459 | 可是 460 | 可以 461 | 况且 462 | 啦 463 | 来 464 | 来着 465 | 离 466 | 例如 467 | 哩 468 | 连 469 | 连同 470 | 两者 471 | 了 472 | 临 473 | 另 474 | 另外 475 | 另一方面 476 | 论 477 | 嘛 478 | 吗 479 | 慢说 480 | 漫说 481 | 冒 482 | 么 483 | 每 484 | 每当 485 | 们 486 | 莫若 487 | 某 488 | 某个 489 | 某些 490 | 拿 491 | 哪 492 | 哪边 493 | 哪儿 494 | 哪个 495 | 哪里 496 | 哪年 497 | 哪怕 498 | 哪天 499 | 哪些 500 | 哪样 501 | 那 502 | 那边 503 | 那儿 504 | 那个 505 | 那会儿 506 | 那里 507 | 那么 508 | 那么些 509 | 那么样 510 | 那时 511 | 那些 512 | 那样 513 | 乃 514 | 乃至 515 | 呢 516 | 能 517 | 你 518 | 你们 519 | 您 520 | 宁 521 | 宁可 522 | 宁肯 523 | 宁愿 524 | 哦 525 | 呕 526 | 啪达 527 | 旁人 528 | 呸 529 | 凭 530 | 凭借 531 | 其 532 | 其次 533 | 其二 534 | 其他 535 | 其它 536 | 其一 537 | 其余 538 | 其中 539 | 起 540 | 起见 541 | 起见 542 | 岂但 543 | 恰恰相反 544 | 前后 545 | 前者 546 | 且 547 | 然而 548 | 然后 549 | 然则 550 | 让 551 | 人家 552 | 任 553 | 任何 554 | 任凭 555 | 如 556 | 如此 557 | 如果 558 | 如何 559 | 如其 560 | 如若 561 | 如上所述 562 | 若 563 | 若非 564 | 若是 565 | 啥 566 | 上下 567 | 尚且 568 | 设若 569 | 设使 570 | 甚而 571 | 甚么 572 | 甚至 573 | 省得 574 | 时候 575 | 什么 576 | 什么样 577 | 使得 578 | 是 579 | 是的 580 | 首先 581 | 谁 582 | 谁知 583 | 顺 584 | 顺着 585 | 似的 586 | 虽 587 | 虽然 588 | 虽说 589 | 虽则 590 | 随 591 | 随着 592 | 所 593 | 所以 594 | 他 595 | 他们 596 | 他人 597 | 它 598 | 它们 599 | 她 600 | 她们 601 | 倘 602 | 倘或 603 | 倘然 604 | 倘若 605 | 倘使 606 | 腾 607 | 替 608 | 通过 609 | 同 610 | 同时 611 | 哇 612 | 万一 613 | 往 614 | 望 615 | 为 616 | 为何 617 | 为了 618 | 为什么 619 | 为着 620 | 喂 621 | 嗡嗡 622 | 我 623 | 我们 624 | 呜 625 | 呜呼 626 | 乌乎 627 | 无论 628 | 无宁 629 | 毋宁 630 | 嘻 631 | 吓 632 | 相对而言 633 | 像 634 | 向 635 | 向着 636 | 嘘 637 | 呀 638 | 焉 639 | 沿 640 | 沿着 641 | 要 642 | 要不 643 | 要不然 644 | 要不是 645 | 要么 646 | 要是 647 | 也 648 | 也罢 649 | 也好 650 | 一 651 | 一般 652 | 一旦 653 | 一方面 654 | 一来 655 | 一切 656 | 一样 657 | 一则 658 | 依 659 | 依照 660 | 矣 661 | 以 662 | 以便 663 | 以及 664 | 以免 665 | 以至 666 | 以至于 667 | 以致 668 | 抑或 669 | 因 670 | 因此 671 | 因而 672 | 因为 673 | 哟 674 | 用 675 | 由 676 | 由此可见 677 | 由于 678 | 有 679 | 有的 680 | 有关 681 | 有些 682 | 又 683 | 于 684 | 于是 685 | 于是乎 686 | 与 687 | 与此同时 688 | 与否 689 | 与其 690 | 越是 691 | 云云 692 | 哉 693 | 再说 694 | 再者 695 | 在 696 | 在下 697 | 咱 698 | 咱们 699 | 则 700 | 怎 701 | 怎么 702 | 怎么办 703 | 怎么样 704 | 怎样 705 | 咋 706 | 照 707 | 照着 708 | 者 709 | 这 710 | 这边 711 | 这儿 712 | 这个 713 | 这会儿 714 | 这就是说 715 | 这里 716 | 这么 717 | 这么点儿 718 | 这么些 719 | 这么样 720 | 这时 721 | 这些 722 | 这样 723 | 正如 724 | 吱 725 | 之 726 | 之类 727 | 之所以 728 | 之一 729 | 只是 730 | 只限 731 | 只要 732 | 只有 733 | 至 734 | 至于 735 | 诸位 736 | 着 737 | 着呢 738 | 自 739 | 自从 740 | 自个儿 741 | 自各儿 742 | 自己 743 | 自家 744 | 自身 745 | 综上所述 746 | 总的来看 747 | 总的来说 748 | 总的说来 749 | 总而言之 750 | 总之 751 | 纵 752 | 纵令 753 | 纵然 754 | 纵使 755 | 遵照 756 | 作为 757 | 兮 758 | 呃 759 | 呗 760 | 咚 761 | 咦 762 | 喏 763 | 啐 764 | 喔唷 765 | 嗬 766 | 嗯 767 | 嗳 768 | -------------------------------------------------------------------------------- /data_analysis.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | from scipy.stats import norm 5 | import numpy as np 6 | import jieba 7 | from wordcloud import WordCloud 8 | from collections import Counter 9 | import re 10 | import july 11 | from july.utils import date_range 12 | # 设置中文字体 13 | plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体为黑体 14 | plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 15 | 16 | # 读取CSV文件 17 | df = pd.read_csv('你的csv文件路径') 18 | 19 | # TODO 数据预处理(根据需求进行) 20 | # 只保留文本聊天 21 | df = df[df['Type'] == 1] 22 | 23 | # 只取'IsSender','StrContent','StrTime'列 24 | selected_columns = ['IsSender', 'StrContent', 'StrTime'] # 将要保留的列名放入一个列表中 25 | df = df[selected_columns] 26 | # TODO 每天聊天频率柱状图 27 | # 将StrTime列的数据转换为日期时间格式 28 | df['StrTime'] = pd.to_datetime(df['StrTime']) 29 | 30 | # 创建一个新的Date列,只保留日期部分 31 | df['Date'] = df['StrTime'].dt.date 32 | 33 | # 根据每一天统计聊天频率 34 | chat_frequency = df['Date'].value_counts().sort_index() 35 | 36 | # 生成柱状图 37 | chat_frequency.plot(kind='bar', color='#DF9F9B') 38 | plt.xlabel('Date') 39 | plt.ylabel('Frequency') 40 | plt.title('Chat Frequency by Day') 41 | 42 | # 调整刻度标签,只保留月份和日期 43 | date_labels = [date.strftime('%m-%d') for date in chat_frequency.index] # 格式化日期 44 | plt.xticks(range(len(date_labels)), date_labels) # 设置新的刻度标签 45 | plt.xticks(fontsize=5) 46 | plt.show() 47 | 48 | # # TODO 制作日历热力图 (此部分需将matplotlib版本降级为3.7.0,与july兼容) 49 | # # 将日期列转换为datetime类型 50 | # df['Date'] = pd.to_datetime(df['Date']) 51 | # 52 | # # 获取日期范围 53 | # start_date = df['Date'].min() 54 | # end_date = df['Date'].max() 55 | # dates = date_range(start_date, end_date) 56 | # july.calendar_plot(dates, chat_frequency, cmap = 'Oranges') 57 | # plt.show() 58 | 59 | 60 | # july.month_plot(dates, chat_frequency, month=1, date_label=True) 61 | # plt.show() 62 | 63 | # TODO 双方信息数量对比 64 | # 双方发送的聊天信息 65 | sent_by_me = df[df['IsSender'] == 1]['StrContent'] 66 | sent_by_others = df[df['IsSender'] == 0]['StrContent'] 67 | # 统计数量 68 | count_sent_by_me = len(sent_by_me) 69 | count_sent_by_others = len(sent_by_others) 70 | # 创建饼状图 71 | labels = ['西红柿', '头头'] 72 | sizes = [count_sent_by_me, count_sent_by_others] 73 | colors = ['#FF6347','#9ACD32'] 74 | 75 | explode = (0, 0.05) 76 | plt.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90) 77 | 78 | plt.axis('equal') 79 | plt.title('Comparison of the number of chats') 80 | 81 | # 添加图例 82 | plt.legend() 83 | # 显示图表 84 | plt.show() 85 | 86 | 87 | # TODO 根据一天中的每一个小时进行统计聊天频率,并生成柱状图 88 | # 将时间字符串转换为时间类型并提取小时 89 | df['DateTime'] = pd.to_datetime(df['StrTime']) 90 | df['Hour'] = df['DateTime'].dt.hour 91 | 92 | # 统计每个小时的聊天频率 93 | hourly_counts = df['Hour'].value_counts().sort_index().reset_index() 94 | hourly_counts.columns = ['Hour', 'Frequency'] 95 | 96 | # 绘制柱状图和数据拟合曲线 97 | plt.figure(figsize=(10, 6)) 98 | ax = sns.barplot(x='Hour', y='Frequency', data=hourly_counts, color="#E6AAAA") 99 | 100 | # 添加核密度估计曲线 101 | sns.kdeplot(df['Hour'], color='#C64F4F', linewidth=1, ax=ax.twinx()) 102 | 103 | # 设置图形标题和轴标签 104 | plt.title('Chat Frequency by Hour') 105 | plt.xlabel('Hour of the Day') 106 | plt.ylabel('Frequency') 107 | 108 | # 显示图形 109 | plt.show() 110 | # TODO 词频分析 111 | sent_by_me_text = ' '.join(sent_by_me.astype(str)) 112 | sent_by_others_text=' '.join(sent_by_others.astype(str)) 113 | all_text = ' '.join(df['StrContent'].astype(str)) 114 | # 使用jieba进行中文分词 115 | words = list(jieba.cut(all_text, cut_all=False)) 116 | mywords = list(jieba.cut(sent_by_me_text, cut_all=False)) 117 | herwords = list(jieba.cut(sent_by_others_text, cut_all=False)) 118 | 119 | 120 | def is_chinese_word(word): 121 | for char in word: 122 | if not re.match(r'[\u4e00-\u9fff]', char): 123 | return False 124 | return True 125 | with open('stopwords_hit.txt', encoding='utf-8') as f: # 可根据需要打开停用词库,然后加上不想显示的词语 126 | con = f.readlines() 127 | stop_words = set() # 集合可以去重 128 | for i in con: 129 | i = i.replace("\n", "") # 去掉读取每一行数据的\n 130 | stop_words.add(i) 131 | stop_words 132 | 133 | def correct(a): 134 | b=[] 135 | for word in a: 136 | if len(word) > 1 and is_chinese_word(word) and word not in stop_words: 137 | b.append(word) 138 | return b 139 | 140 | Words = correct(words) 141 | Mywords = correct(words) 142 | Herwords = correct(herwords) 143 | 144 | words_space_split = ' '.join(Words) 145 | print(words_space_split) 146 | def word_fre_draw(a): 147 | a_counts = Counter(a) 148 | top_30_a= a_counts.most_common(30) 149 | words, frequencies = zip(*top_30_a) 150 | 151 | # 绘制水平柱状图 152 | plt.figure(figsize=(10, 15)) 153 | plt.barh(words, frequencies, color='skyblue') 154 | plt.xlabel('Frequency') 155 | plt.ylabel('Words') 156 | plt.title('Top 30 Words in Chat Messages') 157 | plt.show() 158 | 159 | word_fre_draw(Words) 160 | word_fre_draw(Mywords) 161 | word_fre_draw(Herwords) 162 | 163 | # TODO 词云制作 164 | 165 | wordcloud = WordCloud(font_path='‪C:\Windows\Fonts\STCAIYUN.TTF', # 字体路径,例如'SimHei.ttf' 166 | width=800, height=600, 167 | background_color='white', # 背景颜色 168 | max_words=200, # 最大显示的词数 169 | max_font_size=100, # 字体最大值 170 | ).generate(words_space_split) 171 | 172 | # 使用Matplotlib展示词云图 173 | plt.figure(figsize=(10, 8)) 174 | plt.imshow(wordcloud, interpolation='bilinear') 175 | plt.axis('off') # 关闭坐标轴 176 | plt.show() 177 | 178 | #TODO 一周贡献率 179 | df['Weekday'] = df['StrTime'].dt.day_name() 180 | 181 | # 计算每天的消息数量 182 | weekday_counts = df['Weekday'].value_counts().reindex([ 183 | "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" 184 | ]) 185 | 186 | # 找出频率最高的那天 187 | max_day = weekday_counts.idxmax() 188 | 189 | # 制作饼状图 190 | plt.figure(figsize=(8, 8)) 191 | explode = [0.1 if day == max_day else 0 for day in weekday_counts.index] # 突出显示频率最高的那天 192 | plt.pie(weekday_counts, labels=weekday_counts.index, explode=explode, autopct='%1.1f%%', 193 | startangle=140, colors=plt.cm.Paired.colors) 194 | plt.title('Distribution of Messages During the Week') 195 | plt.show() 196 | 197 | # TODO 最多的天数及月份 198 | df['Date'] = pd.to_datetime(df['Date']) 199 | 200 | # 提取年月日 201 | df['YearMonth'] = df['Date'].dt.to_period('M') 202 | df['Day'] = df['Date'].dt.date 203 | 204 | # 计算每天的消息数量 205 | daily_counts = df['Day'].value_counts() 206 | 207 | # 找出消息最多的那一天 208 | max_day = daily_counts.idxmax() 209 | max_day_count = daily_counts.max() 210 | 211 | # 计算每月的消息数量 212 | monthly_counts = df['YearMonth'].value_counts() 213 | 214 | # 找出消息最多的那个月 215 | max_month = monthly_counts.idxmax() 216 | max_month_count = monthly_counts.max() 217 | 218 | # 打印结果 219 | print(f"最多消息的一天是 {max_day},共有 {max_day_count} 条消息。") 220 | print(f"最多消息的一个月是 {max_month},共有 {max_month_count} 条消息。") 221 | 222 | 223 | # 计算消息总数 224 | total_messages = len(df) 225 | 226 | # 打印消息总数 227 | print(f"一共聊了 {total_messages} 条消息。") --------------------------------------------------------------------------------