ReAlEd: Relational Algebra for Education
A simple wrapper around pandas to demonstrate common relational algebra operations:
- Selection (
select) - Projection (
project) - Renaming (
rename) - Extending (
extend) - Sorting (
sort) - Grouping (
groupby) - Cartesian product (
product) - Concatenation (
union) - Natural join (
njoin) - Equijoin (
ejoin) - Semijoin (
sjoin) - Antijoin (
ajoin) - Outer join (
ojoin)
This implementation is a thin layer above the underlying DataFrame with some error handling and data cleaning added to make the resulting operations match those of the relational algebra.
Each operation returns a new relation without modifying the original. Operations can be chained in various combinations.
It is a rewrite of Brad Miller's reframe with the following major differences:
- expanded list of operations (various joins)
- the more recent version of
pandasas a dependency - composition used instead of inheritance
Initialization
A new relation can be created from a CSV file, a DataFrame, or another Relation.
Some arguments can be passed to the constructor in order to override defaults used by pandas:
penguins = Relation("palmerpenguins.csv")
penguins = Relation("palmerpenguins.csv", sep=",")
Test data includes Palmer penguins and information about a couple Palmer islands to demonstrate multi-relation operations.
>>> from realed import Relation
>>> penguins = Relation("palmerpenguins.csv")
>>> penguins
rowid species island bill_length_mm ... flipper_length_mm body_mass_g sex year
0 1 Adelie Torgersen 39.1 ... 181.0 3750.0 male 2007
1 2 Adelie Torgersen 39.5 ... 186.0 3800.0 female 2007
2 3 Adelie Torgersen 40.3 ... 195.0 3250.0 female 2007
3 4 Adelie Torgersen NaN ... NaN NaN NaN 2007
4 5 Adelie Torgersen 36.7 ... 193.0 3450.0 female 2007
.. ... ... ... ... ... ... ... ... ...
339 340 Chinstrap Dream 55.8 ... 207.0 4000.0 male 2009
340 341 Chinstrap Dream 43.5 ... 202.0 3400.0 female 2009
341 342 Chinstrap Dream 49.6 ... 193.0 3775.0 male 2009
342 343 Chinstrap Dream 50.8 ... 210.0 4100.0 male 2009
343 344 Chinstrap Dream 50.2 ... 198.0 3775.0 female 2009
>>> islands = Relation("palmerislands.csv")
>>> islands
id island explored expedition name_origin
0 1 Amsler 1955 Falkland Islands Dependencies Survey Margaret Amsler
1 2 Anvers 1832 Southern Ocean Expedition Antwerp province
2 3 Biscoe 1832 Southern Ocean Expedition John Biscoe
3 4 Christiania 1898 Belgian Antarctic Expedition Oslo
4 5 Dream 1956 British Naval Hydrographic Survey Unit Natural features
5 6 Torgersen 1955 Falkland Islands Dependencies Survey Torstein Torgersen
>>>
Certain helper methods and properties are available:
- property
data: internalDataFrame - property
columns: names of the attributes - property
dim: dimensions of the relation (rows, columns) - property
one: the first record of the relation as a dictionary - method
head: return the first couple of records - method
tail: return the last couple of records - method
to_csv: export thedatato a csv file - method
copy: deep copy of thedata - method
cleanup: removal of empty records - method
equals: comparison of two relations - method
minus: difference between two relations - method
intersection: intersection of two relations
Selection (select)
Selection is a simplified version of the query method available in pandas.DataFrame.
It only takes one argument, the query expression.
>>> islands.select("expedition == 'Southern Ocean Expedition'")
id island explored expedition name_origin
1 2 Anvers 1832 Southern Ocean Expedition Antwerp province
2 3 Biscoe 1832 Southern Ocean Expedition John Biscoe
>>>
>>> penguins.select("species == 'Adelie' and island == 'Torgersen' and year == 2009 and body_mass_g >= 4000")
rowid species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
125 126 Adelie Torgersen 40.6 19.0 199.0 4000.0 male 2009
127 128 Adelie Torgersen 41.5 18.3 195.0 4300.0 male 2009
129 130 Adelie Torgersen 44.1 18.0 210.0 4000.0 male 2009
Since each operation returns a Relation, operations can be chained:
>>> penguins.select("species == 'Adelie'").select("island == 'Torgersen'").select("year == 2009").select("body_mass_g >= 4000")
rowid species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
125 126 Adelie Torgersen 40.6 19.0 199.0 4000.0 male 2009
127 128 Adelie Torgersen 41.5 18.3 195.0 4300.0 male 2009
129 130 Adelie Torgersen 44.1 18.0 210.0 4000.0 male 2009
A separate method,query, is also available.
It passes all provided named arguments and keyword arguments to the query method of the pandas.DataFrame.
Projection (project)
For the sake of consistency names of attributes must be a list of strings, even if a single attribute is being projected.
>>> penguins.project(["island"])
island
0 Torgersen
20 Biscoe
30 Dream
>>> penguins.project(["species", "island"])
species island
0 Adelie Torgersen
20 Adelie Biscoe
30 Adelie Dream
152 Gentoo Biscoe
276 Chinstrap Dream
>>>
Renaming (rename)
If the old name is not among the attributes or the new name is already there, a ValueError is raised.
>>> penguins.rename("island", "location").project(["species", "location"])
species location
0 Adelie Torgersen
20 Adelie Biscoe
30 Adelie Dream
152 Gentoo Biscoe
276 Chinstrap Dream
>>>
Sorting (sort)
>>> islands.sort(["explored"])
id island explored expedition name_origin
1 2 Anvers 1832 Southern Ocean Expedition Antwerp province
2 3 Biscoe 1832 Southern Ocean Expedition John Biscoe
3 4 Christiania 1898 Belgian Antarctic Expedition Oslo
0 1 Amsler 1955 Falkland Islands Dependencies Survey Margaret Amsler
5 6 Torgersen 1955 Falkland Islands Dependencies Survey Torstein Torgersen
4 5 Dream 1956 British Naval Hydrographic Survey Unit Natural features
>>> islands.sort(["explored"], ascending=False)
id island explored expedition name_origin
4 5 Dream 1956 British Naval Hydrographic Survey Unit Natural features
0 1 Amsler 1955 Falkland Islands Dependencies Survey Margaret Amsler
5 6 Torgersen 1955 Falkland Islands Dependencies Survey Torstein Torgersen
3 4 Christiania 1898 Belgian Antarctic Expedition Oslo
1 2 Anvers 1832 Southern Ocean Expedition Antwerp province
2 3 Biscoe 1832 Southern Ocean Expedition John Biscoe
>>> penguins.sort(["species", "rowid"]).head(3)
rowid species island bill_length_mm ... flipper_length_mm body_mass_g sex year
0 1 Adelie Torgersen 39.1 ... 181.0 3750.0 male 2007
1 2 Adelie Torgersen 39.5 ... 186.0 3800.0 female 2007
2 3 Adelie Torgersen 40.3 ... 195.0 3250.0 female 2007
Be mindful of the order of sorting operations.
>>> penguins.sort(["body_mass_g"], ascending=False).sort(["species"]).head(3)
rowid species island bill_length_mm ... flipper_length_mm body_mass_g sex year
111 112 Adelie Biscoe 45.6 ... 191.0 4600.0 male 2009
76 77 Adelie Torgersen 40.9 ... 191.0 3700.0 female 2008
14 15 Adelie Torgersen 34.6 ... 198.0 4400.0 male 2007
>>> penguins.sort(["species"]).sort(["body_mass_g"], ascending=False).head(3)
rowid species island bill_length_mm ... flipper_length_mm body_mass_g sex year
169 170 Gentoo Biscoe 49.2 ... 221.0 6300.0 male 2007
185 186 Gentoo Biscoe 59.6 ... 230.0 6050.0 male 2007
269 270 Gentoo Biscoe 48.8 ... 222.0 6000.0 male 2009
Extending (extend)
The new name cannot match the name of an existing attribute.
New data must be a of type pandas.Series.
>>> penguins.extend("years_since_study", 2026-penguins["year"])
rowid species island bill_length_mm ... body_mass_g sex year years_since_study
0 1 Adelie Torgersen 39.1 ... 3750.0 male 2007 19
1 2 Adelie Torgersen 39.5 ... 3800.0 female 2007 19
2 3 Adelie Torgersen 40.3 ... 3250.0 female 2007 19
3 4 Adelie Torgersen NaN ... NaN NaN 2007 19
4 5 Adelie Torgersen 36.7 ... 3450.0 female 2007 19
.. ... ... ... ... ... ... ... ... ...
339 340 Chinstrap Dream 55.8 ... 4000.0 male 2009 17
340 341 Chinstrap Dream 43.5 ... 3400.0 female 2009 17
341 342 Chinstrap Dream 49.6 ... 3775.0 male 2009 17
342 343 Chinstrap Dream 50.8 ... 4100.0 male 2009 17
343 344 Chinstrap Dream 50.2 ... 3775.0 female 2009 17
>>>
Grouping (groupby) and aggregation
Grouping without an aggregate function eliminates duplicates in the chosen categories.
>>> penguins.groupby(["species"])
species
0 Adelie
1 Chinstrap
2 Gentoo
Typically, grouping is followed by an aggregate function count, sum, min, max, mean (avg), or median.
The aggregate function is added as a prefix to the name of the resulting column.
>>> penguins.groupby(["species"]).count("rowid")
species count_rowid
0 Adelie 152
1 Chinstrap 68
2 Gentoo 124
>>> penguins.groupby(["island"]).count("rowid")
island count_rowid
0 Biscoe 168
1 Dream 124
2 Torgersen 52
>>> penguins.groupby(["species", "island"]).count("rowid")
species island count_rowid
0 Adelie Biscoe 44
1 Adelie Dream 56
2 Adelie Torgersen 52
3 Chinstrap Dream 68
4 Gentoo Biscoe 124
>>> penguins.groupby(["island"]).sum("body_mass_g")
island sum_body_mass_g
0 Biscoe 787575.0
1 Dream 460400.0
2 Torgersen 189025.0
>>> penguins.groupby(["island"]).min("body_mass_g")
island min_body_mass_g
0 Biscoe 2850.0
1 Dream 2700.0
2 Torgersen 2900.0
>>> penguins.groupby(["island"]).max("body_mass_g")
island max_body_mass_g
0 Biscoe 6300.0
1 Dream 4800.0
2 Torgersen 4700.0
>>> penguins.groupby(["island"]).mean("body_mass_g")
island mean_body_mass_g
0 Biscoe 4716.017964
1 Dream 3712.903226
2 Torgersen 3706.372549
>>> penguins.groupby(["island"]).avg("body_mass_g")
island avg_body_mass_g
0 Biscoe 4716.017964
1 Dream 3712.903226
2 Torgersen 3706.372549
>>> penguins.groupby(["island"]).median("body_mass_g")
island median_body_mass_g
0 Biscoe 4775.0
1 Dream 3687.5
2 Torgersen 3700.0
Cartesian product (product)
Two relations must have disjoint attributes for this operation to work.
>>> penguins.product(islands.rename("island", "name"))
rowid species ... expedition name_origin
0 1 Adelie ... Falkland Islands Dependencies Survey Margaret Amsler
1 1 Adelie ... Southern Ocean Expedition Antwerp province
2 1 Adelie ... Southern Ocean Expedition John Biscoe
3 1 Adelie ... Belgian Antarctic Expedition Oslo
4 1 Adelie ... British Naval Hydrographic Survey Unit Natural features
... ... ... ... ... ...
2059 344 Chinstrap ... Southern Ocean Expedition Antwerp province
2060 344 Chinstrap ... Southern Ocean Expedition John Biscoe
2061 344 Chinstrap ... Belgian Antarctic Expedition Oslo
2062 344 Chinstrap ... British Naval Hydrographic Survey Unit Natural features
2063 344 Chinstrap ... Falkland Islands Dependencies Survey Torstein Torgersen
Concatenation (union)
Attributes of both relations must be the same.
>>> penguins.select("species == 'Adelie'").union(penguins.select("species == 'Gentoo'"))
rowid species island bill_length_mm ... flipper_length_mm body_mass_g sex year
0 1 Adelie Torgersen 39.1 ... 181.0 3750.0 male 2007
1 2 Adelie Torgersen 39.5 ... 186.0 3800.0 female 2007
2 3 Adelie Torgersen 40.3 ... 195.0 3250.0 female 2007
3 4 Adelie Torgersen NaN ... NaN NaN NaN 2007
4 5 Adelie Torgersen 36.7 ... 193.0 3450.0 female 2007
.. ... ... ... ... ... ... ... ... ...
271 272 Gentoo Biscoe NaN ... NaN NaN NaN 2009
272 273 Gentoo Biscoe 46.8 ... 215.0 4850.0 female 2009
273 274 Gentoo Biscoe 50.4 ... 222.0 5750.0 male 2009
274 275 Gentoo Biscoe 45.2 ... 212.0 5200.0 female 2009
275 276 Gentoo Biscoe 49.9 ... 213.0 5400.0 male 2009
Natural join (naturaljoin or njoin)
There must be at least one common attribute in both relations.
>>> penguins.njoin(islands)
rowid species island ... explored expedition name_origin
0 1 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
1 2 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
2 3 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
3 4 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
4 5 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
.. ... ... ... ... ... ... ...
339 340 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
340 341 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
341 342 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
342 343 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
343 344 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
Equijoin (equijoin or ejoin)
A simple version of the theta-join that checks two attributes for equality. Both left and right attributes must be strings.
>>> penguins.ejoin(islands, "island", "island")
rowid species island ... explored expedition name_origin
0 1 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
1 2 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
2 3 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
3 4 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
4 5 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
.. ... ... ... ... ... ... ...
339 340 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
340 341 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
341 342 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
342 343 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
343 344 Chinstrap Dream ... 1956 British Naval Hydrographic Survey Unit Natural features
Semijoin (semijoin or sjoin)
Both left and right attributes must be strings.
>>> islands.sjoin(penguins, "island", "island")
id island explored expedition name_origin
2 3 Biscoe 1832 Southern Ocean Expedition John Biscoe
4 5 Dream 1956 British Naval Hydrographic Survey Unit Natural features
5 6 Torgersen 1955 Falkland Islands Dependencies Survey Torstein Torgersen
Antijoin (antijoin or ajoin)
Both left and right attributes must be strings.
>>> islands.ajoin(penguins, "island", "island")
id island explored expedition name_origin
0 1 Amsler 1955 Falkland Islands Dependencies Survey Margaret Amsler
1 2 Anvers 1832 Southern Ocean Expedition Antwerp province
3 4 Christiania 1898 Belgian Antarctic Expedition Oslo
Outer join (outerjoin or ojoin)
Full outer join.
>>> islands.ojoin(penguins)
id island explored ... body_mass_g sex year
0 1 Amsler 1955 ... NaN NaN NaN
1 2 Anvers 1832 ... NaN NaN NaN
2 3 Biscoe 1832 ... 3400.0 female 2007.0
3 3 Biscoe 1832 ... 3600.0 male 2007.0
4 3 Biscoe 1832 ... 3800.0 female 2007.0
.. .. ... ... ... ... ... ...
342 6 Torgersen 1955 ... 4300.0 male 2009.0
343 6 Torgersen 1955 ... 3050.0 female 2009.0
344 6 Torgersen 1955 ... 4000.0 male 2009.0
345 6 Torgersen 1955 ... 3325.0 female 2009.0
346 6 Torgersen 1955 ... 3500.0 male 2009.0
>>> penguins.ojoin(islands)
rowid species island ... explored expedition name_origin
0 NaN NaN Amsler ... 1955 Falkland Islands Dependencies Survey Margaret Amsler
1 NaN NaN Anvers ... 1832 Southern Ocean Expedition Antwerp province
2 21.0 Adelie Biscoe ... 1832 Southern Ocean Expedition John Biscoe
3 22.0 Adelie Biscoe ... 1832 Southern Ocean Expedition John Biscoe
4 23.0 Adelie Biscoe ... 1832 Southern Ocean Expedition John Biscoe
.. ... ... ... ... ... ... ...
342 128.0 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
343 129.0 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
344 130.0 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
345 131.0 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
346 132.0 Adelie Torgersen ... 1955 Falkland Islands Dependencies Survey Torstein Torgersen
Metadata
Release files for realed 2026.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| realed-2026.10.tar.gz | 8.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| realed-2026.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.2 kB
Release files / realed-2026.10.tar.gz
| Download URL | realed-2026.10.tar.gz |
|---|---|
| Size | 8.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c4f8eb0b98ca71853fbbd0ff95c3ba7c18212b17b2c8eb9831d46c289647d64
|
|
BLAKE2b-256 checksum How to use checksums |
983c447c392c6029e5a79e4588832b09986df288a02a762a057ce3568500f052
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / realed-2026.10-py3-none-any.whl
| Download URL | realed-2026.10-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
887d05bc7e22b1d1330764b40661c80ee3d11814cc6e1fd28fe224fc9c9083ae
|
|
BLAKE2b-256 checksum How to use checksums |
014080ccf694bafbc580bae9496267a384d2f2167d60f246a32050006fea8ca2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for realed 2026.10
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.