def groups(self, labels, collect=None):
<EOL>if not _is_non_string_iterable(labels):<EOL><INDENT>return self.group(labels, collect=collect)<EOL><DEDENT>collect = _zero_on_type_error(collect)<EOL>columns = []<EOL>labels = self._as_labels(labels)<EOL>for label in labels:<EOL><INDENT>if label not in self.labels:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>columns.append(self._get_column(label))<EOL><DEDENT>grouped = self.group(list(zip(*columns)), lambda s: s)<EOL>grouped._columns.popitem(last=False) <EOL>counts = [len(v) for v in grouped[<NUM_LIT:0>]]<EOL>for label in labels[::-<NUM_LIT:1>]:<EOL><INDENT>grouped[label] = grouped.apply(_assert_same, label)<EOL>grouped.move_to_start(label)<EOL><DEDENT>if collect is None:<EOL><INDENT>count = '<STR_LIT:count>' if '<STR_LIT:count>' not in labels else self._unused_label('<STR_LIT:count>')<EOL>return grouped.select(labels).with_column(count, counts)<EOL><DEDENT>else:<EOL><INDENT>for label in grouped.labels:<EOL><INDENT>if label in labels:<EOL><INDENT>continue<EOL><DEDENT>column = [collect(v) for v in grouped[label]]<EOL>del grouped[label]<EOL>grouped[_collected_label(collect, label)] = column<EOL><DEDENT>return grouped<EOL><DEDENT>
Group rows by multiple columns, count or aggregate others.

        Args:
            ``labels``: list of column names (or indices) to group on

            ``collect``: a function applied to values in other columns for each group

        Returns: A Table with each row corresponding to a unique combination of values in
            the columns specified in ``labels``, where the first columns are those
            specified in ``labels``, followed by a column of counts for each of the unique
            values. If ``collect`` is provided, a Table is returned with all original
            columns, each containing values calculated by first grouping rows according to
            to values in the ``labels`` column, then applying ``collect`` to each set of
            grouped values in the other columns.

        Note:
            The grouped columns will appear first in the result table. If ``collect`` does not
            accept arguments with one of the column types, that column will be empty in the resulting
            table.

        >>> marbles = Table().with_columns(
        ...    "Color", make_array("Red", "Green", "Blue", "Red", "Green", "Green"),
        ...    "Shape", make_array("Round", "Rectangular", "Rectangular", "Round", "Rectangular", "Round"),
        ...    "Amount", make_array(4, 6, 12, 7, 9, 2),
        ...    "Price", make_array(1.30, 1.30, 2.00, 1.75, 1.40, 1.00))
        >>> marbles
        Color | Shape       | Amount | Price
        Red   | Round       | 4      | 1.3
        Green | Rectangular | 6      | 1.3
        Blue  | Rectangular | 12     | 2
        Red   | Round       | 7      | 1.75
        Green | Rectangular | 9      | 1.4
        Green | Round       | 2      | 1
        >>> marbles.groups(["Color", "Shape"])
        Color | Shape       | count
        Blue  | Rectangular | 1
        Green | Rectangular | 2
        Green | Round       | 1
        Red   | Round       | 2
        >>> marbles.groups(["Color", "Shape"], sum)
        Color | Shape       | Amount sum | Price sum
        Blue  | Rectangular | 12         | 2
        Green | Rectangular | 15         | 2.7
        Green | Round       | 2          | 1
        Red   | Round       | 11         | 3.05