Harness the power of speLL with spellops to write clean and readable pipelines.
Project description
spellops
Developer Guide
Instead of writing this:
L(f_c(L(...).map(f_a).filter(f_b))).map(f_d)[0]
With spellops’s pipe operator, you can visually maintain the order
of execution (f_a → filter(f_b) → f_c → f_d) by writing this:
L(...).map(f_a).filter(f_b).pipe(f_c).map(f_d)[0]
And with tee operator, you can inspect and document your pipeline like
this:
(L(...) .tee('input data')
.map(f_a) .tee('apply f_a to individual elements')
.filter(f_b) .tee('filter by f_b')
.pipe(f_c) .tee('apply f_c to the whole list')
.map(f_d)[0] .tee('finally apply f_d to the resulting elements and returning first value')
)
And once you’re done, you can keep all of this in your production code:
def process(data,verbose=False):
s = dict(show=verbose)
return (L(data) .tee('input data', **s)
.map(f_a) .tee('apply f_a to individual elements', **s)
.filter(f_b) .tee('filter by f_b', **s)
.pipe(f_c) .tee('apply f_c to the whole list', **s)
.map(f_d) .tee('finally apply f_d to the resulting elements and returning first value', **s)
.unwrap() # Extract the element out of the list
)
Here are some examples:
Import L and spellops
NOTE: order of import does not matters.
import spellops
from fastcore.foundation import L
Fluent string manipulaiton
L(['a', 'b', 'c']).map(str.upper).pipe('->'.join, wrap=False)
'A->B->C'
Or using convenience mehtod pipen that stands for pipe with
wrap=False (no wrap):
L(['a', 'b', 'c']).map(str.upper).pipen('->'.join)
'A->B->C'
Count how many elements are present in both sublists
(L([[1,2,3,2],[2,4,1]]) .tee('input')
.map(set) .tee('to set')
.starpipe(set.intersection) .tee('common elements')
.pipe(len) .tee('count elements')
.unwrap()
)
input
[[1, 2, 3, 2], [2, 4, 1]]
to set
[{1, 2, 3}, {1, 2, 4}]
common elements
[1, 2]
count elements
[2]
2
An alternative approach can be to pipe with wrap=False:
NOTE: In this case, you can’t use tee after the final pipe
operator because it no longer returns an L instance.
(L([[1,2,3,2],[2,4,1]]) .tee('input')
.map(set) .tee('to set')
.starpipe(set.intersection) .tee('common elements')
.pipe(len, wrap=False) # count elements
)
input
[[1, 2, 3, 2], [2, 4, 1]]
to set
[{1, 2, 3}, {1, 2, 4}]
common elements
[1, 2]
2
Solve an AoC task
SPOILER ALERT: the following is a speLL to solve AoC 2024 part B.
GOAL: For each number in the left list, we need to count how many times it appears in the right list, multiply these together, and sum all results. This creates a “similarity score”, counting occurrences and multiplying, that measures how frequently numbers from the left list appear in the right list.
sample = '''3 4
4 3
2 5
1 3
3 9
3 3
'''
# This function will be "mapped" to individual elements
def to_int_tuple(a,b): return (int(a),int(b))
# this function acts on the whole list
def count_instances(As,Bs): return [(o,len([t for t in Bs if t==o])) for o in As]
from math import prod
(L(sample.splitlines())
.map(str.split) .tee('input data')
.starmap(to_int_tuple) .tee('int to tuples')
.zip(cycled=True) .tee('tuple of lists')
.starpipe(count_instances) .tee('apply count_instances to the "whole list"')
.map(prod) .tee('multiply tuple elements')
.sum()
)
input data
[['3', '4'], ['4', '3'], ['2', '5'], ['1', '3'], ['3', '9'], ['3', '3']]
int to tuples
[(3, 4), (4, 3), (2, 5), (1, 3), (3, 9), (3, 3)]
tuple of lists
[(3, 4, 2, 1, 3, 3), (4, 3, 5, 3, 9, 3)]
apply count_instances to the "whole list"
[(3, 3), (4, 1), (2, 0), (1, 0), (3, 3), (3, 3)]
multiply tuple elements
[9, 4, 0, 0, 9, 9]
31
Visually inspect images transformations:
import matplotlib.pyplot as plt
import numpy as np
def plot_images(x:L, msg):
N = len(x)
plt.figure(figsize=(4*N,4))
for i,o in enumerate(x):
plt.subplot(1,N,i+1)
plt.imshow(o)
plt.title(f'Image: {i}')
plt.suptitle(msg)
(L([[[0,0,0],[1,1,1],[0,0,0]],[[1,0,0],[0,1,0],[0,0,1]]]) .tee('Input images',f=plot_images)
.map(lambda x: np.rot90(x,k=1)) .tee('Rotate 90 degree',f=plot_images)
)
(#2) [array([[0, 1, 0],
[0, 1, 0],
[0, 1, 0]]),array([[0, 0, 1],
[0, 1, 0],
[1, 0, 0]])]
Install spellops in Development mode
# make sure spellops package is installed in development mode
$ pip install -e .
# make changes under nbs/ directory
# ...
# compile to have changes apply to spellops
$ nbdev_prepare
Usage
Installation
Install latest from the GitHub repository:
$ pip install git+https://github.com/artste/spellops.git
or from pypi
$ pip install spellops
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 spellops-0.0.1.tar.gz.
File metadata
- Download URL: spellops-0.0.1.tar.gz
- Upload date:
- Size: 6.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0064533e52751cb721c8a627614e3a0062b53137fda460795af800c2888c7f7
|
|
| MD5 |
205e395833a61634b112a086027d1386
|
|
| BLAKE2b-256 |
172d37796f763d1c472d1b31e0b0c93a8ba4fd68c9dec7949e214ec2892b5f5e
|
File details
Details for the file spellops-0.0.1-py3-none-any.whl.
File metadata
- Download URL: spellops-0.0.1-py3-none-any.whl
- Upload date:
- Size: 5.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
174692d2c5ad9f27ac3ee812590e1d6794b216a10d9bab6007764e7bd8edf8a8
|
|
| MD5 |
294806705f00b9a511eadd5d5fa3236c
|
|
| BLAKE2b-256 |
ff49d530041d85bb4e408ed532c88a111715c8527eef53810a91883f0d204514
|