{ "info": { "author": "Michael-Keith Bernard", "author_email": "mkbernard.dev@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Topic :: Software Development :: Libraries" ], "description": "# graffiti - a library for declarative computation\n\n## Installation\n\n`pip install graffiti`\n\n*or*\n\n1. `git clone https://github.com/SegFaultAX/graffiti`\n1. `cd graffiti/`\n1. To install: `python setup.py install`\n1. To get development dependencies: `pip install -r requirements.txt`\n1. To run tests: `nosetests`\n1. To audit source: `python setup.py audit`\n\nCheck out my [blog post](http://mkbernard.com/blog/2014/06/graffiti-a-python-library-for-declarative-computation/)\nfor more background on the \"why\" of this project. Get in touch if you have any\ncomments or questions!\n\nInspired by Prismatic's Graph library (https://github.com/prismatic/plumbing)\n\n## Overview\n\n```python\nfrom graffiti import Graph\n\nstats_descriptor = {\n \"n\": lambda xs: len(xs),\n \"m\": lambda xs, n: sum(xs) / n,\n \"m2\": lambda xs, n: sum(x ** 2 for x in xs) / n,\n \"v\": lambda m, m2: m2 - m ** 2\n}\n\ngraph = Graph(stats_descriptor)\ngraph({ \"xs\": [1, 2, 3, 4, 5] })\n#=> {'xs': [1, 2, 3, 4, 5], 'n': 5, 'm': 3, 'm2': 11, 'v': 2}\n\n# if pydot is installed\ngraph.visualize()\n```\n\n## Tutorial\n\nConsider the following perfectly reasonable Python code:\n\n```python\ndef stats(xs):\n n = len(xs)\n m = sum(xs) / n\n m2 = sum(x ** 2 for x in xs) / n\n v = m2 - m ** 2\n\n return {\n \"xs\": xs,\n \"n\": n,\n \"m\": m,\n \"m2\": m2,\n \"v\": v,\n }\n```\n\nThe first computation is based on our input (xs), and each additional\ncomputation builds up more values based on things we've computed already. It's\nconvenient to visualize this computation as a graph with the user inputs at the\nroot and values we're interested in at the leaves.\n\ngraffiti allows you to structure your computation in exactly that fashion:\n\n```python\nstats_graph = {\n \"n\": lambda xs: len(xs),\n \"m\": lambda xs, n: sum(xs) / n,\n \"m2\": lambda xs, n: sum(x ** 2 for x in xs) / n,\n \"v\": lambda m, m2: m2 - m ** 2\n}\ngraph = Graph(stats_graph)\ngraph({ \"xs\": range(100) })\n```\n\ngraffiti finds the relationships between the nodes in your graph and determines\nthe optimal execution order. Leaves in the graph are computed lazily which means\nwe can choose to only evaluate the ones we're interested in:\n\n```python\ngraph({ \"xs\": range(100) }, _keys={\"m\", \"n\"})\n```\n\nIn this case, graffiti will only evaluate what's needed to compute m and n, but\nnot the rest of the graph. You can also build nested graphs with dependencies\nacross nesting levels:\n\n```python\nstats_graph = {\n \"n\": lambda xs: len(xs),\n \"m\": lambda xs, n: sum(xs) / n,\n \"m2\": lambda xs, n: sum(x ** 2 for x in xs) / n,\n \"v\": lambda m, m2: m2 - m ** 2,\n \"order\": {\n \"sorted\": lambda xs: sorted(xs),\n \"reversed\": lambda xs: sorted(xs, reverse=True)\n },\n}\n\ngraph = Graph(stats_graph)\ngraph({ \"xs\": range(100) })\ngraph({ \"xs\": range(10) }, _keys={\"order\"})\n```\n\nAgain, none of the unecessary nodes will be computed. Nodes in the graph can\nalso contain optional arguments:\n\n```python\ngraph = Graph({ \"mul\": lambda n, p=10: n * p })\ngraph({ \"n\": 10 }) # mul == 100\ngraph({ \"n\": 10, \"p\": 20 }) # mul == 200\n```\n\nIf the optional key is provided as an input to the graph evaluator, it will\noverride the default value of the node. This makes it easy to play with\ndifferent values or settings as the computation flows through your pipeline.\n\nFinally, graphs are resumable. Since a graph object takes a dict as input and\nreturns a dict as output, you can replay a previously generated dict as the\ninput to the next thereby reusing all previously computed values:\n\n```python\nstats_graph = {\n \"n\": lambda xs: len(xs),\n \"m\": lambda xs, n: sum(xs) / n,\n \"m2\": lambda xs, n: sum(x ** 2 for x in xs) / n,\n \"v\": lambda m, m2: m2 - m ** 2,\n}\n\ngraph = Graph(stats_graph)\nv1 = graph({ \"xs\": [1,2,3] }, _keys={\"n\"}) # just n\nv2 = graph(v1, _keys={\"m\"}) # v1 + just m\nv3 = graph(v2) # the rest of the graph\n```\n\nThis allows you to defer the evaluation of expensive keys until the moment\nthey're actually needed without duplicating previous computations.\n\ngraffiti also supports drawing the transitive graph of dependencies:\n\n```python\nstats_graph = {\n \"n\": lambda xs: len(xs),\n \"m\": lambda xs, n: sum(xs) / n,\n \"m2\": lambda xs, n: sum(x ** 2 for x in xs) / n,\n \"v\": lambda m, m2: m2 - m ** 2,\n \"order\": {\n \"sorted\": lambda xs: sorted(xs),\n \"reversed\": lambda xs: sorted(xs, reverse=True)\n },\n}\ngraph = Graph(stats_graph)\ngraph.visualize()\n```\n\n![](https://raw.githubusercontent.com/SegFaultAX/graffiti/master/example_graph.png)\n\nUsing graffiti allows you to structure your computation and the complex\ninterdependencies therein naturally and efficiently. Most importantly, your\ncomputational pipeline is just data. That means it's easy to inspect, easy to\nreason about, and easy to build tooling around.\n\nThis project is still under active development. Contact me on Twitter\n@SegFaultAX if you have any questions, comments, or bug reports.\n\n## License\n\nCopyright 2014 Michael-Keith Bernard\n\nAvailabe under the MIT License. See LICENSE for full details.\n", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/SegFaultAX/graffiti", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "graffiti", "package_url": "https://pypi.org/project/graffiti/", "platform": "any", "project_url": "https://pypi.org/project/graffiti/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/SegFaultAX/graffiti" }, "release_url": "https://pypi.org/project/graffiti/0.1.2/", "requires_dist": null, "requires_python": null, "summary": "A library for declarative computation.", "version": "0.1.2" }, "last_serial": 1119951, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "b65aee2f828b20436621b363271ced17", "sha256": "ae0a2dc317083051c2a70eacf21ed62dc6e3108951bfe3da3416175725d5a047" }, "downloads": -1, "filename": "graffiti-0.1.0.tar.gz", "has_sig": false, "md5_digest": "b65aee2f828b20436621b363271ced17", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24834, "upload_time": "2014-06-07T21:39:54", "url": "https://files.pythonhosted.org/packages/44/8d/7342fe807d37571cd429ce2a6ee8987c9117bd125cb0dd9999ddfcc2ac4a/graffiti-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "9929cc3ebfd9f1023fb41f07d6022ed0", "sha256": "c1d9b1a5a336e9ef281cf0f2a666028d6ecb9f045b257e64393d82f8b3aec5cb" }, "downloads": -1, "filename": "graffiti-0.1.1.tar.gz", "has_sig": false, "md5_digest": "9929cc3ebfd9f1023fb41f07d6022ed0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25046, "upload_time": "2014-06-08T00:44:50", "url": "https://files.pythonhosted.org/packages/b3/7d/c12355503de20590759443c52966bf5bbce16fc7d26328f235271c2c1bfb/graffiti-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "9836389443cf088861d5039ff3332964", "sha256": "39b7abc33508abff5d82e9f154104e842e7319ec8d5d46b1418b29393a579604" }, "downloads": -1, "filename": "graffiti-0.1.2.tar.gz", "has_sig": false, "md5_digest": "9836389443cf088861d5039ff3332964", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9558, "upload_time": "2014-06-10T02:58:00", "url": "https://files.pythonhosted.org/packages/49/c5/c35f3ae1515e04eae7ea7c02a8be33c50adc2db1f2cc108aa5151b8ffab2/graffiti-0.1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9836389443cf088861d5039ff3332964", "sha256": "39b7abc33508abff5d82e9f154104e842e7319ec8d5d46b1418b29393a579604" }, "downloads": -1, "filename": "graffiti-0.1.2.tar.gz", "has_sig": false, "md5_digest": "9836389443cf088861d5039ff3332964", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9558, "upload_time": "2014-06-10T02:58:00", "url": "https://files.pythonhosted.org/packages/49/c5/c35f3ae1515e04eae7ea7c02a8be33c50adc2db1f2cc108aa5151b8ffab2/graffiti-0.1.2.tar.gz" } ] }