AS07 Collocation for finding enthusiastic commentors#
在文字探勘的第二單元我們介紹了Collocation來找出significant word pair。同樣的方法,我也可以把他改造來找出,在討論板上哪兩個人老是一起出現。你可以想像說在一個討論板中,某一主題一出來,某些人就會突然冒出來開始Comment。並且A下了Comment後,很快的B也會跟著下Comment。我們可以用Collocation的概念來找出這些總是一起出現的Commentors。但社會科學會把這樣的關係稱為Cooccurrence(共現)。
Convert Collocation as Network#
# colab
import pickle
# !wget https://github.com/P4CSS/PSS/raw/master/data/pttpost_20210509_n178.dat -O pttpost_20210509_n178.dat
with open("data/pttpost_20210509_n178.dat", "rb") as fin:
all_post = pickle.load(fin)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[1], line 4
2 import pickle
3 # !wget https://github.com/P4CSS/PSS/raw/master/data/pttpost_20210509_n178.dat -O pttpost_20210509_n178.dat
----> 4 with open("data/pttpost_20210509_n178.dat", "rb") as fin:
5 all_post = pickle.load(fin)
File ~/anaconda3/lib/python3.10/site-packages/IPython/core/interactiveshell.py:282, in _modified_open(file, *args, **kwargs)
275 if file in {0, 1, 2}:
276 raise ValueError(
277 f"IPython won't let you open fd={file} by default "
278 "as it is likely to crash IPython. If you know what you are doing, "
279 "you can use builtins' open."
280 )
--> 282 return io_open(file, *args, **kwargs)
FileNotFoundError: [Errno 2] No such file or directory: 'data/pttpost_20210509_n178.dat'
import pandas as pd
df = pd.DataFrame(all_post)
post_df = df[['author', 'title', 'content', 'authorid', 'nickname', 'link', 'timestamp']]
import sqlite3
conn = sqlite3.connect('data/pttpost.db')
post_df.to_sql('posts', conn, if_exists='replace', index=False)
178
print(all_post[0].keys())
print(all_post[0]['comments'][0])
all_post[0]
dict_keys(['author', 'authorid', 'nickname', 'link', 'title', 'timestamp', 'content', 'comments'])
{'tag': '推 ', 'userid': 'shiriri', 'content': ': 有很多了 黑人跟拉丁裔比較高 亞裔跟白人差不多', 'timestamp': ' 05/09 10:59\n'}
{'author': 's72005ming (QQ)',
'authorid': 's72005ming',
'nickname': 'QQ',
'link': 'https://www.ptt.cc/bbs/Gossiping/M.1620528765.A.DBD.html',
'title': '[問卦] 美國會研究因武漢肺炎死亡的人種嗎?',
'timestamp': 'Sun May 9 10:52:43 2021',
'content': '美國是民族大熔爐\n\n全世界各種人種都有\n\n但是依照各個人種做研究又有歧視的問題\n\n以美國這麼重視人權和觀感的國家\n\n會有研究因武漢肺炎死亡人種的論文嗎?\n\n\n好奇如果有哪一個人種死亡數最少\n\n我猜是華裔就是了!\n\n聽說疫情發生後一堆華裔老人連出門剪頭髮都不敢!--',
'comments': [{'tag': '推 ',
'userid': 'shiriri',
'content': ': 有很多了 黑人跟拉丁裔比較高 亞裔跟白人差不多',
'timestamp': ' 05/09 10:59\n'},
{'tag': '→ ',
'userid': 'HELLDIVER',
'content': ': 有趣的是 剛開始大爆發時 還說亞洲人比較會得武漢病毒',
'timestamp': ' 05/09 11:01\n'},
{'tag': '噓 ',
'userid': 'redsa12',
'content': ': 網路即時數據就都有按人種按年齡區分的數據了...',
'timestamp': ' 05/09 11:06\n'},
{'tag': '→ ',
'userid': 'redsa12',
'content': ': 問之前先估狗好嗎 加油好嗎',
'timestamp': ' 05/09 11:06\n'}]}
# jupyterlab
# import pickle
# # with open("../data/pttpost_20210509_n178.dat", "rb") as fin:
# all_post = pickle.load(fin)
print(len(all_post))
print(all_post[0])
print("-"*80)
for comment in all_post[5]['comments']:
print(comment)
178
{'author': 's72005ming (QQ)', 'authorid': 's72005ming', 'nickname': 'QQ', 'link': 'https://www.ptt.cc/bbs/Gossiping/M.1620528765.A.DBD.html', 'title': '[問卦] 美國會研究因武漢肺炎死亡的人種嗎?', 'timestamp': 'Sun May 9 10:52:43 2021', 'content': '美國是民族大熔爐\n\n全世界各種人種都有\n\n但是依照各個人種做研究又有歧視的問題\n\n以美國這麼重視人權和觀感的國家\n\n會有研究因武漢肺炎死亡人種的論文嗎?\n\n\n好奇如果有哪一個人種死亡數最少\n\n我猜是華裔就是了!\n\n聽說疫情發生後一堆華裔老人連出門剪頭髮都不敢!--', 'comments': [{'tag': '推 ', 'userid': 'shiriri', 'content': ': 有很多了 黑人跟拉丁裔比較高 亞裔跟白人差不多', 'timestamp': ' 05/09 10:59\n'}, {'tag': '→ ', 'userid': 'HELLDIVER', 'content': ': 有趣的是 剛開始大爆發時 還說亞洲人比較會得武漢病毒', 'timestamp': ' 05/09 11:01\n'}, {'tag': '噓 ', 'userid': 'redsa12', 'content': ': 網路即時數據就都有按人種按年齡區分的數據了...', 'timestamp': ' 05/09 11:06\n'}, {'tag': '→ ', 'userid': 'redsa12', 'content': ': 問之前先估狗好嗎 加油好嗎', 'timestamp': ' 05/09 11:06\n'}]}
--------------------------------------------------------------------------------
{'tag': '推 ', 'userid': 'bignoob', 'content': ': 邊抱怨邊打啊 台灣人的不都這樣', 'timestamp': ' 05/08 14:32\n'}
{'tag': '→ ', 'userid': 'ethan0419', 'content': ': 好騙', 'timestamp': ' 05/08 14:32\n'}
{'tag': '噓 ', 'userid': 'geesegeese', 'content': ': 感染源不明出現了,你不打我先打', 'timestamp': ' 05/08 14:33\n'}
{'tag': '→ ', 'userid': 's3z15a3z15a', 'content': ': 沒人搶得時候不急,現在人多就開始搶了', 'timestamp': ' 05/08 14:33\n'}
{'tag': '→ ', 'userid': 'ckvir', 'content': ': 你怎知抱怨的人和打的人是同一個?', 'timestamp': ' 05/08 14:33\n'}
{'tag': '推 ', 'userid': 'clv', 'content': ': 為什麼 就跟當初口罩不夠用有人拿內褲當口罩一樣啊', 'timestamp': ' 05/08 14:34\n'}
{'tag': '→ ', 'userid': 'clv', 'content': ': 怕死啊 沒有最好 只好勉強啦', 'timestamp': ' 05/08 14:34\n'}
{'tag': '噓 ', 'userid': 'Dia149', 'content': ': 以前的我ok你先打呢', 'timestamp': ' 05/08 14:41\n'}
{'tag': '→ ', 'userid': 'Dia149', 'content': ': 綠共真的很善變', 'timestamp': ' 05/08 14:41\n'}
{'tag': '推 ', 'userid': 'radi035', 'content': ': 早在爆發前就先打了 不過小道消息月底莫德那會進來', 'timestamp': ' 05/08 14:44\n'}
{'tag': '→ ', 'userid': 'radi035', 'content': ': 所以符合公費施打的人 可以忍忍 等莫德納', 'timestamp': ' 05/08 14:44\n'}
1. Collocation as Cooccurrence#
只要在同一則貼文的comments內,我們把任兩個commentor視為有co-comment,也就是Cooccurrence(共現)的關係。請計算出共現於本資料集中,頻率最高的前20對commentor(必須印出Collocation times作為參考)。
s72005ming sl11pman 450
loham sl11pman 450
sl11pman s72005ming 450
sl11pman loham 450
cwh0105 sl11pman 360
sl11pman cwh0105 360
iampig951753 Runna 294
Runna iampig951753 294
frank355571 sl11pman 270
sl11pman frank355571 270
NICEGOGO sl11pman 180
sl11pman NICEGOGO 180
sl11pman dawson0130 180
sl11pman userlance 180
sl11pman carryton 180
sl11pman CheshireS 180
sl11pman vic4580849 180
sl11pman justeit 180
dawson0130 sl11pman 180
userlance sl11pman 180
# YOUR CODE SHOULD BE HERE
2. Using MI#
MI的計算方式主要是為了要標準化任一字的出現次數和任兩個字的出現次數的影響。請用MI的方式計算出哪兩個人特別常一起出現在同一則貼文的comments中。請用most_common()
印出M前20大MI的Pairs(必須印出MI值作為參考)。
loham sl11pman 450 5.535381
sl11pman loham 450 5.535381
frank355571 sl11pman 270 5.535381
sl11pman frank355571 270 5.535381
NICEGOGO sl11pman 180 5.535381
sl11pman NICEGOGO 180 5.535381
sl11pman dawson0130 180 5.535381
sl11pman CheshireS 180 5.535381
sl11pman vic4580849 180 5.535381
sl11pman justeit 180 5.535381
dawson0130 sl11pman 180 5.535381
CheshireS sl11pman 180 5.535381
vic4580849 sl11pman 180 5.535381
justeit sl11pman 180 5.535381
goddamnhuge sl11pman 90 5.535381
donyin sl11pman 90 5.535381
crazywiwi sl11pman 90 5.535381
kaerusiro sl11pman 90 5.535381
mmrhahaha sl11pman 90 5.535381
EBOD081 sl11pman 90 5.535381
# YOUR CODE SHOULD BE HERE
3. Cooccurrence with distance#
就上述的資料集,我想定義的人與人的關係是「這兩個人老是一前一後出現」,所以我規劃僅計算前後5則以內的comments,也就是說,在同一貼文中,如果A是第一則comment,B是第六則comment,C是第七則,那我不列計A和C的關係,但列計A和B的關係。請用collocation with distance的觀念,計算任兩個comment間的平均距離,並用most_comment()
列印出平均距離最短的前二十對commentors。
chen0625-Qinsect 1.500000 2
kenryu-bar1005 1.500000 2
bar1005-jetalpha 1.500000 2
typeklng-GARRETH 1.500000 2
GARRETH-fenix220 1.500000 2
KaiManSo-nikewang 1.500000 2
gwenwoo-s359999 1.500000 2
a410046-apatosaurus 1.500000 2
apatosaurus-t934140225 1.500000 2
username1-TsmcEE 1.500000 2
bigwun73-yheb88 1.500000 2
l88-sali921 1.500000 2
ab4daa-cecille 1.500000 2
kingstongyu-ntlutw 1.500000 2
ntlutw-kid1a2b3c4d 1.500000 2
kuan12065-lazarus1121 1.500000 2
show282-kuosambition 1.000000 2
Yonhao-jump693 1.000000 2
sellgd-smalltwo 1.000000 2
lazarus1121-tenka92417 1.000000 2
# YOUR CODE SHOULD BE HERE
Drawing collocation network#
以下已經提供給你部分不同網絡的視覺化方法和參數調整方法。如果你要看懂每個函式有可能要查閱Networkx的Document,不過這是為了push你去查閱document來理解這些程式碼。
列印出mi值或count值前500大、前1000大、前2000大(會有點吃力)的pairs of user,並觀察該圖型。基於co-commentor的網絡視覺化,你認為這群co-commenter有什麼特性?請多列印幾種版本,並將你的看法寫在以下的ANSWER後:
Construct edgelist dataframe#
import pandas as pd
li = [(u1, u2, user_pair_counts[(u1, u2)], mi) for (u1, u2), mi in pmi_scores.most_common()]
df = pd.DataFrame.from_records(li, columns =['u1', 'u2', 'count', 'mi'])
df
u1 | u2 | count | mi | |
---|---|---|---|---|
0 | devilscry | kinghung88 | 6 | 10.892489 |
1 | kinghung88 | devilscry | 6 | 10.892489 |
2 | SwordGod | duoloveyang | 6 | 10.892489 |
3 | duoloveyang | SwordGod | 6 | 10.892489 |
4 | enso | kmaj7 | 6 | 10.892489 |
... | ... | ... | ... | ... |
36349 | s505015 | jma306 | 1 | 2.307526 |
36350 | gt0404 | mudee | 1 | 2.277779 |
36351 | mudee | gt0404 | 1 | 2.277779 |
36352 | marktak | iampig951753 | 1 | 2.130938 |
36353 | iampig951753 | marktak | 1 | 2.130938 |
36354 rows × 4 columns
Filter high frequency pairs#
li = [(u1, u2, n)for (u1, u2), n in user_pair_counts.most_common() if n > 3]
df = pd.DataFrame.from_records(li, columns =['u1', 'u2', 'n'])
df
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [5], in <cell line: 1>()
----> 1 li = [(u1, u2, n)for (u1, u2), n in user_pair_counts.most_common() if n > 3]
2 df = pd.DataFrame.from_records(li, columns =['u1', 'u2', 'n'])
3 df
NameError: name 'user_pair_counts' is not defined
import pandas as pd
import matplotlib.pyplot as plt
li = [(w1, w2, pair_count[(w1, w2)], mi) for (w1, w2), mi in pmi.most_common()]
df = pd.DataFrame.from_records(li, columns =['w1', 'w2', 'count', 'mi'])
import networkx as nx
fig = plt.figure(1, figsize=(30, 30), dpi=60)
G = nx.from_pandas_edgelist(df[:2000],
source = 'w1',
target = 'w2',
edge_attr = 'mi')
widths = nx.get_edge_attributes(G, 'mi')
nodelist = G.nodes()
# nx.draw_kamada_kawai(G,
# node_size = 5,
# edge_color = "#8833FF",
# with_labels = True)
pos = nx.spring_layout(G)
# nx.draw_spring(G,
# node_size = 5,
# edge_color = "#8833FF",
# font_size = 16,
# with_labels = True)
nx.draw_networkx_nodes(G,pos,
nodelist=nodelist,
node_size=15,
node_color='black',
alpha=0.7)
nx.draw_networkx_edges(G,pos,
edgelist = widths.keys(),
width=list([w/2 for w in widths.values()]),
edge_color='black',
alpha=0.2)
nx.draw_networkx_labels(G, pos=pos,
labels=dict(zip(nodelist,nodelist)),
font_color='blue')
plt.box(False)
plt.show()