You are on page 1of 101

BỘ GIÁO DỤC & ĐÀO TẠO

TRƯỜNG ĐẠI HỌC KINH TẾ TP.HCM


----

DỰ ÁN KẾT THÚC HỌC PHẦN


MÔN: TRỰC QUAN HOÁ VÀ HỆ THỐNG THÔNG TIN ĐỊA LÝ

Đề tài: Biên dịch sách tài liệu “Python for Data Analysis”

Tên thành viên:


Trần Ngọc Tuyền – 31221023605
Huỳnh Bảo Ngọc Vy – 31221021378
Lớp: IV001
Giảng viên hướng dẫn: Nguyễn Khánh Duy

Thành phố Hồ Chí Minh, ngày 28, tháng 12 năm 2023


PHÂN CHIA CÔNG VIỆC

HỌ VÀ TÊN MSSV CÔNG VIỆC MỨC ĐỘ HOÀN THÀNH


Trần Ngọc Tuyền 31221023605 Trang bìa
Lời mở đầu 100%
Chương 1 & 11
Huỳnh Bảo Ngọc Vy 31221021378 Mục lục
Chương 6 & 7 100%
Kết luận
Tài liệu tham
khảo
MỤC LỤC
LỜI MỞ ĐẦU .........................................................................................................................1
CHƯƠNG 1: MỞ ĐẦU SƠ BỘ .............................................................................................2
CUỐN SÁCH NÀY NÓI VỀ ĐIỀU GÌ? ...................................................................................... 2
TẠI SAO PYTHON CHO PHÂN TÍCH DỮ LIỆU? .................................................................. 2
Python như chất kết dính .......................................................................................................... 3
Giải quyết vấn đề "Hai ngôn ngữ" ........................................................................................... 3
Tại sao không phải là Python? .................................................................................................. 3
NHỮNG THƯ VIỆN PYTHON THIẾT YẾU ............................................................................ 4
NumPy ......................................................................................................................................... 4
pandas ......................................................................................................................................... 4
matplotlib .................................................................................................................................... 5
IPython ........................................................................................................................................ 5
SciPy ............................................................................................................................................ 6
CÀI ĐẶT VÀ THIẾT LẬP ........................................................................................................... 6
Windows...................................................................................................................................... 7
Apple OS X ................................................................................................................................. 8
GNU/Linux ............................................................................................................................... 10
Python 2 and Python 3 ............................................................................................................. 11
Integrated Development Environments (IDEs) ..................................................................... 11
CỘNG ĐỒNG VÀ HỘI NGHỊ ................................................................................................... 11
ĐIỀU HƯỚNG CỦA CUỐN SÁCH ........................................................................................... 12
Ví dụ về mã ............................................................................................................................... 12
Dữ liệu dùng cho các ví dụ ...................................................................................................... 12
Bộ quy ước chung ..................................................................................................................... 13
Thuật ngữ.................................................................................................................................. 13
SỰ CÔNG NHẬN ........................................................................................................................ 13
CHƯƠNG 6: TẢI DỮ LIỆU, LƯU TRỮ VÀ ĐỊNH DẠNG TẬP TIN ..........................15
ĐỌC VÀ GHI DỮ LIỆU Ở ĐỊNH DẠNG VĂN BẢN .............................................................. 15
Đọc tập tin văn bản theo từng phần ....................................................................................... 19
Ghi dữ liệu ra định dạng văn bảng ......................................................................................... 20
Làm việc thủ công với định dạng được phân tách ................................................................ 22
Dữ liệu JSON ............................................................................................................................ 23
XML và HTML: Quét web ..................................................................................................... 24
Phân tích cú pháp XML bằng lxml.objectify ........................................................................ 27
ĐỊNH DẠNG DỮ LIỆU NHỊ PHÂN .......................................................................................... 29
Sử dụng định dạng HDF5 ........................................................................................................ 29
Đọc tệp Microsoft Excel ........................................................................................................... 30
TƯƠNG TÁC VỚI HTLM VÀ API WEB ................................................................................ 30
TƯƠNG TÁC VỚI CƠ SỞ DỮ LIỆU ....................................................................................... 32
Lưu trữ và tải dữ liệu trong cơ sở dữ liệu MongoDB ........................................................... 33
CHƯƠNG 7: SẮP XẾP DỮ LIỆU: LÀM SẠCH, CHUYỂN ĐỔI, HỢP NHẤT, ĐỊNH
HÌNH LẠI .............................................................................................................................34
KẾT HỢP VÀ HỢP NHẤT CÁC TẬP DỮ LIỆU .................................................................... 34
Hợp nhất DataFrame kiểu cơ sở dữ liệu ................................................................................ 34
Hợp nhất chỉ mục ..................................................................................................................... 38
Ghép nối dọc theo một trục ..................................................................................................... 40
Kết hợp dữ liệu với sự chồng chéo .......................................................................................... 44
ĐỊNH DẠNG LẠI VÀ XOAY VÒNG ........................................................................................ 45
Định hình lại bằng lập chỉ mục phân cấp .............................................................................. 45
Xoay định dạng “dài” sang “rộng” ........................................................................................ 47
CHUYỂN ĐỔI DỮ LIỆU ............................................................................................................ 49
Loại bỏ trùng lặp ...................................................................................................................... 49
Chuyển đổi dữ liệu bằng hàm hoặc ánh xạ ............................................................................ 50
Thay thế giá trị ......................................................................................................................... 51
Đổi tên chỉ mục trục ................................................................................................................. 52
Sự rời rạc hóa và Binning........................................................................................................ 53
Phát hiện và lọc các ngoại lệ .................................................................................................... 55
Hoán vị và lấy mẫu ngẫu nhiên .............................................................................................. 56
Chỉ báo tính toán/Biến giả ....................................................................................................... 57
THAO TÁC CHUỖI .................................................................................................................... 59
Phương thức đối tượng chuỗi.................................................................................................. 59
Biểu thức chính quy ................................................................................................................. 60
Các hàm chuỗi được vector hóa trong pandas ...................................................................... 63
VÍ DỤ: CƠ SỞ DỮ LIỆU THỰC PHẨM CỦA USDA ............................................................ 65
CHƯƠNG 11: CÁC ỨNG DỤNG DỮ LIỆU TÀI CHÍNH VÀ KINH TẾ ............................. 70
CHỦ ĐỀ TRỘN DỮ LIỆU .......................................................................................................... 70
Sắp xếp chuỗi thời gian và mặt cắt ngang ............................................................................. 70
Hoạt động với chuỗi thời gian có tần số khác nhau .............................................................. 72
Sử dụng chu kỳ thay vì dấu thời gian..................................................................................... 74
Thời gian trong ngày và lựa chọn dữ liệu “as of” ................................................................. 76
Ghép nối các nguồn dữ liệu với nhau ..................................................................................... 78
Chỉ số trả về và lợi nhuận tích lũy .......................................................................................... 80
BIẾN ĐỔI NHÓM VÀ PHÂN TÍCH ......................................................................................... 82
Nhóm yếu tố rủi ro ................................................................................................................... 84
Phân tích thập phân và tứ phân vị ......................................................................................... 85
CÁC ỨNG DỤNG VÍ DỤ KHÁC ............................................................................................... 87
Phân tích biên giới tín hiệu...................................................................................................... 88
Hợp đồng tương lai luân chuyển ............................................................................................ 90
Tương quan luân chuyển và hồi quy tuyến tính ................................................................... 93
KẾT LUẬN ...........................................................................................................................95
LỜI MỞ ĐẦU
Dịch thuật đóng vai trò quan trọng trong đời sống xã hội và giao lưu văn hoá của con
người, là chiếc cầu nối giúp con người vượt qua những khác biệt về ngôn ngữ, văn hoá và các
kiến thức chuyên sâu từ nhiều nguồn tài liệu khác nhau. Và hôm nay, nhóm chúng em xin
phép được biên dịch quyển sách “Python for Data Analysis: Data Wrangling with Pandas,
Numpy, and IPython” được viết bởi tác giả Wes McKinney. Cuốn sách này cung cấp đầy đủ
kiến thức cũng như rất nhiều ví dụ dễ hiểu để đọc và thực hành phân tích dữ liệu với các thư
viện pandas và numpy. Đặc biệt, tác giả Wes McKinney chính là người tạo ra thư viện pandas,
nên cuốn sách được trình bày rất dễ hiểu ngay cả với những người chỉ mới bắt đầu làm quen
với Python. Bởi vì cuốn sách có phần giới thiệu về các công cụ khoa học dữ liệu sử dụng
Python. Hơn nữa trong quyển sách Python này, bạn cũng sẽ khám phá được phiên bản mới
nhất của NumPy, IPython, Pandas và Jupyter.

Ở đây nhóm chúng em xin được phép biên dịch bốn chương. Chương 1 về các lý thuyết
cơ bản mở đầu sơ bộ giới thiệu về sách và các hàm cơ bản sẽ được hướng dẫn trong sách.
Chương 6 về các cách tải dữ liệu, lưu trữ và cách định dạng tập tin. Ở chương 7 về cách sắp
xếp dữ liệu làm sạch, chuyển đổi, hợp nhất và định hình lại dữ liệu. Và trong chương cuối là
chương 11 là các ứng dụng dữ liệu tài chính và kinh tế sẽ được mô tả chính trong chương này.
Bài dự án này chúng em đã cố gắng biên dịch sát nhất có thể, tuy nhiên trong quá trình làm
dự án, vì biên dịch sách là một bài khá mới với chúng em nên có thể sẽ còn nhiều sai sót khi
chúng em phiên dịch các từ chuyên ngành của môn học, mong thầy sẽ thông cảm cho chúng
em. Chúng em xin cảm ơn quá trình dạy về môn học Trực quan và cụ thể là python của thầy
rất nhiều, đó cũng chính là nguồn cảm hứng cho chúng em lựa chọn quyển sách này.

1
CHƯƠNG 1: MỞ ĐẦU SƠ BỘ
CUỐN SÁCH NÀY NÓI VỀ ĐIỀU GÌ?
Cuốn sách này đề cập đến những chi tiết cơ bản về thao tác, xử lý, làm sạch và xử lý dữ liệu
trong Python. Đây cũng là phần giới thiệu thực tế, hiện đại về tính toán khoa học bằng Python,
được thiết kế riêng cho các ứng dụng sử dụng nhiều dữ liệu chuyên sâu. Đây là một cuốn sách
về các phần của ngôn ngữ và thư viện Python mà bạn sẽ cần để giải quyết một cách hiệu quả
nhiều vấn đề phân tích dữ liệu. Cuốn sách này không phải là một trình bày về các phương
pháp phân tích sử dụng Python làm ngôn ngữ lập trình.

Khi tôi nói "dữ liệu", chính xác thì tôi đang ám chỉ đến điều gì? Trọng tâm chính là dữ liệu
có cấu trúc, một thuật ngữ cố ý mơ hồ bao gồm nhiều loại dữ liệu phổ biến khác nhau, chẳng
hạn như
• Mảng đa chiều (ma trận)
• Dữ liệu dạng bảng hoặc bảng tính trong đó mỗi cột có thể là một loại khác nhau
(chuỗi, số, ngày tháng hoặc loại khác). Điều này bao gồm hầu hết các loại dữ liệu
thường được lưu trữ trong cơ sở dữ liệu quan hệ hoặc các tệp văn bản được phân
cách bằng tab hoặc dấu phẩy
• Nhiều bảng dữ liệu có liên quan với nhau bằng các cột chính (cái gì là chính hoặc
chính các khoá nước ngoài cho người dùng SQL)
• Chuỗi thời gian cách đều hoặc không đều

Đây không phải là một bản danh sách hoàn thiện. Mặc dù nó có thể không phải lúc nào cũng
rõ ràng, nhưng một tỷ lệ lớn các tập dữ liệu có thể được chuyển đổi thành dạng có cấu trúc
phù hợp hơn cho việc phân tích và lập mô hình. Nếu không, có thể tách các đặc điểm từ một
tập dữ liệu thành một dạng có cấu trúc. Ví dụ, một tập hợp các bài báo có thể được xử lý thành
bảng tần số từ sau đó có thể được sử dụng để thực hiện phân tích cảm tính.

Hầu hết người dùng các chương trình bảng tính như Microsoft Excel, có lẽ là công cụ phân
tích dữ liệu được sử dụng rộng rãi nhất trên thế giới, sẽ không còn xa lạ với những loại dữ
liệu này.

TẠI SAO PYTHON CHO PHÂN TÍCH DỮ LIỆU?


Đối với nhiều người (bao gồm cả tôi), ngôn ngữ Python rất dễ yêu thích. Kể từ lần xuất hiện
đầu tiên vào năm 1991, Python trở thành một trong những ngôn ngữ lập trình, năng động nhất,
cùng với Perl, Ruby, và nhiều ngôn ngữ khác. Python và Ruby trở nên đặc biệt phổ biến trong
những năm gần đây để xây dựng các trang web sử dụng nhiều khung web của họ, như Rails
(Ruby) và Django (Python). Những ngôn ngữ như thế thường gọi là ngôn ngữ kịch bản như
chúng có thể được sử dụng để viết các chương trình nhỏ nhanh và đơn giản, hoặc kịch bản.
Tôi không thích thuật ngữ "ngôn ngữ kịch bản" vì nó mang ý nghĩa rằng chúng không thể
được sử dụng để xây dựng phần mềm mang nhiệm vụ quan trọng. Trong số các ngôn ngữ
được giải thích Python nổi bật bởi cộng đồng máy tính khoa học rộng lớn và năng động của
nó. Việc áp dụng Python cho tính toán khoa học trong cả các ứng dụng công nghiệp và nghiên
cứu học thuật đã tăng lên đáng kể từ đầu những năm 2000.

Để phân tích dữ liệu và tương tác, khám phá và trực quan hoá dữ liệu, Python chắc chắn sẽ
đưa ra so sánh với nhiều ngôn ngữ và công cụ lập trình thương mại chuyên biệt miền khác
2
được sử dụng rộng rãi, chẳng hạn như R, MATLAB, SAS, Stata, và những thứ khác. Trong
những năm gần đây, Python đã cải thiện việc hỗ trợ thư viện (chủ yếu là Pandas) để thực hiện
các tác vụ xử lý dữ liệu. Kết hợp với điểm mạnh của Python trong lập trình đa mục đích, đây
là một lựa chọn tuyệt vời như một ngôn ngữ duy nhất để xây dựng các ứng dụng lấy dữ liệu
làm trung tâm.

Python như chất kết dính


Một phần thành công của Python trong nền tảng điện toán khoa học là việc dễ dàng tích hợp
mã C, C++, và FORTRAN. Hầu hết các môi trường điện toán hiện đại đều có chung một bộ
thư viện FORTRAN và C để thực hiện đại số tuyến tính, tối ưu hóa, tích hợp, biến đổi phạm
vi nhanh và các thuật toán khác. Câu chuyện này cũng đúng đối với nhiều công ty và các
phòng thí nghiệm quốc gia đã sử dụng Python để gắn kết phần mềm kế thừa có giá trị 30 năm
lại với nhau.

Hầu hết các chương trình bao gồm các phần nhỏ mã chiếm phần lớn thời gian, với một lượng
lớn "mã keo" không chạy thường xuyên. Trong nhiều trường hợp, thời gian thực thi mã keo
không đáng kể; nỗ lực được đầu tư hiệu quả nhất trong việc tối ưu hóa các tắc nghẽn tính
toán, đôi khi bằng cách chuyển mã sang một ngôn ngữ cấp thấp hơn như C.

Trong vài năm gần đây, dự án Cython (http://cython.org) đã trở thành một trong những cách
được ưa thích để vừa tạo ra phần mở rộng được biên dịch nhanh cho Python và vừa tương tác
với mã C và C++.

Giải quyết vấn đề "Hai ngôn ngữ"


Trong nhiều tổ chức, nó phổ biến để nghiên cứu, tạo nguyên mẫu, và thử nghiệm các ý tưởng
mới bằng cách sử dụng một ngôn ngữ máy tính dành riêng cho miền chuyên biệt hơn như
MATLAB hoặc R sau đó chuyển những ý tưởng đó để trở thành một phần của một hệ thống
sản xuất lớn hơn được viết bằng Java, C#, hoặc C++. Mọi người đang ngày càng thấy rằng
Python là một ngôn ngữ thích hợp không chỉ để nghiên cứu và tạo mẫu mà còn xây dựng các
hệ thống sản xuất nữa. Tôi tin rằng ngày càng nhiều công ty sẽ đi theo con đường này vì
thường có lợi ích về mặt tổ chức quan trọng cho việc có cả nhà khoa học và kỹ thuật viên sử
dụng cùng một bộ công cụ lập trình.

Tại sao không phải là Python?


Mặc dù Python là một môi trường tuyệt vời để xây dựng các ứng dụng khoa học chuyên sâu
vào tính toán và xây dựng hầu hết các loại hệ thống chung, nhưng có một số ứng dụng mà
Python có thể ít phù hợp hơn.

Python là một ngôn ngữ lập trình được giải thích, nói chung hầu hết mã Python chạy chậm
hơn so với mã viết bằng ngôn ngữ biên dịch như Java hoặc C++. Giống như thời gian lập
trình viên thường có giá trị hơn thời gian CPU, nhiều người vui mừng khi thực hiện sự đánh
đổi này. Tuy nhiên, trong một ứng dụng với các yêu cầu về độ trễ rất thấp (ví dụ, một hệ thống
giao dịch tần số cao), thời gian dành cho việc lập trình bằng ngôn ngữ cấp thấp hơn, năng suất
thấp hơn như C++ để đạt được hiệu suất tối đa có thể là thời gian xứng đáng.

Python không phải là một ngôn ngữ lý tưởng cho các ứng dụng đa luồng, tính đồng bộ cao,
đặc biệt là các ứng dụng có nhiều luồng liên kết với CPU. Lý do cho điều này là nó có cái
3
được gọi là khoá phiên dịch toàn cầu (GIL), một cơ chế ngăn chặn trình thông dịch thực hiện
nhiều hơn một lệnh mã byte Python cùng một lúc. Các lý do kỹ thuật giải thích tại sao GIL
tồn tại là vượt quá phạm vi của cuốn sách này, nhưng nhưng tính đến thời điểm viết bài này
dường như không có khả năng GIL sẽ biến mất sớm. Mặc dù đúng là trong nhiều ứng dụng
xử lý dữ liệu lớn, một cụm máy tính có thể được yêu cầu để xử lý một tập dữ liệu trong khoảng
thời gian hợp lý, nhưng vẫn có những tình huống cần một hệ thống đơn, xử lý đa luồng.

Điều này không có nghĩa là Python không thể thực thi mã song song, đa luồng thực sự; mã
đó không thể được thực thi chỉ trong một tiến trình Python. Ví dụ, dự án Cython có tính năng
tích hợp dễ dàng với OpenMP, khung C cho tính toán song song, nhằm song song hóa các
vòng lặp và do đó tăng tốc đáng kể các thuật toán số.

NHỮNG THƯ VIỆN PYTHON THIẾT YẾU


Đối với những người không quen thuộc với hệ sinh thái khoa học Python và các thư viện được
dùng xuyên suốt cuốn sách, tôi trình bày sơ lược sau đây về từng thư viện.

NumPy
NumPy, viết tắt của Numerical Python, là gói nền tảng cho tính toán khoa học bằng Python.
Phần lớn cuốn sách này sẽ dựa trên NumPy và các thư viện được xây dựng dựa trên NumPy.
Ngoài những điều khác, nó còn cho thấy:

• Một đối tượng mảng đa chiều nhanh chóng và hiệu quả ndarray
• Các hàm để thực hiện các tính toán theo từng phần tử với mảng hoặc các phép toán
giữa các mảng
• Các công cụ để đọc và ghi bộ dữ liệu theo mảng vào đĩa
• Phép toán đại số tuyến tính, biến đổi Fourier và tạo số ngẫu nhiên
• Công cụ để tích hợp kết nối mã C, C++, và Fortran với Python

Ngoài khả năng xử lý mảng nhanh mà NumPy bổ sung vào Python, một trong những mục
đích chính của nó liên quan đến phân tích dữ liệu là làm nơi chứa chính cho dữ liệu được
truyền giữa các thuật toán. Đối với dữ liệu số, mảng NumPy là một cách lưu trữ và thao tác
dữ liệu hiệu quả hơn nhiều so với các cấu trúc dữ liệu Python tích hợp khác. Ngoài ra, các thư
viện được viết bằng ngôn ngữ cấp thấp hơn, như C hoặc Fortran, có thể hoạt động trên dữ liệu
được lưu trong mảng NumPy mà không cần sao chép bất kỳ dữ liệu nào.

pandas
Pandas cung cấp các cấu trúc dữ liệu phong phú và các chức năng được thiết kế để làm việc
với dữ liệu cấu trúc nhanh, dễ dàng và mang tính biểu cảm. Như các bạn sẽ thấy, nó là một
trong những yếu tố quan trọng giúp Python trở thành một môi trường phân tích dữ liệu mạnh
và hiệu quả. Đối tượng chính trong Pandas sẽ được sử dụng trong cuốn sách này là DataFrame,
một cấu trúc dữ liệu hướng hai chiều với nhãn hàng và cột:

>>> frame
total_bill tip sex smoker day time size
1 16.99 1.01 Female No Sun Dinner 2
2 10.34 1.66 Male No Sun Dinner 3
3 21.01 3.5 Male No Sun Dinner 3
4
4 23.68 3.31 Male No Sun Dinner 2
5 24.59 3.61 Female No Sun Dinner 4
6 25.29 4.71 Male No Sun Dinner 4
7 8.77 2 Male No Sun Dinner 2
8 26.88 3.12 Male No Sun Dinner 4
9 15.04 1.96 Male No Sun Dinner 2
10 14.78 3.23 Male No Sun Dinner 2

Pandas kết hợp các tính năng tính toán mảng hiệu suất cao của NumPy với khả năng thao tác
dữ liệu linh hoạt của bảng tính và cơ sở dữ liệu quan hệ (chẳng hạn như SQL). Nó cung cấp
chức năng lập chỉ mục tinh vi để làm cho nó dễ dàng định hình lại, cắt lát, thực hiện tổng hợp,
và chọn các tập hợp con của dữ liệu. Pandas là công cụ chính mà chúng ta sẽ dùng trong cuốn
sách này.

Đối với người dùng tài chính, pandas có tính năng phong phú, tính năng chuỗi thời gian hiệu
suất cao và các công cụ rất phù hợp để làm việc với dữ liệu tài chính. Thực ra, ban đầu tôi đã
thiết kế pandas như một công cụ lý tưởng cho các ứng dụng phân tích dữ liệu tài chính.

Đối với người dùng ngôn ngữ R để tính toán thống kê, tên DataFrame sẽ rất quen thuộc, vì
đối tượng được đặt tên theo đối tượng R data.frame tương tự. Tuy nhiên, chúng không giống
nhau; Các chức năng được cung cấp bởi data.frame trong R về cơ bản là một tập hợp con
nghiêm ngặt trong đó được cung cấp bởi DataFrame pandas. Mặc dù đây là một cuốn sách về
Python, nhưng thỉnh thoảng tôi sẽ đưa ra so sánh với R vì nó là một trong những môi trường
phân tích dữ liệu nguồn mở được sử dụng rộng rãi nhất và sẽ quen thuộc với nhiều độc giả.

Tên Pandas chính nó có nguồn gốc từ panel data, một thuật ngữ kinh tế lượng cho các bộ dữ
liệu cấu trúc đa chiều và phân tích dữ liệu python chính nó

matplotlib
matplotlib là thư viện Python phổ biến nhất để tạo biểu đồ và trực quan hóa dữ liệu. Nó được
tạo ra bởi John D. Hunter (JDH) và hiện được duy trì bởi một nhóm lớn các nhà phát triển.
Nó rất phù hợp để tạo ra biểu đồ phù hợp để công bố. Nó tích hợp tốt với IPython (xem bên
dưới), do đó cung cấp một môi trường tương tác thoải mái để vẽ đồ thị và khám phá dữ liệu.
Biểu đồ cũng có tính tương tác Bạn có thể phóng to một phần của biểu đồ và xoay xung quanh
biểu đồ bằng cách sử dụng thanh công cụ trong cửa sổ biểu đồ.

IPython
IPython là thành phần trong bộ công cụ Python chuẩn gắn kết mọi thứ lại với nhau. Nó cung
cấp một môi trường mạnh mẽ và hiệu quả cho tính toán tương tác và khám phá. Nó là Python
shell nâng cao được thiết kế để tăng tốc việc viết, kiểm tra và gỡ lỗi mã Python. Nó đặc biệt
hữu ích cho việc tương tác làm việc với dữ liệu và trực quan hóa dữ liệu với Matplotlib.
IPython thường tham gia vào phần lớn các công việc Python của tôi, bao gồm chạy, gỡ lỗi và
kiểm tra mã.
Bên cạnh tiêu chuẩn IPython Shell dựa trên thiết bị đầu cuối tiêu chuẩn, dự án cũng cung cấp

• Một sổ ghi chép HTML giống Mathematica để kết nối IPython thông qua trình
duyệt web (sẽ nói thêm về điều này sau).
5
• Bảng điều khiển GUI dựa trên khung công tác Qt với tính năng vẽ biểu đồ nội
tuyến, chỉnh sửa đa tuyến, và nhấn mạnh cú pháp
• Cơ sở hạ tầng cho tính toán tương tác song song và phân tán

Tôi sẽ dành một chương cho IPython và cách tận dụng tối đa các tính năng của nó. Tôi thật
sự khuyên bạn nên sử dụng nó khi đọc cuốn sách này.

SciPy
SciPy là một tập hợp các gói giải quyết một số miền vấn đề tiêu chuẩn khác nhau trong tính
toán khoa học. Dưới đây là mẫu của các gói bao gồm:
• scipy.integrate: Các phương pháp tích phân số và các bộ giải phương trình vi phân
• scipy.linalg: các thủ tục đại số tuyến tính và phân rã ma trận mở rộng hơn những
gì được cung cấp trong numpy.linalg.
• scipy.optimize: bộ tối ưu hóa chức năng (bộ giảm thiểu) và thuật toán tìm gốc
• scipy.signal Công cụ xử lý tín hiệu
• scipy.sparse: ma trận thưa thớt và bộ giải hệ thống tuyến tính thưa thớt
scipy.special: trình bao bọc xung quanh SPECFUN, thư viện Fortran triển khai
nhiều hàm toán học phổ biến, chẳng hạn như hàm gamma
• scipy.stats: Phân phối xác suất liên tục và rời rạc tiêu chuẩn (hàm mật độ, bộ lấy
mẫu, hàm phân phối liên tục), các kiểm tra thống kê khác nhau và thống kê mô tả
khác
• scipy.weave: Công cụ dùng mã nội tuyến C++ để tăng tốc độ tính toán mảng

NumPy và SciPy cùng nhau tạo thành một sự thay thế tính toán hoàn chỉnh hợp lý cho nhiều
MATLAB cùng với một số hộp công cụ bổ sung của nó.

CÀI ĐẶT VÀ THIẾT LẬP


Vì mọi người đều sử dụng Python cho các ứng dụng khác nhau, không có giải pháp duy nhất
để thiết lập Python và các gói tiện ích bổ sung cần thiết. Nhiều độc giả sẽ không có môi trường
Python khoa học hoàn chỉnh phù hợp để theo dõi cuốn sách này, vì vậy ở đây tôi sẽ đưa ra
các hướng dẫn chi tiết để thiết lập trên mỗi hệ điều hành. Tôi khuyên bạn nên sử dụng một
trong những bản phân phối Python cơ bản:

• Enthought Python Distribution: một bản phân phối Python theo định hướng khoa
học từ Enthought (http://www.enthought.com). Điều này bao gồm EPDFree, một
phân phối khoa học cơ sở miễn phí (với NumPy, SciPy, matplotlib, Chaco, và
IPython) và EPD Full, một bộ toàn diện gồm hơn 100 gói khoa học trên nhiều lĩnh
vực. EPD Full miễn phí cho mục đích học tập nhưng có đăng ký hàng năm cho
người dùng phi học thuật.
• Python (x, y) (http://pythonxy.googlecode.com) : Bản phân phối Python theo định
hướng khoa học miễn phí dành cho Windows.

Tôi sẽ sử dụng EPDFree cho hướng dẫn cài đặt, tuy nhiên bạn có thể sử dụng cách tiếp
cận khác phụ thuộc vào nhu cầu của bạn. Tại thời điểm viết này, EPD bao gồm Python
2.7, mặc dù điều này có thể thay đổi tại một thời điểm nào đó trong tương lai. Sau khi cài
đặt, bạn sẽ có các gói sau và nhập mật khẩu:

6
• Cơ sở Python khoa học: NumPy, SciPy, matplotlib, và IPython. Tất cả đều nằm
trong EPDFree.
• IPython Notebook Dependencies: Tornado và Pyzmq. Chúng được bao gồm trong
EPDFree.
• Pandas (phiên bản 0.8.2 hoặc cao hơn).

Tại một thời điểm nào đó trong khi đọc bạn có thể muốn cài đặt một hoặc nhiều gói sau:
statsmodels, PyTables, PyQt (hoặc tương đương, PySide), xlrd, lxml, basemap, pymongo, và
các yêu cầu. Chúng được sử dụng trong nhiều ví dụ khác nhau. Việc cài đặt các thư viện tùy
chọn này là không cần thiết, và tôi sẽ đề nghị là chờ cho đến khi bạn cần chúng. Ví dụ, việc
cài đặt PyQt hoặc PyTables từ nguồn trên OS X hoặc Linux có thể khá khó khăn. Hiện tại,
điều quan trọng nhất là bắt đầu và chạy với một mức tối thiểu: EPDFree và Pandas.

Để biết thông tin trên mỗi gói Python và các liên kết đến trình cài đặt nhị phân hoặc trợ giúp
khác, xem Python Package Index (PyPI, http://pypi.python.org). Đây cũng là một nguồn tài
nguyên tuyệt vời để tìm các gói Python mới.

Để tránh nhầm lẫn và giữ cho mọi việc đơn giản, tôi tránh thảo luận các công cụ
quản lý môi trường phức tạp hơn như pip và virtualenv. Có rất nhiều hướng dẫn
tuyệt vời cho các công cụ này trên Internet.

Một số người dùng có thể quan tâm đến việc triển khai Python thay thế, chẳng
hạn như IronPython, Jython, hoặc PyPy. để tận dụng những công cụ được trình
bày Cuốn sách này (hiện tại) cần phải sử dụng Python dựa trên C tiêu chuẩn
thông dịch viên, được gọi là CPython.

Windows
Để bắt đầu trên Windows, tải xuống trình cài đặt EPDFree từ http://www.enthought.com, đây
phải là trình cài đặt MSI có tên epd_free-7.3-1-winx86.msi. Chạy trình cài đặt và chấp nhận
vị trí cài đặt mặc định C:\Python27. Nếu trước đó bạn đã cài đặt Python ở vị trí này, bạn có
thể muốn xoá nó theo cách thủ công (hoặc dùng các chương trình Add/Remove).

Tiếp theo, bạn cần kiểm chứng Python đã được thêm vào đường dẫn của hệ thống một cách
thành công và không có xung đột với bất kì phiên bản Python nào được cài đặt trước đó.
Trước tiên, mở một dấu nhắc lệnh bằng cáchvào Menu Start và khởi động ứng dụng the
Command Prompt application, còn được gọi là cmd.exe. Thử bắt đầu trình thông dịch Python
bằng gõ python. Bạn sẽ thấy thông báo khớp với phiên bản EPDFree mà bạn đã cài đặt:
C:\Users\Wes>python
Python 2.7.3 |EPD_free 7.3-1 (32-bit)| (default, Apr 12 2012, 14:30:37) on win32
Type "credits", "demo" or "enthought" for more information.
>>>
Nếu bạn thấy một thông điệp cho một phiên bản khác của EPD hoặc nó không hoạt động gì
cả, bạn sẽ cần phải làm sạch các biến môi trường Windows của bạn. Trên Windows 7 bạn có
thể bắt đầu gõ " environment variables " trong trường tìm kiếm chương trình và chọn Edit
environment variables for your account. Trên Windows XP, bạn sẽ phải lên Control Panel >
System > Advanced > Environment Variables. Trên cửa sổ hiện ra, bạn đang tìm kiếm biến
đường đi. Cần chứa hai đường dẫn thư mục sau, tách ra bằng các dấu chấm phẩy:

7
C:\Python27;C:\Python27\Scripts

Nếu bạn cài đặt các phiên bản khác của Python, hãy nhớ xóa mọi thư mục liên quan đến
Python khác khỏi cả biến đường dẫn hệ thống và người dùng Path. Sau khi thực hiện thay đổi
đường dẫn, bạn phải khởi động lại lệnh nhắc nhở cho các thay đổi có hiệu lực.

Một khi bạn có thể khởi chạy Python thành công từ dấu nhắc lệnh, bạn cần phải cài pandas.
Cách dễ nhất là tải xuống trình cài đặt nhị phân thích hợp từ
http://pypi.python.org/pypi/pandas. Đối với EPDFree, nó nên là pandas-0.9.0.win32-
py2.7.exe. Sau khi chạy chương trình này, chúng ta hãy khởi động IPython và kiểm tra xem
mọi thứ đã được cài đặt đúng cách bằng cách nhập pandas và tạo ra một biểu đồ matplotlib
đơn giản:

C:\Users\Wes>ipython --pylab
Python 2.7.3 |EPD_free 7.3-1 (32-bit)|
Type "copyright", "credits" or "license" for more information.

IPython 0.12.1 -- An enhanced Interactive Python.


? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.

Welcome to pylab, a matplotlib-based Python environment [backend:WXAgg].


For more information, type 'help(pylab)'.
n [1]: import pandas
n [2]: plot(arange(10))

Nếu thành công, sẽ không có thông báo lỗi và cửa sổ vẽ đồ thị sẽ xuất hiện. Bạn cũng có thể
kiểm tra xem sổ ghi chép HTML IPython có thể chạy thành công hay không bằng cách nhập:
$ ipython notebook --pylab=inline

Nếu bạn sử dụng ứng dụng sổ ghi chép IPython trên Windows và thường
sử dụng Internet Explorer, bạn có thể sẽ cần cài đặt và chạy Mozilla
Thay vào đó là Firefox hoặc Google Chrome.

EPDFree trên Windows chỉ chứa các tệp thực thi 32 bit. Nếu bạn muốn hoặc cần thiết lập 64-
bit trên Windows, sử dụng EPD Full là cách dễ dàng nhất để thực hiện điều đó. Nếu bạn muốn
cài đặt từ đầu và không trả tiền đăng ký EPD, Christoph Gohlke tại Đại học California, Irvine,
xuất bản các trình cài đặt nhị phân không chính thức cho tất cả các gói cần thiết của cuốn sách
(http://www.lfd.uci.edu/~gohlke/pythonlibs/) cho Windows 32 và 64 bit.

Apple OS X
Để bắt đầu sử dụng OS X, trước tiên bạn phải cài đặt Xcode, bao gồm bộ công cụ phát triển
phần mềm của Apple. Thành phần cần thiết cho mục đích của chúng tôi là bộ biên dịch gcc

8
C và C++. Bạn có thể tìm thấy trình cài đặt Xcode trên DVD cài đặt OS X đi kèm với máy
tính của bạn hoặc được tải xuống trực tiếp từ Apple.

Sau khi bạn đã cài đặt Xcode, hãy khởi chạy thiết bị đầu cuối (Terminal.app) bằng cách điều
hướng đến Applications > Utilities Kiểu gcc và nhấn enter. Bạn có thể sẽ thấy một số điều
như:
$ gcc
i686-apple-darwin10-gcc-4.2.1: no input files

Bây giờ bạn cần cài đặt EPDFree. Tải xuống trình cài đặt phải là hình ảnh đĩa có tên như
epd_free-7.3-1-macosx-i386.dmg. Bấm đúp vào tập tin .dmg để gắn nó, sau đó bấm đúp vào
tập tin .mpkg bên trong để chạy trình cài đặt.

Khi trình cài đặt chạy, nó sẽ tự động thêm đường dẫn thực thi EPDFree vào tài liệu
.bash_profile. Cái này nằm ở /Users/your_uname/.bash_profile:

# Setting PATH for EPD_free-7.3-1


PATH="/Library/Frameworks/Python.framework/Versions/Current/bin:${PATH}"
export PATH

Nếu bạn gặp phải bất kỳ vấn đề nào trong các bước sau, bạn sẽ muốn kiểm tra .bash_profile
và có khả năng thêm thư mục trên vào đường dẫn của bạn.
Bây giờ là lúc cài đặt pandas. Thực hiện lệnh này trong terminal:

$ sudo easy_install pandas


Searching for pandas
Reading http://pypi.python.org/simple/pandas/
Reading http://pandas.pydata.org
Reading http://pandas.sourceforge.net
Best match: pandas 0.9.0
Downloading http://pypi.python.org/packages/source/p/pandas/pandas-0.9.0.zip
Processing pandas-0.9.0.zip
Writing /tmp/easy_install-H5mIX6/pandas-0.9.0/setup.cfg
Running pandas-0.9.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-H5mIX6/
pandas-0.9.0/egg-dist-tmp-RhLG0z
Adding pandas 0.9.0 to easy-install.pth file
Installed /Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/
site-packages/pandas-0.9.0-py2.7-macosx-10.5-i386.egg
Processing dependencies for pandas
Finished processing dependencies for pandas

Để xác minh mọi thứ đang hoạt động, hãy khởi chạy IPython ở chế độ Pylab và kiểm tra việc
nhập pandas sau đó tạo một biểu đồ tương tác:

$ ipython --pylab
22:29 ~/VirtualBox VMs/WindowsXP $ ipython
Python 2.7.3 |EPD_free 7.3-1 (32-bit)| (default, Apr 12 2012, 11:28:34)

9
Type "copyright", "credits" or "license" for more information.
IPython 0.12.1 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
Welcome to pylab, a matplotlib-based Python environment [backend: WXAgg].
For more information, type 'help(pylab)'.
In [1]: import pandas
In [2]: plot(arange(10))

Nếu thành công, một cửa sổ đồ thị có đường thẳng sẽ bật lên

GNU/Linux

Một số, nhưng không phải tất cả, các bản phân phối Linux bao gồm các bản
cập nhật đầy đủ phiên bản của tất cả các gói Python cần thiết và có thể được
cài đặt bằng cách sử dụng công cụ quản lý gói tích hợp như apt. Tôi thiết lập
chi tiết bằng EPDFree vì nó có thể dễ dàng tái tạo trên các bản phân phối.

Thông tin chi tiết về Linux sẽ thay đổi một chút tùy theo phiên bản Linux của bạn, nhưng ở
đây tôi cung cấp thông tin chi tiết về các hệ thống GNU/Linux dựa trên Debian như Ubuntu
và Mint. Thiết lập tương tự như OS X ngoại trừ cách cài đặt EPDFree. Trình cài đặt là tập
lệnh shell phải được thực thi trong thiết bị đầu cuối. Tùy thuộc vào việc bạn có hệ thống 32
bit hay 64 bit, bạn sẽ cần phải cài đặt trình cài đặt x86 (32-bit) hoặc x86-64 (64-bit). Sau đó
bạn sẽ có một tập tin có tên tương tự như epd_free-7.3-1-rh5-x86_64.sh. Để cài đặt nó, hãy
thực thi tập lệnh này bằng bash:

$ bash epd_free-7.3-1-rh5-x86_64.sh

Sau khi chấp nhận giấy phép, bạn sẽ được lựa chọn nơi đặt các tệp EPDFree. Tôi khuyên bạn
nên cài đặt các tập tin trong thư mục chính của bạn, chẳng hạn như /home/wesm/ epd (thay
thế tên người dùng của riêng bạn cho wesm).

Sau khi trình cài đặt hoàn tất, bạn cần them thư mục EPDFree's bin của bạn vào giá trị $PATH.
Nếu bạn đang sử dụng bash shell (ví dụ: mặc định trong Ubuntu), điều này có nghĩa là thêm
phần bổ sung đường dẫn sau vào .bashrc:

export PATH=/home/wesm/epd/bin:$PATH

Tất nhiên, hãy thay thế thư mục cài đặt mà bạn đã sử dụng /home/wesm/epd/. Sau khi thực
hiện việc này, bạn có thể bắt đầu một quy trình đầu cuối mới hoặc thực thi .bashrc một lần
nữa với source ~/.bashrc.

Bạn cần một trình biên dịch C như gcc để chuyển tiếp; nhiều bản phân phối Linux bao gồm
gcc, nhưng một số khác thì không. Trên hệ thống Debian, bạn có thể cài đặt gcc bằng cách
thực thi:
10
sudo apt-get install gcc
Nếu bạn gõ gcc trên dòng lệnh nó sẽ viết điều gì đó như:
$ gcc
gcc: no input files
Bây giờ là lúc cài đặt pandas:
$ easy_install pandas
Nếu bạn đã cài đặt EPDFree với quyền root, bạn có thể cần thêm sudo vào lệnh và nhập mật
khẩu sudo hoặc root. Để xác minh mọi thứ đang hoạt động, hãy thực hiện các bước kiểm tra
tương tự như trong phần OS X.

Python 2 and Python 3


Cộng đồng Python hiện đang trải qua quá trình chuyển đổi kéo dài từ loạt trình thông dịch
Python 2 sang loạt Python 3. Cho đến khi Python 3.0 xuất hiện, tất cả mã Python đều tương
thích ngược. Cộng đồng quyết định rằng để đưa ngôn ngữ chuyển tiếp, một số thay đổi không
tương thích ngược là cần thiết.

Tôi đang viết cuốn sách này với Python 2.7 làm nền tảng, vì phần lớn cộng đồng Python khoa
học vẫn chưa chuyển sang Python 3. Tin vui là, với một vài ngoại lệ, bạn sẽ không gặp khó
khăn gì khi theo dõi cuốn sách nếu bạn tình cờ đang sử dụng Python 3.2.

Integrated Development Environments (IDEs)


Khi được hỏi về môi trường phát triển tiêu chuẩn của tôi, tôi hầu như luôn nói “IPython cộng
hưởng việc trình soạn thảo văn bản”. Tôi thường viết một chương trình và lặp đi lặp lại việc
kiểm tra và gỡ lỗi từng phần của chương trình đó trong IPython. Cũng rất hữu ích khi có thể
xử lý dữ liệu một cách tương tác và xác minh trực quan rằng một tập hợp thao tác dữ liệu cụ
thể đang thực hiện đúng. Các thư viện như pandas và NumPy được thiết kế để dễ sử dụng
trong shell.

Tuy nhiên, một số vẫn thích làm việc trong IDE thay vì trình soạn thảo văn bản. Chúng cung
cấp nhiều tính năng “mã thông minh” tốt như hoàn thành hoặc nhanh chóng lấy tài liệu liên
quan đến các chức năng và lớp. Dưới đây là một số thứ mà bạn có thể khám phá:
• Eclipse with PyDev Plugin
• Python Tools for Visual Studio (for Windows users)
• PyCharm
• Spyder
• Komodo IDE

CỘNG ĐỒNG VÀ HỘI NGHỊ


Ngoài việc tìm kiếm trên Internet, danh sách gửi thư khoa học Python nhìn chung rất hữu ích
và đáp ứng các câu hỏi. Một số cái cần xem xét là:
• pydata: danh sách Google Group dành cho các câu hỏi liên quan đến Python để phân tích
dữ liệu và pandas
• pystatsmodels: dành cho các mô hình thống kê hoặc các câu hỏi liên quan đến pandas
• numpy-discussion: dành cho các câu hỏi liên quan đến NumPy
• scipy-user: dành cho các câu hỏi chung về SciPy hoặc khoa học Python

11
Tôi đã cố tình không đăng URL của những trang này phòng trường hợp chúng thay đổi. Chúng
có thể được định vị dễ dàng thông qua tìm kiếm trên Internet.

Mỗi năm có nhiều hội nghị được tổ chức trên toàn thế giới dành cho các lập trình viên Python.
PyCon và EuroPython lần lượt là hai hội nghị Python chung chính ở Hoa Kỳ và Châu Âu.
SciPy và EuroSciPy là các hội nghị Python theo định hướng khoa học, nơi bạn có thể sẽ tìm
thấy nhiều “những con chim lông vũ” nếu bạn quan tâm nhiều hơn đến việc sử dụng Python
để phân tích dữ liệu sau khi đọc cuốn sách này.

ĐIỀU HƯỚNG CỦA CUỐN SÁCH


Nếu bạn chưa từng lập trình Python trước đây, bạn có thể thực sự muốn bắt đầu từ end của
cuốn sách, nơi tôi đã đưa ra một hướng dẫn cô đọng về cú pháp Python, các tính năng ngôn
ngữ và các cấu trúc dữ liệu tích hợp như bộ dữ liệu, danh sách và lệnh. Những điều này được
coi là kiến thức tiên quyết cho phần còn lại của cuốn sách.

Cuốn sách bắt đầu bằng việc giới thiệu cho bạn môi trường IPython. Tiếp theo, tôi giới thiệu
ngắn gọn về các tính năng chính của NumPy, để lại cách sử dụng NumPy nâng cao hơn cho
một chương khác ở cuối cuốn sách. Sau đó, tôi giới thiệu về pandas và dành phần còn lại của
cuốn sách cho các chủ đề phân tích dữ liệu áp dụng pandas, NumPy và matplotlib (để trực
quan hóa). Tôi đã cấu trúc tài liệu theo cách tăng dần nhất có thể, mặc dù đôi khi có một số
điểm đan xen nhỏ giữa các chương.

Các tệp dữ liệu và tài liệu liên quan cho mỗi chương được lưu trữ dưới dạng
http://github.com/pydata/pydata-book
Tôi khuyến khích bạn tải xuống dữ liệu và sử dụng nó để sao chép các ví dụ về mã của cuốn
sách và thử nghiệm các công cụ được trình bày trong mỗi chương. Tôi sẽ vui vẻ chấp nhận
những đóng góp, tập lệnh, sổ ghi chép IPython hoặc bất kỳ tài liệu nào khác mà bạn muốn
đóng góp vào kho sách để mọi người cùng thưởng thức.

Ví dụ về mã
Hầu hết các ví dụ về mã trong sách đều được hiển thị với đầu vào và đầu ra giống như được
thực thi trong shell IPython.
In [5]: code
Out[5]: output
Đôi khi, để rõ ràng, nhiều ví dụ về mã sẽ được hiển thị cạnh nhau. Chúng nên được đọc từ
trái sang phải và thực hiện riêng biệt.
In [5]: code In [6]: code2
Out[5]: output Out[6]: output2

Dữ liệu dùng cho các ví dụ


Bộ dữ liệu cho các ví dụ trong mỗi chương được lưu trữ trong kho lưu trữ trên GitHub:
http://github.com/pydata/pydata-book. Bạn có thể tải xuống dữ liệu này bằng cách sử dụng
chương trình dòng lệnh kiểm soát sửa đổi git hoặc bằng cách tải xuống tệp zip của kho lưu
trữ từ trang web.

12
Tôi đã cố gắng hết sức để đảm bảo rằng nó chứa mọi thứ cần thiết để mô tả lại các ví dụ,
nhưng tôi có thể đã mắc một số lỗi hoặc thiếu sót. Nếu được xin vui lòng gửi email cho tôi:
wesmckinn@gmail.com.

Bộ quy ước chung


Cộng đồng Python đã áp dụng một số quy ước đặt tên cho các mô-đun thường được sử dụng:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Điều này có nghĩa là khi bạn nhìn thấy np.arange, đây là một tài liệu tham khảo cho chức
năng arrange trong NumPy. Điều này được thực hiện vì việc nhập mọi thứ (from numpy
import *) từ một gói lớn như NumPy.

Thuật ngữ
Tôi sẽ sử dụng một số thuật ngữ phổ biến cho cả lập trình và khoa học dữ liệu mà bạn có thể
không quen. Vì vậy, đây là một số định nghĩa ngắn gọn:

Munge/Munging/Wrangling
Mô tả toàn bộ quá trình xử lý dữ liệu phi cấu trúc/hoặc dữ liệu lộn xộn thành
dạng có cấu trúc hoặc rõ ràng. Từ này đã lẻn vào thuật ngữ của nhiều hacker
dữ liệu hiện đại. Munge có vần với “lunge”.
Pseudocode
Mô tả về một thuật toán hoặc quy trình có dạng giống mã trong khi có thể
không phải là mã nguồn hợp lệ thực tế.
Syntactic sugar
Cú pháp lập trình không thêm các tính năng mới nhưng làm cho nội dung nào
đó thuận tiện hơn hoặc dễ gõ hơn.

SỰ CÔNG NHẬN
Sẽ rất khó cho tôi khi viết cuốn sách này nếu không có sự ủng hộ của đông đảo mọi người.

Về đội ngũ nhân viên của O'Reilly, tôi rất biết ơn các biên tập viên Meghan Blanchette và
Julie Steele, những người đã hướng dẫn tôi trong suốt quá trình này. Mike Loukides cũng làm
việc với tôi trong giai đoạn đề xuất và giúp biến cuốn sách thành hiện thực.
Tôi đã nhận được rất nhiều đánh giá kỹ thuật từ nhiều nhân vật. Đặc biệt, Martin Blais và
Hugh White đã cực kỳ hữu ích trong việc cải thiện các ví dụ, sự rõ ràng và cách tổ chức của
cuốn sách từ đầu đến cuối. James Long, Drew Conway, Fer nando Pérez, Brian Granger,
Thomas Kluyver, Adam Klein, Josh Klein, Chang She và Stéfan van der Walt, mỗi người đã
xem xét một hoặc nhiều chương, đưa ra phản hồi rõ ràng từ nhiều góc độ khác nhau.

Tôi nhận được nhiều ý tưởng tuyệt vời về ví dụ và tập dữ liệu từ bạn bè và đồng nghiệp trong
cộng đồng dữ liệu, trong số đó: Mike Dewar, Jeff Hammerbacher, James Johndrow, Kristian
Lum, Adam Klein, Hilary Mason, Chang She và Ashley Williams.

Tất nhiên, tôi mang ơn nhiều nhà lãnh đạo trong cộng đồng Python khoa học nguồn mở,
những người đã xây dựng nền tảng cho công việc phát triển của tôi và đã khuyến khích tôi
khi tôi viết cuốn sách này: nhóm nòng cốt IPython (Fernando Pérez, Brian Granger, Min
13
Ragan -Kelly, Thomas Kluyver và những người khác), John Hunter, Skipper Seabold, Travis
Oliphant, Peter Wang, Eric Jones, Robert Kern, Josef Perktold, Francesc Alted, Chris
Fonnesbeck và rất nhiều người khác không thể kể hết. Một số người khác đã hỗ trợ, đưa ra ý
tưởng và động viên rất nhiều trong suốt chặng đường: Drew Conway, Sean Taylor, Giuseppe
Paleologo, Jared Lander, David Epstein, John Krowas, Joshua Bloom, Den Pilsworth, John
Myles-White và nhiều người khác tôi quên mất rồi.

Tôi cũng muốn cảm ơn một số người trong những năm tôi mới bắt đầu. Đầu tiên, các đồng
nghiệp AQR cũ của tôi, những người đã cổ vũ tôi trong công việc gấu trúc của tôi trong nhiều
năm qua: Alex Reyf man, Michael Wong, Tim Sargen, Oktay Kurbanov, Matthew Tschantz,
Roni Israelov, Michael Katz, Chris Uga, Prasad Ramanan, Ted Square và Hoon Kim. Cuối
cùng là cố vấn học tập của tôi Haynes Miller (MIT) và Mike West (Duke).

Về mặt cá nhân, Casey Dinkin đã cung cấp sự hỗ trợ vô giá hàng ngày trong quá trình viết,
chấp nhận những thăng trầm của tôi khi tôi cùng nhau hoàn thành bản thảo cuối cùng trên
một lịch trình đã quá thoả thuận. Cuối cùng, bố mẹ tôi, Bill và Kim, đã dạy tôi phải luôn theo
đuổi ước mơ của mình và không bao giờ chấp nhận những điều kém cỏi hơn.

14
CHƯƠNG 6: TẢI DỮ LIỆU, LƯU TRỮ VÀ ĐỊNH DẠNG TẬP TIN
Các công cụ trong quyển sách này sẽ ít sử dụng nếu bạn không thể dễ dàng nhập và xuất dữ
liệu trong Python. Tôi sẽ tập trung vào đầu vào và đầu ra với các đối tượng pandas, mặc dù
tất nhiên có rất nhiều công cụ trong các thư viện khác hỗ trợ quá trinh này. Ví dụ, NumPy có
tính năng tải và lưu trữ dữ liệu nhị phân ở mức độ thấp nhưng cực kỳ nhanh, bao gồm hỗ trợ
mảng ánh xạ bộ nhớ. Xem chương 12 để biết thêm về những điều đó.
Đầu vào và ra thường rơi vào một số danh mục chính: đọc tệp văn bản và những cái khác hiệu
quả hơn như các định dạng trên đĩa, tải dữ liệu từ cơ sở dữ liệu và tương tác với các nguồn
mạng như API web.
ĐỌC VÀ GHI DỮ LIỆU Ở ĐỊNH DẠNG VĂN BẢN
Python đã trở thành ngôn ngữ được yêu thích cho việc trộn văn bản và tệp, do cú pháp của nó
đơn giản để kết nối với các tệp, cấu trúc dữ liệu trực quan và các tính năng tiện lợi như đóng
gói và giải nén bộ dữ liệu.
pandas có một số hàm để đọc dữ liệu dạng bảng dưới dạng đối tượng DataFrame. Bảng 6-1
có tóm tắt tất cả về chúng, tuy nhiên read_csv và read_table có thể là những thứ bạn sẽ sử
dụng nhiều nhất.
Bảng 6-1: Phân tích các cú pháp trong pandas
Hàm Mô tả
read_csv Tải dữ liệu được phân tách từ một một tệp hoặc đối tượng giống như tệp.
Sử dụng dấu phẩy làm dấu phân cách mặc định.
read_table Tải dữ liệu được phân tách từ một một tệp hoặc đối tượng giống như tệp.
Sử dụng tab (‘\t’) làm dấu phân cách mặc định.
read_fwf Đọc dữ liệu ở định dạng cột có chiều rộng cố định (nghĩa là không có dấu
phân cách)
read_clipboard Phiên bản của read_table đọc dữ liệu từ clipboard. Hữu ích cho việc
chuyển đổi bảng từ các trang web.

Tôi sẽ cung cấp cái nhìn tổng quan về cơ chế của các hàm này, các hàm này nhằm mục đích
chuyển đổi dữ liệu văn bản thành một DataFrame. Các tùy chọn của những hàm này thuộc
một số loại sau:
• Lập chỉ mục: có thể xử lý một hoặc nhiều cột như DataFrame được trả về và có nên
lấy tên cột từ tệp, người dùng hay không.
• Suy luận kiểu và chuyển đổi dữ liệu: điều này bao gồm các chuyển đổi giá trị do người
dùng xác định và danh sách tùy chỉnh các điểm đánh dấu giá trị bị thiếu.
• Phân tích ngày giờ: bao gồm khả năng kết hợp, bao gồm kết hợp thông tin ngày và giờ
trải rộng trên nhiều cột thành một cột duy nhất trong kết quả.
• Lặp lại: hỗ trợ lặp lại các đoạn tệp rất lớn.
• Các vấn đề dữ liệu không sạch: bỏ qua các hàng hoặc chân trang, nhận xét hoặc những
thứ nhỏ nhặt khác như dữ liệu số có hàng nghìn được phân tách bằng dấu phẩy.
Suy luận kiểu là một trong những tính năng quan trọng hơn các hàm này; điều đó có nghĩa là
bạn không cần phải chỉ định cột nào là số, số nguyên, boolean (kiểu dữ liệu có 2 giá trị True,
False) hoặc chuỗi. Tuy nhiên, việc xử lý ngày tháng và các loại tùy chỉnh khác đòi hỏi nhiều
nỗ lực hơn một chút. Hãy bắt đầu với một tệp băn bản (CSV) nhỏ được phân tách bằng dấu
phẩy:

15
Vì đây được phân tách bởi dấu phẩy, nên chúng ta có thể dùng hàm read_csv để đọc nó dưới
dạng một DataFrame:

Chúng ta cũng có thể sử dụng read_table và chỉ định dấu phân cách:

Ở đây, tôi đã sử dụng lệnh Unix cat shell để in nội dung thô của tệp ra màn hình.
Nếu bạn đang dùng Windows, bạn có thể sử dụng type thay vì cat để đạt được hiệu
quả tương tự.

Một tập tin không phải lúc nào cũng có hàng tiêu đề. Hãy xem tệp này:

Để đọc nội dung này, bạn có một vài lựa chọn. Bạn có thể cho phép pandas gán tên cột mặc
định hoặc bạn có thể tự mình chỉ định tên:

Giả sử bạn muốn cột message là chỉ mục của DataFrame được trả về. Bạn có thể cho biết bạn
muốn cột ở chỉ mục số 4 hoặc được đặt tên là ‘message’ bằng cách sử dụng đối số index_col:

16
Trong trường hợp bạn muốn tạo chỉ mục phân cấp từ nhiều cột, chỉ cần chuyển một danh sách
số hoặc tên cột:

Trong một số trường hợp, một bảng có thể không có dấu phân cách cố định, sử dụng khoảng
trắng hoặc một số kiểu khác để phân tách các trường. Trong những trường hợp này, bạn có
thể chuyển một biểu thức chính quy làm dấu phân cách cho read_table. Hãy xem một tệp văn
bản trông như thế này:

Trong khi bạn có thể thực hiện một số thao tác thủ công, thì trong trường hợp này các trường
được phân tách bằng một khoảng trắng khác nhau. Điều này có thể được biểu diễn bằng biểu
thức chính quy \s+, vì vậy chúng ta có:

17
Bởi vì có ít tên cột hơn số hàng dữ liệu nên read_table suy luận rằng cột đầu tiên phải là chỉ
mục của DataFrame trong trường hợp này.
Các hàm phân tích cú pháp có nhiều đối số bổ sung để giúp bạn xử lý nhiều định dạng tệp
ngoại lệ khác nhau xảy ra (xem bảng 6-2). Ví dụ, bạn có thể bỏ qua hàng đầu tiên, thứ ba và
bốn của tệp bằng skiprows:

Xử lý các giá trị rỗng là một phần quan trọng và thường xuyên có nhiều sắc thái trong quá
trình phân tích cú pháp tệp. Dữ liệu rỗng thường không có (chuỗi trống) hoặc được đánh dấu
bằng một số giá trị sentinel. Theo mặc định, pandas sử dụng một tập hợp các sentinel thường
xuyên xuất hiện, như là NA, -1, #IND và NULL:

Tùy chọn na_values có thể lấy danh sách hoặc tập hợp các chuỗi để xem xét các giá bị rỗng:

Khác với NA có thể được chỉ định cho mỗi cột trong một dict:

18
Bảng 6-2: Các đối số hàm read_csv/read_table
Hàm Mô tả
path Chuỗi biểu thị vị trí hệ thống tệp, URL hoặc đối tượng file-like.
sep or delimiter Chuỗi ký tự hoặc biểu thức chính quy được sử dụng đẻ phân tách các
trường trong mỗi tiêu đề hàng.
header Số hàng để sử dụng làm tên cột. Mặc định là 0 (hàng đầu tiên), nhưng
phải là None nếu không có hàng tiêu đề.
index_col Số cột hoặc tên để sử dụng làm chỉ mục hàng trong kết quả. Có thể là
một tên/số hoặc một danh sách của chúng cho tên chỉ mục phân cấp.
names Danh sách tên cột cho kết quả, kết hợp với header=None
skiprows Số hàng ở đầu tệp cần bỏ qua hoặc danh sách số hàng (bắt đầu từ 0)
đến skip.
na_values Trình tự các giá trị để thay thế bằng NA.
comment Ký tự hoặc các ký tự để phân tách các nhận xét ở cuối dòng.
parse_dates Cố gắng phân tích dữ liệu theo datetime; Sai theo mặc định. Nếu Đúng,
sẽ cố gắng phân tích tất cả các cột. Ngược lại có thể chỉ định danh sách
số cột hoặc tên để phân tích. Nếu phần tử của list là tuple hoặc list, sẽ
kết hợp nhiều cột lại với nhau và phân tích theo ngày (ví dụ nếu
ngày/giờ được chia thành hai cột).
keep_date_col Nếu nối các cột để phân tích ngày, hãy bỏ các cột đã nối. Mặc định
Đúng.
converters Dict chứa số cột ánh xạ tên tới các hàm. Ví dụ {'foo': f} sẽ áp dụng
hàm f cho tất cả các giá trị trong cột 'foo'.
dayfirst Khi phân tích cú pháp các ngày có thể không rõ ràng, hãy xử lý theo
định dạng quốc tế (ví dụ 6/7/2012 -> 7 tháng 6 năm 2012). Mặc định
Sai.
date_praser Hàm sử dụng để phân tích ngày.
nrows Số hàng cần đọc từ đầu tệp.
iterator Trả về một đối tượng TextParser để đọc tệp từng phần.
chunksize Để lặp lại, kích thước của các khối tệp.
skip_footer Số dòng cần bỏ qua ở cuối tệp.
verbose In nhiều trình phân tích cú pháp thông tin đầu ra, chẳng hạn như số
lượng giá trị bị thiếu được đặt trong các cột không phải số
encoding Mã hóa văn bản cho unicode. Ví dụ 'utf-8' để nén văn bản được mã
hóa UTF-8.
squeeze Nếu dữ liệu được phân tích cú pháp chỉ chứa một cột trả về một Series.
thousands Phân cách hàng nghìn, ví dụ: ',' hoặc '.'

Đọc tập tin văn bản theo từng phần


Khi xử lý các tập tin quá lớn hoặc tìm ra tập hợp các đối số phù hợp để xử lý chính xác một
tệp lớn, bạn có thể chỉ muốn đọc một phần nhỏ của tệp hoặc lặp lại các phần nhỏ của tệp:

19
Nếu bạn chỉ muốn đọc một số lượng nhỏ hàng (tránh đọc toàn bộ tệp), hãy chỉ định điều đó
bằng nrows:

Để đọc từng phần trong tập tin, hãy chỉ định kích thước khối của một số hàng:

Đối tượng TextParser được trả về bởi read_csv cho phép bạn lặp lại các phần của tệp theo
kích thước khối. Ví dụ, chúng ta có thể lặp lại ex6.csv, tổng hợp số lượng giá trị trong cột key
như sau:

Sau đó, chúng ta có:

TextParser cũng được trang bị với một phương pháp get_chunk cho phép bạn đọc các phần
có kích thước tùy ý.

Ghi dữ liệu ra định dạng văn bảng


Dữ liệu cũng có thể được xuất sang định dạng được phân tách. Hãy xem một trong các tệp
CSV đọc ở trên:

20
Sử dụng phương pháp to_csv của DataFrame, chúng ta có thể ghi dữ liệu ra tệp được phân
tách bằng dấu phẩy:

Tất nhiên, các dấu phân cách khác có thể được sử dụng ( ghi vào sys.stdout để nó chỉ in kết
quả văn bản):

Các giá trị bị thiếu sẽ xuất hiện dưới dạng chuỗi rỗng ở đầu ra. Bạn có lẽ muốn biểu thị chúng
bằng một số giá trị sentinel khác:

Không có tùy chọn nào khác được chỉ định, cả nhãn hàng và cột đều được ghi. Cả hai đều có
thể bị vô hiệu hóa:

Bạn cũng có thể chỉ ghi một tập hợp con của các cột và theo thứ tự bạn chọn:

Series cũng có phương thức to_csv:

Với một chút sắp xếp (không có tiêu đề, cột đầu tiên làm chỉ mục), bạn có thể đọc phiên bản
csv của một Series với read_csv, nhưng cũng có một phương pháp tiện lợi như from_csv giúp
việc này đơn giản hơn một chút:
21
Xem các chuỗi tài liệu từ to_csv và from_csv để biết thêm thông tin.
Làm việc thủ công với định dạng được phân tách
Hầu hết các dạng dữ liệu bảng có thể được tải từ đĩa bằng các hàm như pandas.read_table.
Tuy nhiên, trong một số trường hợp, việc xử lý thủ công có thể rất cần thiết. Không có gì mới
lạ khi nhận được một tệp với một hoặc hơn nhiều dòng không đúng định dạng làm chồng lên
read_table . Để minh họa các công cụ cơ bản, hãy xem xét một tệp CSV nhỏ:

Đối với bất kỳ tệp nào có dấu phân cách một ký tự, bạn có thể sử dụng module csv tích hợp
của Python. Để sử dụng nó, hãy chuyển bất kỳ tệp đang mở hoặc đối tượng giống tệp nào tới
csv.reader:

Việc lặp lại qua trình đọc giống như một tệp mang lại các bộ giá trị với bất kỳ ký tự trích dẫn
nào bị loại bỏ:

Từ đó, bạn có thể thực hiện các thao tác cần thiết để đưa dữ liệu vào dạng mà bạn cần. Ví dụ:

Tệp CSV có nhiều loại khác nhau. Việc xác định một định dạng mới với một dấu phân cách,
quy ước trích dẫn chuỗi hoặc dấu kết thúc dòng được thực hiện bằng cách xác định một lớp
con đơn giản của csv.Dialect:

Các tham số phương ngữ CSV riêng lẻ cũng có thể được cung cấp dưới dạng từ khóa cho
csv.reader mà không cần phải xác định lớp con:

22
Bạn có thể tìm thấy các tùy chọn có thể có (thuộc tính của csv.Dialet) và chức năng của chúng
trong bảng 6-3.
Bảng 6-3: Các tùy chọn phương ngữ CSV
Hàm Mô tả
delimiter Chuỗi một ký tự để phân tách các trường. Mặc định là ','.
lineterminator Dấu kết thúc dòng để viết, mặc định là '\r\n'. Trình đọc bỏ qua điều
này và nhận ra các ký tự kết thúc dòng cross-platform.
quotechar Ký tự trích dẫn cho các trường có ký tự đặc biệt (như dấu phân cách).
Mặc định là ' " '.
quoting Quy ước trích dẫn. Các tùy chọn bao gồm csv.QUOTE_ALL (trích
dẫn tất cả các trường), csv.QUOTE_MINIMAL (chỉ các trường có
ký tự đặc biệt như dấu phân cách), csv.QUOTE_NONNUMERIC và
csv.QUOTE_NON (không có trích dẫn). Xem tài liệu Python để biết
chi tiết đầy đủ. Mặc định là QUOTE_MINIMAL.
skipintialspace Bỏ qua khoảng trắng sau mỗi dấu phân cách. Mặc định Sai.
doublequote Cách xử lý ký tự trích dẫn bên trong một trường. Nếu Đúng, nó được
nhân đôi. Xem tài liệu trực tuyến để biết đầy đủ chi tiết và cách thức.
escapechar Chuỗi thoát khỏi dấu phân cách nếu trích dẫn được đặt thành
csv.QUOTE_NONE. Bị tắt theo mặc định

Đối với các tệp có dấu phân cách nhiều ký tự cố định hoặc phức tạp hơn, bạn
sẽ không thể sử dụng module csv. Trong những trường hợp đó, bạn sẽ phải
thực hiện việc tách dòng và dọn dẹp khác bằng cách sử dụng phương pháp
phân tách của chuỗi hoặc phương pháp biểu thức chính quy re.split.

Để ghi các tệp được phân tách theo cách thủ công, bạn có thể sử dụng csv.writer. Nó chấp
nhận một đối tượng tệp mở, có thể ghi và các tùy chọn định dạng và phương ngữ giống như
csv.reader:

Dữ liệu JSON
JSON (viết tắt của JavaScript Object Notation) đã trở thành một trong những định dạng tiêu
chuẩn để gửi dữ liệu theo yêu cầu HTTP giữa trình duyệt web và các ứng dụng khác. Đây là
định dạng dữ liệu linh hoạt hơn nhiều so với dạng văn bản dạng bảng như CSV. Đây là một
ví dụ:

JSON gần như là mã Python hợp lệ, ngoại trừ giá trị null cuả nó là null và một số hình thái
khác (chẳng hạn như không cho phép dấu phẩy ở cuối danh sách). Các loại cơ bản là các đối

23
tượng (dicts), mảng (danh sách), chuỗi, số, boolean và null. Tất cả các key trong một đối
tượng phải là chuỗi. Có một số thư viện Python để đọc và ghi dữ liệu JSON. Tôi sẽ sử dụng
json ở đây vì nó được tích hợp vào thư viện chuẩn Python. Để chuyển đổi chuỗi JSON thành
dạng Python, hãy sử dụng json.loads:

Mặt khác, json.dumps chuyển đổi một đối tượng Python trở lại JSON:

Cách bạn chuyển đổi một đối tượng JSON hoặc danh sách các đối tượng thành DataFrame
hoặc một số cấu trúc dữ liệu khác để phân tích sẽ tùy thuộc vào bạn. Một cách thuận tiện, bạn
có thể chuyển danh sách các đối tượng JSON tới hàm tạo DataFrame và chọn một tập hợp
con của các trường dữ liệu:

Để biết ví dụ mở rộng về cách đọc và thao tác dữ liệu JSON (bao gồm các bản ghi lồng nhau),
hãy xem ví dụ về Cơ sở dữ liệu thực phẩm USDA trong chương tiếp theo.

Một nỗ lực đang được tiến hành để thêm tính năng suất JSON gốc nhanh
(to_json) và giải mã (from_json) vào pandas. Điều này chưa sẵn sàng tại thời
điểm viết bài.

XML và HTML: Quét web


Python có nhiều thư viện để đọc và ghi dữ liệu ở các định dạng HTML và XML phổ biến.
lxml (http://lxml.de) là một công cụ có hiệu suất ổn định trong việc phân tích các tệp rất lớn,
lxml có nhiều giao diện lập trình; đầu tiên tôi sẽ hiển thị bằng lxml.html cho HTML, sau đó
phân tích cú pháp một số XML bằng lxml.objectify.

Nhiều trang web cung cấp dữ liệu dưới dạng bảng HTML để xem trong trình duyệt nhưng
không thể tải xuống dưới dạng định dạng dễ đọc bằng máy như JSON, HTML hoặc XML.
Tôi nhận thấy rằng đây là trường hợp của Yahoo! Dữ liệu quyền chọn cổ phiếu của Finance.
Nếu bạn không quen với dữ liệu này; quyền chọn là các hợp đồng phái sinh cho bạn quyền
mua (quyền chọn mua) hoặc bán (quyền chọn bán) cổ phiếu của công ty ở một mức giá cụ
thể nào đó (giá thực hiện) từ bây giờ đến một thời điểm cố định nào đó trong tương lai (hết
hạn). Mọi người giao dịch cả quyền chọn mua và quyền chọn bán trong nhiều thời điểm đình
24
công và hết hạn; tất cả dữ liệu này có thể được tìm thấy cùng nhau trong các bảng trên Yahoo!
Finance.
Để bắt đầu, hãy tìm URL bạn muốn trích xuất dữ liệu, mở nó bằng urllib2 và phân tích luồng
bằng lxml như sau:

Sử dụng đối tượng này, bạn có thể trích xuất tất cả các thẻ HTML thuộc một loại cụ thể, chẳng
hạn như thẻ bảng chứa dữ liệu quan tâm. Như một ví dụ thúc đẩy đơn giản, giả sử bạn muốn
có danh sách mọi URL được liên kết đến trong tài liệu; liên kết là một thẻ trong HTML. Sử
dụng phương thức findall của tài liệu gốc cùng với XPath (một phương tiện thể hiện “truy
vấn” trên tài liệu):

Nhưng đây là những đối tượng đại diện cho các phần tử HTML; để lấy URL và văn bản liên
kết, bạn phải sử dụng phương thức của get (đối với URL) và phương thức text_content (đối
với văn bản hiển thị) của từng phần tử:

Do đó, việc có được danh sách tất cả các URL trong tài liệu chỉ là vấn đề viết cách hiểu danh
sách này:

25
Bây giờ, việc tìm đúng bảng trong tài liệu có thể là một vấn đề cần thử và sai; một số trang
web làm cho việc này trở nên dễ dàng hơn bằng cách cung cấp thuộc tính id cho bảng quan
tâm. Tôi xác định rằng đây là hai bảng chứa dữ liệu call và dữ liệu put tương ứng:

Mỗi bảng có một hàng tiêu đề theo sau là mỗi hàng dữ liệu:

Đối với tiêu đề cũng như các hàng dữ liệu, chúng tôi muốn trích xuất văn bản từ mỗi ô; trong
trường hợp tiêu đề, đây là các ô th và ô td cho dữ liệu:

Vì vậy, chúng tôi có được:

Bây giờ, vấn đề là kết hợp tất cả các bước này lại với nhau để chuyển đổi dữ liệu này thành
DataFrame. Vì dữ liệu số vẫn ở định dạng chuỗi nên chúng tôi muốn chuyển đổi một vài thứ,
nhưng có lẽ không phải tất cả các cột sang định dạng dấu phẩy động. Bạn có thể thực hiện
việc này bằng thủ công, nhưng may mắn thay, pandas có một lớp TextParser được sử dụng
nội bộ trong read_csv và các hàm phân tích cú pháp khác để thực hiện chuyển đổi kiểu tự
động thích hợp:

26
Cuối cùng, chúng tôi gọi hàm phân tích cú pháp này trên các đối tượng bảng lxml và nhận kết
quả DataFrame:

Phân tích cú pháp XML bằng lxml.objectify


XML (ngôn ngữ đánh dấu mở rộng) là một định dạng dữ liệu có cấu trúc phổ biến khác, hỗ
trợ dữ liệu phân cấp, lồng nhau với siêu dữ liệu. Các tệp tạo ra cuốn sách bạn đang đọc, thực
sự tạo thành một chuỗi các tài liệu XML lớn.

Ở trên, tôi đã giới thiệu thư viện lxml và giao diện lxml.html của nó. Ở đây, tôi sẽ giới thiệu
một giao diện thay thế thuận tiện cho dữ liệu XML, lxml.objectify.

Cơ quan Giao thông Đô thị New York (MTA) xuất bản một số chuỗi dữ liệu về các dịch vụ
xe buýt và xe lửa của mình (http://www.mta.info/developers/download.html). Ở đây chúng ta
sẽ xem xét dữ liệu hiệu suất có trong một tập hợp các tệp XML. Mỗi dịch vụ xe lửa hoặc xe
buýt có một tệp khác nhau (như Performance_MNR.xml dành cho Đường sắt MetroNorth)
chứa dữ liệu hàng tháng dưới dạng một loạt bản ghi XML trông như thế này:

27
Sử dụng lxml.objectify, chúng tôi phân tích tệp và lấy tham chiếu đến nút gốc của tệp XML
bằng getroot:

root.INDICATOR trả về một trình tạo mang lại từng phần tử <INDICATOR> XML. Đối với
mỗi bản ghi, chúng ta có thể điền một lệnh tên thẻ (như YTD_ACTUAL) vào các giá trị dữ
liệu (không bao gồm một vài thẻ):

Cuối cùng, chuyển đổi danh sách các ký tự này thành DataFrame:

Dữ liệu XML có thể phức tạp hơn nhiều so với ví dụ này. Mỗi thẻ cũng có thể có siêu dữ liệu.
Hãy xem xét thẻ liên kết HTML cũng là XML hợp lệ:

28
Bây giờ bạn có thể truy cập bất kỳ trường nào (như href) trong thẻ hoặc văn bản liên kết:

ĐỊNH DẠNG DỮ LIỆU NHỊ PHÂN


Một trong những cách dễ nhất để lưu trữ dữ liệu hiệu quả ở định dạng nhị phân là sử dụng
tính năng tuần tự hóa pickle có sẵn của Python. Một cách thuận tiện, tất cả các đối tượng
pandas đều có một phương thức lưu trữ để ghi dữ liệu vào đĩa dưới dạng pickle:

Bạn đọc dữ liệu Python trước bằng pandas.load, một hàm tiện lợi pickle khác:

Pickle chỉ được khuyến nghị làm định dạng lưu trữ ngắn hạn. Vấn đề là khó có
thể đảm bảo định dạng sẽ ổn định theo thời gian; một đối tượng được chọn ngày
hôm nay có thể không được giải nén bằng phiên bản mới hơn của thư viện. Tôi
đã cố gắng hết sức để đảm bảo rằng điều này không xảy ra với pandas, nhưng
tại một thời điểm nào đó trong tương lai, nó có thể là cần thiết “phá vỡ” định
dạng pickle.

Sử dụng định dạng HDF5


Có một số công cụ tạo điều kiện thuận lợi cho việc đọc và ghi một lượng lớn dữ liệu khoa học
dưới định dạng nhị phân trên đĩa một cách hiệu quả. Thư viện cấp ngành phổ biến cho việc
này là HDF5, là thư viện C có giao diện bằng nhiều ngôn ngữ khác như Java, Python và
MATLAB. “HDF” trong HDF5 là viết tắt của định dạng dữ liệu phân cấp. Mỗi tệp HDF5
chứa cấu trúc nút giống như hệ thống tệp nội bộ cho phép bạn lưu trữ nhiều bộ dữ liệu và siêu
dữ liệu hỗ trợ. So với các định dạng đơn giản hơn, HDF5 hỗ trợ nén nhanh chóng với nhiều
loại máy nén khác nhau, cho phép lưu trữ dữ liệu có mẫu lặp lại hiệu quả hơn. Đối với các

29
tập dữ liệu rất lớn không vừa với bộ nhớ, HDF5 là một lựa chọn tốt vì bạn có thể đọc và ghi
các phần nhỏ của mảng lớn hơn nhiều một cách hiệu quả.
Không có một mà là hai giao diện cho thư viện HDF5 trong Python, PyTables và h5py, mỗi
giao diện có một cách tiếp cận vấn đề khác nhau. h5py cung cấp giao diện trực tiếp nhưng
cấp cao cho API HDF5, trong khi PyTables tóm tắt nhiều chi tiết về HDF5 để cung cấp nhiều
bộ chứa dữ liệu linh hoạt, lập chỉ mục bảng, khả năng truy vấn và một số hỗ trợ cho các tính
thuật toán out-of-core.
pandas có lớp HDFStore giống như dict tối thiểu, sử dụng PyTables để lưu trữ các đối tượng
pandas:

Các đối tượng chứa trong tệp HDF5 có thể được truy xuất theo kiểu giống như lệnh:

Nếu bạn làm việc với lượng dữ liệu khổng lồ, tôi khuyến khích bạn khám phá PyTables và
h5py để xem chúng có thể phù hợp với nhu cầu của bạn như thế nào. Vì nhiều vấn đề phân
tích dữ liệu liên quan đến IO (chứ không phải liên quan đến CPU), nên việc sử dụng một công
cụ như HDF5 có thể tăng tốc đáng kể các ứng dụng của bạn.
HDF5 không phải là cơ sở dữ liệu. Nó phù hợp nhất cho các bộ dữ liệu ghi một
lần, đọc nhiều lần. Mặc dù dữ liệu có thể được thêm vào tệp bất kỳ lúc nào, nhưng
nếu nhiều người ghi đồng thời làm như vậy thì tệp có thể bị hỏng.

Đọc tệp Microsoft Excel


Pandas cũng hỗ trợ đọc dữ liệu dạng bảng được lưu trữ trong tệp Excel 2003 (và cao hơn)
bằng cách sử dụng lớp ExcelFile. Về mặt ExcelFile sử dụng các gói xlrd và openpyxl, vì vậy
bạn có thể phải cài đặt chúng trước. Để sử dụng ExcelFile, hãy tạo một phiên bản bằng cách
chuyển đường dẫn đến tệp xls hoặc xlsx:

Dữ liệu được lưu trữ trong một trang tính có thể được đọc vào DataFrame bằng cách sử dụng
phân tích:

TƯƠNG TÁC VỚI HTLM VÀ API WEB


Nhiều trang web có API công khai cung cấp nguồn cấp dữ liệu qua JSON hoặc một số định
dạng khác. Có một số cách để truy cập các API này từ Python; một phương pháp dễ sử dụng
mà tôi khuyên dùng là gói yêu cầu (http://docs.python-requests.org). Để tìm kiếm từ “python
pandas” trên Twitter, chúng ta có thể thực hiện yêu cầu HTTP GET như sau:
30
Thuộc tính văn bản của đối tượng Response chứa nội dung của truy vấn GET. Nhiều API web
sẽ trả về một chuỗi JSON phải được tải vào đối tượng Python:

Trường kết quả trong response chứa danh sách các tweet, mỗi tweet được biểu diễn dưới dạng
một dict Python trông giống như:

Chúng ta có thể tạo danh sách các trường tweet quan tâm rồi chuyển danh sách kết quả tới
DataFrame:

31
Mỗi hàng trong DataFrame hiện có dữ liệu được trích xuất từ mỗi tweet:

Với một chút nỗ lực, bạn có thể tạo một số giao diện cấp cao hơn cho các API web phổ biến
trả về các đối tượng DataFrame để dễ dàng phân tích.

TƯƠNG TÁC VỚI CƠ SỞ DỮ LIỆU


Trong nhiều dữ liệu ứng dụng hiếm khi đến từ các tệp văn bản, đó là cách khá kém hiệu quả
để lưu trữ lượng lớn dữ liệu. Cơ sở dữ liệu quan hệ dựa trên SQL (chẳng hạn như SQL Server,
PostgreSQL và MySQL) đang được sử dụng rộng rãi và nhiều cơ sở dữ liệu thay thế không
phải SQL (được gọi là NoSQL) đã trở nên khá phổ biến. Việc lựa chọn cơ sở dữ liệu thường
phụ thuộc vào hiệu suất, tính toàn vẹn dữ liệu và nhu cầu mở rộng của ứng dụng.

Việc tải dữ liệu từ SQL vào DataFrame khá đơn giản và pandas có một số chức năng để đơn
giản hóa quy trình. Ví dụ, tôi sẽ sử dụng cơ sở dữ liệu SQLite trong bộ nhớ bằng trình điều
khiển sqlite3 tích hợp của Python:

Sau đó, chèn một vài hàng dữ liệu:

Hầu hết các trình điều khiển Python SQL (PyODBC, psycopg2, MySQLdb, pymssql, v.v.)
đều trả về danh sách các bộ dữ liệu khi chọn dữ liệu từ một bảng:

Bạn có thể chuyển danh sách các bộ dữ liệu tới hàm tạo DataFrame, nhưng bạn cũng cần tên
cột có trong thuộc tính mô tả của con trỏ:

32
Đây là một chút thao tác phức tạp mà bạn không muốn lặp lại mỗi lần truy vấn cơ sở dữ liệu.
pandas có hàm read_frame trong module pandas.io.sql giúp đơn giản hóa quy trình. Chỉ cần
truyền câu lệnh chọn và đối tượng kết nối:

Lưu trữ và tải dữ liệu trong cơ sở dữ liệu MongoDB


Cơ sở dữ liệu NoSQL có nhiều dạng khác nhau. Một số là các kho lưu trữ key value dict-like
như BerkeleyDB hoặcTokyo Cabiner, trong khi một số khác dựa trên tài liệu, với đối tượng
dict-like là đơn vị lưu trữ cơ bản. Tôi đã chọn MongoDB (http://mongodb.org) làm ví dụ của
mình. Tôi đã khởi động một phiên bản MongoDB cục bộ trên máy của mình và kết nối với
nó trên cổng mặc định bằng pymongo, trình điều khiển chính thức cho MongoDB:

Các tài liệu được lưu trữ trong MongoDB được tìm thấy trong các bộ sưu tập bên trong cơ sở
dữ liệu. Mỗi phiên bản đang chạy của máy chủ MongoDB có thể có nhiều cơ sở dữ liệu và
mỗi cơ sở dữ liệu có thể có nhiều bộ sưu tập. Giả sử tôi muốn lưu trữ dữ liệu API Twitter từ
phần trước của chương. Đầu tiên, tôi có thể truy cập bộ sưu tập tweet (hiện đang trống):

Sau đó, tôi tải danh sách các tweet và viết từng tweet vào bộ sưu tập bằng cách sử dụng
tweets.save (ghi lệnh Python vào MongoDB):

Bây giờ, nếu tôi muốn lấy tất cả các tweet của mình (nếu có) từ bộ sưu tập, tôi có thể truy vấn
bộ sưu tập theo cú pháp sau:

Cursor được trả về là một trình vòng lặp mang lại mỗi tài liệu dưới dạng dict. Như trên, tôi
có thể chuyển đổi dữ liệu này thành DataFrame, tùy ý trích xuất một tập hợp con các trường
dữ liệu trong mỗi tweet:

33
CHƯƠNG 7: SẮP XẾP DỮ LIỆU: LÀM SẠCH, CHUYỂN ĐỔI, HỢP NHẤT, ĐỊNH
HÌNH LẠI

Phần lớn công việc lập trình trong phân tích và mô hình hóa dữ liệu được dành cho việc chuẩn
bị dữ liệu: tải, làm sạch, chuyển đổi và sắp xếp lại. Đôi khi cách mà dữ liệu được lưu trữ trong
tệp hoặc cơ sở dữ liệu không phải là cách bạn cần cho ứng dụng xử lý dữ liệu. Nhiều người
chọn ad hoc thực hiện xử lý dữ liệu đặc biệt từ dạng này sang dạng khác bằng cách sử dụng
chương trình có mục đích chung, như Python, Perl, R hoặc Java hoặc các công cụ xử lý văn
bản UNIX như sed hoặc awk. May mắn thay, pandas cùng với thư viện chuẩn Python cung
cấp cho bạn một bộ thuật toán và thao tác cốt lõi ở cấp độ cao, linh hoạt và hiệu suất cao để
cho phép bạn sắp xếp dữ liệu vào đúng dạng mà không gặp nhiều khó khăn.

Nếu bạn nhận ra một loại thao tác dữ liệu không có trong cuốn sách này hoặc ở nơi nào khác
trong thư viện pandas, vui lòng đề xuất nó trên danh sách gửi thư hoặc trang GitHub. Thật
vậy, phần lớn việc thiết kế và triển khai pandas đều được thúc đẩy bởi nhu cầu ứng dụng trong
thế giới thực.

KẾT HỢP VÀ HỢP NHẤT CÁC TẬP DỮ LIỆU


Dữ liệu chứa trong các đối tượng pandas có thể được kết hợp với nhau theo một số cách có
sẵn:
• pandas.merge kết nối các hàng trong DataFrames dựa trên một hoặc nhiều key. Điều
này sẽ quen thuộc với người dùng SQL hoặc các cơ sở dữ liệu quan hệ khác vì nó thực
hiện các hoạt động nối cơ sở dữ liệu.
• pandas.concat dán hoặc xếp các đối tượng lại với nhau dọc theo một trục.
• combine_first cho phép ghép các dữ liệu chồng chéo lại với nhau để điền vào các giá
trị còn thiếu trong một đối tượng với các giá trị từ một đối tượng khác.
Tôi sẽ giải quyết từng vấn đề này và đưa ra một số ví dụ. Chúng sẽ được sử dụng làm ví dụ
xuyên suốt phần còn lại của cuốn sách.
Hợp nhất DataFrame kiểu cơ sở dữ liệu
Các thao tác hợp nhất hoặc nối, kết hợp các tập dữ liệu bằng cách liên kết các hàng sử dụng
một hoặc nhiều key. Các hoạt động này là trung tâm của cơ sở dữ liệu quan hệ. Hàm hợp nhất
trong pandas là điểm bắt đầu chính để sử dụng các thuật toán này trên dữ liệu của bạn.

Hãy bắt đầu với một ví dụ đơn giản:

34
Đây là ví dụ về tình huống hợp nhất nhiều-một; dữ liệu trong df1 có nhiều hàng được gắn
nhãn a và b, trong khi df2 chỉ có một hàng cho mỗi giá trị trong cột key. Gọi merge với các
đối tượng này, chúng ta được:

Lưu ý rằng tôi không chỉ định cột nào sẽ tham gia. Nếu không được chỉ định, merge sẽ sử
dụng tên cột chồng chéo làm những key. Tuy nhiên, đó là một cách thực hành tốt để xác định
rõ ràng:

Nếu tên cột khác nhau trong mỗi đối tượng, bạn có thể chỉ định chúng một cách riêng biệt:

Bạn có thể nhận thấy rằng giá trị 'c' và 'd' cũng như dữ liệu liên quan bị thiếu trong kết quả.
Theo mặc định, việc hợp nhất sẽ thực hiện phép nối 'bên trong'; các key trong kết quả là giao
điểm. Các tùy chọn khả thi khác là 'trái', 'phải' và 'bên ngoài'. Phép nối ngoài lấy sự kết hợp
của các key, kết hợp tác dụng của việc áp dụng cả phép nối trái và phép nối phải:

35
Sự hợp nhất nhiều-nhiều có hành vi được xác định rõ ràng mặc dù không nhất thiết phải trực
quan. Đây là một ví dụ:

Các phép nối nhiều-nhiều tạo thành sản phẩm Cartesian của các hàng. Vì có 3 hàng 'b' ở
DataFrame bên trái và 2 hàng ở bên phải nên có 6 hàng 'b' trong kết quả. Phương thức nối chỉ
ảnh hưởng đến các key value riêng biệt xuất hiện trong kết quả:

Để hợp nhất với nhiều key, hãy chuyển danh sách tên cột:

36
Để xác định tổ hợp key nào sẽ xuất hiện trong kết quả tùy thuộc vào việc lựa chọn phương
pháp hợp nhất, hãy coi nhiều key như tạo thành một mảng các bộ dữ liệu được sử dụng làm
một key nối duy nhất (mặc dù nó không thực sự được triển khai theo cách đó).
Khi nối các cột trên cột, các chỉ mục trên các đối tượng DataFrame đã truyền sẽ
bị loại bỏ.

Vấn đề cuối cùng cần xem xét trong hoạt động hợp nhất là việc xử lý các tên cột chồng chéo.
Mặc dù bạn có thể giải quyết phần chồng chéo theo cách thủ công (xem phần sau về đổi tên
nhãn trục), việc hợp nhất có tùy chọn hậu tố để chỉ định các chuỗi để nối vào các tên chồng
chéo trong các đối tượng DataFrame bên trái và bên phải:

Xem Bảng 7-1 để biết tham chiếu đối số khi hợp nhất. Tham gia chỉ mục là chủ đề của phần
tiếp theo.
Bảng 7-1. hợp nhất các đối số hàm
Hàm Mô tả
left DataFrame được hợp nhất bên trái.
right DataFrame được hợp nhất bên phải.
how Một trong 'bên trong', 'bên ngoài', 'trái' hoặc 'phải'. 'bên trong' theo mặc
định
on Tên cột để tham gia. Phải được tìm thấy trong cả hai đối tượng
DataFrame. Nếu không được chỉ định và không có key nào khác được
cung cấp, sẽ sử dụng giao điểm của các tên cột ở bên trái và bên phải
làm key.

37
left_on Các cột trong DataFrame bên trái để sử dụng làm các key.
right_on Tương tự như left_on cho DataFrame trái.
left_index Sử dụng chỉ mục hàng ở bên trái làm key của nó (hoặc các key, nếu là
MultiIndex).
right_index Tương tự như sắp xếp left_index.
sort Sắp xếp dữ liệu đã hợp nhất theo các key; Đúng theo mặc định. Tắt để
có hiệu suất tốt hơn trong một số trường hợp trên các tập dữ liệu lớn.
sufixes Bộ giá trị chuỗi để nối vào tên cột trong trường hợp trùng lặp; mặc định
là ('_x', '_y'). Ví dụ, nếu 'dữ liệu' trong cả hai đối tượng DataFrame, sẽ
xuất hiện dưới dạng 'data_x' và 'data_y' trong kết quả.
copy Nếu sai, hãy tránh sao chép dữ liệu vào cấu trúc dữ liệu kết quả trong
một số trường hợp ngoại lệ. Theo mặc định luôn sao chép

Hợp nhất chỉ mục


Trong một số trường hợp, key hợp nhất hoặc các key trong DataFrame sẽ được tìm thấy trong
chỉ mục của nó. Trong trường hợp này, bạn có thể chuyển left_index=True hoặc
right_index=True (hoặc cả hai) để cho biết rằng chỉ mục nên được sử dụng làm key hợp nhất:

Vì phương thức hợp nhất mặc định là giao nhau với các key nối, nên thay vào đó, bạn có thể
tạo liên kết của chúng bằng một liên kết ngoài:

Với dữ liệu được lập chỉ mục theo thứ bậc, mọi thứ phức tạp hơn một chút:

38
Trong trường hợp này, bạn phải chỉ ra nhiều cột để hợp nhất thành một list (chú ý đến việc
xử lý các giá trị chỉ mục trùng lặp):

Việc sử dụng chỉ mục của cả hai phía của việc hợp nhất cũng không phải là vấn đề:

39
DataFrame có phiên bản tham gia thuận tiện hơn để hợp nhất theo chỉ mục. Nó cũng có thể
được sử dụng để kết hợp nhiều đối tượng DataFrame có cùng chỉ mục hoặc tương tự nhau
nhưng các cột không chồng chéo. Trong ví dụ trước, chúng ta có thể viết:

Một phần vì lý do cũ (các phiên bản trước đó của pandas), phương thức nối của DataFrame
thực hiện nối trái trên các key. Nó cũng hỗ trợ nối chỉ mục của DataFrame đã truyền trên một
trong các cột của DataFrame đang gọi:

Cuối cùng, để hợp nhất chỉ mục trên chỉ mục đơn giản, bạn có thể chuyển danh sách
DataFrames để tham gia thay thế cho việc sử dụng hàm concat tổng quát hơn được mô tả bên
dưới:

Ghép nối dọc theo một trục


Ngoài ra, một loại hoạt động kết hợp dữ liệu khác được nhắc đến như nối, liên kết hoặc xếp
chồng. NumPy có chức năng concatenate để thực hiện việc này với mảng NumPy thô:

40
Trong ngữ cảnh của các đối tượng pandas như Series và DataFrame, việc có các trục được
gắn nhãn cho phép bạn khái quát hóa hơn nữa việc nối mảng. Đặc biệt, bạn có thêm một số
điều cần suy nghĩ:
• Nếu các đối tượng được lập chỉ mục khác nhau trên các trục khác, tập hợp các trục
nên được hợp nhất hay giao nhau?
• Các nhóm có cần được nhận dạng trong đối tượng resulting không?
• Trục nối có quan trọng không?

Hàm concat trong pandas cung cấp một cách nhất quán để giải quyết từng vấn đề này. Tôi sẽ
đưa ra một số ví dụ để minh họa cách thức hoạt động của nó. Giả sử chúng ta có ba Series
không có chỉ mục trùng lặp:

Gọi concat với những đối tượng này trong danh sách sẽ gắn kết các giá trị và chỉ mục lại với
nhau:

Theo mặc định, concat hoạt động dọc theo axis=0, tạo ra một chuỗi khác. Nếu bạn chuyển
axis=1, thay vào đó, kết quả sẽ là DataFrame (axis=1 là các cột):

41
Trong trường hợp này, không có sự chồng chéo trên trục khác, mà như bạn có thể thấy là liên
kết được sắp xếp (nối 'bên ngoài') của các chỉ mục. Thay vào đó, bạn có thể giao nhau bằng
cách chuyển join='inner':

Bạn thậm chí có thể chỉ định các trục sẽ được sử dụng trên các trục khác bằng join_axes:

Một vấn đề là các phần được nối không thể xác định được trong kết quả. Thay vào đó, giả sử
bạn muốn tạo chỉ mục phân cấp trên trục nối. Để thực hiện việc này, hãy sử dụng đối số key:

Trong trường hợp kết hợp chuỗi dọc theo trục=1, các key sẽ trở thành tiêu đề cột DataFrame:

42
Logic tương tự mở rộng cho các đối tượng DataFrame:

Nếu bạn truyền một dict của các đối tượng thay vì một list, các key của dict đó sẽ được sử
dụng cho tùy chọn key:

Có một số đối số bổ sung chi phối cách tạo chỉ mục phân cấp (xem Bảng 7-2):

Cân nhắc cuối cùng liên quan đến DataFrames trong đó chỉ mục hàng không có ý nghĩa trong
bối cảnh phân tích:

Trong trường hợp này, bạn có thể truyền ignore_index=True:

43
Bảng 7-2. đối số hàm concat
Hàm Mô tả
objs List hoặc dict của các đối tượng pandas được nối. Trục đối số bắt buộc
duy nhất.
axis Axis để nối dọc theo; mặc định là 0.
join Một trong số 'bên trong', 'bên ngoài', mặc định là 'bên ngoài'; liệu giao
nhau các chỉ mục (bên trong) hay kết hợp (bên ngoài) dọc theo các trục
khác.
join_axes Các chỉ mục cụ thể để sử dụng cho các trục n-1 khác thay vì thực hiện
các phím logic hợp/giao nhau.
keys Các giá trị để liên kết với các đối tượng được nối, tạo thành một chỉ mục
phân cấp dọc theo trục nối. Có thể là một danh sách hoặc mảng các giá
trị tùy ý, một mảng các bộ dữ liệu hoặc một danh sách các mảng (nếu
mảng nhiều cấp được truyền theo cấp) .
levels Chỉ mục cụ thể để sử dụng làm cấp hoặc cấp chỉ mục phân cấp nếu các
key truyền qua.
names Tên cho các cấp phân cấp được tạo nếu các key and/or cấp độ được thông
qua.
verify_integrity Kiểm tra trục mới trong đối tượng được nối để tìm các bản sao và đưa ra
ngoại lệ nếu có. Theo mặc định (Sai) cho phép trùng lặp
ignore_index Không bảo toàn các chỉ mục dọc theo trục nối, thay vào đó tạo chỉ mục
phạm vi (total_length) mới.

Kết hợp dữ liệu với sự chồng chéo


Một tình huống kết hợp dữ liệu khác không thể được biểu thị dưới dạng thao tác hợp nhất
hoặc nối. Bạn có thể có hai tập dữ liệu có chỉ mục trùng nhau toàn bộ hoặc một phần. Để làm
ví dụ thúc đẩy, hãy xem xét hàm where của NumPy, biểu thị if-else được vector hóa:

44
Series có phương thức combine_first, thực hiện tương đương với thao tác này cộng với việc
căn chỉnh dữ liệu:

Với DataFrames, combine_first tự nhiên thực hiện đều tương tự theo từng cột, vì vậy bạn có
thể coi đó là việc “vá” dữ liệu bị thiếu trong đối tượng gọi với dữ liệu từ đối tượng bạn truyền:

ĐỊNH DẠNG LẠI VÀ XOAY VÒNG


Có một số thao tác cơ bản để sắp xếp lại dữ liệu dạng bảng. Chúng lần lượt được gọi là các
hoạt động định hình lại hoặc xoay vòng.
Định hình lại bằng lập chỉ mục phân cấp
Lập chỉ mục phân cấp cung cấp một cách nhất quán để sắp xếp lại dữ liệu trong DataFrame.
Có hai hành động chính:
• ngăn xếp: thao tác này “xoay” hoặc xoay từ các cột trong dữ liệu sang các hàng
• unstack: thao tác này xoay từ hàng vào cột

Tôi sẽ minh họa các hoạt động này thông qua một loạt ví dụ. Hãy xem xét một DataFrame
nhỏ với các mảng chuỗi dưới dạng chỉ mục hàng và cột:

Sử dụng phương thức stack trên dữ liệu này sẽ xoay các cột thành các hàng, tạo ra một Series:

45
Từ Chuỗi được lập chỉ mục theo thứ bậc, bạn có thể sắp xếp lại dữ liệu vào DataFrame mà
không cần giải nén:

Theo mặc định, mức trong cùng không được xếp chồng (tương tự với ngăn xếp). Bạn có thể
hủy xếp một cấp độ khác bằng cách chuyển số hoặc tên cấp độ:

Việc hủy xếp chồng có thể tạo ra dữ liệu bị thiếu nếu không tìm thấy tất cả các giá trị ở cấp
độ trong mỗi nhóm con:

Việc xếp chồng sẽ lọc ra dữ liệu bị thiếu theo mặc định, do đó thao tác này có thể dễ dàng
đảo ngược:

46
Khi hủy xếp chồng trong DataFrame, mức không xếp chồng sẽ trở thành mức thấp nhất trong
kết quả:

Xoay định dạng “dài” sang “rộng”


Một cách phổ biến để lưu trữ nhiều chuỗi thời gian trong cơ sở dữ liệu và CSV là ở định dạng
long hoặc stacked:

Dữ liệu thường được lưu trữ theo cách này trong cơ sở dữ liệu quan hệ như MySQL dưới
dạng lược đồ cố định (tên cột và kiểu dữ liệu) cho phép số lượng giá trị riêng biệt trong cột
item tăng hoặc giảm khi dữ liệu được thêm hoặc xóa trong bảng. Trong ví dụ trên, date và
item thường là những key chính (theo cách nói của cơ sở dữ liệu quan hệ), cung cấp cả tính
toàn vẹn quan hệ cũng như các phép nối và truy vấn có lập trình dễ dàng hơn trong nhiều
trường hợp. Tất nhiên, nhược điểm là dữ liệu có thể không dễ làm việc ở định dạng long; bạn
có lẽ muốn có một DataFrame chứa một cột cho mỗi giá trị item riêng biệt được lập chỉ mục
theo dấu thời gian trong cột date. Phương thức xoay vòng của DataFrame thực hiện chính xác
phép chuyển đổi này:

47
Hai giá trị đầu tiên được truyền là các cột sẽ được sử dụng làm chỉ mục hàng và cột và cuối
cùng là cột giá trị tùy chọn để điền vào DataFrame. Giả sử bạn có hai cột giá trị mà bạn muốn
định hình lại đồng thời:

Bằng cách bỏ qua đối số cuối cùng, bạn có được một DataFrame với các cột phân cấp:

48
Lưu ý rằng trục xoay chỉ là một phím tắt để tạo chỉ mục phân cấp bằng cách sử dụng set_index
và định hình lại bằng cách giải nén:

CHUYỂN ĐỔI DỮ LIỆU


Cho đến chương này, chúng ta đã quan tâm đến việc sắp xếp lại dữ liệu. Lọc, làm sạch và các
biến đổi khác là một loại hoạt động quan trọng khác.
Loại bỏ trùng lặp
Các hàng trùng lặp có thể được tìm thấy một trong DataFrame vì bất kỳ lý do nào. Đây là một
ví dụ:

Phương thức DataFrame được nhân đôi trả về một Series boolean cho biết mỗi hàng có trùng
lặp hay không:

Tương tự, drop_duplicates trả về một DataFrame trong đó mảng trùng lặp là True:

49
Theo mặc định, cả hai phương pháp này đều xem xét tất cả các cột; ngoài ra, bạn có thể chỉ
định bất kỳ tập hợp con nào trong số chúng để phát hiện các bản sao. Giả sử chúng ta có một
cột giá trị bổ sung và chỉ muốn lọc các bản sao dựa trên cột 'k1':

duplicated và drop_duplicates theo mặc định giữ kết hợp giá trị được quan sát đầu tiên. Truyền
take_last=True sẽ trả về cái cuối cùng:

Chuyển đổi dữ liệu bằng hàm hoặc ánh xạ


Đối với nhiều tập dữ liệu, bạn có thể muốn thực hiện một số phép biến đổi dựa trên các giá
trị trong mảng, Series hoặc cột trong DataFrame. Hãy xem xét dữ liệu giả định sau đây được
thu thập về một số loại thịt:

Giả sử bạn muốn thêm một cột cho biết loại động vật mà mỗi loại thực phẩm có nguồn gốc
từ đó. Hãy viết ra sơ đồ của từng loại thịt riêng biệt cho từng loại động vật:

50
Phương thức bản đồ trên Series chấp nhận một hàm hoặc đối tượng dict-like có chứa ánh xạ,
nhưng ở đây chúng tôi gặp một vấn đề nhỏ là một số thịt ở trên được viết hoa còn một số khác
thì không. Vì vậy, chúng ta cũng cần chuyển đổi từng giá trị thành chữ thường:

Chúng ta cũng có thể truyền một hàm thực hiện tất cả công việc:

Sử dụng bản đồ là một cách thuận tiện để thực hiện các phép biến đổi theo phần tử và các
hoạt động liên quan đến làm sạch dữ liệu khác.

Thay thế giá trị


Việc điền dữ liệu còn thiếu bằng phương pháp fillna có thể được coi là trường hợp đặc biệt
của việc thay thế giá trị tổng quát hơn. Trong khi bản đồ, như bạn đã thấy ở trên, có thể được
sử dụng để sửa đổi một tập hợp con các giá trị trong một đối tượng, thì thay thế cung cấp một
cách đơn giản và linh hoạt hơn để thực hiện điều đó. Hãy xem xét Series này:

Giá trị -999 có thể là giá trị sentinel cho dữ liệu bị thiếu. Để thay thế các giá trị này bằng các
giá trị NA mà pandas hiểu được, chúng ta có thể sử dụng phương pháp thay thế, tạo ra một
Series mới:

51
Nếu bạn muốn thay thế nhiều giá trị cùng một lúc, thay vào đó bạn chuyển một danh sách rồi
đến giá trị thay thế:

Để sử dụng một giá trị thay thế khác cho từng giá trị, hãy chuyển danh sách các giá trị thay
thế:

Đối số được thông qua cũng có thể là một dict:

Đổi tên chỉ mục trục


Giống như các giá trị trong Series, các nhãn trục có thể được biến đổi tương tự bằng một hàm
hoặc ánh xạ của một số dạng để tạo ra các đối tượng mới được gắn nhãn khác nhau. Các trục
cũng có thể được sửa đổi tại chỗ mà không cần tạo cấu trúc dữ liệu mới. Đây là một ví dụ đơn
giản:

Giống như Series, các chỉ mục trục có phương thức bản đồ:

Bạn có thể gán cho chỉ mục, sửa đổi DataFrame tại chỗ:

52
Nếu bạn muốn tạo phiên bản đã chuyển đổi của tập dữ liệu mà không sửa đổi bản gốc, một
phương pháp hữu ích là đổi tên:

Đáng chú ý, rename có thể được sử dụng cùng với một đối tượng dict-like cung cấp các giá
trị mới cho một tập hợp con các nhãn trục:

rename giúp tiết kiệm việc phải sao chép DataFrame theo cách thủ công và gán cho các thuộc
tính chỉ mục và cột của nó. Nếu bạn muốn sửa đổi tập dữ liệu tại chỗ, hãy truyền inplace=True:

Sự rời rạc hóa và Binning


Dữ liệu liên tục thường được rời rạc hóa hoặc được tách thành các “bins” để phân tích. Giả
sử bạn có dữ liệu về một nhóm người trong một nghiên cứu và bạn muốn nhóm họ thành các
nhóm tuổi riêng biệt:

Hãy chia chúng thành bins từ 18 đến 25, 26 đến 35, 35 đến 60 và cuối cùng là 60 và lớn hơn.
Để làm như vậy, bạn phải sử dụng cut, một hàm trong pandas:

53
Đối tượng pandas trả về là một đối tượng Phân loại đặc biệt. Bạn có thể coi nó giống như một
mảng của các chuỗi chỉ tên bin; bên trong nó chứa một mảng cấp độ cho biết tên danh mục
riêng biệt cùng với nhãn cho dữ liệu độ tuổi trong thuộc tính nhãn:

Phù hợp với ký hiệu toán học cho các khoảng, dấu ngoặc đơn có nghĩa là một bên mở trong
khi dấu ngoặc vuông có nghĩa là nó đóng (inclusive). Bên nào bị đóng có thể được thay đổi
bằng cách truyền right=False:

Bạn cũng có thể truyền tên bin của riêng mình bằng cách chuyển danh sách hoặc mảng tới
tùy chọn nhãn:

Nếu bạn cắt một số nguyên của bins thay cho các cạnh bin rõ ràng, nó sẽ tính toán các bin có
độ dài bằng nhau dựa trên giá trị tối thiểu và tối đa trong dữ liệu. Hãy xem xét trường hợp
một số dữ liệu phân bố đồng đều được chia thành bốn phần:

Một hàm liên quan chặt chẽ, qcut, đóng gói dữ liệu dựa trên số lượng mẫu. Tùy thuộc vào sự
phân bố dữ liệu, việc sử dụng cut thường không dẫn đến mỗi bin có cùng số điểm dữ liệu. Vì
qcut sử dụng định lượng mẫu thay vào đó, nên theo định nghĩa, bạn sẽ thu được các bins có
kích thước gần bằng nhau:
54
Tương tự như cut, bạn có thể chuyển số lượng của riêng mình (bao gồm các số từ 0 đến 1):

Chúng ta sẽ quay lại cut và qcut ở phần sau của chương về phép tính tổng và nhóm, vì các
hàm rời rạc này đặc biệt hữu ích cho phân tích lượng tử và phân tích nhóm.
Phát hiện và lọc các ngoại lệ
Việc lọc hoặc chuyển đổi các giá trị ngoại lệ phần lớn là vấn đề áp dụng các phép toán mảng.
Hãy xem xét một DataFrame với một số dữ liệu được phân phối thông thường:

Giả sử bạn muốn tìm các giá trị ở một trong các cột có độ lớn lớn hơn ba:

55
Để chọn tất cả các hàng có giá trị vượt quá 3 hoặc -3, bạn có thể sử dụng phương thức bất kỳ
trên DataFrame boolean:

Các giá trị có thể dễ dàng được thiết lập dựa trên các tiêu chí này. Đây là mã để giới hạn các
giá trị ngoài khoảng -3 đến 3:

ufunc np.sign trả về một mảng 1 và -1 tùy thuộc vào dấu của các giá trị.
Hoán vị và lấy mẫu ngẫu nhiên
Việc hoán đổi (sắp xếp lại ngẫu nhiên) một Series hoặc các hàng trong DataFrame rất dễ thực
hiện bằng cách sử dụng hàm numpy.random.permutation. Gọi permutation theo độ dài của
trục bạn muốn hoán vị sẽ tạo ra một mảng các số nguyên biểu thị thứ tự mới:

Sau đó, mảng có thể được sử dụng trong lập chỉ mục ix-based hoặc hàm take:

Để chọn một tập hợp con ngẫu nhiên mà không cần thay thế, một cách là cắt bỏ phần tử k đầu
tiên của mảng được trả về bằng phép hoán vị, trong đó k là kích thước tập hợp con mong
muốn. Có nhiều thuật toán lấy mẫu mà không cần thay thế hiệu quả hơn, nhưng đây là một
chiến lược dễ dàng sử dụng các công cụ sẵn có:

56
Để tạo mẫu có thể thay thế, cách nhanh nhất là sử dụng np.random.randint để vẽ các số nguyên
ngẫu nhiên:

Chỉ báo tính toán/Biến giả


Một loại chuyển đổi khác cho các ứng dụng mô hình thống kê hoặc machine learning là
chuyển đổi một biến phân loại thành ma trận “giả” hoặc “chỉ báo”. Nếu một cột trong
DataFrame có giá trị riêng biệt k, bạn sẽ rút ra một ma trận hoặc DataFrame chứa cột k chứa
tất cả số 1 và số 0. pandas có chức năng get_dummies để thực hiện việc này, mặc dù việc tự
mình nghĩ ra một chức năng không khó. Hãy quay lại một DataFrame ví dụ trước đó:

Trong một số trường hợp, bạn có lẽ muốn thêm tiền tố vào các cột trong DataFrame chỉ báo,
sau đó có thể hợp nhất tiền tố này với dữ liệu khác. get_dummies có một đối số tiền tố để thực
hiện việc này:

Nếu một hàng trong DataFrame thuộc nhiều danh mục thì mọi thứ sẽ phức tạp hơn một chút.
Hãy quay lại tập dữ liệu MovieLens 1M ở phần trước của cuốn sách:

57
Việc thêm các biến chỉ báo cho từng thể loại đòi hỏi một chút rắc rối. Đầu tiên, chúng tôi trích
xuất danh sách các thể loại độc đáo trong tập dữ liệu (sử dụng thủ thuật set.union hay):

Bây giờ, một cách để xây dựng DataFrame chỉ báo là bắt đầu với DataFrame toàn số 0:

Bây giờ, lặp lại từng bộ phim và đặt các mục trong mỗi hàng dumies thành 1:

Sau đó, như trên, bạn có thể kết hợp điều này với phim:

Đối với dữ liệu lớn hơn nhiều, phương pháp xây dựng các biến chỉ báo có nhiều
thành viên này đặc biệt không nhanh chóng. Chắc chắn có thể viết được một
hàm cấp thấp hơn tận dụng phần bên trong của DataFrame.

58
Một công thức hữu ích cho các ứng dụng thống kê là kết hợp get_dummies với hàm rời rạc
như cut:

THAO TÁC CHUỖI


Python từ lâu đã trở thành ngôn ngữ trộn dữ liệu phổ biến một phần do dễ sử dụng để xử lý
chuỗi và văn bản. Hầu hết các thao tác văn bản đều được thực hiện đơn giản bằng các phương
thức có sẵn của đối tượng chuỗi. Để phù hợp mẫu và thao tác văn bản phức tạp hơn, có thể
cần đến các biểu thức thông thường. pandas bổ sung thêm sự kết hợp bằng cách cho phép bạn
áp dụng chuỗi và biểu thức chính quy một cách chính xác trên toàn bộ mảng dữ liệu, đồng
thời xử lý sự rắc rối do thiếu dữ liệu.
Phương thức đối tượng chuỗi
Trong nhiều ứng dụng trộn chuỗi và tạo tập lệnh, các phương thức chuỗi tích hợp sẵn là đủ.
Ví dụ, một chuỗi được phân tách bằng dấu phẩy có thể được chia thành nhiều phần bằng cách
phân tách:

split thường được kết hợp với strip để cắt khoảng trắng (bao gồm cả dòng mới):

Các chuỗi con này có thể được nối với nhau bằng dấu phân cách hai dấu hai chấm bằng cách
sử dụng phép cộng:

Tuy nhiên, đây không phải là một phương pháp chung thực tế. Một cách Pythonic nhanh hơn
và nhiều hơn là chuyển một danh sách hoặc bộ dữ liệu tới phương thức join trên chuỗi '::':

Các phương pháp khác liên quan đến việc định vị chuỗi con. Sử dụng in keyword của Python
là cách tốt nhất để phát hiện chuỗi con, mặc dù index và find cũng có thể được sử dụng:
59
Lưu ý sự khác biệt giữa find và index là index sẽ đưa ra một ngoại lệ nếu không tìm thấy
chuỗi (trả về -1):

Tương tự, count trả về số lần xuất hiện của một chuỗi con cụ thể:

replace sẽ thay thế sự xuất hiện của một mẫu này bằng một mẫu khác. Điều này cũng thường
được sử dụng để xóa các mẫu bằng cách chuyển một chuỗi trống:

Biểu thức chính quy cũng có thể được sử dụng với nhiều thao tác trong số này như bạn sẽ
thấy bên dưới.
Bảng 7-3. Các phương thức chuỗi tích hợp của Python
Hàm Mô tả
count Trả về số lần xuất hiện không chồng chéo của chuỗi con trong
chuỗi.
endswith, startswith Trả về True nếu chuỗi kết thúc bằng hậu tố (bắt đầu bằng tiền tố).
join Sử dụng chuỗi làm dấu phân cách để nối một chuỗi các chuỗi khác.
index Trả về vị trí của ký tự đầu tiên trong chuỗi con nếu tìm thấy trong
chuỗi. Tăng giá trị ValueEr nếu không tìm thấy.
find Trả về ký tự đầu tiên của chuỗi con đầu tiên xuất hiện trong chuỗi.
Giống như chỉ mục, nhưng trả về -1 nếu không tìm thấy.
rfind Trả về vị trí ký tự đầu tiên của chuỗi con xuất hiện cuối cùng trong
chuỗi. Trả về -1 nếu không tìm thấy.
replace Thay thế lần xuất hiện của chuỗi bằng một chuỗi khác.
strip, rstrip, lstrip Cắt bỏ khoảng trắng, bao gồm cả dòng mới; tương đương với
x.strip() (và rstrip, lstrip tương ứng) cho từng phần tử.
split Ngắt chuỗi thành danh sách các chuỗi con bằng cách sử dụng dấu
phân cách đã truyền.
lower, upper Chuyển đổi các ký tự bảng chữ cái thành chữ thường hoặc chữ hoa
tương ứng.
ljust, rjust Căn trái hoặc căn phải tương ứng. Đệm phía đối diện của chuỗi
bằng dấu cách (hoặc một số ký tự điền khác) để trả về một chuỗi
có chiều rộng tối thiểu.

Biểu thức chính quy


Biểu thức chính quy cung cấp một cách linh hoạt để tìm kiếm hoặc kết nối các mẫu chuỗi
trong văn bản. Một biểu thức đơn, thường được gọi là biểu thức chính quy, là một chuỗi được
60
hình thành theo ngôn ngữ biểu thức chính quy. Module re tích hợp của Python chịu trách
nhiệm áp dụng các biểu thức chính quy cho chuỗi; tôi sẽ đưa ra một số ví dụ về việc sử dụng
nó ở đây.

Nghệ thuật viết biểu thức chính quy có thể là một chương riêng và do đó nằm
ngoài phạm vi của cuốn sách. Có rất nhiều hướng dẫn và tài liệu tham khảo tuyệt
vời trên internet, chẳng hạn như Learn Regex The Hard Way của Zed Shaw
(http://regex.learncodethehardway.org/book/).

Các chức năng của module re rơi vào ba loại: khớp mẫu, thay thế và phân tách. Đương nhiên
những điều này đều có liên quan; một biểu thức chính quy mô tả một mẫu để định vị trong
văn bản, sau đó có thể được sử dụng cho nhiều mục đích. Hãy xem một ví dụ đơn giản: giả
sử tôi muốn tách một chuỗi có số lượng ký tự khoảng trắng thay đổi (tab, dấu cách và dòng
mới). Regex mô tả một hoặc nhiều ký tự khoảng trắng là \s+:

Khi bạn gọi re.split('\s+', text), biểu thức chính quy sẽ được biên dịch trước tiên, sau đó
phương thức phân tách của nó sẽ được gọi trên văn bản được truyền. Bạn có thể tự biên dịch
biểu thức chính quy bằng re.compile, tạo thành một đối tượng regex có thể tái sử dụng:

Thay vào đó, nếu bạn muốn có danh sách tất cả các mẫu khớp với regex, bạn có thể sử dụng
phương thức findall:

Để tránh việc thoát không mong muốn bằng \ trong biểu thức chính quy, hãy sử
dụng các chuỗi ký tự thô như r'C:\x' thay vì 'C:\\x' tương đương.

Bạn nên tạo một đối tượng regex bằng re.compile nếu bạn có ý định áp dụng cùng một biểu
thức cho nhiều chuỗi; làm như vậy sẽ tiết kiệm chu kỳ CPU.
match và search có liên quan chặt chẽ với findall. Trong khi findall trả về tất cả kết quả khớp
trong một chuỗi thì search chỉ trả về match đầu tiên. Cứng nhắc hơn, match chỉ khớp các kết
quả ở đầu chuỗi. Một ví dụ ít tầm thường hơn, hãy xem xét một khối văn bản và một biểu
thức chính quy có khả năng xác định hầu hết các địa chỉ email:

61
Sử dụng findall trên văn bản sẽ tạo ra danh sách các địa chỉ email:

search trả về một đối tượng match đặc biệt cho địa chỉ email đầu tiên trong văn bản. Đối với
regex ở trên, đối tượng match chỉ có thể cho chúng ta biết vị trí bắt đầu và kết thúc của mẫu
trong chuỗi:

regex.match trả về None, vì nó chỉ khớp nếu mẫu xuất hiện ở đầu chuỗi:

Tương tự, sub sẽ trả về một chuỗi mới với các lần xuất hiện của mẫu được thay thế bằng chuỗi
mới:

Giả sử bạn muốn tìm địa chỉ email và đồng thời phân chia từng địa chỉ thành 3 thành phần:
tên người dùng, tên miền và hậu tố tên miền. Để thực hiện việc này, hãy đặt dấu ngoặc đơn
xung quanh các phần của mẫu cần phân đoạn:

Một đối tượng match được tạo bởi regex đã sửa đổi này trả về một bộ các thành phần mẫu
bằng phương thức nhóm của nó:

findall trả về danh sách các bộ dữ liệu khi mẫu có các nhóm:

sub cũng có quyền truy cập vào các nhóm trong mỗi match bằng các ký hiệu đặc biệt như \1,
\2, v.v.:

Còn nhiều biểu thức chính quy khác trong Python, hầu hết đều nằm ngoài phạm vi của cuốn
sách. Để giúp bạn hình dung, một biến thể của regex email ở trên sẽ đặt tên cho các nhóm
match:
62
Đối tượng match được tạo bởi regex như vậy có thể tạo ra một dict chính xác tiện dụng với
tên nhóm được chỉ định:

Bảng 7-4. Phương pháp biểu thức chính quy


Hàm Mô tả
findall, finditer Trả về tất cả các mẫu khớp không chồng chéo trong một chuỗi. findall
trả về danh sách tất cả các mẫu trong khi finditer trả về từng mẫu một
từ một trình vòng lặp.
match Mẫu match ở đầu chuỗi và tùy ý phân đoạn các thành phần mẫu thành
các nhóm. Nếu mẫu khớp, trả về một match object, nếu không thì
None.
search Quét chuỗi để khớp với mẫu; trả về một match object nếu có. Không
giống match, the match có thể ở bất kỳ đâu trong chuỗi thay vì chỉ ở
đầu.
split Tách chuỗi thành từng mảnh ở mỗi lần xuất hiện mẫu.
sub, subn Thay thế tất cả (sub) hoặc n lần xuất hiện đầu tiên (subn) của mẫu
trong chuỗi bằng biểu thức thay thế. Sử dụng các ký hiệu \1, \2, ... để
chỉ các phần tử nhóm match trong chuỗi thay thế

Các hàm chuỗi được vector hóa trong pandas


Việc dọn dẹp một tập dữ liệu lộn xộn để phân tích thường đòi hỏi rất nhiều thao tác trộn và
chuẩn hóa chuỗi. Để làm phức tạp vấn đề, một cột chứa chuỗi đôi khi sẽ thiếu dữ liệu:

Các phương thức chuỗi và biểu thức chính quy có thể được áp dụng (truyền lambda hoặc hàm
khác) cho từng giá trị bằng data.map, nhưng nó sẽ thất bại trên NA. Để giải quyết vấn đề này,
Series có các phương thức ngắn gọn cho các thao tác chuỗi bỏ qua các giá trị NA. Chúng
được truy cập thông qua thuộc tính str của Series; ví dụ: chúng ta có thể kiểm tra xem mỗi địa
chỉ email có 'gmail' trong đó hay không bằng str.contains:

63
Bạn cũng có thể sử dụng các biểu thức thông thường cùng với bất kỳ tùy chọn lại nào như
IGNORECASE:

Có một số cách để thực hiện truy xuất phần tử được vector hóa. Sử dụng str.get hoặc index
vào thuộc tính str:

Bạn có thể cắt chuỗi tương tự bằng cú pháp sau:

Bảng 7-5. Các phương thức chuỗi được vector hóa


Phương pháp Mô tả
cat Ghép nối các chuỗi theo từng phần tử với dấu phân cách.
contains Trả về mảng boolean nếu mỗi chuỗi chứa pattern/biểu thức.
count Đếm số lần xuất hiện của mẫu .
endswith, startswith Tương đương với x.endswith(pattern) hoặc x.startswith(pattern)
cho mỗi phần tử.
findall Tính toán danh sách tất cả các lần xuất hiện của pattern/regex cho
mỗi chuỗi.
get Chỉ mục cho từng phần tử (truy xuất phần tử thứ i)
join Nối các chuỗi trong mỗi phần tử của Series với dấu phân cách được
truyền.
len Tính độ dài của mỗi chuỗi.
lower, upper Chuyển đổi các trường hợp; tương đương với x.low() hoặc x.upper()
cho mỗi phần tử.

64
match Sử dụng re.match với biểu thức chính quy được truyền trên mỗi
phần tử, trả về các nhóm khớp dưới dạng danh sách.
pad Thêm khoảng trắng vào bên trái, bên phải hoặc cả hai bên của trung
tâm chuỗi.
center Tương đương với lặp lại pad(side='both').
repeat Các giá trị trùng lặp; ví dụ s.str.repeat(3) tương đương với x * 3 cho
mỗi chuỗi.
replace Thay thế các lần xuất hiện của pattern/regex bằng một số chuỗi
khác.
slice Cắt từng chuỗi trong Series.
split Tách các chuỗi trên dải phân cách hoặc dải biểu thức chính quy.
strip, rstrip, lstrip Cắt bỏ khoảng trắng, bao gồm cả dòng mới; tương đương với
x.strip() (và rstrip, lstrip tương ứng) cho từng phần tử.

VÍ DỤ: CƠ SỞ DỮ LIỆU THỰC PHẨM CỦA USDA


Bộ Nông nghiệp Hoa Kỳ cung cấp cơ sở dữ liệu về thông tin dinh dưỡng thực phẩm. Ashley
Williams, một hacker người Anh, đã cung cấp phiên bản của cơ sở dữ liệu này ở định dạng
JSON (http://ashleyw.co.uk/project/food-nutrition-database). Những dữ liệu trông như thế
này:

Mỗi loại thực phẩm có một số thuộc tính nhận dạng cùng với hai danh sách các chất dinh
dưỡng và kích thước khẩu phần ăn. Việc có dữ liệu ở dạng này đặc biệt không phù hợp để
phân tích, vì vậy chúng ta cần thực hiện một số công việc để sắp xếp dữ liệu thành một hình
thức tốt hơn.
Sau khi tải và trích xuất dữ liệu từ liên kết ở trên, bạn có thể tải dữ liệu đó vào Python với bất
kỳ thư viện JSON nào mà bạn chọn. Tôi sẽ sử dụng module json Python có sẵn:

65
Mỗi mục trong db là một bản cập nhật chứa tất cả dữ liệu cho một loại thực phẩm. Trường
'chất dinh dưỡng' là danh sách các dict, mỗi chất dinh dưỡng:

Khi chuyển đổi danh sách các dict thành một DataFrame, chúng ta có thể chỉ định danh sách
các trường cần trích xuất. Chúng tôi sẽ lấy tên thực phẩm, nhóm, id và nhà sản xuất:

Bạn có thể thấy sự phân bố của các nhóm thực phẩm với value_counts:

Bây giờ, để thực hiện một số phân tích về tất cả dữ liệu dinh dưỡng, cách dễ nhất là tập hợp
các chất dinh dưỡng cho từng loại thực phẩm vào một bảng lớn duy nhất. Để làm như vậy,
66
chúng ta cần thực hiện một số bước. Trước tiên, tôi sẽ chuyển đổi từng danh sách chất dinh
dưỡng thực phẩm thành một DataFrame, thêm cột cho id thực phẩm và bổ sung DataFrame
vào danh sách. Sau đó, chúng có thể được nối với nhau bằng concat:

Nếu mọi việc suôn sẻ, chất dinh dưỡng sẽ trông như thế này:

Tôi nhận thấy rằng, vì lý do gì đi nữa, có duplicates trong DataFrame này, khiến việc loại bỏ
chúng dễ dàng hơn:

Vì 'group' và 'description' nằm trong cả hai đối tượng DataFrame nên chúng ta có thể đổi tên
chúng để làm rõ đó là gì:

67
Sau khi hoàn thành tất cả những điều này, chúng tôi đã sẵn sàng hợp nhất thông tin với các
chất dinh dưỡng:

Các công cụ mà bạn cần để chia nhỏ, tổng hợp và trực quan hóa tập dữ liệu này sẽ được khám
phá chi tiết trong hai chương tiếp theo, vì vậy sau khi nắm vững các phương pháp đó, bạn có
thể quay lại tập dữ liệu này. Ví dụ, chúng ta có thể vẽ biểu đồ các giá trị trung bình theo nhóm
thực phẩm và loại chất dinh dưỡng (xem Hình 7-1):

Với một chút thông minh, bạn có thể tìm ra thực phẩm nào dày đặc nhất trong từng chất dinh
dưỡng:

68
Hình 7-1. Giá trị kẽm trung bình theo nhóm dinh dưỡng

DataFrame kết quả hơi quá lớn để hiển thị trong sách; còn đây chỉ là nhóm dinh dưỡng 'Axit
amin':

69
CHƯƠNG 11: CÁC ỨNG DỤNG DỮ LIỆU TÀI CHÍNH VÀ KINH TẾ
Việc sử dụng Python trong ngành tài chính đã tăng lên nhanh chóng kể từ năm 2005, chủ yếu
là do sự phát triển của các thư viện (như NumPy và pandas) và sự sẵn có của các lập trình
viên Python lành nghề. Các tổ chức đã nhận thấy rằng Python rất phù hợp với cả vai trò là
môi trường phân tích tương tác cũng như cho phép phát triển các hệ thống mạnh mẽ thường
xuyên trong một khoảng thời gian ngắn so với Java hoặc C++. Python cũng là một lớp keo lý
tưởng; thật dễ dàng để xây dựng giao diện Python cho các thư viện cũ được xây dựng bằng C
hoặc C++.

Mặc dù lĩnh vực phân tích tài chính rất rộng để viết hết một cuốn sách nhưng tôi hy vọng sẽ
chỉ cho bạn cách áp dụng các công cụ trong cuốn sách này vào một số vấn đề cụ thể trong tài
chính. Cũng như các lĩnh vực nghiên cứu và phân tích khác, quá nhiều nỗ lực lập trình thường
được dành cho việc xử lý dữ liệu thay vì giải quyết các vấn đề nghiên cứu và mô hình hóa cốt
lõi. Cá nhân tôi bắt đầu xây dựng pandas vào năm 2008 khi đang vật lộn với những công cụ
dữ liệu không đầy đủ.
Trong những ví dụ này, tôi sẽ sử dụng thuật ngữ cross-section để chỉ dữ liệu tại một thời điểm
cố định. Ví dụ: giá đóng cửa của tất cả các cổ phiếu trong chỉ số S&P 500 vào một ngày cụ
thể tạo thành một cross-section. Dữ liệu chéo tại nhiều thời điểm trên nhiều mục dữ liệu (ví
dụ: giá cùng với khối lượng) tạo thành một panel. Dữ liệu bảng có thể được biểu diễn dưới
dạng DataFrame được lập chỉ mục theo thứ bậc hoặc sử dụng đối tượng bảng điều khiển ba
chiều pandas.

CHỦ ĐỀ TRỘN DỮ LIỆU


Nhiều công cụ tìm kiếm dữ liệu hữu ích cho các ứng dụng tài chính được trình bày trong các
chương trước. Ở đây tôi sẽ nêu bật một số chủ đề liên quan đến lĩnh vực vấn đề này.

Sắp xếp chuỗi thời gian và mặt cắt ngang


Một trong những vấn đề tốn nhiều thời gian nhất khi làm việc với dữ liệu tài chính được gọi
là vấn đề căn chỉnh dữ liệu. Hai chuỗi thời gian liên quan có thể có các chỉ mục không thẳng
hàng hoàn hảo hoặc hai đối tượng DataFrame có thể có các cột hoặc nhãn hàng không khớp.
Người dùng MATLAB, R và các ngôn ngữ lập trình ma trận khác thường cố gắng nỗ lực đáng
kể vào việc sắp xếp dữ liệu thành các dạng được căn chỉnh hoàn hảo. Theo kinh nghiệm của
tôi, việc phải căn chỉnh dữ liệu bằng tay (và tệ hơn là phải xác minh rằng dữ liệu đã được căn
chỉnh) là một cách làm việc quá cứng nhắc và tẻ nhạt. Nó cũng tiềm ẩn nhiều lỗi do kết hợp
dữ liệu sai lệch.

pandas thực hiện cách tiếp cận khác bằng cách tự động căn chỉnh dữ liệu trong các phép toán
số học. Trong thực tế, điều này mang lại sự tự do to lớn và nâng cao năng suất của bạn. Ví
dụ: hãy xem xét một vài DataFrame chứa chuỗi thời gian về giá và khối lượng cổ phiếu:

In [16]: prices
Out[16]:
AAPL JNJ SPX XOM
2011-09-06 379.74 64.64 1165.24 71.15
2011-09-07 383.93 65.43 1198.62 73.65
2011-09-08 384.14 64.95 1185.90 72.82
70
2011-09-09 377.48 63.64 1154.23 71.01
2011-09-12 379.94 63.59 1162.27 71.84
2011-09-13 384.62 63.61 1172.87 71.65
2011-09-14 389.30 63.73 1188.68 72.64

In [17]: volume
Out[17]:
AAPL JNJ XOM
2011-09-06 18173500 15848300 25416300
2011-09-07 12492000 10759700 23108400
2011-09-08 14839800 15551500 22434800
2011-09-09 20171900 17008200 27969100
2011-09-12 16697300 13448200 26205800

Giả sử bạn muốn tính giá trung bình theo khối lượng bằng cách sử dụng tất cả dữ liệu có sẵn
(và đưa ra giả định đơn giản hóa rằng dữ liệu khối lượng là tập hợp con của dữ liệu giá). Vì
pandas tự động căn chỉnh dữ liệu theo số học và loại trừ dữ liệu bị thiếu trong các hàm như
sum, chúng ta có thể diễn đạt điều này một cách ngắn gọn như sau:

In [18]: prices * volume


Out[18]:
AAPL JNJ SPX XOM
2011-09-06 6901204890 1024434112 NaN 1808369745
2011-09-07 4796053560 704007171 NaN 1701933660
2011-09-08 5700560772 1010069925 NaN 1633702136
2011-09-09 7614488812 1082401848 NaN 1986085791
2011-09-12 6343972162 855171038 NaN 1882624672
2011-09-13 NaN NaN NaN NaN
2011-09-14 NaN NaN NaN NaN

In [19]: vwap = (prices * volume).sum() / volume.sum()


In [20]: vwap In [21]: vwap.dropna()
Out[20]: Out[21]:
AAPL 380.655181 AAPL 380.655181
JNJ 64.394769 JNJ 64.394769
SPX NaN XOM 72.024288
XOM 72.024288

Khi SPX không được tìm thấy trong volume, bạn có thể chọn loại bỏ nó một cách rõ ràng bất
cứ lúc nào. Nếu bạn muốn căn chỉnh bằng tay, bạn có thể sử dụng phương thức DataFrame’s
align trả về một bộ phiên bản được lập của hai đối tượng:

In [22]: prices.align(volume, join='inner')


Out[22]:
( AAPL JNJ XOM
2011-09-06 379.74 64.64 71.15
2011-09-07 383.93 65.43 73.65

71
2011-09-08 384.14 64.95 72.82
2011-09-09 377.48 63.64 71.01
2011-09-12 379.94 63.59 71.84,
AAPL JNJ XOM
2011-09-06 18173500 15848300 25416300
2011-09-07 12492000 10759700 23108400
2011-09-08 14839800 15551500 22434800
2011-09-09 20171900 17008200 27969100
2011-09-12 16697300 13448200 26205800)

Một tính năng không thể thiếu khác là xây dựng DataFrame từ một tập hợp các Sê-ri có thể
được lập chỉ mục khác nhau:

In [23]: s1 = Series(range(3), index=['a', 'b', 'c'])


In [24]: s2 = Series(range(4), index=['d', 'b', 'c', 'e'])
In [25]: s3 = Series(range(3), index=['f', 'a', 'c'])
In [26]: DataFrame({'one': s1, 'two': s2, 'three': s3})
Out[26]:
one three two
a 0 1 NaN
b 1 NaN 1
c 2 2 2
d NaN NaN 0
e NaN NaN 3
f NaN 0 NaN

Như bạn đã thấy trước đó, tất nhiên bạn có thể chỉ định rõ ràng chỉ số của kết quả, loại bỏ
phần dữ liệu còn lại:

In [27]: DataFrame({'one': s1, 'two': s2, 'three': s3}, index=list('face'))


Out[27]:
one three two
f NaN 0 NaN
a 0 1 NaN
c 2 2 2
e NaN NaN 3

Hoạt động với chuỗi thời gian có tần số khác nhau


Chuỗi thời gian kinh tế thường có tần suất hàng năm, hàng quý, hàng tháng, hàng ngày hoặc
một số tần số chuyên biệt hơn. Một số hoàn toàn không đều; ví dụ: điều chỉnh thu nhập của
một cổ phiếu có thể đến bất kỳ lúc nào. Hai công cụ chính để chuyển đổi và sắp xếp lại tần số
là resample và phương pháp reindex. resample chuyển đổi dữ liệu sang tần số cố định trong
khi reindex lại làm cho dữ liệu phù hợp với một index mới. Cả hai đều hỗ trợ logic nội suy
tùy chọn (chẳng hạn như điền chuyển tiếp).

Hãy xem xét một chuỗi thời gian nhỏ hàng tuần:

72
In [28]: ts1 = Series(np.random.randn(3),
....: index=pd.date_range('2012-6-13', periods=3, freq='W-WED'))

In [29]: ts1
Out[29]:
2012-06-13 -1.124801
2012-06-20 0.469004
2012-06-27 -0.117439
Freq: W-WED

Nếu bạn lấy mẫu lại thành tần suất kinh doanh hàng ngày (Thứ Hai-Thứ Sáu), bạn sẽ gặp lỗ
hổng vào những ngày không có dữ liệu:

In [30]: ts1.resample('B')
Out[30]:
2012-06-13 -1.124801
2012-06-14 NaN
2012-06-15 NaN
2012-06-18 NaN
2012-06-19 NaN
2012-06-20 0.469004
2012-06-21 NaN
2012-06-22 NaN
2012-06-25 NaN
2012-06-26 NaN
2012-06-27 -0.117439
Freq: B

Tất nhiên, sử dụng 'ffill' như fill_method chuyển tiếp lấp đầy các giá trị trong những khoảng
trống đó. Đây là cách thực hành phổ biến với dữ liệu tần số thấp hơn khi bạn tính toán chuỗi
giá trị theo thời gian trên mỗi dấu thời gian có giá trị hợp lệ hoặc giá trị “tính đến” mới nhất:

In [31]: ts1.resample('B', fill_method='ffill')


Out[31]:
2012-06-13 -1.124801
2012-06-14 -1.124801
2012-06-15 -1.124801
2012-06-18 -1.124801
2012-06-19 -1.124801
2012-06-20 0.469004
2012-06-21 0.469004
2012-06-22 0.469004
2012-06-25 0.469004
2012-06-26 0.469004
2012-06-27 -0.117439
Freq: B

73
Trong thực tế, việc lấy mẫu dữ liệu tần số thấp hơn lên tần số đều đặn, cao hơn là một giải
pháp tốt, nhưng trong trường hợp chuỗi thời gian không đều tổng quát hơn, nó có thể không
phù hợp. Hãy xem xét chuỗi thời gian được lấy mẫu không đều trong cùng khoảng thời gian
chung:
In [32]: dates = pd.DatetimeIndex(['2012-6-12', '2012-6-17', '2012-6-18',
....: '2012-6-21', '2012-6-22', '2012-6-29'])

In [33]: ts2 = Series(np.random.randn(6), index=dates)

In [34]: ts2
Out[34]:
2012-06-12 -0.449429
2012-06-17 0.459648
2012-06-18 -0.172531
2012-06-21 0.835938
2012-06-22 -0.594779
2012-06-29 0.027197

Nếu bạn muốn thêm giá trị “as of” vào ts1 (điền tiếp) tới ts2. Một tùy chọn là lấy mẫu lại cả
hai theo tần suất thông thường rồi thêm vào, nhưng nếu bạn muốn duy trì chỉ số ngày trong
ts2, sử dụng reindex là một giải pháp chính xác hơn:

In [35]: ts1.reindex(ts2.index, method='ffill')


Out[35]:
2012-06-12 NaN
2012-06-17 -1.124801
2012-06-18 -1.124801
2012-06-21 0.469004
2012-06-22 0.469004
2012-06-29 -0.117439

In [36]: ts2 + ts1.reindex(ts2.index, method='ffill')


Out[36]:
2012-06-12 NaN
2012-06-17 -0.665153
2012-06-18 -1.297332
2012-06-21 1.304942
2012-06-22 -0.125775
2012-06-29 -0.090242

Sử dụng chu kỳ thay vì dấu thời gian


Các khoảng thời gian (biểu thị các khoảng thời gian) cung cấp một phương tiện thay thế để
làm việc với các chuỗi thời gian có tần suất khác nhau, đặc biệt là các chuỗi tài chính hoặc
kinh tế có tần suất hàng năm hoặc hàng quý có quy ước báo cáo cụ thể. Ví dụ: một công ty có
thể công bố thu nhập hàng quý với năm tài chính kết thúc vào tháng 6, do đó thường xuyên
có Q-JUN. Hãy xem xét một cặp chuỗi thời gian kinh tế vĩ mô liên quan đến GDP và lạm
phát:
74
In [37]: gdp = Series([1.78, 1.94, 2.08, 2.01, 2.15, 2.31, 2.46],
....: index=pd.period_range('1984Q2', periods=7, freq='Q-SEP'))

In [38]: infl = Series([0.025, 0.045, 0.037, 0.04],


....: index=pd.period_range('1982', periods=4, freq='A-DEC'))

In [39]: gdp In [40]: infl


Out[39]: Out[40]:
1984Q2 1.78 1982 0.025
1984Q3 1.94 1983 0.045
1984Q4 2.08 1984 0.037
1985Q1 2.01 1985 0.040
1985Q2 2.15 Freq: A-DEC
1985Q3 2.31
1985Q4 2.46
Freq: Q-SEP

Không giống như chuỗi thời gian có dấu thời gian, không thể thực hiện được các thao tác giữa
chuỗi thời gian có tần suất khác nhau được lập chỉ mục theo khoảng thời gian nếu không có
chuyển đổi rõ ràng. Trong trường hợp này, nếu chúng ta biết rằng giá trị infl được quan sát
vào cuối mỗi năm, sau đó chúng ta có thể chuyển đổi sang Q-SEP để có được khoảng thời
gian phù hợp với tần suất đó:

In [41]: infl_q = infl.asfreq('Q-SEP', how='end')


In [42]: infl_q
Out[42]:
1983Q1 0.025
1984Q1 0.045
1985Q1 0.037
1986Q1 0.040
Freq: Q-SEP

Chuỗi thời gian đó sau đó có thể được reindexed bằng cách điền chuyển tiếp để khớp gdp:

In [43]: infl_q.reindex(gdp.index, method='ffill')


Out[43]:
1984Q2 0.045
1984Q3 0.045
1984Q4 0.045
1985Q1 0.037
1985Q2 0.037
1985Q3 0.037
1985Q4 0.037
Freq: Q-SEP

75
Thời gian trong ngày và lựa chọn dữ liệu “as of”
Giả sử bạn có một chuỗi thời gian dài chứa dữ liệu thị trường trong ngày và bạn muốn trích
xuất giá tại một thời điểm cụ thể trong ngày trên mỗi ngày của dữ liệu. Điều gì sẽ xảy ra nếu
dữ liệu không đều đặn đến mức các quan sát không rơi chính xác vào thời gian mong muốn?
Trong thực tế, tác vụ này có thể khiến việc trộn dữ liệu dễ xảy ra lỗi nếu bạn không cẩn thận.
Đây là một ví dụ nhằm mục đích minh họa:

# Make an intraday date range and time series


In [44]: rng = pd.date_range('2012-06-01 09:30', '2012-06-01 15:59', freq='T')

# Make a 5-day series of 9:30-15:59 values


In [45]: rng = rng.append([rng + pd.offsets.BDay(i) for i in range(1, 4)])

In [46]: ts = Series(np.arange(len(rng), dtype=float), index=rng)

In [47]: ts
Out[47]:
2012-06-01 09:30:00 0
2012-06-01 09:31:00 1
2012-06-01 09:32:00 2
2012-06-01 09:33:00 3
...
2012-06-06 15:56:00 1556
2012-06-06 15:57:00 1557
2012-06-06 15:58:00 1558
2012-06-06 15:59:00 1559
Length: 1560

Lập mục lục bằng Python datetime.time đối tượng sẽ trích xuất các giá trị tại những thời điểm
đó:

In [48]: from datetime import time

In [49]: ts[time(10, 0)]


Out[49]:
2012-06-01 10:00:00 30
2012-06-04 10:00:00 420
2012-06-05 10:00:00 810
2012-06-06 10:00:00 1200

Nhìn bên dưới, điều này sử dụng một phương thức ví dụ tại at_time (có sẵn trên từng chuỗi
thời gian và các đối tượng DataFrame):

In [50]: ts.at_time(time(10, 0))


Out[50]:
2012-06-01 10:00:00 30
2012-06-04 10:00:00 420

76
2012-06-05 10:00:00 810
2012-06-06 10:00:00 1200

Bạn có thể chọn các giá trị giữa hai lần bằng cách sử dụng các phương pháp between_time:

In [51]: ts.between_time(time(10, 0), time(10, 1))


Out[51]:
2012-06-01 10:00:00 30
2012-06-01 10:01:00 31
2012-06-04 10:00:00 420
2012-06-04 10:01:00 421
2012-06-05 10:00:00 810
2012-06-05 10:01:00 811
2012-06-06 10:00:00 1200
2012-06-06 10:01:00 1201

Như đã đề cập ở trên, có thể xảy ra trường hợp không có dữ liệu nào thực sự rơi chính xác
vào thời điểm như 10 AM, nhưng bạn có thể muốn biết giá trị được biết cuối cùng vào lúc 10
AM:

# Set most of the time series randomly to NA


In [53]: indexer = np.sort(np.random.permutation(len(ts))[700:])

In [54]: irr_ts = ts.copy()

In [55]: irr_ts[indexer] = np.nan

In [56]: irr_ts['2012-06-01 09:50':'2012-06-01 10:00']


Out[56]:
2012-06-01 09:50:00 20
2012-06-01 09:51:00 NaN
2012-06-01 09:52:00 22
2012-06-01 09:53:00 23
2012-06-01 09:54:00 NaN
2012-06-01 09:55:00 25
2012-06-01 09:56:00 NaN
2012-06-01 09:57:00 NaN
2012-06-01 09:58:00 NaN
2012-06-01 09:59:00 NaN
2012-06-01 10:00:00 NaN

Bằng cách chuyển một mảng dấu thời gian tới phương pháp asof này, bạn sẽ nhận được một
mảng các giá trị hợp lệ (không phải NA) cuối cùng tại hoặc trước mỗi dấu thời gian. Vì vậy,
chúng tôi xây dựng phạm vi ngày vào lúc 10 AM mỗi ngày và chuyển phạm vi đó cho asof:

In [57]: selection = pd.date_range('2012-06-01 10:00', periods=4, freq='B')


In [58]: irr_ts.asof(selection)

77
Out[58]:
2012-06-01 10:00:00 25
2012-06-04 10:00:00 420
2012-06-05 10:00:00 810
2012-06-06 10:00:00 1197
Freq: B

Ghép nối các nguồn dữ liệu với nhau


Trong chương 7, tôi đã mô tả một số chiến lược để hợp nhất hai tập dữ liệu liên quan với
nhau. Trong bối cảnh tài chính hoặc kinh tế, có một số trường hợp sử dụng phổ biến:

• Chuyển từ một nguồn dữ liệu (chuỗi thời gian hoặc tập hợp chuỗi thời gian) sang nguồn
khác tại một thời điểm cụ thể
• “Vá” các giá trị bị thiếu trong chuỗi thời gian ở đầu, giữa hoặc cuối bằng cách sử dụng
một chuỗi thời gian khác
• Thay thế hoàn toàn dữ liệu cho một tập hợp con các ký hiệu (quốc gia, mã tài sản và
những cái khác)
Trong trường hợp đầu tiên, việc chuyển từ tập hợp chuỗi thời gian này sang tập hợp chuỗi
thời gian khác tại một thời điểm cụ thể, đó là vấn đề ghép hai đối tượng TimeSeries hoặc
DataFrame lại với nhau bằng cách sử dụng pandas.concat:
In [59]: data1 = DataFrame(np.ones((6, 3), dtype=float),
....: columns=['a', 'b', 'c'],
....: index=pd.date_range('6/12/2012', periods=6))

In [60]: data2 = DataFrame(np.ones((6, 3), dtype=float) * 2,


....: columns=['a', 'b', 'c'],
....: index=pd.date_range('6/13/2012', periods=6))

In [61]: spliced = pd.concat([data1.ix[:'2012-06-14'], data2.ix['2012-06-15':]])

In [62]: spliced
Out[62]:
abc
2012-06-12 1 1 1
2012-06-13 1 1 1
2012-06-14 1 1 1
2012-06-15 2 2 2
2012-06-16 2 2 2
2012-06-17 2 2 2
2012-06-18 2 2 2

Giả sử trong một ví dụ tương tự rằng data1 đã thiếu một chuỗi thời gian có trong data2:

In [63]: data2 = DataFrame(np.ones((6, 4), dtype=float) * 2,


....: columns=['a', 'b', 'c', 'd'],
....: index=pd.date_range('6/13/2012', periods=6))

78
In [64]: spliced = pd.concat([data1.ix[:'2012-06-14'], data2.ix['2012-06-15':]])

In [65]: spliced
Out[65]:
abcd
2012-06-12 1 1 1 NaN
2012-06-13 1 1 1 NaN
2012-06-14 1 1 1 NaN
2012-06-15 2 2 2 2
2012-06-16 2 2 2 2
2012-06-17 2 2 2 2
2012-06-18 2 2 2 2

Sử dụng combine_first, bạn có thể đưa dữ liệu từ trước điểm nối vào để mở rộng lịch sử cho
'd' mục:

In [66]: spliced_filled = spliced.combine_first(data2)

In [67]: spliced_filled
Out[67]:
abcd
2012-06-12 1 1 1 NaN
2012-06-13 1 1 1 2
2012-06-14 1 1 1 2
2012-06-15 2 2 2 2
2012-06-16 2 2 2 2
2012-06-17 2 2 2 2
2012-06-18 2 2 2 2

Từ data2 không có bất kỳ giá trị nào cho 2012-06-12, không có giá trị nào được điền vào ngày
đó.
DataFrame có một phương thức liên quan update để thực hiện cập nhật tại chỗ. Bạn phải vượt
qua overwrite=False để làm cho nó lấp đầy các lỗ hổng:

In [68]: spliced.update(data2, overwrite=False)

In [69]: spliced
Out[69]:
abcd
2012-06-12 1 1 1 NaN
2012-06-13 1 1 1 2
2012-06-14 1 1 1 2
2012-06-15 2 2 2 2
2012-06-16 2 2 2 2
2012-06-17 2 2 2 2
2012-06-18 2 2 2 2

79
Để thay thế dữ liệu cho một tập hợp con các ký hiệu, bạn có thể sử dụng bất kỳ kỹ thuật nào
ở trên, nhưng đôi khi, việc chỉ đặt trực tiếp các cột bằng lập danh mục DataFrame sẽ đơn giản
hơn:

In [70]: cp_spliced = spliced.copy()

In [71]: cp_spliced[['a', 'c']] = data1[['a', 'c']]

In [72]: cp_spliced
Out[72]:
a b c d
2012-06-12 1 1 1 NaN
2012-06-13 1 1 1 2
2012-06-14 1 1 1 2
2012-06-15 1 2 1 2
2012-06-16 1 2 1 2
2012-06-17 1 2 1 2
2012-06-18 NaN 2 NaN 2

Chỉ số trả về và lợi nhuận tích lũy


Trong bối cảnh tài chính, lợi nhuận thường đề cập đến phần trăm thay đổi về giá của một tài
sản. Hãy xem xét dữ liệu về giá của Apple trong năm 2011 và 2012:

In [73]: import pandas.io.data as web

In [74]: price = web.get_data_yahoo('AAPL', '2011-01-01')['Adj Close']

In [75]: price[-5:]
Out[75]:
Date
2012-07-23 603.83
2012-07-24 600.92
2012-07-25 574.97
2012-07-26 574.88
2012-07-27 585.16
Name: Adj Close

Đối với Apple, công ty không có cổ tức, việc tính toán phần trăm lợi nhuận tích lũy giữa hai
thời điểm chỉ yêu cầu tính phần trăm thay đổi về giá:

In [76]: price['2011-10-03'] / price['2011-3-01'] - 1


Out[76]: 0.072399874037388123

Đối với các cổ phiếu khác có trả cổ tức, việc tính toán số tiền bạn kiếm được từ việc nắm giữ
cổ phiếu có thể phức tạp hơn. Tuy nhiên, các giá trị đóng được điều chỉnh được sử dụng ở
đây đã được điều chỉnh cho việc chia tách và cổ tức. Trong mọi trường hợp, việc lấy chỉ số
lợi nhuận là một chuỗi thời gian biểu thị giá trị của một đơn vị đầu tư (chẳng hạn như một đô
80
la) là điều khá phổ biến. Nhiều giả định có thể làm cơ sở cho chỉ số lợi nhuận; ví dụ, một số
sẽ chọn tái đầu tư lợi nhuận còn những người khác thì không. Trong trường hợp của Apple,
chúng ta có thể tính chỉ số lợi nhuận đơn giản bằng cách sử dụng cumprod:

In [77]: returns = price.pct_change()

In [78]: ret_index = (1 + returns).cumprod()

In [79]: ret_index[0] = 1 # Set first value to 1

In [80]: ret_index
Out[80]:
Date
2011-01-03 1.000000
2011-01-04 1.005219
2011-01-05 1.013442
2011-01-06 1.012623
...
2012-07-24 1.823346
2012-07-25 1.744607
2012-07-26 1.744334
2012-07-27 1.775526
Length: 396

Với chỉ số lợi nhuận trong tay, việc tính toán lợi nhuận tích lũy ở một độ phân giải cụ thể rất
đơn giản:

In [81]: m_returns = ret_index.resample('BM', how='last').pct_change()

In [82]: m_returns['2012']
Out[82]:
Date
2012-01-31 0.127111
2012-02-29 0.188311
2012-03-30 0.105284
2012-04-30 -0.025969
2012-05-31 -0.010702
2012-06-29 0.010853
2012-07-31 0.001986
Freq: BM

Tất nhiên, trong trường hợp đơn giản này (không tính đến cổ tức hoặc các điều chỉnh khác),
những điều này có thể được tính từ phần trăm thay đổi hàng ngày bằng cách lấy mẫu với tổng
hợp (ở đây là theo các giai đoạn):

In [83]: m_rets = (1 + returns).resample('M', how='prod', kind='period') – 1

81
In [84]: m_rets['2012']
Out[84]:
Date
2012-01 0.127111
2012-02 0.188311
2012-03 0.105284
2012-04 -0.025969
2012-05 -0.010702
2012-06 0.010853
2012-07 0.001986
Freq: M

Nếu bạn có ngày và tỷ lệ cổ tức, chúng bao gồm trong tổng lợi nhuận mỗi ngày sẽ như sau:
returns[dividend_dates] += dividend_pcts

BIẾN ĐỔI NHÓM VÀ PHÂN TÍCH


Trong chương 9, bạn đã tìm hiểu những kiến thức cơ bản về tính toán số liệu thống kê nhóm
và áp dụng các phép biến đổi của riêng bạn cho các nhóm trong tập dữ liệu.
Hãy xem xét một tập hợp các danh mục đầu tư chứng khoán giả định. Lần đầu tiên tôi tạo
ngẫu nhiên một universe rộng lớn gồm 2000 mã:

import random; random.seed(0)


import string
N = 1000
def rands(n):
choices = string.ascii_uppercase
return ''.join([random.choice(choices) for _ in xrange(n)])
tickers = np.array([rands(5) for _ in xrange(N)])

Sau đó, tôi tạo một DataFrame chứa 3 cột biểu thị các danh mục giả định nhưng ngẫu nhiên
cho một tập hợp con các mã đánh dấu:

M = 500
df = DataFrame({'Momentum' : np.random.randn(M) / 200 + 0.03,
'Value' : np.random.randn(M) / 200 + 0.08,
'ShortInterest' : np.random.randn(M) / 200 - 0.02},
index=tickers[:M])

Tiếp theo, hãy tạo một phân loại ngành ngẫu nhiên cho các mã chứng khoán. Để đơn giản hóa
mọi thứ, tôi sẽ chỉ giữ nó ở 2 ngành, lưu trữ mapping trong một series:

ind_names = np.array(['FINANCIAL', 'TECH'])


sampler = np.random.randint(0, len(ind_names), N)
industries = Series(ind_names[sampler], index=tickers,
name='industry')

82
Bây giờ chúng ta có thể nhóm theo industries và thực hiện tập hợp và chuyển đổi nhóm:

In [90]: by_industry = df.groupby(industries)

In [91]: by_industry.mean()
Out[91]:
Momentum ShortInterest Value
industry
FINANCIAL 0.029485 -0.020739 0.079929
TECH 0.030407 -0.019609 0.080113

In [92]: by_industry.describe()
Out[92]:
Momentum ShortInterest Value
industry
FINANCIAL count 246.000000 246.000000 246.000000
mean 0.029485 -0.020739 0.079929
std 0.004802 0.004986 0.004548
min 0.017210 -0.036997 0.067025
25% 0.026263 -0.024138 0.076638
50% 0.029261 -0.020833 0.079804
75% 0.032806 -0.017345 0.082718
max 0.045884 -0.006322 0.093334
TECH count 254.000000 254.000000 254.000000
mean 0.030407 -0.019609 0.080113
std 0.005303 0.005074 0.004886
min 0.016778 -0.032682 0.065253
25% 0.026456 -0.022779 0.076737
50% 0.030650 -0.019829 0.080296
75% 0.033602 -0.016923 0.083353
max 0.049638 -0.003698 0.093081

Bằng cách xác định các hàm chuyển đổi, thật dễ dàng để chuyển đổi các danh mục đầu tư này
theo ngành. Ví dụ, tiêu chuẩn hóa trong ngành được sử dụng rộng rãi trong việc xây dựng
danh mục đầu tư vốn cổ phần:

# Within-Industry Standardize
def zscore(group):
return (group - group.mean()) / group.std()
df_stand = by_industry.apply(zscore)

Bạn có thể xác minh rằng mỗi ngành có giá trị trung bình là 0 và độ lệch chuẩn là 1:

In [94]: df_stand.groupby(industries).agg(['mean', 'std'])


Out[94]:
Momentum ShortInterest Value
mean std mean std mean std

83
industry
FINANCIAL 0 1 0 1 0 1
TECH -0 1 -0 1 -0 1

Các loại biến đổi tích hợp khác, như rank, có thể được sử dụng chính xác hơn:

# Within-industry rank descending


In [95]: ind_rank = by_industry.rank(ascending=False)

In [96]: ind_rank.groupby(industries).agg(['min', 'max'])


Out[96]:
Momentum ShortInterest Value
min max min max min max
industry
FINANCIAL 1 246 1 246 1 246
TECH 1 254 1 254 1 254

Trong công bằng về mặt định lượng, “xếp hạng và tiêu chuẩn hóa” là một chuỗi biến đổi phổ
biến. Bạn có thể làm điều này bằng cách xâu chuỗi rank và zscore lại với nhau như vậy:

# Industry rank and standardize


In [97]: by_industry.apply(lambda x: zscore(x.rank()))
Out[97]:
<class 'pandas.core.frame.DataFrame'>
Index: 500 entries, VTKGN to PTDQE
Data columns:
Momentum 500 non-null values
ShortInterest 500 non-null values
Value 500 non-null values
dtypes: float64(3)

Nhóm yếu tố rủi ro


Phân tích nhân tố là một kỹ thuật trong quản lý danh mục đầu tư định lượng. Việc nắm giữ
danh mục đầu tư và hiệu suất (lợi nhuận trở xuống) được phân tách bằng cách sử dụng một
hoặc nhiều yếu tố (yếu tố rủi ro là một ví dụ) được biểu thị dưới dạng danh mục đầu tư có
trọng số. Ví dụ: sự biến động đồng thời của giá cổ phiếu với điểm chuẩn (như chỉ số S&P
500) được gọi là phiên bản beta của nó, một yếu tố rủi ro phổ biến. Hãy xem xét một ví dụ
giả định về danh mục đầu tư được xây dựng từ 3 yếu tố được tạo ngẫu nhiên (thường được
gọi là hệ số tải yếu tố) và một số trọng số đó:

rom numpy.random import rand


fac1, fac2, fac3 = np.random.rand(3, 1000)

ticker_subset = tickers.take(np.random.permutation(N)[:1000])

# Weighted sum of factors plus noise


port = Series(0.7 * fac1 - 1.2 * fac2 + 0.3 * fac3 + rand(1000),
84
index=ticker_subset)
factors = DataFrame({'f1': fac1, 'f2': fac2, 'f3': fac3},
index=ticker_subset)

Mối tương quan vectơ giữa từng yếu tố và danh mục đầu tư có thể không biểu thị quá nhiều:

In [99]: factors.corrwith(port)
Out[99]:
f1 0.402377
f2 -0.680980
f3 0.168083

Cách tiêu chuẩn để tính toán mức độ rủi ro của yếu tố là hồi quy bình phương tối thiểu; sử
dụng pandas.ols với factors như các biến giải thích, chúng ta có thể tính toán mức độ rủi ro
trên toàn bộ bộ mã:

In [100]: pd.ols(y=port, x=factors).beta


Out[100]:
f1 0.761789
f2 -1.208760
f3 0.289865
intercept 0.484477

Như bạn có thể thấy, trọng số của yếu tố ban đầu gần như có thể được phục hồi do không có
quá nhiều nhiễu ngẫu nhiên bổ sung vào danh mục đầu tư. sử dụng groupby bạn có thể xếp
hạng mức độ rủi ro theo ngành. Để làm như vậy, hãy viết một hàm như sau:
def beta_exposure(chunk, factors=None):
return pd.ols(y=chunk, x=factors).beta

Sau đó nhóm theo industries và áp dụng chức năng đó, chuyển DataFrame của hệ số tải:

In [102]: by_ind = port.groupby(industries)

In [103]: exposures = by_ind.apply(beta_exposure, factors=factors)

In [104]: exposures.unstack()
Out[104]:
f1 f2 f3 intercept
industry
FINANCIAL 0.790329 -1.182970 0.275624 0.455569
TECH 0.740857 -1.232882 0.303811 0.508188

Phân tích thập phân và tứ phân vị


Phân tích dữ liệu dựa trên lượng tử mẫu là một công cụ quan trọng khác dành cho các nhà
phân tích tài chính. Ví dụ: hiệu suất của danh mục đầu tư chứng khoán có thể được chia thành
các phần tư (bốn phần có kích thước bằng nhau) dựa trên giá thu nhập của mỗi cổ phiếu. sử

85
dụng pandas.qcut kết hợp với groupby làm cho việc phân tích lượng tử trở nên đơn giản một
cách hợp lý.
Ví dụ: hãy xem xét một chiến lược theo xu hướng hoặc động lượng đơn giản khi giao dịch
chỉ số S&P 500 thông qua quỹ giao dịch trao đổi SPY. Bạn có thể tải xuống lịch sử giá từ
Yahoo! Finance:

In [105]: import pandas.io.data as web

In [106]: data = web.get_data_yahoo('SPY', '2006-01-01')

In [107]: data
Out[107]:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1655 entries, 2006-01-03 00:00:00 to 2012-07-27 00:00:00
Data columns:
Open 1655 non-null values
High 1655 non-null values
Low 1655 non-null values
Close 1655 non-null values
Volume 1655 non-null values
Adj Close 1655 non-null values
dtypes: float64(5), int64(1)

Bây giờ, chúng ta sẽ tính toán lợi nhuận hàng ngày và hàm chuyển đổi lợi nhuận thành tín
hiệu xu hướng được hình thành từ tổng chuyển động có độ trễ:

px = data['Adj Close']
returns = px.pct_change()
def to_index(rets):
index = (1 + rets).cumprod()
first_loc = max(index.notnull().argmax() - 1, 0)
index.values[first_loc] = 1
return index

def trend_signal(rets, lookback, lag):


signal = pd.rolling_sum(rets, lookback, min_periods=lookback - 5)
return signal.shift(lag)

Sử dụng chức năng này, chúng ta có thể (một cách ngây thơ) tạo và thử nghiệm chiến lược
giao dịch tín hiệu động lượng này vào thứ sáu hàng tuần:

In [109]: signal = trend_signal(returns, 100, 3)

In [110]: trade_friday = signal.resample('W-FRI').resample('B', fill_method='ffill')

In [111]: trade_rets = trade_friday.shift(1) * returns

86
We can then convert the strategy returns to a return index and plot them (see Figure 11-
1):

In [112]: to_index(trade_rets).plot()

Giả sử bạn muốn chia hiệu suất chiến lược thành các giai đoạn giao dịch ngày càng ít biến
động hơn. Độ lệch chuẩn hàng năm kéo dài một năm là một thước đo đơn giản về mức độ
biến động và chúng ta có thể tính tỷ lệ Sharpe để đánh giá tỷ lệ phần thưởng trên rủi ro trong
các chế độ biến động khác nhau:

vol = pd.rolling_std(returns, 250, min_periods=200) * np.sqrt(250)


def sharpe(rets, ann=250):
return rets.mean() / rets.std() * np.sqrt(ann)

Bây giờ chia vol thành các phần tư với qcut và tổng hợp với sharpe chúng ta đạt được:

In [114]: trade_rets.groupby(pd.qcut(vol, 4)).agg(sharpe)


Out[114]:
[0.0955, 0.16] 0.490051
(0.16, 0.188] 0.482788
(0.188, 0.231] -0.731199
(0.231, 0.457] 0.570500

Những kết quả này cho thấy chiến lược hoạt động tốt nhất trong khoảng thời gian có độ biến
động cao nhất.

CÁC ỨNG DỤNG VÍ DỤ KHÁC


Đây là một tập hợp nhỏ các ví dụ bổ sung.

87
Phân tích biên giới tín hiệu
Trong phần này, tôi sẽ mô tả một danh mục động lượng cắt ngang được đơn giản hóa và chỉ
ra cách bạn có thể khám phá một mạng lưới các tham số hóa mô hình. Đầu tiên, tôi sẽ tải giá
lịch sử cho danh mục cổ phiếu tài chính và công nghệ:

names = ['AAPL', 'GOOG', 'MSFT', 'DELL', 'GS', 'MS', 'BAC', 'C']


def get_px(stock, start, end):
return web.get_data_yahoo(stock, start, end)['Adj Close']
px = DataFrame({n: get_px(n, '1/1/2009', '6/1/2012') for n in names})

Chúng ta có thể dễ dàng vẽ biểu đồ lợi nhuận tích lũy của mỗi cổ phiếu (xem Figure 11-2):

In [117]: px = px.asfreq('B').fillna(method='pad')
In [118]: rets = px.pct_change()
In [119]: ((1 + rets).cumprod() - 1).plot()

Đối với việc xây dựng danh mục đầu tư, chúng tôi sẽ tính toán động lượng qua một khoảng
thời gian nhìn lại nhất định, sau đó xếp hạng theo thứ tự giảm dần và chuẩn hóa:

def calc_mom(price, lookback, lag):


mom_ret = price.shift(lag).pct_change(lookback)
ranks = mom_ret.rank(axis=1, ascending=False)
demeaned = ranks - ranks.mean(axis=1)
return demeaned / demeaned.std(axis=1)

Với hàm biến đổi này trong tay, chúng ta có thể thiết lập hàm kiểm tra lại chiến lược để tính
toán danh mục đầu tư cho một khoảng thời gian xem lại và nắm giữ cụ thể (số ngày giữa các
giao dịch), trả về tỷ lệ Sharpe tổng thể:

compound = lambda x : (1 + x).prod() - 1


daily_sr = lambda x: x.mean() / x.std()
def strat_sr(prices, lb, hold):
# Compute portfolio weights
freq = '%dB' % hold
port = calc_mom(prices, lb, lag=1)

daily_rets = prices.pct_change()

# Compute portfolio returns


port = port.shift(1).resample(freq, how='first')
returns = daily_rets.resample(freq, how=compound)
port_rets = (port * returns).sum(axis=1)

return daily_sr(port_rets) * np.sqrt(252 / hold)

88
Khi được gọi với giá và tổ hợp tham số, hàm này trả về giá trị vô hướng:

In [122]: strat_sr(px, 70, 30)


Out[122]: 0.27421582756800583

Từ đó, bạn có thể đánh giá strat_sr hoạt động trên một lưới các tham số, lưu trữ chúng khi
bạn đi vào defaultdict và cuối cùng đưa kết quả vào DataFrame:

from collections import defaultdict

lookbacks = range(20, 90, 5)


holdings = range(20, 90, 5)
dd = defaultdict(dict)
for lb in lookbacks:
for hold in holdings:
dd[lb][hold] = strat_sr(px, lb, hold)
ddf = DataFrame(dd)
ddf.index.name = 'Holding Period'
ddf.columns.name = 'Lookback Period'

Để hình dung kết quả và biết được điều gì đang diễn ra, đây là một hàm sử dụng matplotlib
để tạo bản đồ nhiệt với một số đồ trang trí:

import matplotlib.pyplot as plt

def heatmap(df, cmap=plt.cm.gray_r):


fig = plt.figure()
ax = fig.add_subplot(111)
axim = ax.imshow(df.values, cmap=cmap, interpolation='nearest')
ax.set_xlabel(df.columns.name)
89
ax.set_xticks(np.arange(len(df.columns)))
ax.set_xticklabels(list(df.columns))
ax.set_ylabel(df.index.name)
ax.set_yticks(np.arange(len(df.index)))
ax.set_yticklabels(list(df.index))
plt.colorbar(axim)

Gọi hàm này trên kết quả backtest, chúng tôi nhận được Figure 11-3:

In [125]: heatmap(ddf)

Hợp đồng tương lai luân chuyển


Hợp đồng tương lai là một dạng hợp đồng phái sinh phổ biến; đó là một thỏa thuận nhận giao
một tài sản nhất định (chẳng hạn như dầu, vàng hoặc cổ phiếu của chỉ số FTSE 100) vào một
ngày cụ thể. Trong thực tế, mô hình hóa và giao dịch các hợp đồng tương lai về cổ phiếu, tiền
tệ, hàng hóa, trái phiếu và các loại tài sản khác rất phức tạp do tính chất giới hạn thời gian của
mỗi hợp đồng. Ví dụ: tại bất kỳ thời điểm nào đối với một loại hợp đồng tương lai (chẳng hạn
như hợp đồng tương lai bạc hoặc đồng), nhiều hợp đồng có ngày hết hạn khác nhau có thể
được giao dịch. Trong nhiều trường hợp, hợp đồng tương lai sắp hết hạn (hợp đồng gần) sẽ
có tính thanh khoản cao nhất (khối lượng cao nhất và chênh lệch giá chào mua thấp nhất).

Với mục đích lập mô hình và dự báo, có thể dễ dàng hơn nhiều khi làm việc với chỉ số lợi
nhuận liên tục biểu thị lãi và lỗ liên quan đến việc luôn nắm giữ hợp đồng gần. Việc chuyển
từ hợp đồng sắp hết hạn sang hợp đồng tiếp theo (hoặc hợp đồng xa) được gọi là hợp đồng
luân chuyển. Việc tính toán một chuỗi tương lai liên tục từ dữ liệu hợp đồng riêng lẻ không
nhất thiết là một công việc đơn giản và thường đòi hỏi sự hiểu biết sâu sắc hơn về thị trường
cũng như cách thức giao dịch các công cụ. Ví dụ: trong thực tế, bạn sẽ giao dịch từ một hợp
đồng sắp hết hạn sang hợp đồng tiếp theo khi nào và nhanh như thế nào? Ở đây tôi mô tả một
quá trình như vậy.
90
Đầu tiên, tôi sẽ sử dụng giá điều chỉnh cho quỹ giao dịch trao đổi SPY làm đại diện cho chỉ
số S&P 500:

In [127]: import pandas.io.data as web

# Approximate price of S&P 500 index


In [128]: px = web.get_data_yahoo('SPY')['Adj Close'] * 10

In [129]: px
Out[129]:
Date
2011-08-01 1261.0
2011-08-02 1228.8
2011-08-03 1235.5
...
2012-07-25 1339.6
2012-07-26 1361.7
2012-07-27 1386.8
Name: Adj Close, Length: 251

Bây giờ, một chút thiết lập. Tôi đặt một vài hợp đồng tương lai S&P 500 và ngày hết hạn
trong Series:

from datetime import datetime


expiry = {'ESU2': datetime(2012, 9, 21),
'ESZ2': datetime(2012, 12, 21)}
expiry = Series(expiry).order()

Sau đó expiry trông như này:

In [131]: expiry
Out[131]:
ESU2 2012-09-21 00:00:00
ESZ2 2012-12-21 00:00:00

Sau đó, tôi sử dụng Yahoo! Finance cùng với bước ngẫu nhiên và một số noise để mô phỏng
hai hợp đồng trong tương lai:

np.random.seed(12347)
N = 200
walk = (np.random.randint(0, 200, size=N) - 100) * 0.25
perturb = (np.random.randint(0, 20, size=N) - 10) * 0.25
walk = walk.cumsum()

rng = pd.date_range(px.index[0], periods=len(px) + N, freq='B')


near = np.concatenate([px.values, px.values[-1] + walk])

91
far = np.concatenate([px.values, px.values[-1] + walk + perturb])
prices = DataFrame({'ESU2': near, 'ESZ2': far}, index=rng)

Price sau đó có hai chuỗi thời gian cho các hợp đồng khác nhau một lượng ngẫu nhiên:

In [133]: prices.tail()
Out[133]:
ESU2 ESZ2
2013-04-16 1416.05 1417.80
2013-04-17 1402.30 1404.55
2013-04-18 1410.30 1412.05
2013-04-19 1426.80 1426.05
2013-04-22 1406.80 1404.55

Một cách để ghép các chuỗi thời gian lại với nhau thành một chuỗi liên tục là xây dựng ma
trận trọng số. Các hợp đồng đang hoạt động sẽ có trọng số là 1 cho đến khi gần hết hạn. Tại
thời điểm đó, bạn phải quyết định quy ước cuộn. Đây là hàm tính toán ma trận trọng số có
phân rã tuyến tính qua một số khoảng thời gian dẫn đến hết hạn:

def get_roll_weights(start, expiry, items, roll_periods=5):


# start : first date to compute weighting DataFrame
# expiry : Series of ticker -> expiration dates
# items : sequence of contract names

dates = pd.date_range(start, expiry[-1], freq='B')


weights = DataFrame(np.zeros((len(dates), len(items))),
index=dates, columns=items)

prev_date = weights.index[0]
for i, (item, ex_date) in enumerate(expiry.iteritems()):
if i < len(expiry) - 1:
weights.ix[prev_date:ex_date - pd.offsets.BDay(), item] = 1
roll_rng = pd.date_range(end=ex_date - pd.offsets.BDay(),
periods=roll_periods + 1, freq='B')

decay_weights = np.linspace(0, 1, roll_periods + 1)


weights.ix[roll_rng, item] = 1 - decay_weights
weights.ix[roll_rng, expiry.index[i + 1]] = decay_weights
else:
weights.ix[prev_date:, item] = 1

prev_date = ex_date
return weights

Trọng số trông như thế này xung quanh ESU2 hết hạn:

In [135]: weights = get_roll_weights('6/1/2012', expiry, prices.columns)

92
In [136]: weights.ix['2012-09-12':'2012-09-21']
Out[136]:
ESU2 ESZ2
2012-09-12 1.0 0.0
2012-09-13 1.0 0.0
2012-09-14 0.8 0.2
2012-09-17 0.6 0.4
2012-09-18 0.4 0.6
2012-09-19 0.2 0.8
2012-09-20 0.0 1.0
2012-09-21 0.0 1.0

Cuối cùng, lợi nhuận gộp trong tương lai chỉ là tổng trọng số của lợi nhuận hợp đồng:

In [137]: rolled_returns = (prices.pct_change() * weights).sum(1)

Tương quan luân chuyển và hồi quy tuyến tính


Các mô hình động đóng vai trò quan trọng trong mô hình tài chính vì chúng có thể được sử
dụng để mô phỏng các quyết định giao dịch trong một giai đoạn lịch sử. Cửa sổ di chuyển và
các hàm chuỗi thời gian có trọng số theo cấp số nhân là một ví dụ về các công cụ được sử
dụng cho các mô hình động.

Mối tương quan là một cách để xem xét sự đồng chuyển động giữa những thay đổi trong chuỗi
thời gian của hai nội dung. Hàm pandas’s rolling_corr có thể được gọi với hai chuỗi trả về để
tính toán mối tương quan của cửa sổ di chuyển. Đầu tiên, tôi tải một số chuỗi giá từ Yahoo!
Finance và tính toán lợi nhuận hàng ngày:

aapl = web.get_data_yahoo('AAPL', '2000-01-01')['Adj Close']


msft = web.get_data_yahoo('MSFT', '2000-01-01')['Adj Close']

aapl_rets = aapl.pct_change()
msft_rets = msft.pct_change()

Sau đó, tôi tính toán và vẽ biểu đồ tương quan biến động trong một năm (xem Figure 11-4):
In [140]: pd.rolling_corr(aapl_rets, msft_rets, 250).plot()

Một vấn đề liên quan đến mối tương quan giữa hai tài sản là nó không nắm bắt được sự khác
biệt trong sự biến động. Hồi quy bình phương nhỏ nhất cung cấp một phương tiện khác để
mô hình hóa mối quan hệ động giữa một biến và một hoặc nhiều biến dự đoán khác.

In [142]: model = pd.ols(y=aapl_rets, x={'MSFT': msft_rets}, window=250)

In [143]: model.beta
Out[143]:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2913 entries, 2000-12-28 00:00:00 to 2012-07-27 00:00:00
93
Data columns:
MSFT 2913 non-null values
intercept 2913 non-null values
dtypes: float64(2)

In [144]: model.beta['MSFT'].plot()

Hàm pandas’ ols thực hiện hồi quy bình phương nhỏ nhất tĩnh và động (mở rộng hoặc cửa sổ
cuộn). Để biết các mô hình thống kê và kinh tế lượng phức tạp hơn, hãy xem dự án mô hình
thống kê (http://statsmodels.sourceforge.net).

94
KẾT LUẬN
Sau khi tìm hiểu một vài chương của quyển sách “Python for Data Analysis” được viết
bởi Wes McKinney, giúp cho chúng ta có cái nhìn tổng quát, hiểu sâu sắc hơn về ngôn ngữ
lập trình Python. Một quyển sách cực kỳ hữu ích và giàu thông tin, cung cấp phần giới thiệu
chắc chắn về cách sử dụng Python để phân tích dữ liệu và đi kèm đó là các ví dụ cũng như
bài tập thực tế giúp cho người đọc củng cố hiểu biết của mình về các khái niệm trong Python.
Python đã trở thành một trong những ngôn ngữ lập trình phổ biến và ưa chuộng nhất trên thế
giới bởi cú pháp dễ đọc, dễ sử dụng dành cho cả người mới học lập trình. Vì thế mà Python
được sử dụng rộng rãi trong nhiều lĩnh vực và các mục đích khác nhau, do đây là ngôn ngữ
dễ đọc và dễ viết, đa năng, có thư viện phong phú và đặc biệt là hỗ trợ đa nền tảng. Từ đó mà
Python được ứng dụng vào việc phát triển web, công cụ tự động hóa, khoa học máy tính và
machine learning, phát triển game… Thế nên tìm hiểu và học một ngôn ngữ lập trình nào đó
chẳng hạn như Python sẽ giúp cho bạn rất nhiều sau này, đặc biệt là mở rộng cơ hội nghề
nghiệp của bạn trong tương lai!

95
TÀI LIỆU THAM KHẢO
File sách: https://bedford-computing.co.uk/learning/wp-content/uploads/2015/10/Python-for-
Data-Analysis.pdf
File data: https://github.com/wesm/pydata-book/tree/2nd-edition
Video thuyết trình:
https://drive.google.com/file/d/1vWsFF9CvKra_LZL3prkNSK4ILeqLgcJS/view?usp=drive_l
ink

You might also like