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, float>
Installation
pip install serif
Serif has no required runtime dependencies. Its pure-Python implementation defines the semantics; optional accelerators improve performance without changing behavior.
NumPy and PyArrow are used when available, but only when they preserve the same Python values and behavior. Otherwise, Serif falls back to pure Python.
pip install "serif[numpy]"
pip install "serif[pyarrow]"
pip install "serif[numpy,pyarrow]"
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.
v = Vector([10, None, 30])
v + 1 # 11, None, 31
v > 15 # False, None, True
v.sum() # 40
len(v) # 3
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
# [str] [int] [int]
# A 250 2
# B 200 1
#
# 2×3 table <int, str>
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")
recent = u[u.date >= cutoff] # remaining columns load through this mask
read_parquet reads the footer first and materializes columns only when they
are touched. Boolean filtering stays ordinary Serif filtering: the concrete
mask is carried into columns that have not been read yet, so the first fully
materialized table can be the filtered one. Keep the source file readable and
unchanged until the table has materialized.
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 — copy-on-write
- 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.
Metadata
Release files for serif 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| serif-0.2.1.tar.gz | 254.8 kB | Without provenance | Details for serif-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| serif-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for serif-0.2.1-py3-none-any.whl |
Total release size: 403.4 kB
Release files / serif-0.2.1.tar.gz
| Download URL | serif-0.2.1.tar.gz |
|---|---|
| Size | 254.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
46fa7b3b9aac4219afb4819c5a3e0bc58ed2171eab33e4a6c3ac1bbebee2aa3b
|
|
BLAKE2b-256 checksum How to use checksums |
dcd0e0908e799114f029ef5b197bda0a4121e47a22dcb316e3e58a2400a349bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / serif-0.2.1-py3-none-any.whl
| Download URL | serif-0.2.1-py3-none-any.whl |
|---|---|
| Size | 148.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a8486e89964080e985c537b51ae8795f71057217e342701b95534a6631560c8b
|
|
BLAKE2b-256 checksum How to use checksums |
78857baf05b1944a7b33e99ce4e880bd38f32175b62a7b7a46832bdcaaf00562
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Security for serif 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.