code: 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