前期,我們將微信聊天記錄匯出後,並選擇微信群聊天記錄作為分析物件,進行了一些預處理工作後,從發言頻次和發言型別進行了簡單的分析。本次,我們從微信群聊的時間偏好進行分析,並選定記錄最多的群聊分析該群中最受關注的人物,最後,對文字訊息進行分詞並繪製詞雲。
與上期一樣,本次資料分析工作在 Jupyter 環境下開展,使用到的庫主要有:
- pandas
- numpy
- matplotlib
- seaborn
- jieba
- wordcloud
- collections
1 聊天的時間偏好
即通過對微信群中不同時間的發言頻次進行統計分析。
# 後續分析需要用到的新庫
from collections import Counter
import jieba
import jieba.analyse
from wordcloud import WordCloud
首先匯入需要使用到的庫並進行簡單的設定。
print('Min Time: ', message.index.min())
print('Max Time: ', message.index.max())
print('Total Message: ', message['real_content'].count())
Min Time: 2018-08-11 13:52:14+08:00
Max Time: 2020-02-16 11:38:21+08:00
Total Message: 155407
即本次分析的微信群聊天記錄發言時間在 2018-08-11 到 2020-02-06 之間,共計 155407 條記錄,下面將從三個時間進度對發言頻次進行分析:
- 按 ‘%Y-%m’ 精度進行頻次統計;
- 按 ‘%w’ 精度進行頻次統計;
- 按 ‘%H’ 進度進行頻次統計。
1.1 按年月的發言統計
month_count = message['real_content'].resample('M', kind='period').count()
month_count.head()
createTime
2018-08 1064
2018-09 4092
2018-10 3214
2018-11 3116
2018-12 4376
Freq: M, Name: real_content, dtype: int64
fig, ax = plt.subplots(figsize=(9, 6))
month_count.plot(kind='bar', ax = ax)
也即,我們可以明顯得看出從 2019 年 7 月開始,微信群聊天的記錄明顯增加 ~ 實際上是自己加入了一些很活躍的聊天群的緣故。
1.2 每週發言情況
weekday_count = message.groupby(lambda x: x.weekday_name)['real_content'].count()
weekday_count
Friday 27563
Monday 25577
Saturday 12692
Sunday 10291
Thursday 26028
Tuesday 27318
Wednesday 25938
Name: real_content, dtype: int64
weekday_per = weekday_count / weekday_count.sum() * 100
ax = weekday_per.plot.pie(figsize=(6, 6), autopct='%.2f')
ax.set_ylabel('')
plt.title('微信群中一週發言情況')
plt.show()
也即,整體而言,微信群中的發言活躍程度工作日明顯高於週末。
1.3 一天內發言情況
hour_count = message.groupby(message.index.hour)['real_content'].count()
hour_count
createTime
0 1994
1 169
2 155
3 15
4 18
...
19 8381
20 7666
21 5600
22 5791
23 3902
Name: real_content, Length: 24, dtype: int64
fig = plt.figure(figsize=(10, 8))
plt.step(hour_count.index.values, hour_count.values, where='post')
plt.plot(hour_count.index.values, hour_count.values, 'o--')
plt.xticks(hour_count.index)
plt.xlabel('發言時間')
plt.ylabel('發言次數')
plt.title('微信群中每天發言情況')
plt.show()
我們發現兩個在微信群中發言的高峰期分別出現在 11-12 點以及 17-18 點,大致是在飯點左右,哈哈。
當然,上述三種基於時間的發言計次都是基於所有微信群的,如果要對特定的微信群的聊天時間習慣進行分析,只需將 message.talker
限定為指定的微信群即可。
2 微信群裡最受關注的人物
微信群的聊天記錄,我們可以依據「聊天是否連續」進行分段,當某個人參與到這段聊天后,這段聊天參與的人數明顯增多、聊天記錄總量明顯變多。我們就認為,這個人在群裡受到比較多的關注。
target_group = message.groupby('talker')['real_content'].count().idxmax()
target_message = message[message.talker == target_group]
print('聊天記錄最多的微信群:', target_group)
print(' 該群聊天記錄總數:', target_message['real_content'].count())
聊天記錄最多的微信群: 12***92@chatroom
該群聊天記錄總數: 79606
2.1 聊天記錄「分段」
選定聊天記錄最多的群進行分析,將兩次聊天的時間差作為 target_message
的新列 diff_second
,設定為 60 秒(實際上就是不太好找拆分的時間點隨便選的)。
diff_second = (target_message.index[1:] - target_message.index[0:-1]).values / 1e9
diff_second = diff_second.astype('d')
target_message['diff_second'] = np.concatenate(([0.], diff_second))
len(diff_second[diff_second <= 60]) / target_message['real_content'].count()
0.8264578046880888
兩次聊天間隔在 60 秒以內佔比達到 82.65% 左右,拆分時也具備一定的合理性。
target_message['talk_segid'] = np.where(target_message['diff_second'] <= 60, 0, 1).cumsum()
target_message = target_message[['isSend', 'message_type', 'username', 'real_content',
'diff_second', 'talk_segid']]
target_message.talk_segid.value_counts().value_counts().sort_index()
1 5370
2 2722
3 1447
4 883
5 539
...
371 1
378 1
527 1
638 1
925 1
Name: talk_segid, Length: 146, dtype: int64
藉助於 cumsum()
實現分段標記,並選取後續要用到的資料列。也即,有 5370 次聊天與上一句聊天和下一句聊天的時間差均超過 60 秒,佔聊天總記錄數的 6.75% 左右。連續聊天最長在 925 句。
2.2 發言影響力計算
def impact_factor(df):
'''
計算一段發言中每個人的影響力
'''
impact_list = []
if len(df) == 1:
impact_list.append([df.username.iloc[0], 0, 0])
else:
names = df.username.unique()
for name in names:
# 第一次發言的位置
first_index = np.argmax(df.username == name) - df.index.min()
diff_people = len(df[first_index+1:].username.unique()) - len(df[:first_index+1].username.unique())
diff_talks = len(df) - first_index*2 - 1
impact_list.append([name, diff_people, diff_talks])
# print('第 {} 段對話處理完成'.format(df.talk_segid.iloc[0]))
return pd.DataFrame(impact_list, columns=['username', 'diff_people', 'diff_talks'])
計算一個人在這段聊天中第一次發言前後「參與發言人數變化、發言數量變化」。
- 計算髮言前後參與聊天的人數差;
- 計算髮言前後聊天總量的差;
說明:此處發言前後以一個人在該段聊天中第一次發言為準;聊天密度採用聊天時間間隔平均值。
impact = target_message.reset_index().groupby('talk_segid').apply(impact_factor)
impact_people_mean = impact.groupby('username')['diff_people'].mean()
impact_talks_mean = impact.groupby('username')['diff_talks'].mean()
talks = target_message[['username', 'talk_segid']].drop_duplicates().groupby('username')['talk_segid'].count()
分別計算參與聊天的每個人在所有聊天的參與次數 talks
,參與每段聊天影響的人數差的均值 impact_people_mean
,參與每段聊天影響的聊天記錄的均值 impact_talks_mean
。
talks = pd.merge(pd.merge(talks, impact_people_mean, left_index=True, right_index=True),
impact_talks_mean, left_index=True, right_index=True)
talks.columns = ['total_talks', 'diff_people_mean', 'diff_talks_mean']
talks
username | total_talks | diff_people_mean | diff_talks_mean |
---|---|---|---|
FJ***21 | 20 | -0.950000 | -5.900000 |
Ha***73 | 32 | -0.093750 | 1.500000 |
Hx***55 | 1166 | 0.845626 | 9.343911 |
JA***37 | 76 | -0.828947 | 0.763158 |
JJ***er | 4 | -2.500000 | -1.500000 |
… | … | … | … |
zj***mc | 1 | 11.000000 | 37.000000 |
zl***69 | 186 | 0.032258 | 2.365591 |
zs***24 | 11 | 0.727273 | 7.090909 |
zs***41 | 43 | 0.232558 | 8.209302 |
zw***247459 | 14 | -0.785714 | -0.214286 |
313 rows × 3 columns
2.3 最受關注人選
fig = plt.figure(figsize=(10, 8))
talks['sqrt_total_talks'] = np.sqrt(talks.total_talks)
sns.scatterplot(x='sqrt_total_talks', y='diff_people_mean', hue='diff_people_mean',
size='diff_people_mean', data=talks)
plt.annotate('target', xy=(5, 10), xytext=(10, 15),
arrowprops=dict(width=1, headwidth=5))
plt.show()
因為群中部分成員很活躍,因此將參與的「聊天段」開方後畫散點圖。
實際上,計算髮言引起的人數差和聊天量差都有一個明顯的 bug
~ 如果一個人發言次數很少,並且緊隨受大家關注的人發言(或者在熱點話題討論時發言較早),那麼相應的 diff_people_mean, diff_talks_mean
值就比較大。
發言次數較多後,發言人是否受到大家的關注用 diff_people_mean, diff_talks_mean
來度量就有一定的合理性了,如兩圖中 target
點。
fig = plt.figure(figsize=(10, 8))
sns.scatterplot(x='sqrt_total_talks', y='diff_talks_mean', hue='diff_talks_mean',
size='diff_talks_mean', data=talks)
plt.annotate('target', xy=(5, 95), xytext=(10, 150),
arrowprops=dict(width=1, headwidth=5))
plt.show()
print('發言五次及以上最受關注人:', talks[talks.total_talks >= 5]['diff_people_mean'].idxmax())
print('發言五次及以上話題引領人:', talks[talks.total_talks >= 5]['diff_talks_mean'].idxmax())
發言五次及以上最受關注人: ad****47
發言五次及以上話題引領人: ad****47
也即,'ad****47'
發言後發言的人和發言量都比較多 ~ 實際上這個群也是因他而建立的,與預期相符。
talks.loc['ad****47']
total_talks 23.000000
diff_people_mean 9.695652
diff_talks_mean 92.565217
sqrt_total_talks 4.795832
Name: ad814156147, dtype: float64
也即,'ad***47'
總共參與 23 次聊天,平均每次聊天后有 9 個多人會出來冒泡、讓發言量增加 92 次左右。
最終評選出 'ad****47'
為「整個群裡最靚的崽」!
3 聊天的用詞偏好
最後,我們利用 target_message
中的聊天記錄簡單做個詞雲。主要使用 jieba
進行分詞,使用 wordcloud
庫繪製詞雲圖。
3.1 分詞
chats = target_message[target_message.message_type == '文字']['real_content']
chats
createTime
2019-07-01 22:04:12+08:00 昨天的圖片還在嗎
2019-07-01 22:04:17+08:00 各位大佬們
2019-07-01 22:10:03+08:00 出現了
2019-07-01 22:10:20+08:00 ????
2019-07-01 22:10:28+08:00 謝謝
...
2020-02-16 10:30:54+08:00 ??
2020-02-16 10:38:48+08:00 嗯
2020-02-16 10:43:05+08:00 我感覺我回不去了
2020-02-16 11:33:02+08:00 健康碼就能進,也不用隔離了。
2020-02-16 11:36:42+08:00 綠的健康碼就可以了
Name: real_content, Length: 69171, dtype: object
進行分詞時僅選擇 '文字'
類的聊天記錄。
為新增微信表情所轉化成的文字新增到字典中,如 '[捂臉], [奸笑], [笑哭]'
等,需對 jieba 庫的 __init__.py
檔案進行修改,具體可參見 https://github.com/fxsjy/jieba/issues/423.
jieba.load_userdict('wechat_emoji_dict.txt')
jieba.analyse.set_stop_words('stop_words.txt')
Building prefix dict from the default dictionary ...
Dumping model to file cache /var/folders/bf/yt56193d35lbzfh4rn_xxct40000gn/T/jieba.cache
Loading model cost 0.966 seconds.
Prefix dict has been built successfully.
在詞典中新增微信表情符,並去除掉一些停用詞,藉助於 collections.Counter
簡化詞頻統計。
word_counts = Counter()
for chat in chats:
word_counts.update(jieba.analyse.extract_tags(chat))
word_counts = pd.Series(word_counts)
word_counts.sort_values().tail()
不是 1402
這個 1787
[奸笑] 1818
可以 1825
[捂臉] 2783
dtype: int64
3.2 詞雲製作
藉助於 wordcloud 繪製詞頻最高的兩百個詞的詞雲。
wc = WordCloud(font_path='/fontpath/simhei.ttf', background_color="white", repeat=False)
wc.fit_words(word_counts.sort_values()[-200:])
fig, ax = plt.subplots(figsize=(10, 8))
ax.axis('off')
ax.imshow(wc)
那我們微信群聊天記錄分析就到這裡啦。
4 參考資料
- 韋陽的部落格, 韋陽, 微信聊天記錄匯出為電腦txt檔案教程, 2020/3/2.
- hangcom寫字的地方, hangcom, 微信聊天記錄匯出–釋出, 2020/3/2.
- Asher117, 【Python】DataFrame一列拆成多列以及一行拆成多行, 2020/3/2.
- 風淺安然, Matplotlib及Seaborn中文顯示問題, 2020/3/2.
- alpiny, 【分享】好多人需要的:關鍵詞帶空格和特殊字元方法~~, 2020/3/9.
本作品採用《CC 協議》,轉載必須註明作者和本文連結