def combine_indices(groups, prefix='', sep='.', return_labels=False):
    if isinstance(groups, tuple):
        groups = np.column_stack(groups)
    else:
        groups = np.asarray(groups)

    dt = groups.dtype

    is2d = (groups.ndim == 2)  # need to store

    if is2d:
        ncols = groups.shape[1]
        if not groups.flags.c_contiguous:
            groups = np.array(groups, order='C')

        groups_ = groups.view([('', groups.dtype)] * groups.shape[1])
    else:
        groups_ = groups

    uni, uni_idx, uni_inv = npc_unique(groups_, return_index=True,
                                       return_inverse=True)

    if is2d:
        uni = uni.view(dt).reshape(-1, ncols)

        # avoiding a view would be
        # for t in uni.dtype.fields.values():
        #     assert (t[0] == dt)
        #
        # uni.dtype = dt
        # uni.shape = (uni.size//ncols, ncols)

    if return_labels:
        label = [(prefix+sep.join(['%s']*len(uni[0]))) % tuple(ii)
                 for ii in uni]
        return uni_inv, uni_idx, uni, label
    else:
        return uni_inv, uni_idx, uni


# written for and used in try_covariance_grouploop.py
Set the graceful state.
dataset/ETHPy150Open statsmodels/statsmodels/statsmodels/tools/grouputils.py/combine_indices--crossover with--dataset/ETHPy150Open home-assistant/home-assistant/homeassistant/components/switch/thinkingcleaner.py/ThinkingCleanerSwitch.set_graceful_lock