All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.tablesaw.table.ViewGroup Maven / Gradle / Ivy

package tech.tablesaw.table;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;

import tech.tablesaw.api.CategoryColumn;
import tech.tablesaw.api.FloatColumn;
import tech.tablesaw.api.Table;
import tech.tablesaw.columns.Column;
import tech.tablesaw.reducing.NumericReduceFunction;
import tech.tablesaw.reducing.NumericSummaryTable;
import tech.tablesaw.util.BitmapBackedSelection;
import tech.tablesaw.util.Selection;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * A group of tables formed by performing splitting operations on an original table
 */
public class ViewGroup implements Iterable {

    private static final String SPLIT_STRING = "~~~";
    private static final Splitter SPLITTER = Splitter.on(SPLIT_STRING);


    private final Table sortedOriginal;

    private List subTables = new ArrayList<>();

    // the name(s) of the column(s) we're splitting the table on
    private String[] splitColumnNames;

    public ViewGroup(Table original, Column... columns) {
        splitColumnNames = new String[columns.length];
        for (int i = 0; i < columns.length; i++) {
            splitColumnNames[i] = columns[i].name();
        }
        this.sortedOriginal = original.sortOn(splitColumnNames);
        splitOn(splitColumnNames);
    }

    public static ViewGroup create(Table original, String... columnsNames) {
        List columns = original.columns(columnsNames);
        return new ViewGroup(original, columns.toArray(new Column[columns.size()]));
    }

    /**
     * Splits the sortedOriginal table into sub-tables, grouping on the columns whose names are given in
     * splitColumnNames
     */
    private void splitOn(String... columnNames) {

        List columns = sortedOriginal.columns(columnNames);
        int byteSize = getByteSize(columns);

        byte[] currentKey = null;
        String currentStringKey = null;
        TemporaryView view;

        Selection selection = new BitmapBackedSelection();

        for (int row = 0; row < sortedOriginal.rowCount(); row++) {

            ByteBuffer byteBuffer = ByteBuffer.allocate(byteSize);
            String newStringKey = "";

            for (int col = 0; col < columnNames.length; col++) {
                if (col > 0) {
                    newStringKey = newStringKey + SPLIT_STRING;
                }

                Column c = sortedOriginal.column(columnNames[col]);
                String groupKey = sortedOriginal.get(row, sortedOriginal.columnIndex(c));
                newStringKey = newStringKey + groupKey;
                byteBuffer.put(c.asBytes(row));
            }
            byte[] newKey = byteBuffer.array();
            if (row == 0) {
                currentKey = newKey;
                currentStringKey = newStringKey;
            }
            if (!Arrays.equals(newKey, currentKey)) {
                currentKey = newKey;
                view = new TemporaryView(sortedOriginal, selection);
                view.setName(currentStringKey);
                currentStringKey = newStringKey;
                addViewToSubTables(view);
                selection = new BitmapBackedSelection();
                selection.add(row);
            } else {
                selection.add(row);
            }
        }
        if (!selection.isEmpty()) {
            view = new TemporaryView(sortedOriginal, selection);
            view.setName(currentStringKey);
            addViewToSubTables(view);
        }
    }

    private int getByteSize(List columns) {
        int byteSize = 0;
        {
            for (Column c : columns) {
                byteSize += c.byteSize();
            }
        }
        return byteSize;
    }

    private void addViewToSubTables(TemporaryView view) {
        subTables.add(view);
    }

    public List getSubTables() {
        return subTables;
    }

    public TemporaryView get(int i) {
        return subTables.get(i);
    }

    @VisibleForTesting
    public Table getSortedOriginal() {
        return sortedOriginal;
    }

    public int size() {
        return subTables.size();
    }


    /**
     * For a subtable that is grouped by the values in more than one column, split the grouping column into separate
     * cols and return the revised view
     */
    private NumericSummaryTable splitGroupingColumn(NumericSummaryTable groupTable) {

        List newColumns = new ArrayList<>();

        List columns = sortedOriginal.columns(splitColumnNames);
        for (Column column : columns) {
            Column newColumn = column.emptyCopy();
            newColumns.add(newColumn);
        }
        // iterate through the rows in the table and split each of the grouping columns into multiple columns
        for (int row = 0; row < groupTable.rowCount(); row++) {
            List strings = SPLITTER.splitToList(groupTable.categoryColumn("Group").get(row));
            for (int col = 0; col < newColumns.size(); col++) {
                newColumns.get(col).appendCell(strings.get(col));
            }
        }
        for (int col = 0; col < newColumns.size(); col++) {
            Column c = newColumns.get(col);
            groupTable.addColumn(col, c);
        }
        groupTable.removeColumns("Group");
        return groupTable;
    }


    public NumericSummaryTable reduce(String numericColumnName, NumericReduceFunction function) {
        Preconditions.checkArgument(!subTables.isEmpty());
        NumericSummaryTable groupTable = NumericSummaryTable.create(sortedOriginal.name() + " summary");
        CategoryColumn groupColumn = new CategoryColumn("Group", subTables.size());
        FloatColumn resultColumn = new FloatColumn(reduceColumnName(numericColumnName, function.functionName()),
                subTables.size());
        groupTable.addColumn(groupColumn);
        groupTable.addColumn(resultColumn);

        for (TemporaryView subTable : subTables) {
            double result = subTable.reduce(numericColumnName, function);
            groupColumn.add(subTable.name());
            resultColumn.append((float) result);
        }
        return splitGroupingColumn(groupTable);
    }

    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator iterator() {
        return subTables.iterator();
    }

    private String reduceColumnName(String columnName, String functionName) {
        return String.format("%s [%s]", functionName, columnName);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy