{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "The following tutorial gives a quick overview of both data algebra and the algebraixlib Python package." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Data in an algebraixlib program is represented as `MathObject`s. `MathObject`s come in four types: `Atom`, `Couplet`, `Set`, and `Multiset`. `Multiset`s will not be covered in this tutorial. Values that aren't themselves modeled by Data Algebra, such as strings and numbers, are represented by `Atom`s." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from algebraixlib.mathobjects import Atom\n", "peanut_butter = Atom(\"peanut butter\")\n", "jelly = Atom(\"jelly\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every `MathObject` can be pretty-printed to the console using `print()`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'peanut butter'\n" ] } ], "source": [ "print(peanut_butter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The non-`MathObject` value of the `Atom` can be accessed by its `value` property." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 + 2 = 3\n", "Error: unsupported operand type(s) for +: 'Atom' and 'Atom'\n" ] } ], "source": [ "try:\n", " one = Atom(1)\n", " two = Atom(2)\n", " print(\"1 + 2 = {}\".format(one.value + two.value))\n", " print(\"Will throw\", one + two)\n", "except TypeError as e:\n", " print(\"Error:\", e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Couplet`s relate two pieces of information together. Those pieces of information must be represented as `MathObject`s; our two `Atom`s from earlier qualify." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$together = \\mbox{'peanut butter'}{\\mapsto}{\\mbox{'jelly'}}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$nested = \\left(\\mbox{'peanut butter'}{\\mapsto}{\\mbox{'jelly'}}\\right){\\mapsto}{\\left(\\mbox{'peanut butter'}{\\mapsto}{\\mbox{'jelly'}}\\right)}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from algebraixlib.mathobjects import Couplet\n", "from algebraixlib.util.latexprinter import iprint_latex\n", "from IPython.display import Math, display\n", "\n", "import algebraixlib.util.latexprinter\n", "algebraixlib.util.latexprinter.Config.colorize_output = False\n", "\n", "together = Couplet(peanut_butter, jelly)\n", "iprint_latex(\"together\")\n", "iprint_latex(\"nested\", Couplet(together, together))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`MathObject` initializers will coerce their arguments to be `Atom`s if non-`MathObject`s are passed." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Couplet(left=Atom('this'), right=Atom('that'))\n" ] } ], "source": [ "coerced = Couplet(\"this\", \"that\")\n", "print(repr(coerced))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The components of a `Couplet` are known as its `left` and `right`. Sometimes initializng a `Couplet` with named arguments can add clarity." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "left is 'up', right is 'down'\n" ] } ], "source": [ "up_down = Couplet(left=\"up\", right=\"down\")\n", "print(\"left is {}, right is {}\".format(up_down.left, up_down.right))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `Couplet`'s components can be swapped by evaluating the unary operation $transpose$." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A couplet (1->2) and its transpose (2->1)\n" ] }, { "data": { "text/latex": [ "$$one\\_two = \\mbox{1}{\\mapsto}{\\mbox{2}}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$transpose\\_result = \\mbox{2}{\\mapsto}{\\mbox{1}}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import algebraixlib.algebras.couplets as couplets\n", "one_two = Couplet(1, 2)\n", "transpose_result = couplets.transpose(one_two)\n", "print(\"A couplet {} and its transpose {}\".format(one_two, transpose_result))\n", "iprint_latex(\"one_two\")\n", "iprint_latex(\"transpose_result\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When an expression is undefined in algebraixlib, it returns a special value, the singleton `Undef`. `Undef` cannot be used as a value in a `MathObject` and cannot be compared to any value (even itself). Use the `is` and `is not` operators to test if a value is undefined." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n", "True\n" ] } ], "source": [ "from algebraixlib.undef import Undef\n", "print(Undef() is Undef())\n", "print(Undef() is not Undef())\n", "print(None is not Undef())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The binary operation $composition(a{\\mapsto}b, c{\\mapsto}d)$ evaluates to $c{\\mapsto}b$ when $a = d$, otherwise it is undefined. Composition is often written with the infix operator $\\circ$." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$b{\\mapsto}c \\circ a{\\mapsto}b = \\mbox{'a'}{\\mapsto}{\\mbox{'c'}}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$a{\\mapsto}b \\circ b{\\mapsto}c = \\mathit{undef}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "a_to_b = Couplet('a', 'b') # a->b\n", "b_to_c = Couplet('b', 'c') # b->c\n", "iprint_latex(\"b{\\mapsto}c \\circ a{\\mapsto}b\", couplets.compose(b_to_c, a_to_b)) # b->c * a->b = a->c\n", "iprint_latex(\"a{\\mapsto}b \\circ b{\\mapsto}c\", couplets.compose(a_to_b, b_to_c)) # undef, composition is not commutative" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Set`s are used to create unordered collections of unique `MathObject`s. Note that this is a different class than Python's built-in `set` container. Non-`MathObject`s will be coerced into `Atom`s by `Set`'s initializer." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$many = \\left\\{\\mbox{'duplicate'},\\ \\mbox{'hello'},\\ (\\mbox{'hola'}{\\mapsto}{\\mbox{'mundo'}}),\\ \\mbox{'world'}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "repr = Set(Atom('duplicate'), Atom('hello'), Couplet(left=Atom('hola'), right=Atom('mundo')), Atom('world'))\n" ] } ], "source": [ "from algebraixlib.mathobjects import Set\n", "many = Set(Atom(\"hello\"), \"world\", Couplet(\"hola\", \"mundo\"), \"duplicate\", \"duplicate\")\n", "iprint_latex(\"many\")\n", "print(\"repr = \", repr(many))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Set`s support `for...in` syntax for iteration and `in` and `not in` syntax for membership tests. Because sets are unordered, they do not support random access (no bracket operator)." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n", "5\n", "True\n", "False\n" ] } ], "source": [ "nums = Set(1, 2, 3, 4, 5)\n", "for elem in nums:\n", " print(elem)\n", "print(1 in nums)\n", "print(7 in nums)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Set`s can be unioned, intersected, set-minused. Relations such as `is_subset` and `is_superset` are defined." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "union(a, b) = {1, 2, 3}\n", "intersect(a, b) = {2}\n", "minus(a, b) = {1}\n", "is_subset(a, b) = False\n", "is_superset(a, {1}) = True\n" ] } ], "source": [ "a = Set(1, 2)\n", "b = Set(2, 3)\n", "\n", "import algebraixlib.algebras.sets as sets\n", "\n", "print(\"union(a, b) =\", sets.union(a, b))\n", "print(\"intersect(a, b) =\", sets.intersect(a, b))\n", "print(\"minus(a, b) =\", sets.minus(a, b))\n", "print(\"is_subset(a, b) =\", sets.is_subset_of(a, b))\n", "print(\"is_superset(a, {1}) =\", sets.is_superset_of(a, Set(1)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use a `Couplet` to model a single truth, such as ${sky}{\\mapsto}{blue}$ or ${name}{\\mapsto}{jeff}$. By collecting multiple `Couplet`s together in a `Set`, we form a mathematical model of a data record. This data structure, called a binary relation (abbreviated from here on as simply \"relation\"), is the fundamental set theory construct in a data algebra program." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$record\\_relation = \\left\\{\\mbox{'id'}{\\mapsto}{\\mbox{123}},\\ \\mbox{'loves'}{\\mapsto}{\\mbox{'code'}},\\ \\mbox{'loves'}{\\mapsto}{\\mbox{'math'}},\\ \\mbox{'name'}{\\mapsto}{\\mbox{'jeff'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "record_relation = Set(Couplet('id', 123), Couplet('name', 'jeff'), Couplet('loves', 'math'),\n", " Couplet('loves', 'code'))\n", "iprint_latex(\"record_relation\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some relations specify a function from each couplet's left component to its right. This is the case when every left value maps to exactly one right value. Such a relation is called \"left functional\". Likewise, a relation can be said to be \"right functional\" when every right value maps to exactly one left value." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "123\n", "'subject'\n", "undef\n" ] } ], "source": [ "import algebraixlib.algebras.relations as relations\n", "\n", "functional_relation = Set(Couplet('subject', 123), Couplet('name', 'james'), Couplet('level', 10))\n", "print(relations.get_right(functional_relation, 'subject'))\n", "print(relations.get_left(functional_relation, 123))\n", "print(relations.get_right(record_relation,\n", " 'loves')) # See non-functional record_relation above." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Function evaluation syntax makes this more concise for left functional relations." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$functional\\_relation(\\mbox{'subject'}) = \\mbox{123}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$functional\\_relation(123) = \\mathit{undef}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "subject = functional_relation('subject')\n", "one_two_three = functional_relation(123)\n", "iprint_latex(\"functional_relation(\\mbox{'subject'})\", subject)\n", "iprint_latex(\"functional_relation(123)\", one_two_three)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The power set of a set $S$, which we'll denote as $P(S)$, is the set of all subsets of $S$. Note how in the example below, the elements of `set_s` are numbers, and the elements of `powerset_s` are sets of numbers." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$S = \\left\\{\\mbox{1},\\ \\mbox{2},\\ \\mbox{3}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$P(S) = \\left\\{\\begin{array}{l}\\left\\{\\right\\},\\\\\n", "\\left\\{\\mbox{1}\\right\\},\\\\\n", "\\left\\{\\mbox{1},\\ \\mbox{2}\\right\\},\\\\\n", "\\left\\{\\mbox{1},\\ \\mbox{2},\\ \\mbox{3}\\right\\},\\\\\n", "\\left\\{\\mbox{1},\\ \\mbox{3}\\right\\},\\\\\n", "\\left\\{\\mbox{2}\\right\\},\\\\\n", "\\left\\{\\mbox{2},\\ \\mbox{3}\\right\\},\\\\\n", "\\left\\{\\mbox{3}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "set_s = Set(1, 2, 3)\n", "powerset_s = sets.power_set(set_s)\n", "iprint_latex(\"S\", set_s)\n", "iprint_latex(\"P(S)\", powerset_s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consider that if $C$ is the set of all couplets, then the set of all relations $R$ can be defined as $P(C)$, that is, every relation is an element of the power set of all couplets. It turns out that we can exploit this relationship by \"extending\" operations on couplets to relations and make them useful there. To extend a unary operation such as `couplets.transpose`, we apply it to every `Couplet` in a relation, which results in another relation." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$first\\_relation = \\left\\{\\mbox{'a'}{\\mapsto}{\\mbox{1}},\\ \\mbox{'b'}{\\mapsto}{\\mbox{2}},\\ \\mbox{'c'}{\\mapsto}{\\mbox{3}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$transposed\\_relation = \\left\\{\\mbox{1}{\\mapsto}{\\mbox{'a'}},\\ \\mbox{2}{\\mapsto}{\\mbox{'b'}},\\ \\mbox{3}{\\mapsto}{\\mbox{'c'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import algebraixlib.extension as ext\n", "\n", "first_relation = Set(Couplet('a', 1), Couplet('b', 2), Couplet('c', 3))\n", "transposed_relation = ext.unary_extend(first_relation, couplets.transpose)\n", "iprint_latex(\"first_relation\")\n", "iprint_latex(\"transposed_relation\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, a binary operation like `couplets.composition` can be extended by evaluating it for every element of the cross product of two relations. Notice that `couplets.composition` is a partial binary operation (given two legitimate `Couplet`s, it may be undefined). When `couplets.compose(a, b)` is not defined, it simply isn't included in the membership of the resulting relation. By extending, we have turned $composition$ into a full binary operation in the power set algebra." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$second\\_relation = \\left\\{\\mbox{'four'}{\\mapsto}{\\mbox{'d'}},\\ \\mbox{'one'}{\\mapsto}{\\mbox{'a'}},\\ \\mbox{'won'}{\\mapsto}{\\mbox{'a'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$composed\\_relation = \\left\\{\\mbox{'one'}{\\mapsto}{\\mbox{1}},\\ \\mbox{'won'}{\\mapsto}{\\mbox{1}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$empty\\_relation = \\left\\{\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "second_relation = Set(Couplet('one', 'a'), Couplet('won', 'a'), Couplet('four', 'd'))\n", "composed_relation = ext.binary_extend(first_relation, second_relation, couplets.compose)\n", "empty_relation = ext.binary_extend(second_relation, first_relation,\n", " couplets.compose) # empty relation; still not commutative\n", "iprint_latex(\"second_relation\")\n", "iprint_latex(\"composed_relation\")\n", "iprint_latex(\"empty_relation\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These extended operations are defined as functions in the `relations` module." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "transpose_is_same: True\n", "compose_is_same: True\n" ] } ], "source": [ "transpose_is_same = transposed_relation == relations.transpose(first_relation)\n", "compose_is_same = composed_relation == relations.compose(first_relation, second_relation)\n", "print(\"transpose_is_same:\", transpose_is_same)\n", "print(\"compose_is_same:\", compose_is_same)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following docstring specifies a CSV table of words in various languages, with their meaning normalized to English." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": true }, "outputs": [], "source": [ "vocab_csv = \"\"\"word,language,meaning\n", "hello,English,salutation\n", "what's up,English,salutation\n", "hola,Spanish,salutation\n", "world,English,earth\n", "mundo,Spanish,earth\n", "gallo,Spanish,rooster\n", "Duniyā,Hindi,earth\n", "Kon'nichiwa,Japanese,salutation\n", "hallo,German,salutation\n", "nuqneH,Klingon,salutation\n", "sekai,Japanese,earth\n", "schmetterling,German,butterfly\n", "mariposa,Spanish,butterfly\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tables can be modeled as sets of binary relations, which we call \"clans\". In the case of tables, we can further specify that each relation will be a function from left to right (since each (row, header) coordinate corresponds to exactly one element)." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$vocab\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'world'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'German'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'butterfly'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'schmetterling'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'German'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hallo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Hindi'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'Duniyā'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'sekai'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Klingon'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'nuqneH'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'butterfly'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'mariposa'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'mundo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'rooster'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'gallo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hola'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from io import StringIO\n", "from algebraixlib.io.csv import import_csv\n", "\n", "file = StringIO(vocab_csv)\n", "vocab_clan = import_csv(file)\n", "iprint_latex(\"vocab_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$superstrict(A, B)$ is a partial binary operation on sets. It is defined as $A$ if $A$ is a superset of $B$, otherwise it is undefined. We use the infix operator $\\vartriangleright$ for superstriction." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$hello\\_relation = \\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$hello\\_relation \\vartriangleright \\{ \\mbox{'language'}{\\mapsto}\\mbox{'English'} \\} = \\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$hello\\_relation \\vartriangleright \\{ \\mbox{'language'}{\\mapsto}\\mbox{'Mandarin'} \\} = \\mathit{undef}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "hello_relation = Set(Couplet('word', 'hello'), Couplet('language', 'English'),\n", " Couplet('meaning', 'salutation'))\n", "super_pos = sets.superstrict(hello_relation, Set(Couplet('language', 'English')))\n", "super_neg = sets.superstrict(hello_relation, Set(Couplet('language', 'Mandarin')))\n", "\n", "iprint_latex(\"hello_relation\", hello_relation)\n", "iprint_latex(\"hello_relation \\\\vartriangleright \\{ \\mbox{'language'}{\\mapsto}\\mbox{'English'} \\}\", super_pos)\n", "iprint_latex(\"hello_relation \\\\vartriangleright \\{ \\mbox{'language'}{\\mapsto}\\mbox{'Mandarin'} \\}\", super_neg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By extending the $superstrict$ operation to clans, which are sets of sets (of couplets), we can define a helpful mechanism to restrict `vocab_clan` to only those relations that contain particular values." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$salutation\\_records\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'German'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hallo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Klingon'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'nuqneH'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hola'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$earth\\_records\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'world'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Hindi'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'Duniyā'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'sekai'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'earth'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'mundo'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import algebraixlib.algebras.clans as clans\n", "salutation_records_clan = clans.superstrict(vocab_clan, Set(Set(Couplet('meaning', 'salutation'))))\n", "earth_records_clan = clans.superstrict(vocab_clan, Set(Set(Couplet('meaning', 'earth'))))\n", "iprint_latex(\"salutation_records_clan\")\n", "iprint_latex(\"earth_records_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our extended `relations.compose` operation from earlier can be extended again to work with clans. By choosing an appropriate right-hand argument, clan composition can model the relational algebra notion of projection." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$words\\_langs\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'language'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'word'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "words_langs_clan = Set(Set(Couplet('word', 'word'), Couplet('language', 'language')))\n", "iprint_latex(\"words_langs_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `relations.diag` and `clans.diag` utility functions create a \"diagonal\" relation or clan, respectively, with simpler syntax." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": true }, "outputs": [], "source": [ "assert words_langs_clan == clans.diag('word', 'language')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the meaning of each set of records ('salutation') is invariant among the relations in `salutation_records_clan`, we can drop those `Couplet`s. Note that the cardinality of the resulting clan is the same, but each relation now contains only two `Couplet`s." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$salutation\\_words\\_n\\_langs\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'German'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hallo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Klingon'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'nuqneH'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hola'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "salutation_words_n_langs_clan = clans.compose(salutation_records_clan, words_langs_clan)\n", "iprint_latex(\"salutation_words_n_langs_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, we can take this one step further and \"rename\" the 'word' attribute to something more specific by replacing the value 'word' with 'salutation' everywhere we find it as the left of a `Couplet`. By doing this, we both compress the information in each relation and also set our data up for later processing." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/latex": [ "$$salutations\\_n\\_langs\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hello'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'German'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hallo'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Klingon'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'nuqneH'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hola'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "salutations_n_langs_clan = clans.compose(salutation_words_n_langs_clan,\n", " Set(Set(Couplet(\"salutation\", \"word\"),\n", " Couplet(\"language\", \"language\"))))\n", "iprint_latex(\"salutations_n_langs_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll do the same for `earth_records_clan`, but do the projection and \"rename\" all in one composition operation." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$earths\\_n\\_langs\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'Duniyā'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'Hindi'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'mundo'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'sekai'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'English'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "earths_n_langs_clan = clans.compose(earth_records_clan,\n", " Set(Set(Couplet(\"earth\", \"word\"),\n", " Couplet(\"language\", \"language\"))))\n", "iprint_latex(\"earths_n_langs_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our next task will be to relate these clans to each other in a way that preserves the functional characteristic of every relation. We can define a partial binary operation $functional\\_union(A, B)$ on relations to be $union(A, B)$ if $union(A, B)$ is left functional else undefined." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$func\\_union\\_pos = \\left\\{\\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'meaning'}{\\mapsto}{\\mbox{'salutation'}},\\ \\mbox{'more'}{\\mapsto}{\\mbox{'info'}},\\ \\mbox{'word'}{\\mapsto}{\\mbox{'hello'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/latex": [ "$$func\\_union\\_neg = \\mathit{undef}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "func_union_pos = relations.functional_union(hello_relation,\n", " Set(Couplet('language', 'English'),\n", " Couplet('more', 'info')))\n", "func_union_neg = relations.functional_union(hello_relation,\n", " Set(Couplet('language', 'Spanish'),\n", " Couplet('more', 'info')))\n", "iprint_latex(\"func_union_pos\")\n", "iprint_latex(\"func_union_neg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extending this operation to clans models natural join-like behavior." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$salutations\\_words\\_langs\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'mundo'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'Spanish'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hola'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'sekai'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'Japanese'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'language'}{\\mapsto}{\\mbox{'English'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hello'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "salutations_words_langs_clan = clans.cross_functional_union(salutations_n_langs_clan,\n", " earths_n_langs_clan)\n", "iprint_latex(\"salutations_words_langs_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the clans have been related to each other through their language attributes, we can do another projection. Notice how the \"renaming\" of 'word' to 'salutation' and 'earth' allows us to distinguish each of the words' meaning after joining the clans." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$salutations\\_n\\_words\\_clan = \\left\\{\\begin{array}{l}\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'mundo'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hola'}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'sekai'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"Kon'nichiwa\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{\"what's up\"}}\\right\\},\\\\\n", "\\left\\{\\mbox{'earth'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'salutation'}{\\mapsto}{\\mbox{'hello'}}\\right\\}\\end{array}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "salutations_n_words_clan = clans.compose(salutations_words_langs_clan,\n", " clans.diag('salutation', 'earth'))\n", "iprint_latex(\"salutations_n_words_clan\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we will distill this data down to a single relation describing \"Hello, World\" phrases." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$Greetings!!! = \\left\\{\\mbox{\"Kon'nichiwa\"}{\\mapsto}{\\mbox{'sekai'}},\\ \\mbox{\"what's up\"}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'hello'}{\\mapsto}{\\mbox{'world'}},\\ \\mbox{'hola'}{\\mapsto}{\\mbox{'mundo'}}\\right\\}$$" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "greeting_relation = Set(Couplet(rel('salutation'), rel('earth'))\n", " for rel in salutations_n_words_clan)\n", "iprint_latex(\"Greetings!!!\", greeting_relation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "© Copyright Permission.io, Inc. (formerly known as Algebraix Data Corporation), Copyright (c) 2022.\n", "\n", "This file is part of [`algebraixlib`][] .\n", "\n", "[`algebraixlib`][] is free software: you can redistribute it and/or modify it under the terms of [version 3 of the GNU Lesser General Public License][] as published by the [Free Software Foundation][].\n", "\n", "[`algebraixlib`][] is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.\n", "\n", "You should have received a copy of the GNU Lesser General Public License along with [`algebraixlib`][]. If not, see [GNU licenses][].\n", "\n", "[`algebraixlib`]: (A Python library for data algebra)\n", "[Version 3 of the GNU Lesser General Public License]: \n", "[Free Software Foundation]: \n", "[GNU licenses]: " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3" } }, "nbformat": 4, "nbformat_minor": 0 }