Data Science·Hacker News·3 lượt xem

Lý thuyết danh mục dạy chúng ta điều gì về DataFrames

What Category Theory Teaches Us About DataFrames

AI Summary

Một nghiên cứu gần đây đã đề xuất một "dataframe algebra" chính thức, bao gồm 15 toán tử cốt lõi, lấy cảm hứng từ lý thuyết phạm trù (category theory). Mô hình này có thể diễn giải tới hơn 85% trong số hơn 200 phương thức của pandas. Định nghĩa chính thức này xem dataframe như một tuple với các hàng và cột có thứ tự và được gán nhãn, làm sáng tỏ cấu trúc nền tảng của các phép thao tác dữ liệu. Nó giúp phân biệt rõ ràng các tính năng đặc thù của dataframe như `TRANSPOSE` hay `MAP` với các khái niệm của relational algebra. Đối với các developer, điều này có nghĩa là chúng ta có thể tiến xa hơn việc học thuộc lòng API, hướng tới việc hiểu sâu sắc hơn các phép biến đổi dữ liệu cơ bản (primitives). Từ đó, việc thiết kế thư viện sẽ trở nên mạnh mẽ hơn và giúp chúng ta dễ dàng nắm bắt cách hoạt động của các công cụ hiện có.

Mỗi thư viện khung dữ liệu đều có hàng trăm thao tác. Chỉ riêng gấu trúc đã có hơn 200 phương thức trên DataFrame. Trục có khác với tan chảy không? Áp dụng có khác với bản đồ không? Còn biến đổi, agg, applymap, pipe thì sao? Một số trong số này có vẻ giống như một hoạt động đội những chiếc mũ khác nhau. Những người khác có vẻ thực sự khác biệt. Nếu không có khuôn khổ để phân biệt chúng, cuối cùng bạn sẽ ghi nhớ các API thay vì hiểu cấu trúc.

Mỗi thư viện khung dữ liệu đều có hàng trăm thao tác. Chỉ riêng gấu trúc đã có hơn 200 phương thức trên DataFrame. pivo có khác với melt không? áp dụng có khác với bản đồ không? Còn transform, agg, applymap, pipe thì sao? Một số trong số này có vẻ như hoạt động giống nhau nhưng mặc khác nhau mũ. Những người khác có vẻ thực sự khác biệt. Nếu không có khuôn khổ để phân biệt chúng, cuối cùng bạn sẽ ghi nhớ các API thay vì hiểu cấu trúc.

Tôi gặp phải câu hỏi này khi xây dựng thư viện khung dữ liệu của riêng mình. Tôi cần phải quyết định xem hoạt động nào thực sự cơ bản và hoạt động nào chỉ là những biến thể ở cấp độ bề mặt. Tìm kiếm đó đã đưa tôi đến Hướng tới Hệ thống khung dữ liệu có thể mở rộng của Petersohn và cộng sự. Họ đã xây dựng Modin, một giải pháp thay thế có sẵn cho gấu trúc và cần hiểu cấu trúc thực tế bên dưới API. Họ đã phân tích 1 triệu sổ tay Jupyter, lập danh mục cách mọi người sử dụng gấu trúc và đề xuất đại số khung dữ liệu: một tập hợp chính thức gồm khoảng 15 toán tử có thể biểu diễn tất cả hơn 200 phép toán của gấu trúc.

Đại số đó là một phép nén rất lớn nhưng tôi cứ tự hỏi liệu có cấp độ nào thấp hơn không Nó. Liệu có một tập hợp nhỏ hơn các hoạt động thực sự nguyên thủy mà 15 hoạt động đó được xây dựng từ đó hay không. Nếu những điều đó tồn tại, chúng sẽ là nền tảng thực sự: các thao tác đủ nhỏ để rõ ràng là chính xác, đủ biểu cảm để xây dựng mọi thứ khác.

Đại số khung dữ liệu của Petersohn

Thật đáng để dành chút thời gian cho những gì Petersohn et al. thực sự đã làm như vậy vì nó đóng khung mọi thứ tiếp theo.

Họ bắt đầu bằng việc xác định khung dữ liệu là gì. Đáng ngạc nhiên là trước đây chưa có ai thực hiện việc này một cách chính thức. Định nghĩa 4.1 của họ cho biết khung dữ liệu là một bộ (A, R, C, D): một mảng dữ liệu A, nhãn hàng R, nhãn cột C và vectơ của các miền cột D. Điều này chính xác hơn “bảng” vì nó nắm bắt những thứ làm cho khung dữ liệu khác với bảng quan hệ. Hàng và cột đều được sắp xếp theo thứ tự, được dán nhãn và xử lý đối xứng. Bạn có thể chuyển đổi một khung dữ liệu. Bạn có thể quảng bá giá trị dữ liệu vào nhãn cột. Đây không phải là những việc bạn có thể làm với bảng SQL.

Sau đó, họ xác định được người vận hành. Đây là Bảng 1 của họ, cô đọng:

Toán tử Nguồn gốc Công dụng của nó
CHỌN Quan hệ Xóa hàng
SỰ DỰ ÁN Quan hệ Xóa cột
UNION Quan hệ Kết hợp hai khung dữ liệu theo chiều dọc
SỰ KHÁC BIỆT Quan hệ Các hàng ở một hàng nhưng không ở hàng kia
CHÉO SẢN PHẨM / THAM GIA Quan hệ Kết hợp hai khung dữ liệu theo khóa
THẢ LẠI SAO CHÉP Quan hệ Xóa các hàng trùng lặp
NHÓM THEO Quan hệ Nhóm các hàng theo giá trị cột
Sắp xếp Quan hệ Sắp xếp lại các hàng
RENAME Quan hệ Đổi tên cột
CỬA SỔ SQL Chức năng cửa sổ trượt
TRANSPOSE Khung dữ liệu Hoán đổi hàng và cột
MAP Khung dữ liệu Áp dụng một hàm cho mỗi hàng
TOLABELS Khung dữ liệu Quảng cáo dữ liệu tới nhãn cột/hàng
FROMLABELS Khung dữ liệu Giảm hạng nhãn trở lại dữ liệu

Cột “Xuất xứ” rất quan trọng. Chín toán tử đầu tiên đến từ đại số quan hệ và có những điểm tương tự trực tiếp trong SQL. WINDOW đến từ các phần mở rộng SQL. Bốn cái cuối cùng (TRANSPOSE, MAP, TOLABELS, FROMLABELS) là duy nhất cho các khung dữ liệu. Chúng tồn tại vì các khung dữ liệu xử lý các hàng và cột một cách đối xứng và cho phép dữ liệu di chuyển giữa các giá trị và siêu dữ liệu. Cơ sở dữ liệu quan hệ không thể làm được điều đó.

Petersohn đã chỉ ra rằng hơn 85% API gấu trúc có thể được viết lại dưới dạng kết hợp của các toán tử này. Các hoạt động như fillna, isnull, str.uppercummax đều là các trường hợp đặc biệt của MAP. Các hoạt động như sort_values, set_index, reset_index, merge, groupbypivot đều ánh xạ từng đối một vào các toán tử trong đại số. Đó là mức nén rất lớn: hơn 200 phương thức đặc biệt trở thành 15 phương thức nguyên thủy có thể kết hợp được.

Nhưng tôi vẫn tiếp tục xem xét các toán tử quan hệ trong bảng đó (PROJECTION, RENAME, GROUPBY, JOIN) và nghĩ: những toán tử này có vẻ liên quan với nhau. Tất cả đều thay đổi lược đồ của khung dữ liệu. Có mối quan hệ nào sâu sắc hơn không?

Hình dạng của lược đồ thay đổi

Tôi liên tục nhìn chằm chằm vào bàn của Petersohn và một hình mẫu xuất hiện. Một số toán tử thay đổi lược đồ, nghĩa là cột nào tồn tại và loại cột nào tồn tại. Những người khác để lại lược đồ và chỉ ảnh hưởng đến các hàng. Và nếu bạn tập trung vào những nhóm thay đổi lược đồ thì chúng sẽ được chia thành ba nhóm.

Tái cấu trúc. Bạn sắp xếp lại, tập hợp con hoặc gắn nhãn lại các cột. Dữ liệu vẫn giữ nguyên; chỉ có hình dạng thay đổi. Theo thuật ngữ SQL: CHỌN tên, lương TỪ nhân viên tạo ra kết quả hai cột từ bảng ba cột. Trong thư viện khung dữ liệu:

chọn ["tên", "mức lương"] df
-- lược đồ 3 cột → 2 cột lược đồ
đổi tên "lương" "trả" df
-- Tên cột thay đổi, dữ liệu không bị ảnh hưởng
loại trừ ["bộ phận"] df
-- Bỏ một cột

Phần này bao gồm DỰ ÁN của Petersohn và ĐỔI TÊN. Lược đồ đầu ra là một hàm của lược đồ đầu vào. Bạn có thể tính toán mà không cần xem bất kỳ dữ liệu nào.

Hợp nhất. Bạn thu gọn các hàng có chung khóa thành bản tóm tắt hoặc bộ sưu tập. Trong SQL: SELECT bộ phận, AVG(mức lương) TỪ nhân viên GROUP BY bộ phận. Trong thư viện:

tổng hợp
    [ nghĩa là (col @Gấp đôi "mức lương") `as` "mức lương trung bình"
    , đếm (col @Văn bản "tên") `as` "số lượng nhân viên"
    ]
    (groupBy ["bộ phận"] df)
-- Lược đồ: tên, bộ phận, mức lương
-- → bộ phận, mức lương trung bình, số lượng nhân viên
-- Hoặc giữ nguyên tất cả giá trị mà không giảm:
tổng hợp
    [ collect (col @Double "mức lương") `as` "tất cả_tiền lương" ]
    (groupBy ["bộ phận"] df)
-- Mỗi bộ phận có một danh sách tất cả các tiền lương

Nhiều hàng ánh xạ tới cùng một khóa và được kết hợp. Điều này bao gồm GROUPBY và UNION của Petersohn.

Ghép nối. Bạn tìm thấy các hàng trong hai bảng có chung một khóa chung và ghép chúng thành một hàng rộng hơn. Trong SQL: CHỌN * TỪ nhân viên INNER THAM GIA các bộ phận SỬ DỤNG (bộ phận). Trong thư viện:

innerJoin ["bộ phận"] nhân viên phòng ban
-- Lược đồ: (tên, bộ phận, mức lương) + (bộ phận, ngân sách)
-- → tên, bộ phận, mức lương, ngân sách

Khóa chia sẻ xuất hiện một lần; các cột duy nhất từ ​​​​mỗi bên được nối. Các phép nối bên trái và bên ngoài là cùng một ý tưởng nhưng với các cột có thể rỗng, nơi có thể thiếu kết quả khớp. Điều này bao gồm SẢN PHẨM CHÉO / THAM GIA của Petersohn.

Điều gì không phù hợp. Hai toán tử quan hệ chống lại việc phân nhóm này. Trong SQL: CHỌN * TỪ nhân viên NGOẠI TRỪ CHỌN * TỪ nhà thầu trả về các hàng trong một bảng chứ không phải bảng kia. Và CHỌN DISTINCT * TỪ nhân viên thu gọn các hàng trùng lặp. Trong thư viện:

distinct df
-- Cùng một lược đồ, ít hàng hơn: xóa các bản sao trùng lặp
-- DIFFERENCE chưa được triển khai nhưng ý tưởng là:
-- nhân viên khác biệt là nhà thầu
-- Cùng một lược đồ, ít hàng hơn: xóa các hàng có trong cả hai

DIFFERENCE và DROP DUPLICATE đều thay đổi những hàng hiện có nhưng chúng không cấu trúc lại các cột, thu gọn theo khóa hoặc ghép nối giữa các bảng. Họ cảm thấy giống như lý thuyết tập hợp: một người tính toán phần bù, người kia tính toán một hình ảnh. Bây giờ tôi sẽ đặt chúng sang một bên nhưng chúng sẽ xuất hiện trở lại khi hình ảnh phân loại trở nên sắc nét hơn.

Vì vậy, năm toán tử quan hệ của Petersohn (PROJECTION, RENAME, GROUPBY, UNION, JOIN) ánh xạ rõ ràng vào ba mẫu: tái cơ cấu, hợp nhất, ghép nối. Các toán tử bảo toàn lược đồ (SELECTION, SORT, WINDOW) là trực giao. Chúng thay đổi những hàng bạn nhìn thấy hoặc theo thứ tự nào, nhưng không thay đổi cấu trúc cột. Và các toán tử dành riêng cho khung dữ liệu (TRANSPOSE, MAP, TOLABELS, FROMLABELS) hoàn toàn nằm bên ngoài mô hình quan hệ.

Tôi có ba mẫu và hai ngoại lệ. Điều tôi không có là lý do tại sao nó phải là ba mẫu này. Việc tái cấu trúc, sáp nhập và ghép nối có thực sự cơ bản hay tôi chỉ tình cờ nhóm mọi thứ theo cách này? Sự KHÁC BIỆT và BỎ CUỘC SAO thuộc về đâu? Có lý thuyết nào dự đoán được tất cả những điều này không?

Đó là câu hỏi mà người cố vấn Sam Stites của tôi đã chỉ cho tôi khi ông đề nghị tôi đọc Seven Sketches in Compositionality của Fong và Spivak. Câu trả lời hóa ra đến từ lý thuyết phạm trù. Phiên bản lý thuyết phạm trù trong Chương 3 của cuốn sách đó rất cụ thể và phù hợp với cơ sở dữ liệu. Bạn không cần phải biết toán trừu tượng. Bạn chỉ cần suy nghĩ cẩn thận về lược đồ là gì và ý nghĩa của việc thay đổi lược đồ.

Tại sao lại có ba hàm di chuyển

Hãy bắt đầu bằng một ví dụ cụ thể. Hãy tưởng tượng bạn có hai lược đồ:

Nhân viên: tên, bộ phận, mức lương
Phòng ban: phòng ban, ngân sách

Giữa họ có mối quan hệ tự nhiên. Cột bộ phận trong Nhân viên tham chiếu cột bộ phận trong Bộ phận. Đó là một khóa ngoại. Đó là ánh xạ từ lược đồ này sang lược đồ khác: mỗi hàng nhân viên đều trỏ vào một hàng phòng ban.

Bây giờ, bạn có thể làm gì với bản đồ đó? Chương 3 của Fong và Spivak xác định ba hoạt động cơ bản.

Bạn có thể hạn chế dữ liệu để phù hợp với một lược đồ khác. Lược đồ {name, mức lương là tập hợp con của Nhân viên. Khi bạn gọi chọn ["tên", "lương"], bạn đang nói: Tôi có dữ liệu được định dạng giống như lược đồ đầy đủ nhưng tôi chỉ muốn phần phù hợp với lược đồ nhỏ hơn này. Dữ liệu không thay đổi. Bạn vừa xóa các cột không có trong tập hợp con.

Điều tạo nên sự chung chung này là ý tưởng về ánh xạ giữa các lược đồ. Đối với select, ánh xạ cho biết “cột name trong lược đồ nhỏ tương ứng với name trong lược đồ lớn và salary tương ứng với lương.” Đó là một sự bao gồm. Đối với đổi tên "lương" "trả", ánh xạ cho biết “trả trong lược đồ mới tương ứng với lương trong lược đồ cũ”. Đó là việc dán nhãn lại. Trong cả hai trường hợp, đều có một ánh xạ cho bạn biết cách dịch giữa các lược đồ và dữ liệu sẽ theo sau.

Fong và Spivak gọi đây là Delta (Δ). Đưa ra ánh xạ giữa các lược đồ, Delta sử dụng nó để định hình lại dữ liệu. Nó không bao giờ phát minh ra dữ liệu mới hoặc kết hợp các hàng. Nó chỉ tái cấu trúc những gì đã có.

Bạn có thể thu gọn dữ liệu dọc theo ánh xạ bằng cách hợp nhất. Nhiều nhân viên dùng chung một bộ phận. Nếu bạn muốn dữ liệu có hình dạng giống như lược đồ Departments (một hàng cho mỗi bộ phận), bạn phải quyết định phải làm gì với tất cả nhân viên có cùng một bộ phận. Bạn có thể thu thập chúng vào một danh sách, tính tổng tiền lương của họ hoặc đếm chúng. Đó là groupBy ["department"] theo sau là tập hợp.

Fong và Spivak gọi đây là Sigma (Σ). Đưa ra một ánh xạ trong đó nhiều hàng nguồn trỏ đến cùng một mục tiêu, Sigma thu thập mọi thứ ở mỗi mục tiêu. Hàm collect giữ tất cả các giá trị dưới dạng danh sách, là Sigma thô. Sử dụng tổng hoặc mean sẽ tạo thành bộ sưu tập có nếp gấp.

Bạn có thể kết hợp dữ liệu từ cả hai lược đồ bằng cách ghép nối. Với dữ liệu đã cho về cả Nhân viênPhòng ban, bạn có thể tìm các hàng phù hợp với phòng ban và ghép chúng lại thành một hàng rộng hơn. Đó là các phòng ban của nhân viên innerJoin ["department"]. Mỗi hàng kết quả chứa các cột từ cả hai bảng, khớp với khóa chung của chúng.

Fong và Spivak gọi đây là Pi (Π). Cách ghi nhớ của họ là "ghép nối và truy vấn dữ liệu", với chú thích cuối trang: "thường được các lập trình viên cơ sở dữ liệu gọi là 'tham gia' hơn." Pi tìm thấy tất cả các bộ dữ liệu thỏa mãn các ràng buộc do khóa chia sẻ áp đặt.

Vì vậy, ba mẫu từ phần trước (tái cấu trúc, hợp nhất, ghép nối) có tên: Δ, Σ, Π. Nhưng những cái tên là phần ít thú vị nhất. Điều thú vị là tại sao ba điều này lại xuất hiện một cách tự nhiên từ cấu trúc của ánh xạ lược đồ.

Câu trả lời là về mối liên hệ giữa các lược đồ với nhau. Lược đồ mô hình Fong và Spivak dưới dạng danh mục. Danh mục ở đây là tập hợp các sự vật (bảng, cột) có mối quan hệ giữa chúng (khóa ngoại) và quy tắc tuân theo chuỗi mối quan hệ.

Một phiên bản của lược đồ là những gì bạn nhận được khi gán dữ liệu thực tế cho lược đồ đó. Mỗi bảng có một tập hợp các hàng. Mỗi khóa ngoại có một hàm ánh xạ một hàng tới hàng mà nó tham chiếu.

Lấy lược đồ Nhân viên/Phòng ban. Một phiên bản gán các hàng {Alice, Bob, Carol cho Nhân viên, các hàng {Kỹ thuật, Bán hàng cho Bộ phận và một hàm gửi Alice → Kỹ thuật, Bob → Bán hàng, Carol → Kỹ thuật. Quy tắc duy nhất là tính nhất quán. Nếu Nhân viên tham chiếu đến Các phòng ban tham chiếu đến Vị trí thì việc tra cứu trực tiếp vị trí của Alice phải đưa ra câu trả lời giống như tra cứu phòng ban của cô ấy và sau đó là vị trí của phòng ban đó. Trong lý thuyết danh mục, một phiên bản thỏa mãn quy tắc này được gọi là functor.

Khi bạn có ánh xạ giữa hai lược đồ (cũng là một functor), Fong và Spivak chứng minh rằng nó tạo ra ba hoạt động di chuyển dữ liệu này. Chúng được kết nối bởi một cấu trúc được gọi là bộ ba liền kề:

Σ ⊣ Δ ⊣ Π

Sigma là hào phóng nhất: lấy mọi thứ và hợp nhất. Pi là người bảo thủ nhất: chỉ giữ các bộ dữ liệu khớp với tất cả các thuộc tính được chia sẻ. Delta đi theo hướng khác, hạn chế dữ liệu mà không phát minh hay kết hợp bất cứ thứ gì. Điều chỉnh có nghĩa là ba bước này kết hợp rõ ràng: đầu ra của bất kỳ bước Δ nào là đầu vào hợp lệ cho bất kỳ bước Σ hoặc Π nào và ngược lại. Đó là lý do tại sao bạn có thể xâu chuỗi các lựa chọn để tham gia vào nhómBy và các lược đồ sẽ hoạt động tốt.

Bộ ba liền kề không bao gồm những gì

Bộ ba liền kề chiếm năm trong số bảy toán tử quan hệ của Petersohn. Nhưng KHÁC BIỆT và DROP NHÂN ĐÔI, hai cái mà tôi đã đặt sang một bên trước đó, hoàn toàn không phát sinh từ các hình thái lược đồ. Chúng hoạt động trên các phiên bản của cùng một lược đồ chứ không phải di chuyển giữa các lược đồ.

Hãy nghĩ về những gì DIFFERENCE thực sự làm. Giả sử bạn có hai khung dữ liệu có cùng lược đồ:

all_employees: đã chấm dứt:
tên bộ phận tên bộ phận
Kỹ thuật Alice Kỹ thuật Carol
Bán hàng Bob
Kỹ thuật Carol

DIFFERENCE trả về các hàng trong all_employees không xuất hiện trong terminated: Alice và Bob. Lược đồ không thay đổi. Chỉ có tập hợp các hàng thay đổi. Và DROP DUPLICATES hoạt động trên một khung dữ liệu duy nhất:

với_duplicates: sau khi phân biệt:
tên bộ phận tên bộ phận
Kỹ thuật Alice Kỹ thuật Alice
Bob bán hàng Bob bán hàng
Kỹ thuật Alice

It collapses identical rows into one. Một lần nữa, lược đồ vẫn nguyên vẹn.

Bộ ba liền kề không có gì để nói về chúng vì Δ, Σ và Π liên quan đến việc di chuyển dữ liệu giữa các lược đồ. KHÁC BIỆT và DROP DUPLICATE là cách suy luận về các tập hợp con của các hàng trong một lược đồ duy nhất.

Để xử lý các phép toán trên tập hợp con, bạn cần danh mục của mình hỗ trợ khái niệm “tập hợp con” với các phép toán như phần bù và phần giao. Không phải thể loại nào cũng vậy. Nhưng loại thực thể trên một lược đồ thì có, bởi vì việc gán các tập hợp hàng cho mỗi bảng và các hàm cho từng khóa ngoại sẽ cung cấp cho bạn tất cả cấu trúc lý thuyết tập hợp mà bạn cần. Trong lý thuyết danh mục, danh mục có loại cấu trúc này được gọi là topos. Điều quan trọng về topo đối với mục đích của chúng tôi là nó được trang bị các công cụ để làm việc với các tập hợp con:

  • DIFFERENCE là phép toán bổ sung trên các tập hợp con. Cho hai tập hợp con của hàng A và B (hai khung dữ liệu có cùng lược đồ), DIFFERENCE tính toán các hàng trong A không có trong B. Topos đảm bảo rằng phần bù này được xác định rõ ràng và hoạt động theo cách bạn mong đợi từ lý thuyết tập hợp.

  • DROP DUPLICATE là một thao tác hình ảnh. Hãy coi nội dung của mỗi hàng như một hàm từ danh tính của hàng đến các giá trị của nó. Nhiều hàng có thể ánh xạ tới cùng một giá trị. DROP DUPLICATE thu gọn chúng, giữ lại một đại diện cho mỗi giá trị riêng biệt. Một topos đảm bảo rằng mọi chức năng như vậy đều phân tích rõ ràng thông qua hình ảnh của nó, điều này làm cho DROP DUPLICATE được xác định rõ ràng.

Vì vậy, bức tranh phân loại của các phép toán quan hệ có hai lớp: các hàm di chuyển (Δ, Σ, Π) để di chuyển dữ liệu qua các lược đồ và cấu trúc topos để suy luận về các tập hợp con của các hàng trong một lược đồ.

Đây là bức tranh đầy đủ, ánh xạ các toán tử của Petersohn vào khung phân loại:

Toán tử Petersohn Mẫu Lý thuyết danh mục
SỰ DỰ ÁN Tái cấu trúc Đồng bằng (Δ)
RENAME Tái cấu trúc Đồng bằng (Δ)
NHÓM THEO Hợp nhất Sigma (Σ)
UNION Hợp nhất Sigma (Σ)
SẢN PHẨM CHÉO / THAM GIA Ghép nối Pi (Π)
SỰ KHÁC BIỆT (lý thuyết tập hợp) Phần bổ sung đối tượng phụ (topos)
THẢ LẠI SAO CHÉP (lý thuyết tập hợp) Hệ số hình ảnh (topos)
CHỌN (bảo toàn lược đồ) Biến đổi tự nhiên
Sắp xếp (bảo toàn lược đồ)
CỬA SỔ (bảo toàn lược đồ)
TRANSPOSE (dành riêng cho khung dữ liệu)
MAP (dành riêng cho khung dữ liệu)
TOLABELS (dành riêng cho khung dữ liệu)
FROMLABELS (dành riêng cho khung dữ liệu)

Năm toán tử đầu tiên phân rã thành bộ ba liền kề Δ, Σ, Π. Hai cái tiếp theo sử dụng cấu trúc topos của danh mục các thể hiện. Cùng với nhau, bảy điều này tạo nên cốt lõi của mối quan hệ. Ba cái tiếp theo bảo tồn lược đồ. Chúng là các hình thái giữa các phiên bản trên cùng một lược đồ, không phải là sự di chuyển giữa các lược đồ. Bốn cái cuối cùng hoàn toàn nằm ngoài mô hình quan hệ.

Đây là bản nén: 200 toán tử gấu trúc → 15 toán tử đại số → 3 hàm di chuyển và 2 phép toán lý thuyết topos bao gồm lõi quan hệ. Các toán tử bảo toàn lược đồ và dành riêng cho khung dữ liệu rất quan trọng nhưng chúng không phải là nơi chứa đựng sự phức tạp. Các hàm di chuyển xử lý các hoạt động thay đổi lược đồ và cấu trúc topos xử lý lý luận lý thuyết tập hợp trong một lược đồ.

Thiết kế API dựa trên các mẫu này

Việc phân rã theo danh mục cung cấp cho bạn nguyên tắc thiết kế: mỗi thao tác phải có quy tắc rõ ràng để tính toán lược đồ đầu ra từ lược đồ đầu vào của nó. Các hàm di chuyển thay đổi lược đồ. Các phép toán lý thuyết topo bảo toàn lược đồ nhưng thay đổi những hàng hiện có.

Bắt đầu với hàm di chuyển. Delta cung cấp cho bạn các thao tác định hình lại lược đồ mà không cần tính toán dữ liệu mới. Điều đó có nghĩa là:

  • chọn cột → lược đồ đầu ra là tập hợp con của các cột đầu vào mà bạn đã đặt tên
  • loại trừ các cột → lược đồ đầu ra là đầu vào trừ đi các cột bạn đã đặt tên
  • đổi tên cũ mới → lược đồ đầu ra là đầu vào có một cột được gắn nhãn lại

Các thao tác này có chung một thuộc tính: với lược đồ đầu vào và các đối số của thao tác, bạn có thể tính toán lược đồ đầu ra mà không cần xem bất kỳ dữ liệu nào. Điều đó làm cho chúng rẻ, có thể dự đoán được và an toàn khi sắp xếp lại trong trình tối ưu hóa.

Rồi Sigma. Bạn cần các thao tác thu gọn các hàng theo khóa. Điều đó có nghĩa là:

  • groupBy khóa theo sau là tổng hợp [tổng ..., trung bình ..., đếm ...] → lược đồ đầu ra là các cột chính cộng với một cột mới cho mỗi tập hợp
  • groupBy khóa theo sau là collect → lược đồ đầu ra là các cột chính cộng với các cột có giá trị danh sách

Thông tin chi tiết quan trọng từ bức tranh phân loại là collectaggregate là các biến thể của cùng một mẫu. Sigma thu thập mọi thứ ở mỗi khóa và các hàm tổng hợp như sum hoặc mean là mức giảm tùy chọn ở trên cùng.

Rồi Pi. Bạn cần các thao tác kết hợp hai lược đồ bằng khóa chung. Điều đó có nghĩa là:

  • các phím InnerJoin trái phải → lược đồ đầu ra là các cột chính (một lần) cộng với các cột không có khóa từ cả hai phía
  • tráiCác phím tham gia trái phải → tương tự, nhưng các cột không có khóa ở bên phải sẽ trở thành rỗng
  • các phím đầy đủOuterJoin trái phải → giống nhau, nhưng các cột không có khóa ở cả hai bên sẽ trở thành có thể vô hiệu

Mỗi biến thể kết hợp là Pi có chính sách khác nhau đối với các kết quả trùng khớp bị thiếu. Quy tắc lược đồ là như nhau; chỉ có gói vô hiệu thay đổi.

Sau đó là lớp topos. KHÁC BIỆT và DROP SAO CHÉP không cần các quy tắc lược đồ vì chúng bảo toàn lược đồ. Chữ ký loại của họ phản ánh điều này:

distinct :: TypedDataFrame cols -> TypedDataFrame cols
-- sự khác biệt :: TypedDataFrame cols -> TypedDataFrame cols -> TypedDataFrame cols

Loại đầu vào và đầu ra giống hệt nhau. Những thay đổi nào nằm ở hàng nào hiện diện: distinct thu gọn các hàng trùng lặp và difference sẽ xóa các hàng xuất hiện trong đối số thứ hai. Cả hai đều lấy một khung dữ liệu và trả về một khung dữ liệu có cùng cột nhưng ít hàng hơn, điều này giúp cho việc triển khai và tối ưu hóa ngữ nghĩa của chúng trở nên dễ dàng.

Cuối cùng, các thao tác bảo toàn lược đồ (filter, sort, take, sample) nằm ngoài các mẫu di chuyển và topos. Lược đồ đầu ra của họ luôn giống với lược đồ đầu vào của họ. Chúng quan trọng nhưng chúng không tương tác với thành phần lược đồ, đó là lý do tại sao chúng có thể được thiết kế độc lập.

Sau khi bạn có những phần này, quy trình là một chuỗi các bước di chuyển (Δ, Σ, Π) và các bước cấp hàng (DIFFERENCE, DROP DUPLICATE, filter) và lược đồ đầu ra của mỗi bước là đầu vào hợp lệ cho bước tiếp theo. Trong thư viện khung dữ liệu, hệ thống kiểu của Haskell thực thi điều này. Các lược đồ được mã hóa ở cấp loại và trình biên dịch sẽ kiểm tra mọi chuyển đổi:

loại Nhân viên =
    '[ Cột "name" Văn bản
     , Cột "bộ phận" Văn bản
     , Cột "lương" Gấp đôi
     ]
loại Các bộ phận = '[ Cột "bộ phận" Văn bản, Cột "ngân sách" Gấp đôi ]
kết quả =
    nhân viên
        & T.distinct -- topos: bỏ các hàng trùng lặp
        & T.innerJoin @'["bộ phận"] bộ phận -- Π: lược đồ phát triển
        & T.suy ra @"cost_ratio" -- tăng thêm một
            (T.col @"lương" / T.col @"ngân sách")
        & T.chọn @'["bộ phận", "tỷ lệ chi phí"] -- Δ: lược đồ co lại
        & T.groupBy @'["bộ phận"]
        & T.tổng hợp -- Σ: thu gọn
            ( T.agg @"avg_ratio" (T.có nghĩa là (T.col @"cost_ratio"))
            $ T.aggNil
            )
[[TAG_1112]

Tài liệu tham khảo "lương" sau khi select bỏ nó? Biên dịch lỗi. Lấy một cột đã tồn tại? Biên dịch lỗi. Tham gia bằng một khóa bị thiếu trong một bảng? Biên dịch lỗi. Nếu đường dẫn biên dịch, mọi chuyển đổi lược đồ đều hợp lệ. Đây không phải là vấn đề cụ thể của Haskell. Bất kỳ ngôn ngữ nào có đủ loại biểu cảm đều có thể thực thi các quy tắc tương tự. Sự phân rã theo kiểu phân loại cho bạn biết các quy tắc là gì; hệ thống loại thực thi chúng.

Các mẫu này cũng giúp tối ưu hóa. Nếu bạn biết chính xác từng thao tác thực hiện những gì đối với lược đồ, bạn có thể suy luận về thời điểm an toàn để sắp xếp lại các bước. Thư viện có chế độ đánh giá lười biếng trong đó quy trình được xây dựng dưới dạng kế hoạch hợp lý trước khi được thực thi:

tối ưu hóa :: Int -> Kế hoạch logic -> Gói vật lý
tối ưu hóa batchSz =
    đếnVật lý batchSz
        . loại bỏDeadColumns
        . pushPredicates
        . Bộ lọc cầu chì
[[TAG_1162]

Các bộ lọc liên tiếp được hợp nhất thành một. Các bộ lọc được đẩy qua các hoạt động của cột về phía nguồn dữ liệu. Các cột dẫn xuất không bao giờ được tham chiếu xuôi dòng sẽ bị loại bỏ trước khi thực thi. Việc viết lại này an toàn vì các phép toán tuân theo các quy luật đại số tuân theo cấu trúc phân loại: tái cấu trúc và lọc đi lại khi bộ lọc không chạm vào các cột được tái cấu trúc; sự kết hợp của các vị từ giống như việc lọc hai lần; bước Δ làm rơi cột không thể ảnh hưởng đến bộ lọc không tham chiếu cột đó.

Chuyện này sẽ đi đến đâu

Điều tôi đang hướng tới là chuẩn mực định nghĩa của khung dữ liệu. Petersohn và cộng sự. đã nỗ lực hết mình với mô hình dữ liệu và đại số của họ. Lý thuyết danh mục bổ sung thêm cấu trúc ở trên cùng: ba hàm di chuyển cho các hoạt động thay đổi lược đồ và cấu trúc topos cho lý luận lý thuyết tập hợp trong một lược đồ. Cùng với nhau, những điều này bao gồm cốt lõi quan hệ.

Đó là điều tôi mong muốn khi thành lập thư viện. Một tập hợp nhỏ các hoạt động dựa trên lý thuyết, với trình biên dịch xác minh từng bước. Bài đăng này đề cập đến các toán tử quan hệ, những toán tử chia sẻ các khung dữ liệu với SQL. Các toán tử dành riêng cho khung dữ liệu (TRANSPOSE, TOLABELS, FROMLABELS) và tính đối xứng giữa các hàng và cột xứng đáng được xử lý riêng. Nhưng đối với lõi quan hệ, hình ảnh hai lớp sẽ hoạt động.

Nếu bất kỳ điều nào trong số này nghe có vẻ thú vị: Seven Sketches in Compositionality của Fong và Spivak được viết cho những người không phải là nhà toán học và được xây dựng từ những nguyên tắc đầu tiên. Chương 3 đề cập đến cơ sở dữ liệu và các hàm di chuyển Δ/Σ/Π. Hướng tới Hệ thống khung dữ liệu có thể mở rộng của Petersohn và cộng sự bao gồm đại số, mô hình dữ liệu và những gì mọi người thực sự làm trong 1 triệu sổ ghi chép đó. Cả hai đều đáng để bạn dành thời gian.

Thư viện khung dữ liệu có trên GitHub. API đã nhập nằm trong DataFrame.Typed. Tôi rất muốn nghe những gì bạn xây dựng với nó.

Tác giả: mchav

#discussion