def subsets_from_metadata(metadata, minimum, subset_type, ignore=None):
    """Get subsets from metadata, including boolean and categorical columns

    Parameters
    ----------
    metadata : pandas.DataFrame
        The dataframe whose columns to use to create subsets of the rows
    minimum : int
        Minimum number of rows required for a column or group in the column
        to be included
    subset_type : str
        The name of the kind of subset. e.g. "samples" or "features"
    ignore : list-like
        List of columns to ignore

    Returns
    -------
    subsets : dict
        A name: row_ids mapping of which samples correspond to which group
    """
    subsets = {}
    sorted_bool = False, True
    ignore = () if ignore is None else ignore
    if metadata is not None:
        for col in metadata:
            if col in ignore:
                continue
            if tuple(sorted(metadata[col].dropna().unique())) == sorted_bool:
                series = metadata[col].dropna()
                sample_subset = series[series].index
                subsets[col] = sample_subset
            else:
                grouped = metadata.groupby(col)
                sizes = grouped.size()
                filtered_sizes = sizes[sizes >= minimum]
                for group in filtered_sizes.keys():
                    if isinstance(group, bool):
                        continue
                    name = '{}: {}'.format(col, group)
                    subsets[name] = grouped.groups[group]
        for sample_subset in subsets.copy().keys():
            name = 'not ({})'.format(sample_subset)
            if 'False' in name or 'True' in name:
                continue
            if name not in subsets:
                in_features = metadata.index.isin(subsets[sample_subset])
                subsets[name] = metadata.index[~in_features]
        subsets['all {}'.format(subset_type)] = metadata.index
    return subsets
