A Pythonic, zero-dependency vector and table library for interactive data exploration
Project description
serif
Python in, Python out. Allowed to raise, not allowed to lie.
Serif is typed vectors and tables for Python, built around two promises: touching your data should feel like ordinary Python, and ambiguous operations should fail loudly.
Values stay Python values — int, float, str, date, None — so there is nothing to convert, unwrap, or remember. And serif does not silently coerce, mutate, or guess. When an operation has no clear meaning, it raises.
Vector provides the foundation. Table is the primary interface for exploration, modeling, and analysis.
30-Second Example
from serif import Table
t = Table({
"price ($)": [10, 20, 30],
"quantity": [4, 5, 6]
})
t >>= {'total': t.price * t.quantity}
t >>= {'tax': t.total * 0.1}
t
# 'price ($)' quantity total tax
# .price .quantity .total .tax
# [int] [int] [int] [float]
# 10 4 40 4.0
# 20 5 100 10.0
# 30 6 180 18.0
#
# 3×4 table <int:3, float>
Installation
pip install serif
Zero dependencies — pip freeze in a fresh environment shows exactly one line.
Quickstart
Vectors
from serif import Vector
a = Vector([1, 2, 3, 4, 5])
a * 2 # 2, 4, 6, 8, 10
a > 3 # False, False, False, True, True
a[a > 3] # 4, 5 — boolean masks filter
a.sum() # 15 — a plain Python int
Tables
from serif import Table
t = Table({
"first name": ["ann", "bo", "cy"],
"price ($)": [10, 20, 30],
})
t.price * 2 # names sanitize for dot access
t >>= {'total': t.price * 2} # add a column
t[t.price > 15] # masks filter rows
t._ # what do I have again?
# .first_name str 'first name'
# .price int 'price ($)'
# .total int
None means missing
Element-wise, unknown in is unknown out. Aggregates summarize what you know. And a verdict needs evidence — all()/any() over zero valid values raise rather than guess.
v = Vector([10, None, 30])
v + 1 # 11, None, 31
v > 15 # False, None, True
v.sum() # 40
len(v) # 3
Vector([None, None]).any() # raises SerifEmptyReductionError
Vector([None, None]).any(on_empty=False) # False — you supplied the verdict
Joins and aggregation
customers = Table({'id': [1, 2, 3], 'name': ['Ann', 'Bo', 'Cy']})
scores = Table({'id': [2, 3, 4], 'score': [85, 90, 95]})
customers.inner_join(scores, left_on='id', right_on='id')
# id, name, score — matched key columns are not duplicated
sales = Table({'customer': ['A', 'B', 'A'], 'amount': [100, 200, 150]})
sales.aggregate(
groupby=sales.customer,
aggregations={'total': sales.amount.sum, 'n': len},
)
# customer total n
# A 250 2
# B 200 1
CSV and Parquet
from serif import read_csv, read_parquet
t = read_csv("sales.csv") # types inferred, headers sanitized for dot access
t.to_parquet("sales.parquet") # ints, dates, and nulls arrive intact
u = read_parquet("sales.parquet")
Documentation
- Design Philosophy — the principles everything else answers to
- Serif vs Python — the few intentional departures
- Table Model — the column-major table
- Indexing — slicing, masking, and selection rules
- Null Semantics —
None, three-valued logic, verdicts - Joins & Aggregations — detailed examples and patterns
- Naming — column names and sanitization
- Repr — what the display shows and why
- Invariants — the promises the internals keep
- Exceptions — error types and when they raise
- Aliasing & Fingerprints — copy-on-write and change detection
- Performance — complexity of the core operations
- Gotchas — sharp edges worth knowing up front
- Development — running tests, project structure
License
MIT
Contributing
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file serif-0.1.8.tar.gz.
File metadata
- Download URL: serif-0.1.8.tar.gz
- Upload date:
- Size: 142.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0905ee4926688525c6d64e2a5e1b5b54dd591f2cb89f1f09b28ad3c909daa42
|
|
| MD5 |
a596043f9b98e6e707e7ef463ccc2017
|
|
| BLAKE2b-256 |
0a9876b6695c8e34285577ed2e1cbb340f0fb62cdf68bd5ceb196e182e31c8d9
|
File details
Details for the file serif-0.1.8-py3-none-any.whl.
File metadata
- Download URL: serif-0.1.8-py3-none-any.whl
- Upload date:
- Size: 85.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c60eab62f91e975b5f954721413d6d95d85fd3daf99f84ea103ba6f88ee14fb9
|
|
| MD5 |
2efa4dc1e557bae17d2bcf18b5806abf
|
|
| BLAKE2b-256 |
a06000eded1405c0f7c0cae9c91969d260cba1794fddb350fd047b266cf5264b
|