Lesson 3: 文本分析與視覺化
Last Updated: 2019/10/17
文本收集
文本收集
文本收集
文本分析
"我和順仔早上5點登上「福建號」"
斷詞
原文
我 和 順仔 早上 5點 登上 福建號
http://www.tipi.com.tw/images/proimg/DoMCase-%E5%B0%8F-ss.jpg
http://ox.epage.au.edu.tw/ezfiles/278/1278/pictures/45/part_43922_9440244_30942.jpg
文本分析
"我和順仔早上5點登上「福建號」"
我和<person>順仔</person>早上5點登上「<ship>福建號</ship>」
斷詞
標記
原文
我 和 順仔 早上 5點 登上 福建號
專業術語
r c nr t t v nr
詞性
文本分析
拜訪 我 普林斯 頓 神學院 的 老同 學麥克 切斯 尼 先生 和 幾位 宣教 師 。 晚上 介紹 加拿大 , 哈巴 安德 醫師 翻譯
斷詞
困難: 斷詞錯誤 雜訊
"拜訪我普林斯頓(Princeton)神學院的老同學麥克切斯尼(McChesney)先生和幾位宣教師。晚上介紹加拿大,哈巴安德醫師翻譯。"
自訂辭典
停用字列表
普林斯頓
老同學
麥克切斯尼先生
哈巴安德醫師
宣教師
我
的
和
晚上
幾位
拜訪 普林斯頓 神學院 老同學 麥克切斯尼先生 宣教師 。 介紹 加拿大 , 哈巴安德醫師 翻譯
文本分析
困難: 錯別字、同義詞(不同稱呼/不同翻譯)
拜訪南勢番(南方的原住民),在包干、李流(Li-lau),薄薄、斗蘭(Tau-lan)和七腳川拍了一些照片。
台灣堡圖
1904
我們拜訪流流、薄薄、里腦,又回到大社
問題:究竟是里腦,李流還是流流?
不同翻譯
回到五股坑,蔥仔在前往領事館費里德(A.Frater)先生那裡後和我們一起。
和偕師母及閏虔益一起到五股坑,然後去洲裡,在晚上回來。
同義詞
專家
修正與建議
里漏社
文本分析
文本分析
"""
詞頻統計-不含詞性標記,使用jieba斷詞,載入自定義辭典,自訂停用字
"""
import jieba
import jieba.analyse
import csv
import collections
def readFile(fname):
my_dict = dict() # 建立空字典
with open(fname, encoding="UTF-8") as file:
rows = csv.reader(file) # 讀取所有內容產生串列
for r in rows: # 建立字典
my_dict[r[0]] = r[1]
return my_dict
# 步驟一:讀取日記,建立字典、停用字
diary = readFile('Data/MackayFull.csv')
stop_word = []
# 從檔案讀取停用字
with open("Data/stopword.utf8.txt", encoding="UTF-8") as stop:
rows = stop.readlines()
for r in rows:
stop_word.append(r.strip())
jieba.load_userdict('Data/wordsMackay.utf8.txt') # 載入自定義辭典
# 步驟二:詞頻統計
word_count = dict() # 統計字典初值設定
for key, value in diary.items():
words = jieba.cut(value) # 對每一篇日記斷詞
for w in words: # 處理當天的斷詞結果
if (w in stop_word or w == ' ' or w == ' ' or w.isdigit()): # 移除停用字
continue
if (word_count.get(w) == None): # 詞不在字典裡
word_count[w] = 1 # 建立字典項目, 詞頻為1
else: # 詞在字典項目裡
word_count[w] += 1 # 詞頻加1
# 步驟三:重新排序:根據頻率高低
sorted_w = sorted(word_count.items(), key=lambda kv: kv[1], reverse=True)
# 步驟四:寫出檔案
with open('output_notag.csv', 'w', encoding="UTF-8", newline='') as csvfile:
writer = csv.writer(csvfile) # 建立 CSV 檔寫入器
writer.writerow(['詞彙', '頻率']) # 寫入標題列
# 寫入詞頻統計資料
for item in sorted_w:
writer.writerow([item[0], item[1]])
Demo01:詞頻統計-不含詞性
需先安裝斷詞模組jieba
pip install jieba
文本分析
Python程式
停用字列表
自定義辭典
文本
Q: 有無改善空間?
輸出檔csv
注意:輸出檔output_notag.csv(或output.csv)為utf8編碼
Excel直接開啟會顯示亂碼
❶先用記事本打開
❷檔案/另存新檔
❸選取ANSI編碼後存檔
文本分析
### 使用外部模組jieba進行中文斷詞, jieba.posseg標記詞性
import jieba
import jieba.analyse
import jieba.posseg as posseg
import csv
import collections
def readFile(fname):
my_dict = dict() # 建立空字典
with open(fname, encoding="UTF-8") as file:
rows = csv.reader(file) # 讀取所有內容產生串列
for r in rows: # 建立字典
my_dict[r[0]] = r[1]
return my_dict
# 步驟一:讀取日記,建立字典
diary = readFile('Data/MackayDemo.csv')
stop_word = []
with open("Data/stopword.utf8.txt", encoding="UTF-8") as stop:
rows = stop.readlines()
for r in rows:
stop_word.append(r.strip())
jieba.load_userdict('Data/wordsMackay.utf8.txt')
# 欲省略的詞性列表
pos = {'u': '語助詞', 'r': '代名詞', 'd': '副詞', 'uj': '語助詞', 'c': '連接詞',
'm': '數詞', 'p': '介詞', 'j': '簡稱', 'y': '語氣詞'}
# 步驟二:詞頻統計
word_count = dict() # 詞頻統計字典
for key, value in diary.items():
words = posseg.cut(value) # 對每一篇日記斷詞,標記詞性
for w in words: # 處理每天的斷詞結果
word = w.word
if (word in stop_word or word == ' ' or word == ' ' or word.isdigit()): # 移除停用字
continue
if (w.flag in pos): # 忽略的詞性
continue
if (word_count.get(w) == None): # 詞不在字典裡
word_count[w] = 1 # 建立字典, 詞頻為1
else: # 詞在字典裡
word_count[w] += 1 # 詞頻加1
# 步驟三:重新排序(根據頻率高低)
sorted_w = sorted(word_count.items(), key=lambda kv: kv[1], reverse=True) # 排序
# 步驟四:輸出詞頻到檔案
with open('output.csv', 'w', encoding="UTF-8", newline='') as csvfile:
writer = csv.writer(csvfile) # 建立 CSV 檔寫入器
writer.writerow(['詞彙', '頻率', '詞性']) # 寫入標題列
for item in sorted_w: # 寫入統計資料列
writer.writerow([item[0].word, item[1], item[0].flag])
Demo02:詞頻統計-含詞性
詞性標記jieba.posseg
q: 數量詞
v: 動詞
nr: 人名
n: 名詞
t: 時間詞
文本分析
沒有詞性
刪除部份詞性
刪除更多詞性
Q: 有無改善空間?
哪些詞性應該刪除?
文本分析
檔名 | 日期 | 說明 |
---|---|---|
MackayFull.csv | 1871~1901 | 全部日記內容 |
MackayFirst.csv | 1871~1879 | 第一階段 in 台灣 |
MackayCan01.csv | 1880~1881/11/7 | 第一次返國 in 加拿大 |
MackaySecond.csv | 1881/11/8~1893/8/18 | 第二階段 in台灣 |
MackayCan02.csv | 1893/8/19~1895/10/13 | 第二次返國 in 加拿大 |
MackayThird.csv | 1895/10/14~1901 | 第三階段 in 台灣 |
...
# 步驟一:讀取日記,設定停用字、自定義辭典
diary = readFile('Data/MackayFull.csv')
...
視覺化
文字雲
視覺化
用途
關鍵字該是哪些?重要性?
Answer: Case by case!
正規化(normalization)很重要
視覺化
註冊
視覺化
❶ 新增資料夾
❷ 點選資料夾
❸ 創建文字雲
視覺化
❶ 載入關鍵字
❷ 貼上「詞」、「頻率」
視覺化
❸ 匯入
視覺化
❹ 前往Google Noto Font網站下載免費中文字型
視覺化
❺ 載入中文字型
任選ㄧ.otf檔
視覺化
❻ 檢視文字雲
視覺化
原始頻率
資料檔案:MackayFull.csv
處理方式:詞頻統計
擷取頻率100次以上的字詞
觀察
Case 1: 高頻字詞
視覺化
正規化
正規化
=STANDARDIZE(頻率, 平均, 標準差)
=AVERAGE(所有頻率值)
=STDEV.P(所有頻率值)
移除字詞
Case 1-1: 高頻字詞
修正1-頻率正規化: 根據頻率計算Z分數
修正2-移除部分字詞
Z分數:Excel功能standardize(), average(), stdev.p()
中頻字詞浮現: 生病、教會、上帝
視覺化
Case 2: 含詞性字詞
資料檔案:MackayFirst.csv
處理方式:詞頻統計含詞性
頻率5次以上,不分詞性,共998個詞(共6197個詞)
地點: 五股坑、八里坌、大龍峒、洲裡、三重埔、滬尾、新港、竹塹、中壢
人名: 閏虔益、李庥
視覺化
Case 2-1: 含詞性(只有名詞)
資料檔案:MackayFirst.csv
處理方式:詞頻統計含詞性
頻率3次以上,名詞,共598個詞(名詞2582個詞)
字詞不重複, 大小由頻率決定
What's New?
網路分析
社會網路
節點
關聯
網路分析
小朋友的交友情況解析
連線:透過觀察或是直接詢問小朋友
Q: 誰比較邊緣?為什麼?
網路分析
主角分析 (冰與火之歌第三部:劍刃風暴)
連線:共同出現的次數
(兩個名字在15個字的距離內)
(1) 107個角色, 353個邊
(2) 找出7個群組
(3) 使用6種「中心性」公式計算每
個角色的重要性
Q. 前三名主角是?
Tyrion, Jon, Sansa
網路分析
網路分析
Q: 如果字詞是「節點」,字詞間的「共同出現」代表「關聯」?
「共同出現」:出現於同一句子、或同一文章)
我 和 順仔 早上 5點 登上 福建號
順仔
早上
福建號
社會網路
社會網路: 由圖 G=(V, E) 構成, 其中
關聯矩陣
節點
關聯: 0 不相連, 1 相連
社會網路
節點
網路分析
馬偕日記內容的
網路分析
日記字詞: 可分為13類
社會網路: 單一類(例: 人名共同出現)
兩類字詞(例: 人名-地名)
網路分析
# 共同出現於同一天日記
import jieba
import jieba.analyse
import csv
import collections
def readFile(fname):
my_dict = dict() # 建立空字典
with open(fname, encoding="UTF-8") as file:
rows = csv.reader(file) # 讀取所有內容產生串列
for r in rows: # 建立字典
my_dict[r[0]] = r[1]
return my_dict
# 步驟一:讀取日記,建立字典
diary = readFile('Data/MackayDemo.csv')
stop_word = [] # 1-1 停用字
with open("Data/stopword.utf8.txt", encoding="UTF-8") as stop:
rows = stop.readlines()
for r in rows:
stop_word.append(r.strip())
jieba.load_userdict('Data/wordsMackay.utf8.txt') # 1-2 自定義辭典
# 步驟二:共同出現統計
word_count = dict() # 統計字典初值設定
co_occu = dict()
for key, value in diary.items():
words = jieba.cut(value) # 對每一篇日記斷詞
for w1 in words: # 處理當天的斷詞結果
if (w1 in stop_word or w1 == ' ' or w1 == ' ' or w1.isdigit()): # 移除停用字
continue
for w2 in words:
if (w2 in stop_word or w2 == ' ' or w2 == ' ' or w2.isdigit()): # 移除停用字
continue
if (w1 == w2):
continue
w = (w1 + ' ' + w2) if (w1 < w2) else (w2 + ' ' + w1)
if (word_count.get(w) == None): # 詞不在字典裡
word_count[w] = 1 # 建立字典項目, 詞頻為1
else: # 詞在字典項目裡
word_count[w] += 1 # 詞頻加1
# 步驟三重新排序:根據頻率高低
sorted_w = sorted(word_count.items(), key=lambda kv: kv[1], reverse=True)
# 步驟四:輸出
with open('output_co_occu.csv', 'w', encoding="UTF-8", newline='') as csvfile:
writer = csv.writer(csvfile) # 建立 CSV 檔寫入器
writer.writerow(['詞彙', '頻率']) # 寫入標題列
# 寫入詞頻統計資料
for item in sorted_w:
writer.writerow([item[0], item[1]])
Demo03:共同出現-同一篇
網路分析
支援格式:
有無方向性
節點數量
邊的數量
layout演算法
大小、顏色(Ranking功能)
指標計算
(統計模組)
過濾功能
(Fiilter)
標籤、大小...
指標計算參考:淺談社會網路分析(中心性計算)
時間線圖
時間線圖
Q1:文字年表的問題?
Q2:資料如何自動產生?資訊擷取
時間線圖
資訊快速呈現的方法:時間線圖(Timeline)
事件概述、議程/行程、突顯重要時間點、揭示重要訊息
時間線圖
西北大學Knight lab發展的Timeline JS工具
時間線圖
事件一
事件二
開始日期時間
結束日期時間
❷ 設定每個事件的時間
時間線圖
事件標題
事件說明
媒體網址
標示來源
圖說
❸ 輸入每個事件的標題、說明、多媒體...
時間線圖
❹ 將Google試算表發布到網路
❺ 複製試算表網址
時間線圖
❻ 試算表網址貼到Timeline官網
貼上試算表網址
有許多微調設定(可不設定)
時間線圖
❼ 完成時間線圖(可分享、或內嵌到網頁)
產生的試算表網址
產生的iframe標籤,可內嵌至網頁
預覽按鈕
時間線圖
時間線圖
用資料說故事
下單數量
處理數量
2位員工5月離職;6,7月勉強應付。但8月數量增加後便無法應付,之後再也趕不上進度
掌握情境
善用視覺
故事結構
故事走向:砂礫中找珍珠 vs 展示珍珠項鍊
對象是誰
現況如何?反差何在?
如何弭平反差?
佈局
戲劇張力
主角心境
第一幕
第二幕
第三幕