Source code for node_table_sort

# This file is part of Sympathy for Data.
# Copyright (c) 2013, 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 import node_helper
from sympathy.api.nodeconfig import Tag, Tags, adjust, Port, Ports
import numpy as np


_sort_options = dict([
    ('Ascending', 'Standard'),
    ('Descending', 'Reverse'),
])


[docs]class SortRowsTable(synode.Node): """ Sort the rows of a table according to a sort column. The rows in the Tables are sorted by the selected *Sort column*. If *Sort order* is "Standard" numbers will be sorted increasingly, strings will be sorted alphabetically etc. If it is instead "Reverse" the order is instead reversed. Sorting by multiple rows ======================== The sorting algorithm used is stable, meaning that if two or more rows have the same value in the *Sort column* they will keep the same relative order as in the input. This can be used to sort the table by multiple columns. If you want to sort by multiple columns you can use multple sorting nodes in series. For example if you have the following table ad want to sort the table such that high priority tasks come first, and tasks with equal priority are sorted by lowest time needed: +----------------+----------+-------------+ | Task | Priority | Time needed | +================+==========+=============+ | Do dishes | A | 30 | +----------------+----------+-------------+ | Mop floors | C | 45 | +----------------+----------+-------------+ | Clean windows | C | 30 | +----------------+----------+-------------+ | Take out trash | A | 5 | +----------------+----------+-------------+ | Buy groceries | B | 60 | +----------------+----------+-------------+ then first use a Sort rows in Table node to sort the rows by increasing time, then use a second Sort rows in Table node to sort the rows by highest priority. The result would then be: +----------------+----------+-------------+ | Task | Priority | Time needed | +================+==========+=============+ | Take out trash | A | 5 | +----------------+----------+-------------+ | Do dishes | A | 30 | +----------------+----------+-------------+ | Buy groceries | B | 60 | +----------------+----------+-------------+ | Clean windows | C | 30 | +----------------+----------+-------------+ | Mop floors | C | 45 | +----------------+----------+-------------+ """ author = 'Greger Cronquist' version = '1.1' icon = 'sort_table_rows.svg' tags = Tags(Tag.DataProcessing.TransformStructure) name = 'Sort rows in Table' nodeid = 'org.sysess.sympathy.data.table.sorttable' description = 'Sort rows in Table by selected column' inputs = Ports([Port.Table('Input', name='Input')]) outputs = Ports([Port.Table('Input', name='Output')]) parameters = synode.parameters() parameters.set_list( 'column', label='Sort column', description='Column to sort', editor=synode.editors.combo_editor('', filter=True, edit=True)) parameters.set_list( 'sort_order', label='Sort order', list=['Ascending', 'Descending'], value=[0], description='Sort order', editor=synode.editors.combo_editor(options=_sort_options)) def adjust_parameters(self, node_context): adjust(node_context.parameters['column'], node_context.input['Input']) def execute(self, node_context): in_table = node_context.input['Input'] out_table = node_context.output['Output'] parameters = node_context.parameters if in_table.is_empty(): return column_param = parameters['column'] column = in_table._require_column(column_param) idx = np.ma.argsort(column, kind='mergesort') if parameters['sort_order'].selected == 'Descending': idx = idx[::-1] if isinstance(column, np.ma.MaskedArray): masked = np.ma.getmaskarray(column)[idx] if parameters['sort_order'].selected == 'Ascending': # Ascending: put masked values last idx = np.concatenate((idx[~masked], idx[masked])) else: # Descending: put masked values first idx = np.concatenate((idx[masked], idx[~masked])) for column_name in in_table.column_names(): array = in_table.get_column_to_array(column_name) out_table.set_column_from_array(column_name, array[idx]) out_table.set_attributes(in_table.get_attributes()) out_table.set_name(in_table.get_name())
[docs]@node_helper.list_node_decorator(['Input'], ['Output']) class SortRowsTables(SortRowsTable): name = 'Sort rows in Tables' nodeid = 'org.sysess.sympathy.data.table.sorttables'