Esempio n. 1
0
def tree2table(data, transpose=False, head=None):
    """build table from data.

    The table will be multi-level (main-rows and sub-rows), if:

       1. there is more than one column
       2. each cell within a row is a list or tuple

    If any of the paths contain tuples/lists, these are
    expanded to extra columns as well.

    If head is given, only first head rows are output.

    returns matrix, row_headers, col_headers
    """
    logger = Component.get_logger()

    labels = getPaths(data)

    if len(labels) < 2:
        raise ValueError("expected at least two levels for building table, got %i: %s" %
                         (len(labels), str(labels)))

    effective_labels = count_levels(labels)
    # subtract last level (will be expanded) and 1 for row header
    effective_cols = sum(effective_labels[:-1]) - 1

    col_headers = [""] * effective_cols + labels[-1]
    ncols = len(col_headers)

    paths = list(itertools.product(*labels[1:-1]))
    header_offset = effective_cols
    matrix = []

    logger.debug(
        "Datatree.buildTable: creating table with %i columns" %
        (len(col_headers)))

    # the following can be made more efficient
    # by better use of indices
    row_offset = 0
    row_headers = []

    # iterate over main rows
    for x, row in enumerate(labels[0]):

        first = True
        for xx, path in enumerate(paths):

            # get data - skip if there is None
            work = getLeaf(data, (row,) + path)
            if isinstance(work, pandas.DataFrame):
                if work.empty:
                    continue
            else:
                if not work:
                    continue

            row_data = [""] * ncols

            # add row header only for first row (if there are sub-rows)
            if first:
                if type(row) in Utils.ContainerTypes:
                    row_headers.append(row[0])
                    for z, p in enumerate(row[1:]):
                        row_data[z] = p
                else:
                    row_headers.append(row)
                first = False
            else:
                row_headers.append("")

            # enter data for the first row
            for z, p in enumerate(path):
                row_data[z] = p

            # check for multi-level rows
            is_container = True
            max_rows = None
            for y, column in enumerate(labels[-1]):
                if column not in work:
                    continue
                if type(work[column]) not in Utils.ContainerTypes:
                    is_container = False
                    break
                if max_rows == None:
                    max_rows = len(work[column])
                elif max_rows != len(work[column]):
                    raise ValueError("multi-level rows - unequal lengths: %i != %i" %
                                     (max_rows, len(work[column])))

            # add sub-rows
            if is_container:
                # multi-level rows
                for z in range(max_rows):
                    for y, column in enumerate(labels[-1]):
                        try:
                            row_data[
                                y + header_offset] = Utils.quote_rst(work[column][z])
                        except KeyError:
                            pass

                    if z < max_rows - 1:
                        matrix.append(row_data)
                        row_headers.append("")
                        row_data = [""] * ncols
            else:
                # single level row
                for y, column in enumerate(labels[-1]):
                    try:
                        row_data[
                            y + header_offset] = Utils.quote_rst(work[column])
                    except KeyError:
                        pass

            matrix.append(row_data)

            if head and len(matrix) >= head:
                break

    if transpose:
        row_headers, col_headers = col_headers, row_headers
        matrix = list(zip(*matrix))

    # convert headers to string (might be None)
    row_headers = [str(x) for x in row_headers]
    col_headers = [str(x) for x in col_headers]

    return matrix, row_headers, col_headers