Renames numerical indices in column names returned by patsy dmatrix /
    dmatrices calls based on the corresponding string levels.
    Args:
        columns (list): List of cols from dmatrix's .design_info.column_names
        name_lists (list): List of lists, where the i'th list gives the set of
            level names for the i'th index that needs to be replaced.
    Example:
        ts = ['threecats[1]:subjects[4]', 'threecats[0]:subjects[3]']
        rename_columns(ts, (['a', 'b', 'c'], ['d1', 'd2', 'd3', 'd4', 'd5']))
        # Returns ['threecats[b]:subjects[d5]', 'threecats[a]:subjects[d4]']

def rename_columns(columns, name_lists):
    ''' Renames numerical indices in column names returned by patsy dmatrix /
    dmatrices calls based on the corresponding string levels.
    Args:
        columns (list): List of cols from dmatrix's .design_info.column_names
        name_lists (list): List of lists, where the i'th list gives the set of
            level names for the i'th index that needs to be replaced.
    Example:
        ts = ['threecats[1]:subjects[4]', 'threecats[0]:subjects[3]']
        rename_columns(ts, (['a', 'b', 'c'], ['d1', 'd2', 'd3', 'd4', 'd5']))
        # Returns ['threecats[b]:subjects[d5]', 'threecats[a]:subjects[d4]']
    '''
    def _replace(c, args):
        grps = re.findall('([^\]]*)(\[(\d+)\])', c)
        for i, (prefix, box, ind) in enumerate(grps):
            c = c.replace(prefix + box, prefix + '[%s]' % args[i][int(ind)])
        return c
    return [_replace(c, name_lists) for c in columns]