Name | ldict JSON |
Version |
3.220128.3
JSON |
| download |
home_page | |
Summary | Lazy dict |
upload_time | 2022-12-15 13:23:31 |
maintainer | |
docs_url | None |
author | davips |
requires_python | >=3.8,<4.0 |
license | GPL |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
![test](https://github.com/davips/ldict/workflows/test/badge.svg)
[![codecov](https://codecov.io/gh/davips/ldict/branch/main/graph/badge.svg)](https://codecov.io/gh/davips/ldict)
<a href="https://pypi.org/project/ldict">
<img src="https://img.shields.io/pypi/v/ldict.svg?label=release&color=blue&style=flat-square" alt="pypi">
</a>
![Python version](https://img.shields.io/badge/python-3.8%20%7C%203.9-blue.svg)
[![license: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
<!--- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5501845.svg)](https://doi.org/10.5281/zenodo.5501845) --->
[![arXiv](https://img.shields.io/badge/arXiv-2109.06028-b31b1b.svg?style=flat-square)](https://arxiv.org/abs/2109.06028)
[![API documentation](https://img.shields.io/badge/doc-API%20%28auto%29-a0a0a0.svg)](https://davips.github.io/ldict)
# ldict
A lazy `dict`.
[Latest release](https://pypi.org/project/ldict) |
[Current code](https://github.com/davips/ldict) |
[API documentation](https://davips.github.io/ldict)
## See also
* laziness+identity+persistence ([idict](https://pypi.org/project/idict))
## Overview
A `ldict` is a `dict` with `str` keys.
**Simple usage example**
<details>
<p>
```python3
from ldict import ldict
a = ldict(x=3)
print(a)
"""
{
"x": 3
}
"""
```
```python3
b = ldict(y=5)
print(b)
"""
{
"y": 5
}
"""
```
```python3
print(a >> b)
"""
{
"x": 3,
"y": 5
}
"""
```
</p>
</details>
We consider that every value is generated by a process, starting from an `empty` ldict. The process is a sequence of
transformation steps done through the operator `>>`, which symbolizes a data flow. There are two types of steps:
* **value insertion** - represented by dict-like objects
* **function application** - represented by ordinary python functions
A `ldict` is completely defined by its key-value pairs so that
it can be converted from/to a built-in dict.
Creating a ldict is not different from creating an ordinary dict. Optionally it can be created through the `>>` operator
used after `empty`:
![img.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img.png)
Function application is done in the same way. The parameter names define the input fields, while the keys in the
returned dict define the output fields:
![img_1.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_1.png)
Similarly, for anonymous functions:
![img_5.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_5.png)
Finally, the result is only evaluated at request:
![img_6.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_6.png)
## Installation
### ...as a standalone lib
```bash
# Set up a virtualenv.
python3 -m venv venv
source venv/bin/activate
# Install from PyPI...
pip install --upgrade pip
pip install -U ldict
pip install -U ldict[full] # use this for extra functionality (recommended)
# ...or, install from updated source code.
pip install git+https://github.com/davips/ldict
```
### ...from source
```bash
git clone https://github.com/davips/ldict
cd ldict
poetry install
```
## Examples
**Merging two ldicts**
<details>
<p>
```python3
from ldict import ldict
a = ldict(x=3)
print(a)
"""
{
"x": 3
}
"""
```
```python3
b = ldict(y=5)
print(b)
"""
{
"y": 5
}
"""
```
```python3
print(a >> b)
"""
{
"x": 3,
"y": 5
}
"""
```
</p>
</details>
**Lazily applying functions to ldict**
<details>
<p>
```python3
from ldict import ldict
a = ldict(x=3)
print(a)
"""
{
"x": 3
}
"""
```
```python3
a = a >> ldict(y=5) >> {"z": 7} >> (lambda x, y, z: {"r": x ** y // z})
print(a)
"""
{
"x": 3,
"y": 5,
"z": 7,
"r": "→(x y z)"
}
"""
```
```python3
print(a.r)
"""
34
"""
```
```python3
print(a)
"""
{
"x": 3,
"y": 5,
"z": 7,
"r": 34
}
"""
```
</p>
</details>
**Parameterized functions and sampling**
<details>
<p>
```python3
from random import Random
from ldict import empty, let
# A function provide input fields and, optionally, parameters.
# For instance:
# 'a' is sampled from an arithmetic progression
# 'b' is sampled from a geometric progression
# Here, the syntax for default parameter values is borrowed with a new meaning.
def fun(x, y, a=[-100, -99, -98, ..., 100], b=[0.0001, 0.001, 0.01, ..., 100000000]):
return {"z": a * x + b * y}
def simplefun(x, y):
return {"z": x * y}
# Creating an empty ldict. Alternatively: d = ldict().
d = empty >> {}
print(d)
"""
{}
"""
```
```python3
# Putting some values. Alternatively: d = ldict(x=5, y=7).
d["x"] = 5
d["y"] = 7
print(d)
"""
{
"x": 5,
"y": 7
}
"""
```
```python3
# Parameter values are uniformly sampled.
d1 = d >> simplefun
print(d1)
print(d1.z)
"""
{
"x": 5,
"y": 7,
"z": "→(x y)"
}
35
"""
```
```python3
d2 = d >> simplefun
print(d2)
print(d2.z)
"""
{
"x": 5,
"y": 7,
"z": "→(x y)"
}
35
"""
```
```python3
# Parameter values can also be manually set.
e = d >> let(fun, a=5, b=10)
print(e.z)
"""
95
"""
```
```python3
# Not all parameters need to be set.
e = d >> Random() >> let(fun, a=5)
print("e =", e.z)
"""
e = 25.007
"""
```
```python3
# Each run will be a different sample for the missing parameters.
e = e >> Random() >> let(fun, a=5)
print("e =", e.z)
"""
e = 25.007
"""
```
```python3
# We can define the initial state of the random sampler.
# It will be in effect from its location place onwards in the expression.
e = d >> Random(0) >> let(fun, a=5)
print(e.z)
"""
725.0
"""
```
```python3
# All runs will yield the same result,
# if starting from the same random number generator seed.
e = e >> Random(0) >> let(fun, a=[555, 777])
print("Let 'a' be a list:", e.z)
"""
Let 'a' be a list: 700003885.0
"""
```
```python3
# Reproducible different runs are achievable by using a single random number generator.
e = e >> Random(0) >> let(fun, a=[5, 25, 125, ..., 10000])
print("Let 'a' be a geometric progression:", e.z)
"""
Let 'a' be a geometric progression: 700003125.0
"""
```
```python3
rnd = Random(0)
e = d >> rnd >> let(fun, a=5)
print(e.z)
e = d >> rnd >> let(fun, a=5) # Alternative syntax.
print(e.z)
"""
725.0
700000025.0
"""
```
```python3
# Output fields can be defined dynamically through parameter values.
# Input fields can be defined dynamically through kwargs.
copy = lambda source=None, target=None, **kwargs: {target: kwargs[source]}
d = empty >> {"x": 5}
d >>= let(copy, source="x", target="y")
print(d)
d.evaluate()
print(d)
"""
{
"x": 5,
"y": "→(source target x)"
}
{
"x": 5,
"y": 5
}
"""
```
</p>
</details>
**Composition of sets of functions**
<details>
<p>
```python3
from random import Random
from ldict import empty
# A multistep process can be defined without applying its functions
def g(x, y, a=[1, 2, 3, ..., 10], b=[0.00001, 0.0001, 0.001, ..., 100000]):
return {"z": a * x + b * y}
def h(z, c=[1, 2, 3]):
return {"z": c * z}
# In the ldict framework 'data is function',
# so the alias ø represents the 'empty data object' and the 'reflexive function' at the same time.
# In other words: 'inserting nothing' has the same effect as 'doing nothing'.
fun = empty >> g >> h # empty enable the cartesian product of the subsequent sets of functions within the expression.
print(fun)
"""
«λ{} × λ»
"""
```
```python3
# An unnapplied function has its free parameters unsampled.
# A compostition of functions results in an ordered set (Cartesian product of sets).
# It is a set because the parameter values of the functions are still undefined.
d = {"x": 5, "y": 7} >> (Random(0) >> fun)
print(d)
"""
{
"x": 5,
"y": 7,
"z": "→(c z→(a b x y))"
}
"""
```
```python3
print(d.z)
"""
105.0
"""
```
```python3
d = {"x": 5, "y": 7} >> (Random(0) >> fun)
print(d.z)
"""
105.0
"""
```
```python3
# Reproducible different runs by passing a stateful random number generator.
rnd = Random(0)
e = d >> rnd >> fun
print(e.z)
"""
105.0
"""
```
```python3
e = d >> rnd >> fun
print(e.z)
"""
14050.0
"""
```
```python3
# Repeating the same results.
rnd = Random(0)
e = d >> rnd >> fun
print(e.z)
"""
105.0
"""
```
```python3
e = d >> rnd >> fun
print(e.z)
"""
14050.0
"""
```
</p>
</details>
<!--- ## Persistence
Extra dependencies can be installed to support saving data to disk or to a server in the network.
**[still an ongoing work...]**
`poetry install -E full`
--->
## Concept
A ldict is like a common Python dict, with extra functionality and lazy. It is a mapping between string keys, called
fields, and any serializable (pickable protocol=5) object.
## Grants
This work was partially supported by Fapesp under supervision of
Prof. André C. P. L. F. de Carvalho at CEPID-CeMEAI (Grants 2013/07375-0 – 2019/01735-0).
Raw data
{
"_id": null,
"home_page": "",
"name": "ldict",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "davips",
"author_email": "dpsabc@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/12/47/b2ab81a45cd41bf534bd6d0a6068a1c6b938db49554197ebcbe381baf777/ldict-3.220128.3.tar.gz",
"platform": null,
"description": "![test](https://github.com/davips/ldict/workflows/test/badge.svg)\n[![codecov](https://codecov.io/gh/davips/ldict/branch/main/graph/badge.svg)](https://codecov.io/gh/davips/ldict)\n<a href=\"https://pypi.org/project/ldict\">\n<img src=\"https://img.shields.io/pypi/v/ldict.svg?label=release&color=blue&style=flat-square\" alt=\"pypi\">\n</a>\n![Python version](https://img.shields.io/badge/python-3.8%20%7C%203.9-blue.svg)\n[![license: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n\n<!--- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5501845.svg)](https://doi.org/10.5281/zenodo.5501845) --->\n[![arXiv](https://img.shields.io/badge/arXiv-2109.06028-b31b1b.svg?style=flat-square)](https://arxiv.org/abs/2109.06028)\n[![API documentation](https://img.shields.io/badge/doc-API%20%28auto%29-a0a0a0.svg)](https://davips.github.io/ldict)\n\n# ldict\n\nA lazy `dict`.\n\n[Latest release](https://pypi.org/project/ldict) |\n[Current code](https://github.com/davips/ldict) |\n[API documentation](https://davips.github.io/ldict)\n\n## See also\n\n* laziness+identity+persistence ([idict](https://pypi.org/project/idict))\n\n## Overview\n\nA `ldict` is a `dict` with `str` keys.\n\n**Simple usage example**\n<details>\n<p>\n\n```python3\nfrom ldict import ldict\n\na = ldict(x=3)\nprint(a)\n\"\"\"\n{\n \"x\": 3\n}\n\"\"\"\n```\n\n```python3\n\nb = ldict(y=5)\nprint(b)\n\"\"\"\n{\n \"y\": 5\n}\n\"\"\"\n```\n\n```python3\n\nprint(a >> b)\n\"\"\"\n{\n \"x\": 3,\n \"y\": 5\n}\n\"\"\"\n```\n\n\n</p>\n</details>\n\nWe consider that every value is generated by a process, starting from an `empty` ldict. The process is a sequence of\ntransformation steps done through the operator `>>`, which symbolizes a data flow. There are two types of steps:\n\n* **value insertion** - represented by dict-like objects\n* **function application** - represented by ordinary python functions\n\nA `ldict` is completely defined by its key-value pairs so that\nit can be converted from/to a built-in dict.\n\nCreating a ldict is not different from creating an ordinary dict. Optionally it can be created through the `>>` operator\nused after `empty`:\n![img.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img.png)\n\nFunction application is done in the same way. The parameter names define the input fields, while the keys in the\nreturned dict define the output fields:\n![img_1.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_1.png)\n\nSimilarly, for anonymous functions:\n![img_5.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_5.png)\n\nFinally, the result is only evaluated at request:\n![img_6.png](https://raw.githubusercontent.com/davips/ldict/main/examples/img_6.png)\n\n\n## Installation\n### ...as a standalone lib\n```bash\n# Set up a virtualenv. \npython3 -m venv venv\nsource venv/bin/activate\n\n# Install from PyPI...\npip install --upgrade pip\npip install -U ldict\npip install -U ldict[full] # use this for extra functionality (recommended)\n\n# ...or, install from updated source code.\npip install git+https://github.com/davips/ldict\n```\n\n### ...from source\n```bash\ngit clone https://github.com/davips/ldict\ncd ldict\npoetry install\n```\n\n## Examples\n**Merging two ldicts**\n<details>\n<p>\n\n```python3\nfrom ldict import ldict\n\na = ldict(x=3)\nprint(a)\n\"\"\"\n{\n \"x\": 3\n}\n\"\"\"\n```\n\n```python3\n\nb = ldict(y=5)\nprint(b)\n\"\"\"\n{\n \"y\": 5\n}\n\"\"\"\n```\n\n```python3\n\nprint(a >> b)\n\"\"\"\n{\n \"x\": 3,\n \"y\": 5\n}\n\"\"\"\n```\n\n\n</p>\n</details>\n\n**Lazily applying functions to ldict**\n<details>\n<p>\n\n```python3\nfrom ldict import ldict\n\na = ldict(x=3)\nprint(a)\n\"\"\"\n{\n \"x\": 3\n}\n\"\"\"\n```\n\n```python3\n\na = a >> ldict(y=5) >> {\"z\": 7} >> (lambda x, y, z: {\"r\": x ** y // z})\nprint(a)\n\"\"\"\n{\n \"x\": 3,\n \"y\": 5,\n \"z\": 7,\n \"r\": \"\u2192(x y z)\"\n}\n\"\"\"\n```\n\n```python3\n\nprint(a.r)\n\"\"\"\n34\n\"\"\"\n```\n\n```python3\n\nprint(a)\n\"\"\"\n{\n \"x\": 3,\n \"y\": 5,\n \"z\": 7,\n \"r\": 34\n}\n\"\"\"\n```\n\n\n</p>\n</details>\n\n**Parameterized functions and sampling**\n<details>\n<p>\n\n```python3\nfrom random import Random\n\nfrom ldict import empty, let\n\n\n# A function provide input fields and, optionally, parameters.\n# For instance:\n# 'a' is sampled from an arithmetic progression\n# 'b' is sampled from a geometric progression\n# Here, the syntax for default parameter values is borrowed with a new meaning.\ndef fun(x, y, a=[-100, -99, -98, ..., 100], b=[0.0001, 0.001, 0.01, ..., 100000000]):\n return {\"z\": a * x + b * y}\n\n\ndef simplefun(x, y):\n return {\"z\": x * y}\n\n\n# Creating an empty ldict. Alternatively: d = ldict().\nd = empty >> {}\nprint(d)\n\"\"\"\n{}\n\"\"\"\n```\n\n```python3\n\n# Putting some values. Alternatively: d = ldict(x=5, y=7).\nd[\"x\"] = 5\nd[\"y\"] = 7\nprint(d)\n\"\"\"\n{\n \"x\": 5,\n \"y\": 7\n}\n\"\"\"\n```\n\n```python3\n\n# Parameter values are uniformly sampled.\nd1 = d >> simplefun\nprint(d1)\nprint(d1.z)\n\"\"\"\n{\n \"x\": 5,\n \"y\": 7,\n \"z\": \"\u2192(x y)\"\n}\n35\n\"\"\"\n```\n\n```python3\n\nd2 = d >> simplefun\nprint(d2)\nprint(d2.z)\n\"\"\"\n{\n \"x\": 5,\n \"y\": 7,\n \"z\": \"\u2192(x y)\"\n}\n35\n\"\"\"\n```\n\n```python3\n\n# Parameter values can also be manually set.\ne = d >> let(fun, a=5, b=10)\nprint(e.z)\n\"\"\"\n95\n\"\"\"\n```\n\n```python3\n\n# Not all parameters need to be set.\ne = d >> Random() >> let(fun, a=5)\nprint(\"e =\", e.z)\n\"\"\"\ne = 25.007\n\"\"\"\n```\n\n```python3\n\n# Each run will be a different sample for the missing parameters.\ne = e >> Random() >> let(fun, a=5)\nprint(\"e =\", e.z)\n\"\"\"\ne = 25.007\n\"\"\"\n```\n\n```python3\n\n# We can define the initial state of the random sampler.\n# It will be in effect from its location place onwards in the expression.\ne = d >> Random(0) >> let(fun, a=5)\nprint(e.z)\n\"\"\"\n725.0\n\"\"\"\n```\n\n```python3\n\n# All runs will yield the same result,\n# if starting from the same random number generator seed.\ne = e >> Random(0) >> let(fun, a=[555, 777])\nprint(\"Let 'a' be a list:\", e.z)\n\"\"\"\nLet 'a' be a list: 700003885.0\n\"\"\"\n```\n\n```python3\n\n# Reproducible different runs are achievable by using a single random number generator.\ne = e >> Random(0) >> let(fun, a=[5, 25, 125, ..., 10000])\nprint(\"Let 'a' be a geometric progression:\", e.z)\n\"\"\"\nLet 'a' be a geometric progression: 700003125.0\n\"\"\"\n```\n\n```python3\nrnd = Random(0)\ne = d >> rnd >> let(fun, a=5)\nprint(e.z)\ne = d >> rnd >> let(fun, a=5) # Alternative syntax.\nprint(e.z)\n\"\"\"\n725.0\n700000025.0\n\"\"\"\n```\n\n```python3\n\n# Output fields can be defined dynamically through parameter values.\n# Input fields can be defined dynamically through kwargs.\ncopy = lambda source=None, target=None, **kwargs: {target: kwargs[source]}\nd = empty >> {\"x\": 5}\nd >>= let(copy, source=\"x\", target=\"y\")\nprint(d)\nd.evaluate()\nprint(d)\n\n\"\"\"\n{\n \"x\": 5,\n \"y\": \"\u2192(source target x)\"\n}\n{\n \"x\": 5,\n \"y\": 5\n}\n\"\"\"\n```\n\n\n</p>\n</details>\n\n**Composition of sets of functions**\n<details>\n<p>\n\n```python3\nfrom random import Random\n\nfrom ldict import empty\n\n\n# A multistep process can be defined without applying its functions\n\n\ndef g(x, y, a=[1, 2, 3, ..., 10], b=[0.00001, 0.0001, 0.001, ..., 100000]):\n return {\"z\": a * x + b * y}\n\n\ndef h(z, c=[1, 2, 3]):\n return {\"z\": c * z}\n\n\n# In the ldict framework 'data is function',\n# so the alias \u00f8 represents the 'empty data object' and the 'reflexive function' at the same time.\n# In other words: 'inserting nothing' has the same effect as 'doing nothing'.\nfun = empty >> g >> h # empty enable the cartesian product of the subsequent sets of functions within the expression.\nprint(fun)\n\"\"\"\n\u00ab\u03bb{} \u00d7 \u03bb\u00bb\n\"\"\"\n```\n\n```python3\n\n# An unnapplied function has its free parameters unsampled.\n# A compostition of functions results in an ordered set (Cartesian product of sets).\n# It is a set because the parameter values of the functions are still undefined.\nd = {\"x\": 5, \"y\": 7} >> (Random(0) >> fun)\nprint(d)\n\"\"\"\n{\n \"x\": 5,\n \"y\": 7,\n \"z\": \"\u2192(c z\u2192(a b x y))\"\n}\n\"\"\"\n```\n\n```python3\n\nprint(d.z)\n\"\"\"\n105.0\n\"\"\"\n```\n\n```python3\n\nd = {\"x\": 5, \"y\": 7} >> (Random(0) >> fun)\nprint(d.z)\n\"\"\"\n105.0\n\"\"\"\n```\n\n```python3\n\n# Reproducible different runs by passing a stateful random number generator.\nrnd = Random(0)\ne = d >> rnd >> fun\nprint(e.z)\n\"\"\"\n105.0\n\"\"\"\n```\n\n```python3\n\ne = d >> rnd >> fun\nprint(e.z)\n\"\"\"\n14050.0\n\"\"\"\n```\n\n```python3\n\n# Repeating the same results.\nrnd = Random(0)\ne = d >> rnd >> fun\nprint(e.z)\n\"\"\"\n105.0\n\"\"\"\n```\n\n```python3\n\ne = d >> rnd >> fun\nprint(e.z)\n\"\"\"\n14050.0\n\"\"\"\n```\n\n\n</p>\n</details>\n\n<!--- ## Persistence\nExtra dependencies can be installed to support saving data to disk or to a server in the network. \n\n**[still an ongoing work...]**\n\n`poetry install -E full`\n--->\n\n## Concept\n\nA ldict is like a common Python dict, with extra functionality and lazy. It is a mapping between string keys, called\nfields, and any serializable (pickable protocol=5) object.\n\n## Grants\nThis work was partially supported by Fapesp under supervision of\nProf. Andr\u00e9 C. P. L. F. de Carvalho at CEPID-CeMEAI (Grants 2013/07375-0 \u2013 2019/01735-0).\n",
"bugtrack_url": null,
"license": "GPL",
"summary": "Lazy dict",
"version": "3.220128.3",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "b338d6bfbfe04847de258d095b673949",
"sha256": "a08a1ab573991090720f721950f6313d4958e4209acdaf20230c79fd3b6097bf"
},
"downloads": -1,
"filename": "ldict-3.220128.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b338d6bfbfe04847de258d095b673949",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 29299,
"upload_time": "2022-12-15T13:23:28",
"upload_time_iso_8601": "2022-12-15T13:23:28.956398Z",
"url": "https://files.pythonhosted.org/packages/27/32/66382169df67baa2a813767f5aed3dd03bf4925e19e27d2980177e31d05f/ldict-3.220128.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "62387e8510a3fd23ca4d3d27a5c40dc3",
"sha256": "1e2e3b3d6884c6617496b7202b46dbd2cbda25889c1d555e61709806a939cf7f"
},
"downloads": -1,
"filename": "ldict-3.220128.3.tar.gz",
"has_sig": false,
"md5_digest": "62387e8510a3fd23ca4d3d27a5c40dc3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 20786,
"upload_time": "2022-12-15T13:23:31",
"upload_time_iso_8601": "2022-12-15T13:23:31.218197Z",
"url": "https://files.pythonhosted.org/packages/12/47/b2ab81a45cd41bf534bd6d0a6068a1c6b938db49554197ebcbe381baf777/ldict-3.220128.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-15 13:23:31",
"github": false,
"gitlab": false,
"bitbucket": false,
"lcname": "ldict"
}