Skip to main content
https://circleci.com/gh/ymoch/reltools.svg?style=svg https://codecov.io/gh/ymoch/reltools/branch/master/graph/badge.svg https://badge.fury.io/py/reltools.svg https://img.shields.io/badge/python-3.6+-blue.svg https://img.shields.io/lgtm/grade/python/g/ymoch/reltools.svg

Relation tools for Python. This relates two data (sorted by certain keys) like SQL joining.

Inspired by itertools.groupby, as long as input data are sorted, almost all processes are evaluated lazily, which results in the reduction of memory usage. This feature is for the big data joining without any SQL engines.

Installation

Install with pip.

pip install reltools

Features

One-To-Many

One-to-many relationing is provided by relate_one_to_many.

Here, input left-hand-side (lhs) and right-hand-side (rhs) are sorted in 1st (and also 2nd) keys.

>>> lhs = [
...     (1, 'a', 's'),
...     (2, 'a', 't'),
...     (3, 'b', 'u'),
... ]
>>> rhs = [
...     (1, 'a', 'v'),
...     (1, 'b', 'w'),
...     (3, 'b', 'x'),
... ]

relate_one_to_many relates rhs items to each lhs item using the first items as keys.

>>> from reltools import relate_one_to_many
>>> one_to_many_related = relate_one_to_many(lhs, rhs)
>>> for left, right in one_to_many_related:
...     left, list(right)
((1, 'a', 's'), [(1, 'a', 'v'), (1, 'b', 'w')])
((2, 'a', 't'), [])
((3, 'b', 'u'), [(3, 'b', 'x')])

You can use custom key functions for not only relate_one_to_many but also API functions.

>>> import operator
>>> custom_key = operator.itemgetter(0, 1)
>>> one_to_many_related = relate_one_to_many(
...     lhs, rhs, lhs_key=custom_key, rhs_key=custom_key)
>>> for left, right in one_to_many_related:
...     left, list(right)
((1, 'a', 's'), [(1, 'a', 'v')])
((2, 'a', 't'), [])
((3, 'b', 'u'), [(3, 'b', 'x')])

OneToManyChainer helps to relate many rhs iterables to lhs.

>>> another_rhs = [
...     ('s', 'f'),
...     ('t', 'g'),
...     ('t', 'h'),
... ]
>>> from reltools import OneToManyChainer
>>> chainer = OneToManyChainer(lhs)
>>> chainer.append(rhs)
>>> chainer.append(
...     another_rhs,
...     lhs_key=operator.itemgetter(2),
...     rhs_key=operator.itemgetter(0),
... )
>>> for left, right, another_right in chainer.chain():
...     left, list(right), list(another_right)
((1, 'a', 's'), [(1, 'a', 'v'), (1, 'b', 'w')], [('s', 'f')])
((2, 'a', 't'), [], [('t', 'g'), ('t', 'h')])
((3, 'b', 'u'), [(3, 'b', 'x')], [])

Left Outer Join

Left outer joining is provided by left_join. While SQL left outer joining returns all the combinations, this returns the pair of items. Note that the right can empty, like SQL left joining.

>>> from reltools import left_join
>>> lhs = [(1, 'a'), (1, 'b'), (2, 'c'), (4, 'd')]
>>> rhs = [(1, 's'), (1, 't'), (3, 'u'), (4, 'v')]
>>> relations = left_join(lhs, rhs)
>>> for left, right in relations:
...     list(left), list(right)
([(1, 'a'), (1, 'b')], [(1, 's'), (1, 't')])
([(2, 'c')], [])
([(4, 'd')], [(4, 'v')])

Right Outer Join

Right outer joining is not supported because it is left-and-right-opposite of left joining. Use left_join(rhs, lhs, rhs_key, lhs_key).

Full Outer Join

Full outer joining, which is an original feature of reltools, is provided by outer_join. In contrast to left_join, full outer joining preserve keys that are only in rhs.

>>> from reltools import outer_join
>>> lhs = [(1, 'a'), (1, 'b'), (2, 'c'), (4, 'd')]
>>> rhs = [(1, 's'), (1, 't'), (3, 'u'), (4, 'v')]
>>> relations = outer_join(lhs, rhs)
>>> for left, right in relations:
...     list(left), list(right)
([(1, 'a'), (1, 'b')], [(1, 's'), (1, 't')])
([(2, 'c')], [])
([], [(3, 'u')])
([(4, 'd')], [(4, 'v')])

Inner Join

Inner joining is provided by inner_join. In contrast to left_join, right cannot be empty, like SQL inner joining.

>>> from reltools import inner_join
>>> relations = inner_join(lhs, rhs)
>>> for left, right in relations:
...     list(left), list(right)
([(1, 'a'), (1, 'b')], [(1, 's'), (1, 't')])
([(4, 'd')], [(4, 'v')])

Many-To-Many

SQL-like many-to-many relationing using an internal table is not supported. This is because reltools supports only sorted data and does not prefer random accessing. To achieve many-to-many relationing, unnormalize data on preproceing and use outer joining or inner joining.

Memory Efficiency

Almost all processes are evaluated lazily, which results in the reduction of memory usage. (You can try the efficiency by commands like RELTOOLS_TRY_COUNT=10000000 python3 -m doctest README.rst)

>>> import os
>>> n = int(os.environ.get('RELTOOLS_TRY_COUNT', 1000))
>>> lhs = ((i, 'left') for i in range(n))
>>> rhs = ((i, 'right') for i in range(n))
>>> for left, right in relate_one_to_many(lhs, rhs):
...     assert len(list(right)) == 1

Development

This project’s structure is based on Poetry. All tests are written with doctest and run with pytest.

poetry install
poetry run pytest

For stability, following checks are also run when testing.

License

https://img.shields.io/badge/License-MIT-brightgreen.svg

Copyright (c) 2018 Yu MOCHIZUKI

Metadata

Release files for reltools 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for reltools 1.0.0
File Size Uploaded Provenance Actions
reltools-1.0.0.tar.gz 8.3 kB Without provenance Details for reltools-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for reltools 1.0.0
File Interpreter ABI Platform Actions
reltools-1.0.0-py3-none-any.whl Python 3 none any Details for reltools-1.0.0-py3-none-any.whl

Total release size: 15.7 kB

Release files / reltools-1.0.0.tar.gz

Download URL reltools-1.0.0.tar.gz
Size 8.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b457a6fc8a99dd05313e389f556115b1e0049b11ca8a43a1d2eff731349a9476
BLAKE2b-256 checksum
How to use checksums
a6fc175132cf80417d5eb6a2a703548842ce15dd0bf470a98d724afe59eefc35
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.4 CPython/3.6.12 Linux/4.15.0-1077-aws

Release files / reltools-1.0.0-py3-none-any.whl

Download URL reltools-1.0.0-py3-none-any.whl
Size 7.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8783c007c896cf8b8fe97367a78d9deb3dec4d142ce62470c066363945e58ff2
BLAKE2b-256 checksum
How to use checksums
afbb25724cc6e94e0acc66ac6a39f97af6663c4ead9e1871eca49049e3b03dad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.4 CPython/3.6.12 Linux/4.15.0-1077-aws

Security for reltools 1.0.0

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page