Source code for node_cartesian_product
# This file is part of Sympathy for Data.
# Copyright (c) 2017, Combine Control Systems AB
#
# Sympathy for Data is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# Sympathy for Data 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Sympathy for Data.  If not, see <http://www.gnu.org/licenses/>.
from sympathy.api import node as synode
from sympathy.api.nodeconfig import Port, Ports, Tag, Tags
# from sympathy.api.exceptions import SyDataError
import numpy as np
common_docs = """
Cartesian product of a number of tables create a new table
containing all combinations of rows of the inputs. This output have
one column for each unique column in the input tables. For example two
tables with A and B columns of length N and M each create a new table
of length N * M and containing A + B columns. It is an error to have
duplicate column names.
"""
[docs]class CartesianProductTable(synode.Node):
    __doc__ = common_docs
    name = 'Cartesian Product Table'
    description = 'Cartesian product of two or more Tables into a single Table.'
    nodeid = 'se.combine.sympathy.data.table.cartesian_product_table'
    author = "Mathias Broxvall"
    version = '1.0'
    icon = 'cartesian_product.svg'
    tags = Tags(Tag.DataProcessing.TransformStructure)
    related = ['se.combine.sympathy.data.table.cartesian_product_tables']
    parameters = {}
    parameter_root = synode.parameters(parameters)
    inputs = Ports([
        Port.Custom('table', 'Input Tables', name='in', n=(2, None))])
    outputs = Ports([
        Port.Table('Table with cartesian product of inputs', name='out')])
    def execute(self, node_context):
        """Execute"""
        inputs = node_context.input.group('in')
        output = node_context.output['out']
        lens = [i.number_of_rows() for i in inputs]
        for i in range(len(list(inputs))):
            left = int(np.product(lens[:i]))
            right = int(np.product(lens[i+1:]))
            for column in inputs[i].cols():
                data = [val for val in column.data for _ in range(right)] * left
                output.set_column_from_array(column.name, np.array(data)) 
[docs]class CartesianProductTables(synode.Node):
    __doc__ = common_docs
    name = 'Cartesian Product Tables'
    description = 'Cartesian product of a list of Tables into a single Table.'
    nodeid = 'se.combine.sympathy.data.table.cartesian_product_tables'
    author = "Mathias Broxvall"
    version = '1.0'
    icon = 'cartesian_product.svg'
    tags = Tags(Tag.DataProcessing.TransformStructure)
    related = ['se.combine.sympathy.data.table.cartesian_product_table']
    parameters = {}
    parameter_root = synode.parameters(parameters)
    inputs = Ports([
        Port.Custom('[table]', 'List of input tables', name='in')])
    outputs = Ports([
        Port.Table('Table with cartesian product of inputs', name='out')])
    def execute(self, node_context):
        """Execute"""
        inputs = node_context.input['in']
        output = node_context.output['out']
        lens = [i.number_of_rows() for i in inputs]
        for i in range(len(list(inputs))):
            left = int(np.product(lens[:i]))
            right = int(np.product(lens[i+1:]))
            for column in inputs[i].cols():
                data = [val for val in column.data for _ in range(right)] * left
                output.set_column_from_array(column.name, np.array(data))