Joins multiple event series together into DataFrames. A generalization of
    `survival_table_from_events` to data with groups. Previously called `group_event_series` pre 0.2.3.

    Parameters
    ----------
    groups: a (n,) array
      individuals' group ids.
    durations: a (n,)  array
      durations of each individual
    event_observed: a (n,) array
      event observations, 1 if observed, 0 else.
    birth_times: a (n,) array
      when the subject was first observed. A subject's death event is then at [birth times + duration observed].
      Normally set to all zeros, but can be positive or negative.
    limit:

    Returns
    -------
    unique_groups: np.array
      array of all the unique groups present
    removed: DataFrame
      DataFrame of removal count data at event_times for each group, column names are 'removed:<group name>'
    observed: DataFrame
      DataFrame of observed count data at event_times for each group, column names are 'observed:<group name>'
    censored: DataFrame
      DataFrame of censored count data at event_times for each group, column names are 'censored:<group name>'

    Example
    -------
    >>> #input
    >>> group_survival_table_from_events(waltonG, waltonT, np.ones_like(waltonT)) #data available in test_suite.py
    >>> #output
    >>> [
    >>>     array(['control', 'miR-137'], dtype=object),
    >>>               removed:control  removed:miR-137
    >>>     event_at
    >>>     6                       0                1
    >>>     7                       2                0
    >>>     9                       0                3
    >>>     13                      0                3
    >>>     15                      0                2
    >>>     ,
    >>>               observed:control  observed:miR-137
    >>>     event_at
    >>>     6                        0                 1
    >>>     7                        2                 0
    >>>     9                        0                 3
    >>>     13                       0                 3
    >>>     15                       0                 2
    >>>     ,
    >>>               censored:control  censored:miR-137
    >>>     event_at
    >>>     6                        0                 0
    >>>     7                        0                 0
    >>>     9                        0                 0
    >>>     ,
    >>> ]

    See Also
    --------
    survival_table_from_events

def group_survival_table_from_events(
    groups, durations, event_observed, birth_times=None, limit=-1
):  # pylint: disable=too-many-locals
    """
    Joins multiple event series together into DataFrames. A generalization of
    `survival_table_from_events` to data with groups. Previously called `group_event_series` pre 0.2.3.

    Parameters
    ----------
    groups: a (n,) array
      individuals' group ids.
    durations: a (n,)  array
      durations of each individual
    event_observed: a (n,) array
      event observations, 1 if observed, 0 else.
    birth_times: a (n,) array
      when the subject was first observed. A subject's death event is then at [birth times + duration observed].
      Normally set to all zeros, but can be positive or negative.
    limit:

    Returns
    -------
    unique_groups: np.array
      array of all the unique groups present
    removed: DataFrame
      DataFrame of removal count data at event_times for each group, column names are 'removed:<group name>'
    observed: DataFrame
      DataFrame of observed count data at event_times for each group, column names are 'observed:<group name>'
    censored: DataFrame
      DataFrame of censored count data at event_times for each group, column names are 'censored:<group name>'

    Example
    -------
    >>> #input
    >>> group_survival_table_from_events(waltonG, waltonT, np.ones_like(waltonT)) #data available in test_suite.py
    >>> #output
    >>> [
    >>>     array(['control', 'miR-137'], dtype=object),
    >>>               removed:control  removed:miR-137
    >>>     event_at
    >>>     6                       0                1
    >>>     7                       2                0
    >>>     9                       0                3
    >>>     13                      0                3
    >>>     15                      0                2
    >>>     ,
    >>>               observed:control  observed:miR-137
    >>>     event_at
    >>>     6                        0                 1
    >>>     7                        2                 0
    >>>     9                        0                 3
    >>>     13                       0                 3
    >>>     15                       0                 2
    >>>     ,
    >>>               censored:control  censored:miR-137
    >>>     event_at
    >>>     6                        0                 0
    >>>     7                        0                 0
    >>>     9                        0                 0
    >>>     ,
    >>> ]

    See Also
    --------
    survival_table_from_events

    """

    n = np.max(groups.shape)
    assert n == np.max(durations.shape) == np.max(event_observed.shape), "inputs must be of the same length."

    if birth_times is None:
        # Create some birth times
        birth_times = np.zeros(np.max(durations.shape))
        birth_times[:] = np.min(durations)

    assert n == np.max(birth_times.shape), "inputs must be of the same length."

    groups, durations, event_observed, birth_times = [
        pd.Series(np.asarray(vector).reshape(n)) for vector in [groups, durations, event_observed, birth_times]
    ]
    unique_groups = groups.unique()

    for i, group in enumerate(unique_groups):
        ix = groups == group
        T = durations[ix]
        C = event_observed[ix]
        B = birth_times[ix]
        group_name = str(group)
        columns = [
            event_name + ":" + group_name for event_name in ["removed", "observed", "censored", "entrance", "at_risk"]
        ]
        if i == 0:
            survival_table = survival_table_from_events(T, C, B, columns=columns)
        else:
            survival_table = survival_table.join(survival_table_from_events(T, C, B, columns=columns), how="outer")

    survival_table = survival_table.fillna(0)
    # hmmm pandas its too bad I can't do data.loc[:limit] and leave out the if.
    if int(limit) != -1:
        survival_table = survival_table.loc[:limit]

    return (
        unique_groups,
        survival_table.filter(like="removed:"),
        survival_table.filter(like="observed:"),
        survival_table.filter(like="censored:"),
    )