Chào tất cả anh em Mì AI, hôm nay chúng ta cùng nhau tìm hiểu về Vaex, một công cụ xử lý dữ liệu lớn cực hay và mạnh mẽ thay thế cho Pandas.
Trước khi bắt đầu bài viết này mình cũng nhấn mạnh luôn: BÀI VIẾT HOÀN TOÀN DỰA VÀO HIỂU BIẾT CÁ NHÂN và mong muốn chia sẻ. Mong nhận được góp ý từ các cao thủ đi qua ah.
Rồi, chúng ta bắt đầu nhé!
Phần 1 – Câu chuyện với Pandas
Chắc hẳn anh em xử lý dữ liệu trên Python đều đã quá quen thuộc với Pandas. Trên Group Mì Ai cũng có vài bài giới thiệu về Pandas rồi. Phải nói rằng Pandas là một thư viện khá ổn để bắt đầu với xử lý dữ liệu với các khái niệm:
- DataFrame
- Read CSV
- To CSV
- GroupBy….
Mọi việc có vẻ rất ngon lành, bạn thử các ví dụ đều chạy vèo vèo cho đến một ngày sếp bảo “Anh có dữ liệu 10 năm của 5 triệu khách hàng Ngân hàng mình, em phân tích cho anh cái nhé”.
Bạn hồ hởi bảo vâng, đưa em dữ liệu và bắt đầu pandas.read_csv và…. BUM…. treo máy tính luôn hoặc báo tràn bộ nhớ “System memory too low”…. Lý do? Vì dữ liệu quá lớn để bạn load vào trong RAM máy tính cá nhân của bạn (có hạn, 16GB là kinh lắm rồi, trừ 1 số đại gia).
Đó, đó là lúc bạn nhận ra Pandas cũng có điểm yếu của mình!
Có khoảng 10 điểm yếu của Pandas, mình xin trích nguyên văn như sau:
Internals too far from “the metal”
No support for memory-mapped datasets
Poor performance in database and file ingest / export
Warty missing data support
Lack of transparency into memory use, RAM management
Weak support for categorical data
Complex groupby operations awkward and slow
Appending data to a DataFrame tedious and very costly
Limited, non-extensible type metadata
Eager evaluation model, no query planning
“Slow”, limited multicore algorithms for large datasets
Nguồn: https://wesmckinney.com/blog/apache-arrow-pandas-internals/
Nói một cách đại khái là : không có cơ chế memory-mapping, load all dữ liệu vào RAM gây tràn RAM, chậm máy và tốc độ xử lý, truy xuất của Pandas cũng rất chậm.
Phần 2 – Và đó là lúc ta cần triệu hồi Vaex
Yeah, Vaex sẽ mang lại cho ta sức mạnh mới và khắc phục các điểm yếu nói trên.
Nói một cách tổng quan thì Vaex là một thư viện xử lý dữ liệu lớn với hiệu năng cao (có thể xử lý hàng tỷ row/giây, tất nhiên với máy tính tương đối tý). Và điều đặc biệt của Vaex đó là sử dụng memory mapping và không sử dụng memory copy giúp giảm hao phí bộ nhớ và tăng tốc độ tính toán.
Giải thích một chút về Memory Mapping:
- Chúng ta không load toàn bộ dữ liệu vào RAM như Pandas mà tạo ra memory map (bản đồ bộ nhớ) và chỉ load phần chúng ta cần mà thôi.
- Nếu như phần dữ liệu đó đã sử dụng xong, Vaex sẽ loại bỏ khỏi RAM để ta có thể tái sử dụng RAM cho các tác vụ tiếp theo.
- Các vùng nhớ được chia sẻ, dùng chung giữa các process khác nhau để tránh lãng phí bộ nhớ.
Okie, chắc các bạn đang háo hức muốn xem trận chiến đấu giữa Pandas và Vaex ngay đúng ko? Let’s go! Coding!
Phần 3 – So găng giữa Pandas và Vaex
Rồi, đúng ra chúng ta sẽ thấy được sự khác biệt nhiều hơn nếu thử với vài triệu bản ghi (mỗi bạn ghi 500 cột) nhưng….máy mình cũng yếu nên mình đành thử với 500.000 bản ghi nhé. Tuy nhiên cũng thấy được sự khác biệt đó!
Đầu vào và luật thi đấu
Chúng ta sẽ sử dụng 1 DataFrame gồm 500.000 bản ghi, mỗi bản ghi có 500 cột là các số nguyên từ 1 đến 1000 được sinh ngẫu nhiên.
Chúng ta sẽ thử các trò: save file, load file, tính toán nhân các cột, lọc theo các cột và group by cột (trò nặng nhất đây).
Sinh dữ liệu và save ra file CSV
Ta sẽ dùng pandas để sinh ra file CSV nhé:
import pandas as pd
import numpy as np
import gc
import time
rows_count = 500000 # Số bản ghi
columns_count = 500 # Số cột
rand_start = 0
rand_end = 1000
print("Making random DataFrame...")
np_matrix = np.random.randint(rand_start, rand_end, size=(rows_count, columns_count))
df = pd.DataFrame(np_matrix, columns=['column_%d' % i for i in range(columns_count)])
Code language: PHP (php)
Thử với Pandas xem sao
Rồi, bây giờ chúng ta sẽ chạy code để đo hiệu năng của bác già Pandas xem sao: (chú ý mình bị treo máy phần này khá lâu do hết RAM 😀 )
file_path = 'big_data.csv'
print("Read and convert to h5 file ", file_path)
start = time.time()
df = vaex.from_csv(file_path, convert=True)
print("Process time = ", time.time()-start)
print("Load from file to data frame ")
start = time.time()
df = vaex.open(file_path + ".hdf5")
(df.head())
print("Process time = ", time.time()-start)
#
# print("Show first 10 record")
# print(df.head(10))
print("Try to multiply some columns")
start = time.time()
df['column_test']=df.column_1 * df.column_3
(df.head())
print("Process time = ", time.time()-start)
print("Try to filter by value")
start = time.time()
df = df[df['column_13']>0]
(df.head())
print("Process time = ", time.time()-start)
print("Try to group by column")
start = time.time()
df = df.groupby(df.column_test, agg=vaex.agg.mean(df.column_1))
(df.head())
print("Process time = ", time.time()-start)
df = None
gc.collect()
Code language: PHP (php)
Và đây là kết quả sau khi bỏ máy đi uống cafe:
Save data frame to file big_data.csv
Process time = 39.67472982406616
Load from file to data frame
Process time = 18.51714587211609
Try to multiply some columns
Process time = 0.009230852127075195
Try to filter by value
Process time = 7.909274101257324
Try to group by column
Process time = 4.456965208053589
Code language: PHP (php)
Đơn vị time tính theo giây, groupby chiếm tới 400 giây = gần 7 phút. Nếu số bản ghi tăng lên 5 triệu khách hàng trong 10 năm thì thì sẽ sao đây.
Giờ là lúc Vaex show hàng
Rồi, bây giờ chúng ta sẽ dùng chung bộ dữ liệu đó (đã được save ra file big_data.csv) để xem Vaex làm ăn ra sao nhé:
Đầu tiên chúng ta phải convert từ csv sang hdf5 – dạng file quá phổ biển (xem thêm tại đây)
import vaex
import gc
import time
file_path = 'big_data.csv'
print("Read and convert to h5 file ", file_path)
start = time.time()
df = vaex.from_csv(file_path, convert=True)
print("Process time = ", time.time()-start)
Code language: JavaScript (javascript)
Quá trình convert này chỉ làm 1 lần duy nhất và mình thử với file trên thì mất 0.3s (muỗi nhở)!
Rồi bây giờ cho Vaex thử sức với các task như Pandas xem nhé:
print("Load from file to data frame ")
start = time.time()
df = vaex.open(file_path + ".hdf5")
print(df['column_1'].sum())
print("Process time = ", time.time()-start)
print("Try to multiply some columns")
start = time.time()
df['column_test']=df.column_1 * df.column_3
print(df['column_test'].sum())
print("Process time = ", time.time()-start)
print("Try to filter by value")
start = time.time()
df = df[df['column_13']>0]
print(df['column_test'].sum())
print("Process time = ", time.time()-start)
print("Try to group by column")
start = time.time()
df = df.groupby(df.column_test, agg=vaex.agg.mean(df.column_1))
print(df['column_test'].sum())
print("Process time = ", time.time()-start)
df = None
gc.collect()
Code language: PHP (php)
Kết quả đây! Kết quả đây!
Read and convert to h5 file big_data.csv
Process time = 0.5691401958465576
Load from file to data frame
Process time = 0.18800091743469238
Try to multiply some columns
Process time = 0.0073163509368896484
Try to filter by value
Process time = 0.09036493301391602
Try to group by column
Process time = 0.12833809852600098
Code language: PHP (php)
Okie, nhìn qua thì có vẻ Vaex thắng tuyệt đối rồi anh em ơi =)). Chúng ta gộp lại thành bảng cho nó dễ nhìn nhé!
Load | Multiply | Filter | Group | |
Pandas | 18.52 | 0.01 | 7.91 | 4.46 |
Vaex | 0.19 | 0.01 | 0.09 | 0.13 |
Result | Vaex nhanh hơn 97 lần | Vaex same same | Vaex nhanh hơn 88 lần | Vaex nhanh hơn 34 lần |
Một cái bảng thay cho ngàn lời nói, anh em nghĩ sao? Hãy comment cho mình biết nhé! Giờ thì mình xin tạm biệt và hẹn gặp lại nha!
Chúc các bạn thành công!
#MìAI
Fanpage: http://facebook.com/miaiblog
Group trao đổi, chia sẻ: https://www.facebook.com/groups/miaigroup
Website: https://miai.vn
Youtube: http://bit.ly/miaiyoutube
Tài liệu tham khảo: https://medium.com/analytics-vidhya/rip-pandas-time-to-introduce-the-vaex-70bf428e798c
Em nghĩ trong phần test so sánh giữa pandas với vaex không được đầy đủ cho lắm.
Đối với Spark (Scala), có 2 loại operations là transformation và action. Khi thực hiện các bước transformation (load, filter, query, group by,…) thì Spark có cơ chế sẽ ghi nhớ lại các bước transform (lazy) cho đến khi có actions (save, count, show,….) thì mới thực sự chạy các bước transform.
Vd: Mình thực hiện query, group by xong rồi count thì khi code chạy đến dòng count thì Spark mới chạy query và group by => khi mình đo thời gian ở bước query và group by sẽ thấy rất nhanh.
Vì vậy đối với case Vaex, em nghĩ nó chưa thực sự transform => thời gian measure được khá nhanh => chỗ này em nghĩ cần measure full luồng từ load, transform đến show ra kết quả thì mới cho ra con số ổn được.
P/s: Em chỉ có kinh nghiệm về Spark còn về pandas + vaex thì chưa tìm hiểu nhiều nên có gì xin chỉ giáo thêm 😀
Thanks em nhiều, hôm qua anh sơ sót chỗ đó, nay anh đã thực hiện actions sum và print ra màn hình và đã update lại rồi.
Anh ơi hình như phần code pandas của a bị nhầm lẫn sang vaex rồi ạ.
Thanks em. Anh sẽ tìm và fix lại luôn!