จะทำไงถ้ามี text collection ใหญ่ๆ และอยากดึง meaning ออกมา? เริ่มต้นที่ดีคือแบ่ง text เป็น n-grams
คำอธิบาย:
ใน computational linguistics และ probability n-gram คือ contiguous sequence ของ n items จาก sequence ของ text ที่ให้มา — Wikipedia
เช่น: phrase "Hello there, how are you?" unigrams (ngrams 1 element): "Hello", "there", "how", "are", "you" bigrams (2 elements): ["Hello", "there"], ["there", "how"], ["how", "are"], ["are", "you"]
เรียนรู้ด้วยภาพดีกว่า นี่คือรูป:

มาดู implementation ใน Ruby!
Downloading Sample Data
ก่อนลงมือต้องมี sample data ถ้าไม่มี download Wikipedia หรือ blog articles บางอย่าง กรณีนี้ผม download IRC logs จาก #ruby freenode channel
Logs อยู่ที่: irclog.whitequark.org/ruby
Note on data formats: ถ้า plain text version ไม่มี ใช้ Nokogiri parse หน้าและ extract data IRC logs มี plain text โดย append .txt ท้าย URL ใช้ได้เลย
Class นี้ download และ save data:
require 'restclient'
class LogParser
LOG_DIR = 'irc_logs'
def initialize(date)
@date = date
@log_name = "#{LOG_DIR}/irc-log-#{@date}.txt"
end
def download_page(url)
return log_contents if File.exist? @log_name
RestClient.get(url).body
end
def save_page(page)
File.open(@log_name, "w+") { |f| f.puts page }
end
def log_contents
File.readlines(@log_name).join
end
def get_messages
page = download_page("https://irclog.whitequark.org/ruby/#{@date}.txt")
save_page(page)
page
end
end
log = LogParser.new("2015-04-15")
msg = log.get_messages
Class straightforward ใช้ RestClient เป็น HTTP client save results ไฟล์ เพื่อไม่ request ซ้ำขณะ modify โปรแกรม
Analyzing The Data
ตอนมี data แล้ว มาวิเคราะห์ นี่คือ Ngram class ใช้ Array#each_cons ผลิต ngrams เพราะ return Enumerator ต้อง to_a ได้ Array
class Ngram
def initialize(input)
@input = input
end
def ngrams(n)
@input.split.each_cons(n).to_a
end
end
รวมทั้งหมดด้วย loop, Hash#merge! และ Enumerable#sort_by
แบบนี้:
# Filter words ที่ปรากฏน้อยกว่านี้
MIN_REPETITIONS = 20
total = {}
# Get logs 15 วันแรกของเดือน return trigrams
(1..15).each do |n|
day = '%02d' % [n]
total.merge!(get_trigrams_for_date "2015-04-#{day}") { |k, old, new| old + new }
end
# Sort descending
total = total.sort_by { |k, v| -v }.reject { |k, v| v < MIN_REPETITIONS }
total.each { |k, v| puts "#{v} => #{k}" }
Note:
get_trigrams_for_datemethod ไม่อยู่ที่นี่ เพื่อความกระชับ หาได้ที่ github
Output 长得像这样:
112 => i want to 83 => link for more 82 => is there a 71 => you want to 66 => i don't know 66 => i have a 65 => i need to
เห็นได้ว่า "อยากทำอะไร" เป็นประเด็夯ใน #ruby 🙂
สรุป
ตาคุณลองเล่น n-gram analysis! อีกวิธีดู n-grams คือ Google Ngram Viewer
Natural language processing (NLP) เรื่องน่าสนใจ Wikipedia มี overview ดี
โค้ดครบที่นี่: https://github.com/matugm/ngram-analysis/blob/master/irc_histogram.rb