Hướng dẫn trực quan hóa mạng với Gephi và Sigma.js
Dưới đây là xem trước những gì chúng ta làm hôm nay: đồ thị ảnh hưởng ngôn ngữ lập trình. Kiểm tra link để khám phá mối quan hệ "design influence" (ảnh hưởng thiết kế) giữa hơn 250 ngôn ngữ lập trình quá khứ và hiện tại!
Lượt của bạn!
Trong thế giới siêu kết nối ngày nay, mạng là khía cạnh phổ biến của đời sống hiện đại.
Ví dụ khởi đầu ngày của tôi — tôi dùng mạng giao thông London đi vào trung tâm. Sau đó vào chi nhánh quán cà phê yêu thích và dùng Chromebook kết nối mạng Wi-Fi của họ. Tiếp theo, đăng nhập các trang mạng xã hội (social networking) tôi thường dùng.
Không bí mật rằng một số công ty ảnh hưởng nhất vài thập kỷ gần đây nhờ sức mạnh của mạng.
Facebook, Twitter, Instagram, LinkedIn và nền tảng media xã hội khác dựa vào thuộc tính small-world của mạng xã hội. Điều này cho phép kết nối user với nhau (và quảng cáo) hiệu quả.
Google nắm phần lớn thành công nhờ thống trị sớm thị trường tìm kiếm — một phần nhờ khả năng trả kết quả liên quan với thuật toán Page Rank mạng.
Mạng phân phối hiệu quả của Amazon cho phép giao hàng cùng ngày ở một số thành phố lớn.
Mạng cũng siêu quan trọng trong lĩnh vực AI và Machine Learning. Neural network là lĩnh vực nghiên cứu rất năng động. Nhiều thuật toán phát hiện feature, thiết yếu trong Computer Vision, dựa mạnh vào mạng mô hình hóa các phần khác nhau của hình ảnh.
Một loạt hiện tượng khoa học cũng hiểu được qua mô hình mạng. Bao gồm cơ học lượng tử, đường sinh hóa, và hệ thống sinh thái, kinh tế - xã hội.
Với tầm quan trọng không thể chối cãi, làm sao hiểu tốt hơn về mạng và thuộc tính của chúng?
Nghiên cứu toán học về mạng gọi là "lý thuyết đồ thị" (graph theory), và là một trong những nhánh toán học dễ tiếp cận hơn. Bài viết này nhằm cung cấp giới thiệu, giả định ít kiến thức hoặc kinh nghiệm trước.
Chúng ta sẽ dùng Python 3.x và phần mềm mã nguồn mở tuyệt vời Gephi để lắp ráp trực quan hóa mạng về cách các ngôn ngữ lập trình quá khứ và hiện tại liên kết bằng ảnh hưởng.
Nhưng trước…
Mạng chính xác là gì?
Ví dụ trên cho một số manh mối. Mạng giao thông gồm đích đến (destinations) kết nối bởi tuyến đường (routes). Mạng xã hội gồm cá nhân (individuals), kết nối qua mối quan hệ (relationships) lẫn nhau. Thuật toán tìm kiếm Google đánh giá "rank" các trang web (webpages) bằng cách xem trang nào link ra trang khác.
Nói chung, mạng là bất kỳ hệ thống nào có thể mô tả dưới dạng nodes (nút) và edges (cạnh), hoặc ngôn ngữ phổ thông, "chấm và đường".
Một số hệ thống dễ trừu tượng hóa theo cách này. Mạng xã hội là ví dụ rõ ràng nhất. Hệ thống file máy tính cũng vậy — thư mục và file liên kết bởi quan hệ "cha" (parent) và "con" (child).
Nhưng sức mạnh thực của mạng đến từ việc rất nhiều hệ thống có thể trừu tượng hóa và mô hình hóa dưới dạng mạng, ngay cả khi lúc đầu không rõ ràng làm sao.
Biểu diễn mạng
Cần đi xa hơn phác thảo bút giấy để phân tích và mô tả mạng toán học. Làm sao biến hình chấm và đường thành số để tính toán?
Một giải pháp là vẽ ma trận kề (adjacency matrix) biểu diễn mạng.
Ma trận là khái niệm có thể nghe hơi uy hiếp nếu không quen, nhưng đừng lo. Hãy coi chúng như lưới số dùng thực hiện nhiều phép tính cùng lúc. Ví dụ dưới:
Python Java Scala C#
Python 0 1 0 0
Java 0 0 0 1
Scala 0 1 0 0
C# 0 1 0 0
Trong ma trận này, giao điểm mỗi hàng và cột là 0 hoặc 1, tùy thuộc vào ngôn ngữ tương ứng có liên kết không. Bạn có thể kiểm tra với hình minh họa trên!
Với hầu hết mục đích, ma trận kề là cách tốt biểu diễn mạng toán học. Tuy nhiên, từ góc độ tính toán, đôi khi hơi cồng kềnh.
Ví dụ, với số node vừa phải (giả sử 1000), sẽ có số phần tử lớn hơn nhiều trong ma trận (ví dụ 1000² = 1,000,000).
Nhiều hệ thống thực tế tạo ra mạng thưa (sparse networks). Trong mạng này, hầu hết node chỉ kết nối với tỷ lệ nhỏ các node khác.
Nếu biểu diễn mạng thưa 1000 node trong bộ nhớ máy tính bằng ma trận kề, sẽ có 1,000,000 byte dữ liệu lưu trong RAM. Hầu hết là số 0. Chắc chắn có cách hiệu quả hơn.
Cách thay thế là làm việc với danh sách cạnh (edge lists). Chúng chính xác như tên gọi. Chỉ là danh sách cặp node nào liên kết với nhau.
Ví dụ, mạng ngôn ngữ lập trình trên biểu diễn như sau:
Java, Python
Java, Scala
Java, C#
C#, Java
Với mạng lớn hơn, này là cách hiệu quả tính toán hơn biểu diễn. Tất nhiên có thể sinh ma trận kề từ edge list (và ngược lại). Không như phải chọn một trong hai.
Cách khác biểu diễn mạng là adjacency lists (danh sách kề). Liệt kê mỗi node theo sau bởi node nó liên kết. Ví dụ:
Java: Python, Scala, C#
C#: Java
Thu thập dữ liệu, tạo kết nối
Bất kỳ mô hình và trực quan hóa mạng nào chỉ tốt bằng dữ liệu dùng xây dựng nó. Nghĩa là, ngoài đảm bảo dữ liệu chính xác và hoàn chỉnh, cũng cần chứng minh phương pháp suy ra edge giữa node.
Về nhiều mặt, đây là bước quan trọng nhất. Phân tích và suy luận sau về mạng phụ thuộc vào việc chứng minh được "tiêu chí liên kết" (linkage criterion).
Ví dụ, trong phân tích mạng xã hội, bạn có thể liên kết người dựa trên việc họ follow nhau trên mạng xã hội. Trong sinh học phân tử, liên kết gene dựa trên co-expression (biểu hiện chung).
Thường, phương pháp liên kết node cho phép gán trọng số (weights) cho edge, đưa ra thước đo "độ mạnh".
Ví dụ, bối cảnh bán lẻ online, liên kết sản phẩm dựa trên tần suất mua cùng nhau. Sản phẩm thường mua cùng nhau liên kết bằng weighted edge (cạnh có trọng số) cao hơn sản phẩm đôi khi mua cùng. Sản phẩm mua cùng không nhiều hơn ngẫu nhiên thì không liên kết.
Như bạn tưởng tượng, phương pháp liên kết node có thể tinh vi tùy ý.
Tuy nhiên, cho hướng dẫn này chúng ta dùng cách đơn giản hơn liên kết ngôn ngữ lập trình. Chúng ta sẽ dựa vào độ chính xác của Wikipedia.
Với mục đích này, nên ổn. Thành công của Wikipedia chứng minh nó đang làm đúng điều gì. Phương pháp cộng đồng mở viết bài nên đảm bảo mức độ khách quan.
Thêm thưởng: cấu trúc trang nhất quán làm nó thành sân chơi tiện lợi thử kỹ thuật web-scraping.
Thưởng khác: API Wikipedia mở rộng, có tài liệu tốt, làm truy xuất thông tin dễ hơn. Hãy bắt đầu.
Bước 1 — Cài đặt Gephi
Gephi có trên Linux, Mac và Windows. Tải tại đây.
Dự án này tôi dùng Lubuntu. Nếu bạn dùng Ubuntu/Debian, làm theo bước dưới để chạy Gephi. Ngược lại, quá trình cài đặt có thể giống những gì bạn quen.
Tải phiên bản mới nhất (lúc viết là v.0.9.1) Gephi cho hệ thống. Khi sẵn sàng, giải nén file.
cd Downloads
tar -xvzf gephi-0.9.1-linux.tar.gz
cd gephi-0.9.1/bin
./gephi
Có thể cần kiểm tra phiên bản Java JRE. Gephi yêu cầu phiên bản gần đây. Trên Lubuntu cài mới, tôi chỉ cài default-jre, mọi thứ chạy được.
apt install default-jre
./gephi
Còn một bước trước khi sẵn sàng. Để xuất graph ra Web, dùng plugin Sigma.js cho Gephi.
Trong menu bar Gephi, chọn "Tools", chọn "Plugins".
Click tab "Available Plugins" chọn "SigmaExporter" (Tôi cũng cài JSON Exporter, vì plugin hữu ích khác).
Bấm "Install" và được hướng dẫn quy trình. Cần restart Gephi sau khi xong.
Bước 2 — Viết script Python
Hướng dẫn dùng Python 3.x, thêm vài module làm cho dễ. Dùng pip module installer chạy lệnh:
pip3 install wikipedia
Tạo thư mục mới, tạo file tên kiểu script.py, mở trong editor/IDE yêu thích. Dưới là outline logic chính:
- Đầu tiên, cần danh sách ngôn ngữ lập trình đưa vào.
- Tiếp, duyệt danh sách và lấy HTML bài Wikipedia liên quan.
- Từ đó, trích xuất danh sách ngôn ngữ mà mỗi ngôn ngữ đã ảnh hưởng. Đây là tiêu chí liên kết thô (rough-and-ready).
- Khi đang ở đó, cũng lấy metadata về mỗi ngôn ngữ.
- Cuối, ghi tất cả dữ liệu thu thập vào file .csv
Script đầy đủ ở gist này.
Import một số module
Trong script.py, bắt đầu import vài module làm dễ việc:
import csv
import wikipedia
import urllib.request
from bs4 import BeautifulSoup as BS
import re
OK — bắt đầu tạo danh sách node đưa vào. Module Wikipedia rất tiện truy cập Wikipedia API.
Thêm mã sau:
pageTitle = "List of programming languages"
nodes = list(wikipedia.page(pageTitle).links)
print(nodes)
Lưu và chạy script, sẽ in tất cả link từ bài Wikipedia "List of programming languages". Hay!
Tuy nhiên, luôn hợp lý kiểm tra thủ công dữ liệu tự động thu thập. Nhìn nhanh sẽ thấy, ngoài nhiều ngôn ngữ lập trình thực tế, script cũng nhặt thêm vài link thừa.
Ví dụ, có thể thấy "List of markup languages", "Comparison of programming languages" và khác trong đó.
Mặc dù Gephi cho xóa node không muốn, không hại "làm sạch" dữ liệu trước khi tiến hành. Nếu có, tiết kiệm thời gian sau.
removeList = [
"List of", "Lists of", "Timeline", "Comparison of",
"History of", "Esoteric programming language"
]
nodes = [i for i in nodes if not any(r in i for r in removeList)]
Các dòng này định nghĩa danh sách substring xóa khỏi dữ liệu. Script duyệt dữ liệu, xóa element chứa substring không muốn.
Trong Python, chỉ cần một dòng code!
Một số hàm hỗ trợ
Bây giờ bắt đầu scrape Wikipedia xây dựng edge list (và thu thập metadata). Để dễ, định nghĩa vài hàm.
Lấy HTML
Hàm đầu dùng module BeautifulSoup lấy HTML trang Wikipedia mỗi ngôn ngữ.
base = "https://en.wikipedia.org/wiki/"
def getSoup(n):
try:
with urllib.request.urlopen(base + n) as response:
soup = BS(response.read(), 'html.parser')
table = soup.find_all("table", class_="infobox vevent")[0]
return table
except:
pass
Hàm này dùng urllib.request lấy HTML trang tại "https://en.wikipedia.org/wiki/" + "ngôn ngữ lập trình".
Sau đó truyền cho BeautifulSoup, đọc và parse HTML thành object dùng tìm thông tin.
Tiếp, dùng phương thức find_all() trích xuất element HTML quan tâm.
Ở đây, là bảng tóm tắt đầu mỗi bài ngôn ngữ lập trình. Làm sao nhận diện?
Cách dễ nhất: vào trang ngôn ngữ lập trình. Dùng Developer Tools trình duyệt inspect element quan tâm.
Bảng tóm tắt có tag HTML <table> và CSS class "infobox vevent", dùng để nhận diện bảng trong HTML.
Chỉ định với tham số:
"table"vàclass_="infobox vevent"
find_all() trả về list tất cả element khớp tiêu chí. Để thực sự chỉ định element quan tâm, thêm index [0]. Nếu hàm thành công, trả về object table. Ngược lại, trả về None.
Với mọi thủ tục thu thập dữ liệu tự động, quan trọng xử lý exception kỹ. Nếu không, tốt nhất script crash và bạn phải bắt đầu lại.
Tệ nhất, bạn kết thúc với dataset đầy inconsistency (nhất quán) và lỗi. Sẽ thành ác mộng làm việc sau này.
Lấy metadata
Hàm sau dùng object table tìm metadata. Ở đây, tìm trong bảng năm ngôn ngữ xuất hiện đầu tiên.
def getYear(t):
try:
t = t.get_text()
year = t[t.find("appear"):t.find("appear")+30]
year = re.match(r'.*([1-3][0-9]{3})', year).group(1)
return int(year)
except:
return "Could not determine"
Hàm ngắn này nhận object table làm tham số, dùng get_text() BeautifulSoup tạo string.
Bước tiếp tạo substring year. Lấy 30 ký tự sau lần xuất hiện đầu của từ "appear". String này nên chứa năm ngôn ngữ xuất hiện đầu.
Để trích xuất năm, dùng biểu thức chính quy (regular expression) (nhờ module re) khớp ký tự bắt đầu bằng chữ số 1-3, theo sau ba chữ số.
re.match(r'.*([1-3][0-9]{3})', year)
Nếu thành công, hàm trả về year dạng integer. Ngược lại, trả về "Could not determine" buồn buồn. Bạn có thể scrape thêm metadata — như paradigm (nghệ thuật), designer (người thiết kế), typing discipline (kỷ luật gõ).
Thu thập link
Một hàm nữa — lần này, cho table object ngôn ngữ cho trước, hy vọng nhận list ngôn ngữ lập trình khác.
def getLinks(t):
try:
table_rows = t.find_all("tr")
for i in range(0, len(table_rows)-1):
try:
if table_rows[i].get_text() == "\nInfluenced\n":
out = []
for j in table_rows[i+1].find_all("a"):
try:
out.append(j['title'])
except:
continue
return out
except:
continue
return
except:
return
Trời ơi, xem cái nesting kia… Thực sự xảy ra gì?
Hàm này dùng Tatsache object table có cấu trúc nhất quán. Thông tin trong bảng lưu trong hàng (tag HTML <tr>). Một hàng sẽ chứa text "\nInfluenced\n". Phần đầu hàm tìm hàng nào.
Khi tìm thấy hàng, bạn có thể chắc chắn hàng tiếp theo chứa link đến từng ngôn ngữ bị ảnh hưởng bởi ngôn ngữ hiện tại. Tìm link dùng find_all("a") — tham số "a" tương ứng tag HTML <a>.
Với mỗi link j, thêm thuộc tính ['title'] vào list out. Lý do quan tâm ['title'] là vì nó khớp chính xác tên ngôn ngữ lưu trong nodes.
Ví dụ, Java lưu trong nodes là "Java (programming language)", nên cần dùng chính xác tên này xuyên suốt dataset.
Nếu thành công, getLinks() trả về list ngôn ngữ lập trình. Phần còn lại xử lý exception, phòng khi có lỗi ở bất kỳ giai đoạn nào.
Thu thập dữ liệu
Cuối cùng, gần như sẵn sàng ngồi lại để script làm việc. Nó thu thập dữ liệu lưu trong hai object list.
edgeList = [["Source,Target"]]
meta = [["Id", "Year"]]
Viết vòng lặp áp dụng hàm định nghĩa trước cho mỗi item trong nodes, lưu output vào edgeList và meta.
for n in nodes:
try:
temp = getSoup(n)
except:
continue
try:
influenced = getLinks(temp)
for link in influenced:
if link in nodes:
edgeList.append([n + "," + link])
print([n + "," + link])
except:
continue
year = getYear(temp)
meta.append([n, year])
Hàm này lấy mỗi ngôn ngữ trong nodes và cố gắng lấy bảng tóm tắt từ trang Wikipedia.
Sau đó, lấy tất cả ngôn ngữ bảng liệt kê là bị ảnh hưởng bởi ngôn ngữ đang xét.
Với mỗi ngôn ngữ cũng xuất hiện trong nodes, thêm element vào edgeList dạng ["source,target"]. Cách này xây dựng edge list cho Gephi.
Mục đích debug, in mỗi element thêm vào edgeList — chắc chắn mọi thứ hoạt động như nên. Nếu extra thorough, có thể thêm print vào mệnh đề except.
Tiếp, lấy tên ngôn ngữ và năm, thêm vào list meta.
Ghi ra CSV
Sau khi vòng lặp chạy xong, bước cuối ghi nội dung edgeList và meta ra file comma separated value (CSV). Dễ dàng với module csv import trước.
with open("edge_list.csv", "w") as f:
wr = csv.writer(f)
for e in edgeList:
wr.writerow(e)
with open("metadata.csv", "w") as f2:
wr = csv.writer(f2)
for m in meta:
wr.writerow(m)
Xong! Lưu script, từ terminal chạy:
$ python3 script.py
Bạn thấy script in từng cặp source-target khi xây edge list. Đảm bảo kết nối internet ổn định, ngồi lại xem script làm phép màu.
Bước 3 — Xây dựng đồ thị với Gephi
Hy vọng bạn cài và chạy Gephi trước. Bây giờ tạo project mới và dùng dữ liệu thu thập xây dựng directed graph (đồ thị có hướng). Sẽ chỉ cách các ngôn ngữ lập trình khác nhau ảnh hưởng lẫn nhau!
Bắt đầu tạo project mới Gephi, chuyển sang view "Data Laboratory" (Thí nghiệm dữ liệu). Cung cấp interface giống spreadsheet xử lý dữ liệu Gephi. Việc đầu: import edge list.
- Click "Import spreadsheet".
- Chọn file
edge_list.csvscript Python sinh ra. Đảm bảo Gephi biết dùng dấu phẩy làm separator. - Chọn "Edge List" từ List type.
- Click "Next" kiểm tra import cả cột Source và Target như string.
Nên cập nhật Data Lab với list node. Bây giờ import file metadata.csv. Lần này, chắc chắn chọn "Nodes list" từ List type.
Chuyển sang tab "Preview", xem mạng trông như nào.
À… Hơi… đơn sắc. Và rối. Như đĩa mì spaghetti. Hãy sửa.
Làm cho đẹp
Có nhiều cách làm việc presentation, và đây là nơi chút tự do sáng tạo. Với trực quan hóa mạng, về cơ bản có ba điều cân nhắc:
- Vị trí (Positioning) Có vài thuật toán tạo layout pattern cho mạng. Lựa chọn phổ biến là thuật toán Fruchterman-Reingold, có sẵn trong Gephi.
- Kích thước (Sizing) Kích thước node trong đồ thị có thể biểu diễn thuộc tính thú vị. Thường là đo độ trung tâm (centrality measure). Nhiều cách đo centrality, nhưng tất cả phản ánh "tầm quan trọng" node, về mặt kết nối tốt với phần còn lại mạng.
- Màu sắc (Coloring) Cũng có thể dùng màu chỉ thuộc tính node. Thường dùng màu chỉ cấu trúc cộng đồng (community structure). Định nghĩa rộng: "nhóm node kết nối chặt chẽ với nhau hơn với phần còn lại đồ thị". Trong mạng xã hội, tiết lộ nhóm bạn, gia đình, chuyên nghiệp. Có vài thuật toán phát hiện community structure. Gephi có sẵn phương pháp Louvain.
Để thay đổi, cần tính toán thống kê. Chuyển sang cửa sổ "Overview". Bảng điều khiển bên phải. Nên có tab "Statistics". Mở, thấy nhiều tùy chọn.
Gephi có nhiều khả năng thống kê built-in. Mỗi cái, click "Run" sinh báo cáo tiết lộ insight về mạng.
Một số hữu ích:
- Độ trung bình (Average degree) Ngôn ngữ trung bình kết nối với khoảng 4 ngôn ngữ khác. Báo cáo cũng cho đồ thị phân phối độ. Cho thấy hầu hết ngôn ngữ ít kết nối, tỷ lệ nhỏ nhiều kết nối. Điều này gợi ý đây là mạng scale-free (mạng tỷ lệ tự do). Nhiều nghiên cứu về mạng scale-free và quá trình sinh ra chúng.
- Đường kính (Diameter) Mạng này diameter 12 — nghĩa là số kết nối "rộng nhất" giữa hai ngôn ngữ bất kỳ. Độ dài đường trung bình chỉ dưới 4. Nghĩa là trung bình, hai ngôn ngữ bất kỳ cách nhau 4 cạnh. Con số này đo "kích thước" mạng.
- Độ mô-đun (Modularity) Điểm cho thấy mạng "phân compartment" (compartmentalized) đến đâu. Ở đây modularity ~0.53. Khá cao, gợi ý có module riêng biệt trong mạng. Lại chỉ ra điều thú vị về hệ thống gốc. Ngôn ngữ xu hướng rơi vào "nhóm ảnh hưởng" (influence groups) riêng biệt.
Dù sao, để sửa appearance mạng, qua panel trái.
Tab "Layout", chọn thuật toán layout. Bấm "Run" xem đồ thị dịch chuyển real-time! Xem thuật toán nào tốt nhất.
Trên tab Layout là tab "Appearance". Ở đây, chơi với cài đặt màu, kích thước, label node và edge. Cấu hình dựa trên attribute (kể cả stats Gephi tính).
Gợi ý, bạn có thể:
- Màu node theo attribute Modularity. Màu theo thành viên community.
- Kích thước node theo Degree (độ). Node kết nối tốt hơn to hơn node kém kết nối.
Tuy nhiên, hãy thử nghiệm và tìm layout bạn thích nhất.
Khi hài lòng appearance đồ thị, đến bước cuối — xuất ra Web!
Bước 4 — Sigma.js
Đã có trực quan hóa mạng khám phá trong Gephi. Có thể chụp màn hình, hoặc lưu graph dạng SVG, PDF, PNG.
Tuy nhiên, nếu cài plugin Sigma.js trước, tại sao không xuất graph ra HTML? Sẽ tạo trực quan hóa tương tác host online, hoặc upload GitHub chia sẻ.
Để làm, chọn "Export > Sigma.js template…" từ menu bar Gephi.
Điền chi tiết yêu cầu. Chắc chắn chọn thư mục xuất project. Có thể đổi title, legend, mô tả, hover behavior và nhiều chi tiết khác. Khi sẵn sàng, click "OK".
Bây giờ, nếu điều hướng đến thư mục xuất project, thấy folder chứa tất cả file Sigma.js sinh ra.
Mở index.html trong trình duyệt yêu thích. Ta-da! Đồ thị mạng của bạn! Nếu biết chút CSS và JavaScript, có thể lặn vào các file sinh ra tùy chỉnh output.
Và hướng dẫn này kết thúc!
Tóm tắt
- Nhiều hệ thống có thể mô hình hóa và trực quan hóa như mạng. Lý thuyết đồ thị là nhánh toán cung cấp công cụ hiểu cấu trúc và thuộc tính mạng.
- Bạn dùng Python scrape dữ liệu Wikipedia xây dựng đồ thị ảnh hưởng ngôn ngữ lập trình. Tiêu chí liên kết là ngôn ngữ nào liệt kê là ảnh hưởng thiết kế ngôn ngữ khác.
- Gephi và Sigma.js là công cụ mã nguồn mở phân tích và trực quan hóa mạng. Cho phép xuất mạng dạng ảnh, PDF hoặc Web.
Cảm ơn đã đọc — mong chờ bình luận hoặc câu hỏi! Tài nguyên tuyệt vời học thêm graph theory: sách trực tuyến tương tác của Albert-László Barabási.
Mã đầy đủ hướng dẫn tại đây.