def join(args):
    """
    %prog join file1.txt(pivotfile) file2.txt ..

    Join tabular-like files based on common column.
    --column specifies the column index to pivot on.
      Use comma to separate multiple values if the pivot column is different
      in each file. Maintain the order in the first file.
    --sep specifies the column separators, default to tab.
      Use comma to separate multiple values if the column separator is different
      in each file.
    """
    p = OptionParser(join.__doc__)
    p.add_option("--column", default="0",
                 help="0-based column id, multiple values allowed [default: %default]")
    p.set_sep(multiple=True)
    p.add_option("--noheader", default=False, action="store_true",
                 help="Do not print header [default: %default]")
    p.add_option("--na", default="na",
                 help="Value for unjoined data [default: %default]")
    p.add_option("--compact", default=False, action="store_true",
                 help="Do not repeat pivotal columns in output")
    p.add_option("--keysep", default=",",
                 help="specify separator joining multiple elements in the key column"
                 + " of the pivot file [default: %default]")
    p.set_outfile()

    opts, args = p.parse_args(args)
    nargs = len(args)

    keysep = opts.keysep
    compact = opts.compact

    if len(args) < 2:
        sys.exit(not p.print_help())

    na = opts.na
    c = opts.column
    if "," in c:
        cc = [int(x) for x in c.split(",")]
    else:
        cc = [int(c)] * nargs

    assert len(cc) == nargs, "Column index number != File number"

    s = opts.sep
    if "," in s:
        ss = [x for x in s.split(",")]
    else:
        ss = [s] * nargs

    assert len(ss) == nargs, "column separator number != File number"

    # Maintain the first file line order, and combine other files into it
    pivotfile = args[0]
    files = [DictFile(f, keypos=c, valuepos=None, delimiter=s) \
                        for f, c, s in zip(args, cc, ss)]
    otherfiles = files[1:]
    # The header contains filenames
    headers = []
    for i, x in enumerate(files):
        ncols = x.ncols
        if i and compact:
            ncols -= 1
        headers += [op.basename(x.filename)] * ncols
    header = "\t".join(headers)

    fp = must_open(pivotfile)
    fw = must_open(opts.outfile, "w")
    if not opts.noheader:
        print(header, file=fw)

    for row in fp:
        row = row.rstrip()
        atoms = row.split(ss[0])
        newrow = atoms
        key = atoms[cc[0]]
        keys = key.split(keysep) if keysep in key else [key]
        for d in otherfiles:
            drows = list()
            for key in keys:
                krow = d.get(key, [na] * d.ncols)
                if compact:
                    krow.pop(d.keypos)
                drows.append(krow)
            drow = [keysep.join(x) for x in list(zip(*drows))]
            newrow += drow
        print("\t".join(newrow), file=fw)
def join ( args ) : p = OptionParser ( join . __doc__ ) p . add_option ( "--column" , default = "0" , help = "0-based column id, multiple values allowed [default: %default]" ) p . set_sep ( multiple = True ) p . add_option ( "--noheader" , default = False , action = "store_true" , help = "Do not print header [default: %default]" ) p . add_option ( "--na" , default = "na" , help = "Value for unjoined data [default: %default]" ) p . add_option ( "--compact" , default = False , action = "store_true" , help = "Do not repeat pivotal columns in output" ) p . add_option ( "--keysep" , default = "," , help = "specify separator joining multiple elements in the key column" + " of the pivot file [default: %default]" ) p . set_outfile ( ) opts , args = p . parse_args ( args ) nargs = len ( args ) keysep = opts . keysep compact = opts . compact if len ( args ) < 2 : sys . exit ( not p . print_help ( ) ) na = opts . na c = opts . column if "," in c : cc = [ int ( x ) for x in c . split ( "," ) ] else : cc = [ int ( c ) ] * nargs assert len ( cc ) == nargs , "Column index number != File number" s = opts . sep if "," in s : ss = [ x for x in s . split ( "," ) ] else : ss = [ s ] * nargs assert len ( ss ) == nargs , "column separator number != File number" pivotfile = args [ 0 ] files = [ DictFile ( f , keypos = c , valuepos = None , delimiter = s ) for f , c , s in zip ( args , cc , ss ) ] otherfiles = files [ 1 : ] headers = [ ] for i , x in enumerate ( files ) : ncols = x . ncols if i and compact : ncols -= 1 headers += [ op . basename ( x . filename ) ] * ncols header = "\t" . join ( headers ) fp = must_open ( pivotfile ) fw = must_open ( opts . outfile , "w" ) if not opts . noheader : print ( header , file = fw ) for row in fp : row = row . rstrip ( ) atoms = row . split ( ss [ 0 ] ) newrow = atoms key = atoms [ cc [ 0 ] ] keys = key . split ( keysep ) if keysep in key else [ key ] for d in otherfiles : drows = list ( ) for key in keys : krow = d . get ( key , [ na ] * d . ncols ) if compact : krow . pop ( d . keypos ) drows . append ( krow ) drow = [ keysep . join ( x ) for x in list ( zip ( * drows ) ) ] newrow += drow print ( "\t" . join ( newrow ) , file = fw )
%prog join file1.txt(pivotfile) file2.txt ..

    Join tabular-like files based on common column.
    --column specifies the column index to pivot on.
      Use comma to separate multiple values if the pivot column is different
      in each file. Maintain the order in the first file.
    --sep specifies the column separators, default to tab.
      Use comma to separate multiple values if the column separator is different
      in each file.