def wide_to_long(df, stubnames, i, j, sep="", suffix=r'\d+'):
    r
    def get_var_names(df, stub, sep, suffix):
        regex = r'^{stub}{sep}{suffix}$'.format(
            stub=re.escape(stub), sep=re.escape(sep), suffix=suffix)
        pattern = re.compile(regex)
        return [col for col in df.columns if pattern.match(col)]
    def melt_stub(df, stub, i, j, value_vars, sep):
        newdf = melt(df, id_vars=i, value_vars=value_vars,
                     value_name=stub.rstrip(sep), var_name=j)
        newdf[j] = Categorical(newdf[j])
        newdf[j] = newdf[j].str.replace(re.escape(stub + sep), "")
        newdf[j] = to_numeric(newdf[j], errors='ignore')
        return newdf.set_index(i + [j])
    if not is_list_like(stubnames):
        stubnames = [stubnames]
    else:
        stubnames = list(stubnames)
    if any(col in stubnames for col in df.columns):
        raise ValueError("stubname can't be identical to a column name")
    if not is_list_like(i):
        i = [i]
    else:
        i = list(i)
    if df[i].duplicated().any():
        raise ValueError("the id variables need to uniquely identify each row")
    value_vars = [get_var_names(df, stub, sep, suffix) for stub in stubnames]
    value_vars_flattened = [e for sublist in value_vars for e in sublist]
    id_vars = list(set(df.columns.tolist()).difference(value_vars_flattened))
    melted = [melt_stub(df, s, i, j, v, sep)
              for s, v in zip(stubnames, value_vars)]
    melted = melted[0].join(melted[1:], how='outer')
    if len(i) == 1:
        new = df[id_vars].set_index(i).join(melted)
        return new
    new = df[id_vars].merge(melted.reset_index(), on=i).set_index(i + [j])
    return new