def gather_categories(imap, header, categories=None):
<EOL>if categories is None:<EOL><INDENT>return {"<STR_LIT:default>": DataCategory(set(imap.keys()), {})}<EOL><DEDENT>cat_ids = [header.index(cat)<EOL>for cat in categories if cat in header and "<STR_LIT:=>" not in cat]<EOL>table = OrderedDict()<EOL>conditions = defaultdict(set)<EOL>for i, cat in enumerate(categories):<EOL><INDENT>if "<STR_LIT:=>" in cat and cat.split("<STR_LIT:=>")[<NUM_LIT:0>] in header:<EOL><INDENT>cat_name = header[header.index(cat.split("<STR_LIT:=>")[<NUM_LIT:0>])]<EOL>conditions[cat_name].add(cat.split("<STR_LIT:=>")[<NUM_LIT:1>])<EOL><DEDENT><DEDENT>if not cat_ids and not conditions:<EOL><INDENT>return {"<STR_LIT:default>": DataCategory(set(imap.keys()), {})}<EOL><DEDENT>if cat_ids and not conditions:<EOL><INDENT>for sid, row in imap.items():<EOL><INDENT>cat_name = "<STR_LIT:_>".join([row[cid] for cid in cat_ids])<EOL>if cat_name not in table:<EOL><INDENT>table[cat_name] = DataCategory(set(), {})<EOL><DEDENT>table[cat_name].sids.add(sid)<EOL><DEDENT>return table<EOL><DEDENT>cond_ids = set()<EOL>for k in conditions:<EOL><INDENT>try:<EOL><INDENT>cond_ids.add(header.index(k))<EOL><DEDENT>except ValueError:<EOL><INDENT>continue<EOL><DEDENT><DEDENT>idx_to_test = set(cat_ids).union(cond_ids)<EOL>for sid, row in imap.items():<EOL><INDENT>if all([row[header.index(c)] in conditions[c] for c in conditions]):<EOL><INDENT>key = "<STR_LIT:_>".join([row[idx] for idx in idx_to_test])<EOL>try:<EOL><INDENT>assert key in table.keys()<EOL><DEDENT>except AssertionError:<EOL><INDENT>table[key] = DataCategory(set(), {})<EOL><DEDENT>table[key].sids.add(sid)<EOL><DEDENT><DEDENT>try:<EOL><INDENT>assert len(table) > <NUM_LIT:0><EOL><DEDENT>except AssertionError:<EOL><INDENT>return {"<STR_LIT:default>": DataCategory(set(imap.keys()), {})}<EOL><DEDENT>else:<EOL><INDENT>return table<EOL><DEDENT>
Find the user specified categories in the map and create a dictionary to contain the
relevant data for each type within the categories. Multiple categories will have their
types combined such that each possible combination will have its own entry in the
dictionary.

:type imap: dict
:param imap: The input mapping file data keyed by SampleID
:type header: list
:param header: The header line from the input mapping file. This will be searched for
               the user-specified categories
:type categories: list
:param categories: The list of user-specified category column name from mapping file
:rtype: dict
:return: A sorted dictionary keyed on the combinations of all the types found within
         the user-specified categories. Each entry will contain an empty DataCategory
         namedtuple. If no categories are specified, a single entry with the key
         'default' will be returned