serif 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. serif-0.0.1/LICENSE +21 -0
  2. serif-0.0.1/PKG-INFO +374 -0
  3. serif-0.0.1/README.md +325 -0
  4. serif-0.0.1/pyproject.toml +54 -0
  5. serif-0.0.1/setup.cfg +4 -0
  6. serif-0.0.1/src/jib/__init__.py +39 -0
  7. serif-0.0.1/src/jib/alias_tracker.py +103 -0
  8. serif-0.0.1/src/jib/csv.py +115 -0
  9. serif-0.0.1/src/jib/display.py +377 -0
  10. serif-0.0.1/src/jib/errors.py +23 -0
  11. serif-0.0.1/src/jib/naming.py +83 -0
  12. serif-0.0.1/src/jib/table.py +1821 -0
  13. serif-0.0.1/src/jib/typeutils.py +18 -0
  14. serif-0.0.1/src/jib/typing.py +301 -0
  15. serif-0.0.1/src/jib/vector.py +1726 -0
  16. serif-0.0.1/src/serif.egg-info/PKG-INFO +374 -0
  17. serif-0.0.1/src/serif.egg-info/SOURCES.txt +41 -0
  18. serif-0.0.1/src/serif.egg-info/dependency_links.txt +1 -0
  19. serif-0.0.1/src/serif.egg-info/requires.txt +3 -0
  20. serif-0.0.1/src/serif.egg-info/top_level.txt +1 -0
  21. serif-0.0.1/tests/test_aggregate_regression.py +33 -0
  22. serif-0.0.1/tests/test_aggregate_sort_order.py +75 -0
  23. serif-0.0.1/tests/test_aggregate_window.py +463 -0
  24. serif-0.0.1/tests/test_column_rename_dotaccess.py +126 -0
  25. serif-0.0.1/tests/test_exceptions.py +20 -0
  26. serif-0.0.1/tests/test_fingerprint.py +391 -0
  27. serif-0.0.1/tests/test_joins.py +395 -0
  28. serif-0.0.1/tests/test_method_proxy.py +147 -0
  29. serif-0.0.1/tests/test_pydate.py +124 -0
  30. serif-0.0.1/tests/test_pyfloat.py +64 -0
  31. serif-0.0.1/tests/test_pyint.py +66 -0
  32. serif-0.0.1/tests/test_pystring.py +152 -0
  33. serif-0.0.1/tests/test_pytable.py +188 -0
  34. serif-0.0.1/tests/test_pyvector_basic.py +199 -0
  35. serif-0.0.1/tests/test_pyvector_indexing.py +154 -0
  36. serif-0.0.1/tests/test_pyvector_math.py +213 -0
  37. serif-0.0.1/tests/test_pyvector_naming.py +240 -0
  38. serif-0.0.1/tests/test_pyvector_repr.py +0 -0
  39. serif-0.0.1/tests/test_renaming.py +257 -0
  40. serif-0.0.1/tests/test_sanitize_name.py +260 -0
  41. serif-0.0.1/tests/test_table_setitem.py +320 -0
  42. serif-0.0.1/tests/test_to_object.py +101 -0
  43. serif-0.0.1/tests/test_type_promotion.py +372 -0
serif-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Capricorn Investment Group
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
serif-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,374 @@
1
+ Metadata-Version: 2.4
2
+ Name: serif
3
+ Version: 0.0.1
4
+ Summary: A Pythonic, zero-dependency vector and table library for interactive data exploration
5
+ Author-email: Ryan Larsen <ryan.e.larsen@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Capricorn Investment Group
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/CIG-GitHub/jib
29
+ Project-URL: Repository, https://github.com/CIG-GitHub/jib
30
+ Project-URL: Bug Tracker, https://github.com/CIG-GitHub/jib/issues
31
+ Keywords: vector,table,data,analysis,dataframe,csv
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
42
+ Classifier: Topic :: Scientific/Engineering
43
+ Requires-Python: >=3.10
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Provides-Extra: dev
47
+ Requires-Dist: pytest>=7.0; extra == "dev"
48
+ Dynamic: license-file
49
+
50
+ # jib
51
+ ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
52
+ [![Tests](https://github.com/CIG-GitHub/jib/actions/workflows/tests.yml/badge.svg)](https://github.com/CIG-GitHub/jib/actions/workflows/tests.yml)
53
+
54
+ A clean, typed, composable data layer for Python, built on **Vector** and **Table**.
55
+
56
+ Vector provides the foundation; Table is your primary tool for readable data modeling and analysis workflows.
57
+
58
+ ## 30-Second Example
59
+
60
+ ```python
61
+ from jib import Table
62
+
63
+ # Create a table with automatic column name sanitization
64
+ t = Table({
65
+ "price ($)": [10, 20, 30],
66
+ "quantity": [4, 5, 6]
67
+ })
68
+
69
+ # Add calculated columns with dict syntax
70
+ t >>= {'total': t.price * t.quantity}
71
+ t >>= {'tax': t.total * 0.1}
72
+
73
+ t
74
+ # 'price ($)' quantity total tax
75
+ # .price .quantity .total .tax
76
+ # [int] [int] [int] [float]
77
+ # 10 4 40 4.0
78
+ # 20 5 100 10.0
79
+ # 30 6 180 18.0
80
+ #
81
+ # 3×4 table <mixed>
82
+ ```
83
+
84
+ ## Real-World Example: Interactive CSV Exploration
85
+
86
+ ```python
87
+ from jib import read_csv
88
+
89
+ t = read_csv("sales.csv") # Messy column names? No problem.
90
+
91
+ # Discover columns interactively (no print needed!)
92
+ # t. + [TAB] → shows all sanitized column names
93
+ # t.pr + [TAB] → t.price
94
+ # t.qua + [TAB] → t.quantity
95
+
96
+ # Compose expressions naturally
97
+ total = t.price * t.quantity
98
+
99
+ # Add derived columns
100
+ t >>= {'total': total}
101
+
102
+ # Inspect (original names preserved in display!)
103
+ t
104
+ # 'price ($)' 'quantity' 'total'
105
+ # .price .quantity .total
106
+ # 10 4 40
107
+ # 20 5 100
108
+ # 30 6 180
109
+ #
110
+ # 3×3 table <int>
111
+ ```
112
+
113
+ **The power**: You don't need to know the CSV contents upfront. Tab completion guides you, the repr shows you everything, and messy column names are automatically cleaned for dot-access.
114
+
115
+ ## Installation
116
+
117
+ ```bash
118
+ pip install jib
119
+ ```
120
+
121
+ Zero external dependencies. In a fresh environment:
122
+
123
+ ```bash
124
+ pip freeze
125
+ # jib==0.x.y
126
+ ```
127
+
128
+ ## Why jib?
129
+
130
+ - Explicit, predictable vector semantics
131
+ - Tables compose cleanly from vectors
132
+ - Readable "spreadsheet-like" workflows
133
+ - Table-owns-storage: building a table copies inputs so tables never share columns by accident
134
+ - Controlled mutation: column vectors are live views; in-place updates mutate only that table
135
+ - Immediate visual feedback via `__repr__`
136
+ - Zero hidden magic
137
+
138
+ ## Quickstart
139
+
140
+ ### Vectors: elementwise operations
141
+
142
+ ```python
143
+ from jib import Vector
144
+
145
+ a = Vector([1, 2, 3, 4, 5])
146
+ b = Vector([10, 20, 30, 40, 50])
147
+
148
+ a + b # Vector([11, 22, 33, 44, 55])
149
+ a * 2 # Vector([2, 4, 6, 8, 10])
150
+ a > 3 # Vector([False, False, False, True, True])
151
+ ```
152
+
153
+ ### Tables: compose vectors with `>>`
154
+
155
+ ```python
156
+ from jib import Table
157
+
158
+ # Column names auto-sanitize to valid Python attributes
159
+ t = Table({
160
+ "first name": [1, 2, 3],
161
+ "price ($)": [10, 20, 30]
162
+ })
163
+
164
+ t.first_name # Vector([1, 2, 3])
165
+ t.price # Vector([10, 20, 30])
166
+
167
+ # Add columns with >>= (recommended)
168
+ t >>= (t.first_name * t.price).rename("total")
169
+
170
+ t
171
+ # 'first name' 'price ($)' total
172
+ # .first_name .price .total
173
+ # 1 10 10
174
+ # 2 20 40
175
+ # 3 30 90
176
+ #
177
+ # 3×3 table <int>
178
+ ```
179
+
180
+ ### Boolean masking
181
+
182
+ ```python
183
+ filtered = t[t.price > 15]
184
+
185
+ filtered
186
+ # 'first name' 'price ($)' total
187
+ # .first_name .price .total
188
+ # 2 20 40
189
+ # 3 30 90
190
+ #
191
+ # 2×3 table <int>
192
+ ```
193
+
194
+ ### Joins
195
+
196
+ ```python
197
+ customers = Table({'id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie']})
198
+ scores = Table({'id': [2, 3, 4], 'score': [85, 90, 95]})
199
+
200
+ result = customers.inner_join(scores, left_on='id', right_on='id')
201
+
202
+ result
203
+ # id name id score
204
+ # .id .name .id__2 .score
205
+ # [int] [str] [int] [int]
206
+ # 2 'Bob' 2 85
207
+ # 3 'Charlie' 3 90
208
+ #
209
+ # 2×4 table <mixed>
210
+ ```
211
+
212
+ ### Aggregations
213
+
214
+ ```python
215
+ t = Table({'customer': ['A', 'B', 'A'], 'amount': [100, 200, 150]})
216
+
217
+ result = t.aggregate(
218
+ over=t.customer,
219
+ sum_over=t.amount,
220
+ count_over=t.amount
221
+ )
222
+
223
+ result
224
+ # customer amount_sum amount_count
225
+ # [str] [int] [int]
226
+ # 'A' 250 2
227
+ # 'B' 200 1
228
+ #
229
+ # 2×3 table <mixed>
230
+ ```
231
+
232
+ **See [docs/joins-aggregations.md](docs/joins-aggregations.md) for detailed examples.**
233
+
234
+ ## Key Features
235
+
236
+ ### Automatic `__repr__`: Instant Visual Feedback
237
+
238
+ ```python
239
+ # Dictionary syntax: quick and familiar
240
+ t = Table({'id': range(100), 'value': [x**2 for x in range(100)]})
241
+
242
+ # Or compose from vectors: showcases Vector's design philosophy
243
+ a = Vector(range(100), name='id')
244
+ t = a >> (a**2).rename('value')
245
+
246
+ t
247
+ # id value
248
+ # 0 0
249
+ # 1 1
250
+ # 2 4
251
+ # 3 9
252
+ # 4 16
253
+ #... ...
254
+ # 95 9025
255
+ # 96 9216
256
+ # 97 9409
257
+ # 98 9604
258
+ # 99 9801
259
+ #
260
+ # 100×2 table <int>
261
+ ```
262
+
263
+ Head/tail preview + type annotations + dimensions—no need for `.head()`, `.info()`, etc.
264
+
265
+ ### Column Name Sanitization
266
+
267
+ Column names are sanitized to valid Python identifiers so you can access them with dot notation:
268
+
269
+ ```python
270
+ t = Table({"2023-Q1 Revenue ($M)": [1, 2, 3]})
271
+ t.c2023_q1_revenue_m # Deterministic, predictable access
272
+ ```
273
+
274
+ **Rules:**
275
+ - Non-alphanumeric characters become `_`
276
+ - Leading digits get `c` prefix
277
+ - All lowercase
278
+
279
+ Unnamed columns use system names: `t.col0_`, `t.col1_`, etc.
280
+
281
+ ### Typed Subclasses
282
+
283
+ Vector auto-creates typed subclasses with method proxying:
284
+
285
+ ```python
286
+ from datetime import date
287
+
288
+ dates = Vector([date(2023, 6, 29), date(2024, 1, 2), date(2024, 12, 28)])
289
+ dates += 5 # Add 5 days to each date
290
+ dates.year # Vector([2023, 2024, 2025]) - one crossed the year boundary!
291
+ ```
292
+
293
+ Works for `int`, `float`, `str`, `date` types.
294
+
295
+ ## Common Gotchas
296
+
297
+ ### Don't use subscript lists—use boolean masks
298
+
299
+ ```python
300
+ # ANTI-PATTERN
301
+ indices = [1, 5, 9]
302
+ result = v[indices] # Slow, emits warning
303
+
304
+ # IDIOMATIC
305
+ mask = (v > threshold)
306
+ result = v[mask]
307
+ ```
308
+
309
+ ### Operator overloading: avoid `.index()` on Vector lists
310
+
311
+ ```python
312
+ # WRONG: invokes elementwise equality
313
+ cols = [table.year, table.month]
314
+ idx = cols.index(table.year) # Returns boolean vector!
315
+
316
+ # CORRECT: use enumerate
317
+ for idx, col in enumerate(cols):
318
+ if col is table.year: # identity check
319
+ ...
320
+ ```
321
+
322
+ ### `None` handling
323
+
324
+ `None` is excluded from aggregations but counted in `len()`:
325
+
326
+ ```python
327
+ v = Vector([10, None, 20])
328
+ v.sum() # 30 (None excluded)
329
+ len(v) # 3 (None counted)
330
+ ```
331
+
332
+ ## Just Write Python
333
+
334
+ Not every task fits neatly into a vectorized expression.
335
+ When a loop is the clearest approach, jib keeps it efficient.
336
+
337
+ `for row in table:` is fully supported and stays lightweight, so you can use
338
+ whichever style makes the code easiest to understand.
339
+
340
+
341
+ ## Design Philosophy
342
+
343
+ jib makes a **strategic choice**: clarity and workflow ergonomics over raw speed.
344
+
345
+ **What you get:**
346
+ - Readable, debuggable code
347
+ - No hidden state or aliasing bugs (copy-on-write)
348
+ - Deterministic operations
349
+ - Zero dependencies
350
+ - O(1) fingerprinting for change detection
351
+
352
+ **When to use jib:**
353
+ - Modeling-scale data (10K–1M rows)
354
+ - Correctness and maintainability matter most
355
+ - Interactive workflows (Jupyter notebooks, REPL)
356
+ - Projects where zero dependencies is important
357
+
358
+ ## Further Documentation
359
+
360
+ - **[Performance & Complexity](docs/performance.md)** — O(n) analysis for joins, aggregations, indexing
361
+ - **[Exception Handling](docs/exceptions.md)** — Custom exception types and error handling
362
+ - **[Aliasing & Fingerprints](docs/aliasing.md)** — Copy-on-write and change detection
363
+ - **[Joins & Aggregations](docs/joins-aggregations.md)** — Detailed examples and patterns
364
+ - **[Development Guide](docs/development.md)** — Running tests, project structure
365
+
366
+ ## Philosophy
367
+
368
+ - Clarity beats cleverness
369
+ - Explicit beats implicit
370
+ - Modeling should feel intuitive
371
+ - You should always know what your code is doing
372
+
373
+ ## License
374
+ MIT
serif-0.0.1/README.md ADDED
@@ -0,0 +1,325 @@
1
+ # jib
2
+ ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
3
+ [![Tests](https://github.com/CIG-GitHub/jib/actions/workflows/tests.yml/badge.svg)](https://github.com/CIG-GitHub/jib/actions/workflows/tests.yml)
4
+
5
+ A clean, typed, composable data layer for Python, built on **Vector** and **Table**.
6
+
7
+ Vector provides the foundation; Table is your primary tool for readable data modeling and analysis workflows.
8
+
9
+ ## 30-Second Example
10
+
11
+ ```python
12
+ from jib import Table
13
+
14
+ # Create a table with automatic column name sanitization
15
+ t = Table({
16
+ "price ($)": [10, 20, 30],
17
+ "quantity": [4, 5, 6]
18
+ })
19
+
20
+ # Add calculated columns with dict syntax
21
+ t >>= {'total': t.price * t.quantity}
22
+ t >>= {'tax': t.total * 0.1}
23
+
24
+ t
25
+ # 'price ($)' quantity total tax
26
+ # .price .quantity .total .tax
27
+ # [int] [int] [int] [float]
28
+ # 10 4 40 4.0
29
+ # 20 5 100 10.0
30
+ # 30 6 180 18.0
31
+ #
32
+ # 3×4 table <mixed>
33
+ ```
34
+
35
+ ## Real-World Example: Interactive CSV Exploration
36
+
37
+ ```python
38
+ from jib import read_csv
39
+
40
+ t = read_csv("sales.csv") # Messy column names? No problem.
41
+
42
+ # Discover columns interactively (no print needed!)
43
+ # t. + [TAB] → shows all sanitized column names
44
+ # t.pr + [TAB] → t.price
45
+ # t.qua + [TAB] → t.quantity
46
+
47
+ # Compose expressions naturally
48
+ total = t.price * t.quantity
49
+
50
+ # Add derived columns
51
+ t >>= {'total': total}
52
+
53
+ # Inspect (original names preserved in display!)
54
+ t
55
+ # 'price ($)' 'quantity' 'total'
56
+ # .price .quantity .total
57
+ # 10 4 40
58
+ # 20 5 100
59
+ # 30 6 180
60
+ #
61
+ # 3×3 table <int>
62
+ ```
63
+
64
+ **The power**: You don't need to know the CSV contents upfront. Tab completion guides you, the repr shows you everything, and messy column names are automatically cleaned for dot-access.
65
+
66
+ ## Installation
67
+
68
+ ```bash
69
+ pip install jib
70
+ ```
71
+
72
+ Zero external dependencies. In a fresh environment:
73
+
74
+ ```bash
75
+ pip freeze
76
+ # jib==0.x.y
77
+ ```
78
+
79
+ ## Why jib?
80
+
81
+ - Explicit, predictable vector semantics
82
+ - Tables compose cleanly from vectors
83
+ - Readable "spreadsheet-like" workflows
84
+ - Table-owns-storage: building a table copies inputs so tables never share columns by accident
85
+ - Controlled mutation: column vectors are live views; in-place updates mutate only that table
86
+ - Immediate visual feedback via `__repr__`
87
+ - Zero hidden magic
88
+
89
+ ## Quickstart
90
+
91
+ ### Vectors: elementwise operations
92
+
93
+ ```python
94
+ from jib import Vector
95
+
96
+ a = Vector([1, 2, 3, 4, 5])
97
+ b = Vector([10, 20, 30, 40, 50])
98
+
99
+ a + b # Vector([11, 22, 33, 44, 55])
100
+ a * 2 # Vector([2, 4, 6, 8, 10])
101
+ a > 3 # Vector([False, False, False, True, True])
102
+ ```
103
+
104
+ ### Tables: compose vectors with `>>`
105
+
106
+ ```python
107
+ from jib import Table
108
+
109
+ # Column names auto-sanitize to valid Python attributes
110
+ t = Table({
111
+ "first name": [1, 2, 3],
112
+ "price ($)": [10, 20, 30]
113
+ })
114
+
115
+ t.first_name # Vector([1, 2, 3])
116
+ t.price # Vector([10, 20, 30])
117
+
118
+ # Add columns with >>= (recommended)
119
+ t >>= (t.first_name * t.price).rename("total")
120
+
121
+ t
122
+ # 'first name' 'price ($)' total
123
+ # .first_name .price .total
124
+ # 1 10 10
125
+ # 2 20 40
126
+ # 3 30 90
127
+ #
128
+ # 3×3 table <int>
129
+ ```
130
+
131
+ ### Boolean masking
132
+
133
+ ```python
134
+ filtered = t[t.price > 15]
135
+
136
+ filtered
137
+ # 'first name' 'price ($)' total
138
+ # .first_name .price .total
139
+ # 2 20 40
140
+ # 3 30 90
141
+ #
142
+ # 2×3 table <int>
143
+ ```
144
+
145
+ ### Joins
146
+
147
+ ```python
148
+ customers = Table({'id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie']})
149
+ scores = Table({'id': [2, 3, 4], 'score': [85, 90, 95]})
150
+
151
+ result = customers.inner_join(scores, left_on='id', right_on='id')
152
+
153
+ result
154
+ # id name id score
155
+ # .id .name .id__2 .score
156
+ # [int] [str] [int] [int]
157
+ # 2 'Bob' 2 85
158
+ # 3 'Charlie' 3 90
159
+ #
160
+ # 2×4 table <mixed>
161
+ ```
162
+
163
+ ### Aggregations
164
+
165
+ ```python
166
+ t = Table({'customer': ['A', 'B', 'A'], 'amount': [100, 200, 150]})
167
+
168
+ result = t.aggregate(
169
+ over=t.customer,
170
+ sum_over=t.amount,
171
+ count_over=t.amount
172
+ )
173
+
174
+ result
175
+ # customer amount_sum amount_count
176
+ # [str] [int] [int]
177
+ # 'A' 250 2
178
+ # 'B' 200 1
179
+ #
180
+ # 2×3 table <mixed>
181
+ ```
182
+
183
+ **See [docs/joins-aggregations.md](docs/joins-aggregations.md) for detailed examples.**
184
+
185
+ ## Key Features
186
+
187
+ ### Automatic `__repr__`: Instant Visual Feedback
188
+
189
+ ```python
190
+ # Dictionary syntax: quick and familiar
191
+ t = Table({'id': range(100), 'value': [x**2 for x in range(100)]})
192
+
193
+ # Or compose from vectors: showcases Vector's design philosophy
194
+ a = Vector(range(100), name='id')
195
+ t = a >> (a**2).rename('value')
196
+
197
+ t
198
+ # id value
199
+ # 0 0
200
+ # 1 1
201
+ # 2 4
202
+ # 3 9
203
+ # 4 16
204
+ #... ...
205
+ # 95 9025
206
+ # 96 9216
207
+ # 97 9409
208
+ # 98 9604
209
+ # 99 9801
210
+ #
211
+ # 100×2 table <int>
212
+ ```
213
+
214
+ Head/tail preview + type annotations + dimensions—no need for `.head()`, `.info()`, etc.
215
+
216
+ ### Column Name Sanitization
217
+
218
+ Column names are sanitized to valid Python identifiers so you can access them with dot notation:
219
+
220
+ ```python
221
+ t = Table({"2023-Q1 Revenue ($M)": [1, 2, 3]})
222
+ t.c2023_q1_revenue_m # Deterministic, predictable access
223
+ ```
224
+
225
+ **Rules:**
226
+ - Non-alphanumeric characters become `_`
227
+ - Leading digits get `c` prefix
228
+ - All lowercase
229
+
230
+ Unnamed columns use system names: `t.col0_`, `t.col1_`, etc.
231
+
232
+ ### Typed Subclasses
233
+
234
+ Vector auto-creates typed subclasses with method proxying:
235
+
236
+ ```python
237
+ from datetime import date
238
+
239
+ dates = Vector([date(2023, 6, 29), date(2024, 1, 2), date(2024, 12, 28)])
240
+ dates += 5 # Add 5 days to each date
241
+ dates.year # Vector([2023, 2024, 2025]) - one crossed the year boundary!
242
+ ```
243
+
244
+ Works for `int`, `float`, `str`, `date` types.
245
+
246
+ ## Common Gotchas
247
+
248
+ ### Don't use subscript lists—use boolean masks
249
+
250
+ ```python
251
+ # ANTI-PATTERN
252
+ indices = [1, 5, 9]
253
+ result = v[indices] # Slow, emits warning
254
+
255
+ # IDIOMATIC
256
+ mask = (v > threshold)
257
+ result = v[mask]
258
+ ```
259
+
260
+ ### Operator overloading: avoid `.index()` on Vector lists
261
+
262
+ ```python
263
+ # WRONG: invokes elementwise equality
264
+ cols = [table.year, table.month]
265
+ idx = cols.index(table.year) # Returns boolean vector!
266
+
267
+ # CORRECT: use enumerate
268
+ for idx, col in enumerate(cols):
269
+ if col is table.year: # identity check
270
+ ...
271
+ ```
272
+
273
+ ### `None` handling
274
+
275
+ `None` is excluded from aggregations but counted in `len()`:
276
+
277
+ ```python
278
+ v = Vector([10, None, 20])
279
+ v.sum() # 30 (None excluded)
280
+ len(v) # 3 (None counted)
281
+ ```
282
+
283
+ ## Just Write Python
284
+
285
+ Not every task fits neatly into a vectorized expression.
286
+ When a loop is the clearest approach, jib keeps it efficient.
287
+
288
+ `for row in table:` is fully supported and stays lightweight, so you can use
289
+ whichever style makes the code easiest to understand.
290
+
291
+
292
+ ## Design Philosophy
293
+
294
+ jib makes a **strategic choice**: clarity and workflow ergonomics over raw speed.
295
+
296
+ **What you get:**
297
+ - Readable, debuggable code
298
+ - No hidden state or aliasing bugs (copy-on-write)
299
+ - Deterministic operations
300
+ - Zero dependencies
301
+ - O(1) fingerprinting for change detection
302
+
303
+ **When to use jib:**
304
+ - Modeling-scale data (10K–1M rows)
305
+ - Correctness and maintainability matter most
306
+ - Interactive workflows (Jupyter notebooks, REPL)
307
+ - Projects where zero dependencies is important
308
+
309
+ ## Further Documentation
310
+
311
+ - **[Performance & Complexity](docs/performance.md)** — O(n) analysis for joins, aggregations, indexing
312
+ - **[Exception Handling](docs/exceptions.md)** — Custom exception types and error handling
313
+ - **[Aliasing & Fingerprints](docs/aliasing.md)** — Copy-on-write and change detection
314
+ - **[Joins & Aggregations](docs/joins-aggregations.md)** — Detailed examples and patterns
315
+ - **[Development Guide](docs/development.md)** — Running tests, project structure
316
+
317
+ ## Philosophy
318
+
319
+ - Clarity beats cleverness
320
+ - Explicit beats implicit
321
+ - Modeling should feel intuitive
322
+ - You should always know what your code is doing
323
+
324
+ ## License
325
+ MIT