Khóa học
Trong khoa học dữ liệu, bạn sẽ thường cần tạo báo cáo về công việc của mình để trình bày cho người ra quyết định hoặc những người không chuyên về kỹ thuật. Chuyển đổi Jupyter Notebook của bạn thành một tài liệu PDF hoặc HTML ổn định sẽ dễ chia sẻ hơn với đồng nghiệp không cài đặt Python hoặc Jupyter. Python sử dụng thư viện nbconvert và ngôn ngữ tạo mẫu Jinja2 để chuyển đổi tài liệu. Mẫu (template) xác định cách một tài liệu sẽ hiển thị trên trang web hoặc ở định dạng đầu ra khác. Hiểu cách tuỳ chỉnh mẫu sẽ hữu ích để tạo các báo cáo đẹp mắt từ Notebook của bạn.
Jupyter Notebook là cách để chạy mã Python trong trình duyệt. Nếu bạn muốn tìm hiểu thêm về Python, nhớ xem khoá học miễn phí Giới thiệu Python cho Khoa học Dữ liệu của chúng tôi.
Trong hướng dẫn này, bạn sẽ tìm hiểu các chủ đề sau:
- Mẫu và trình thiết kế mẫu
Jinja2. - Kết xuất (render) mẫu và kế thừa.
- Cách dùng
nbconvertđể xuất Notebook. - Cú pháp và cấu trúc để mở rộng các mẫu mặc định của Jupyter cho xuất HTML.
- Khác biệt khi xuất sang định dạng LaTeX và PDF.
Mẫu (Templates) là gì?
Từ wiki của Python: "Templating, đặc biệt là web templating, là cách biểu diễn dữ liệu ở các dạng khác nhau... Thông thường, giải pháp templating bao gồm một tài liệu (template) và dữ liệu. Các template thường trông rất giống đầu ra cuối cùng, với các placeholder thay cho dữ liệu thực"
Jupyter Notebook có thể được xuất dễ dàng bằng File -> Download As (Trong Jupyter Lab, bạn sẽ thấy Export Notebook As). Tuỳ chọn này dùng các mẫu mặc định có trong môi trường Jupyter chính. Jinja2 là ngôn ngữ tạo mẫu mạnh mẽ cho Python để định nghĩa các khối và cách dàn trang. Mẫu có các phần, được xác định bởi tag, cho biết cách mẫu sẽ kết xuất dữ liệu đầu vào. Dữ liệu sẽ thay thế các biến hoặc biểu thức khi mẫu được kết xuất.
Ký hiệu phân tách cho các phần khác nhau của một mẫu là:
{% ... %}cho Câu lệnh (Statements){{ ... }}cho Biểu thức (Expressions) sẽ in ra đầu ra của mẫu{# ... #}cho Chú thích (Comments) không xuất hiện trong đầu ra của mẫu
Xem ví dụ ngắn về một mẫu Jinja (theo tài liệu Jinja2):
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage </title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid">you </a>.
{% endblock %}
</div>
</body>
</html>
Bạn có thể thấy phần lớn mẫu trông giống một tài liệu HTML thông thường. Jinja chỉ cần vài dòng bổ sung để diễn giải mã như một mẫu. {% block head %} định nghĩa phần head của tài liệu HTML và cách mẫu này sẽ mở rộng định dạng của nó. Phần {% block title %} mô tả vị trí hiển thị tiêu đề đầu vào. Và {% endblock %} xuất hiện nhiều nơi vì nó kết thúc bất kỳ khối mẫu tương ứng nào.
Bây giờ bạn đã sẵn sàng học cách dùng Jinja để tạo các mẫu tuỳ chỉnh của riêng bạn!
Giới thiệu về Jinja
Jinja là một bộ máy tạo mẫu cho phép bạn xác định cách tài liệu của mình sẽ được hiển thị. Cụ thể trong hướng dẫn này, bạn sẽ tập trung vào cách xuất Jupyter Notebook với sự trợ giúp của các mẫu Jinja.
Đầu tiên, từ Jinja2 import Template:
from jinja2 import Template
Hiểu về kết xuất cơ bản với mẫu
Kết xuất dữ liệu trong mẫu Jinja khá đơn giản. Dùng dấu ngoặc và tên biến, bạn có thể hiển thị dữ liệu trong mẫu:
myTemplate = Template("My template is {{ something }}!")
myTemplate.render(something="awesome")
'My template is awesome!'
Gọi .render(input) cho phép bạn hiển thị mẫu trong Notebook, với đầu vào của bạn thay thế cho {{ something }} chung chung trong mẫu. Đây là một ví dụ về cách dùng expression trong Jinja.
Bạn có thể dùng mẫu linh hoạt hơn bằng cách định nghĩa một statement trong Python, mà Jinja sẽ diễn giải.
myFancyTemplate = Template("Let's count to 10: {% for i in range(11) %}{{i}} " "{% endfor %}")
myFancyTemplate.render()
"Let's count to 10: 0 1 2 3 4 5 6 7 8 9 10 "
Lưu ý bạn đã định nghĩa một vòng lặp for và mẫu đã kết xuất các số từ 0 đến 10 với khoảng trắng ở giữa. Cú pháp statement {% expression %} mang lại nhiều linh hoạt để tạo mẫu tuỳ chỉnh.
Kế thừa mẫu
Để tạo các mẫu mở rộng các bản xuất mặc định của Jupyter, bạn có thể dùng kế thừa mẫu. Kế thừa là khái niệm trong lập trình cho phép bạn hiện thực hoá thứ gì đó ở đối tượng con và tận dụng các chức năng đã định nghĩa ở đối tượng cha. Ví dụ đơn giản, nếu bạn có đối tượng Dog có thể sủa, ăn và đi bộ, bạn có thể dùng kế thừa để tạo đối tượng Dalmatian thừa hưởng tất cả thuộc tính đó và thêm thuộc tính khác, như có đốm. Bạn sẽ thấy trong hướng dẫn này vì sao kế thừa rất hữu ích cho việc tạo mẫu, bởi thường chỉ cần thay đổi nhỏ để tuỳ chỉnh đầu ra. Giống các ngôn ngữ hỗ trợ kế thừa khác, Jinja dùng các từ khoá như extends và super để truy cập định nghĩa của cha.
Xem lại mẫu cha từ trên, gọi là base.html:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage </title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid">you </a>.
{% endblock %}
</div>
</body>
</html>
Một mẫu con có thể trông như sau (theo tài liệu Jinja2):
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
Lưu ý mẫu con bắt đầu với {% extends "base.html" %}. Khai báo này cho bộ máy tạo mẫu Jinja biết cách xử lý tài liệu này (như HTML có kế thừa từ base.html). Dùng mẫu con này cho phép chỉ định các thuộc tính khác nhau cho trang chủ (như màu CSS cụ thể). Bạn cũng thấy trong khối head rằng mẫu con kế thừa kiểu của cha với lời gọi super().
Sử dụng nbconvert để xuất Jupyter Notebook
Giờ bạn đã hiểu cú pháp cơ bản và việc kế thừa của mẫu, bạn có thể học cách xuất Jupyter Notebook với nbconvert và định nghĩa mẫu để tuỳ chỉnh đầu ra.
Đầu tiên import nbconvert:
import nbconvert
nbconvert có thể tạo đầu ra ở nhiều định dạng. Hướng dẫn này tập trung vào đầu ra HTML và LaTeX/PDF. Để xem đầu ra trong notebook, dùng hàm hiển thị của IPython và --stdout trong lệnh gọi nbconvert.
example = !jupyter nbconvert --to html 'Example.ipynb' --stdout
from IPython.display import HTML, display
display(HTML('\n'.join(example)))
[NbConvertApp] Đang chuyển đổi notebook Example.ipynb sang html

Đoạn Notebook này trông rất giống tệp .ipynb gốc. Đầu ra hiển thị như một tài liệu độc lập với tiêu đề và văn bản theo cách nó thường hiển thị trong Notebook. Các khối ô mã được hiển thị dưới dạng hộp màu xám (gọi là "Notebook style"). Khác biệt chính bạn có thể thấy là mẫu HTML mặc định của Jupyter không hiển thị nhãn "Out:", như Notebook đang chạy.
Dùng nbconvert và mẫu con tuỳ chỉnh để xuất Jupyter Notebook
Thông thường, bạn sẽ muốn mở rộng các mẫu xuất mặc định của Jupyter Notebook và thực hiện một vài thay đổi thiết kế nhỏ cho đầu ra.
Ví dụ, xem mẫu đơn giản này loại bỏ các ô Markdown khỏi đầu ra (gọi là rmMkdwn.tpl):
{% extends 'basic.tpl'%}
{% block markdowncell -%}
{% endblock markdowncell %}
Áp dụng một mẫu cho nbconvert được thực hiện bằng tuỳ chọn --template=:
example = !jupyter nbconvert --to html 'Example.ipynb' --template='rmMkdwn.tpl' --stdout
display(HTML('\n'.join(example)))
[NbConvertApp] Đang chuyển đổi notebook Example.ipynb sang html
Ngay cả một mẫu rất đơn giản như rmMkdwn.tpl cũng có thể giúp bạn tuỳ chỉnh đầu ra đáng kể.
Xem mẫu phức tạp hơn này, đóng khung các ô bằng màu đỏ (gọi là boxRed.tpl):
{% extends 'full.tpl'%}
{% block any_cell %}
<div style="border:thin solid red">
{{ super() }}
</div>
{% endblock any_cell %}
example = !jupyter nbconvert --to html 'Example.ipynb' --template='boxRed.tpl' --stdout
display(HTML('\n'.join(example)))
[NbConvertApp] Đang chuyển đổi notebook Example.ipynb sang html 
Ở trên, bạn thấy thay đổi phong cách duy nhất là khung màu đỏ quanh mỗi ô. super() được dùng để đảm bảo mỗi ô vẫn giữ kiểu riêng từ mẫu cha full.tpl.
Khác biệt trong mẫu khi xuất sang LaTeX và PDF
Vì { } và % là ký tự đặc biệt trong LaTeX, bạn phải dùng (( )) và *. Ngoài ra, các mẫu LaTeX mặc định là base.tplx, article.tplx và report.tplx, tương ứng với các lớp tài liệu LaTeX.
Xem cách loại bỏ các ô Markdown với một mẫu con LaTeX:
((* extends 'article.tplx' *))
((* block markdowncell -*))
((* endblock markdowncell *))
nbconvert có thể tạo đầu ra LaTeX hoặc PDF - đầu ra PDF được biên dịch từ các mẫu LaTeX. Để dễ xem trong hướng dẫn này, hãy xem điều gì xảy ra khi bạn xuất sang PDF bằng rmMkdwn.tplx.
!jupyter nbconvert --to pdf 'Example.ipynb' --template='rmMkdwn.tplx'
from IPython.display import IFrame
IFrame('Example.pdf', width=800, height=500)
[NbConvertApp] Converting notebook Example.ipynb to pdf
[NbConvertApp] Support files will be in Example_files/
[NbConvertApp] Making directory Example_files
[NbConvertApp] Writing 16047 bytes to notebook.tex
[NbConvertApp] Building PDF
[NbConvertApp] Running xelatex 3 times: ['xelatex', 'notebook.tex']
[NbConvertApp] Running bibtex 1 time: ['bibtex', 'notebook']
[NbConvertApp] WARNING | bibtex had problems, most likely because there were no citations
[NbConvertApp] PDF successfully created
[NbConvertApp] Writing 16102 bytes to Example.pdf

Lưu ý đầu ra này khác khá nhiều so với phiên bản HTML. LaTeX đưa ô Markdown đầu tiên vào tiêu đề bài báo, nên nó vẫn hiển thị trong đầu ra. Mẫu đã loại bỏ đúng phần phụ đề (vì xem ô Markdown đó là Markdown thực sự). Lớp tài liệu article cập nhật với ngày hôm nay - điều này không có trong Notebook gốc. Phông chữ và khoảng cách khác với phiên bản HTML. Cuối cùng, theo mặc định đầu ra dùng kiểu hiển thị IPython cổ điển, không phải kiểu "Notebook style".
Nếu bạn chọn mở rộng một lớp tài liệu LaTeX có sẵn trong Jupyter, hãy đảm bảo bạn hiểu định dạng mà nó sẽ sử dụng.
Kết luận
Có một số điểm chính cần nhớ về mẫu, Jinja2 và nbconvert.
Mẫu xác định cách một tài liệu sẽ hiển thị trên trang web hoặc ở định dạng đầu ra khác. Jupyter Notebook triển khai các mẫu Jinja để hiển thị các định dạng xuất khác nhau. Mẫu có quy tắc kế thừa cho phép bạn định nghĩa mẫu cha và mẫu con cho các trang tương tự, với định dạng hơi khác.
Mẫu Jinja có các statements, expressions và (tuỳ chọn) comments. Statements, được xác định bởi {% statement %}, định nghĩa cấu trúc của mẫu. Expressions, được xác định bởi {{ expression }}, điền dữ liệu của bạn vào mẫu. Và comments, được xác định bởi {# comment #}, không được hiển thị trong đầu ra - chúng chỉ dùng nội bộ trong mẫu.
nbconvert là thư viện Python cho phép bạn chuyển đổi Jupyter Notebook sang các định dạng khác như HTML, LaTeX và PDF. nbconvert dùng các mẫu Jinja để xác định cách Jupyter Notebook sẽ hiển thị ở các định dạng này. Bạn có thể định nghĩa mẫu tuỳ chỉnh hoặc mở rộng các mẫu mặc định của Jupyter bằng kế thừa.
Giờ bạn đã sẵn sàng bắt đầu tạo mẫu của riêng mình và xuất các tài liệu Jupyter Notebook đẹp mắt!
Tài liệu tham khảo: