changeset 0:6bb6c0a30c67 draft default tip

Uploaded
author jjohnson
date Tue, 01 Apr 2014 09:30:45 -0400
parents
children
files column_join.py column_join.xml test-data/column_join_in1.pileup test-data/column_join_in10.pileup test-data/column_join_in11.pileup test-data/column_join_in12.pileup test-data/column_join_in13.tabular test-data/column_join_in14.tabular test-data/column_join_in15.tabular test-data/column_join_in2.pileup test-data/column_join_in3.pileup test-data/column_join_in4.pileup test-data/column_join_in5.pileup test-data/column_join_in6.pileup test-data/column_join_in7.pileup test-data/column_join_in8.pileup test-data/column_join_in9.pileup test-data/column_join_out1.pileup test-data/column_join_out2.pileup test-data/column_join_out3.pileup test-data/column_join_out4.pileup test-data/column_join_out5.tabular
diffstat 22 files changed, 1182 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/column_join.py	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+"""
+This tool takes a tab-delimited text file as input and creates filters on columns based on certain properties. The tool will skip over invalid lines within the file, informing the user about the number of lines skipped.
+
+usage: %prog -o output -1 input1 -2 input2 -c column1[,column2[,column3[,...]]] -g hinge1[,hinge2[,hinge3[,...]]] -f <fill_options_file> [other_input1 [other_input2 [other_input3 ...]]]
+    -o, output=0: the output pileup
+    -1, input1=1: the pileup file to start with
+    -2, input2=2: the second pileup file to join
+    -g, hinge=h: the columns to be used for matching
+    -c, columns=c: the columns that should appear in the output
+    -f, fill_options_file=f: the file specifying the fill value to use
+    other_inputs: the other input files to join
+"""
+
+import optparse, os, re, struct, sys, tempfile
+from galaxy.util.bunch import Bunch
+from galaxy.util import stringify_dictionary_keys
+import json
+
+def stop_err( msg ):
+    sys.stderr.write( msg )
+    sys.exit()
+
+def split_nums( text ):
+    """
+    Splits a string into pieces of numbers and non-numbers, like 'abc23B3' --> [ 'abc', 23, 'B', 3 ]
+    """
+    split_t = []
+    c = ''
+    n = ''
+    for ch in text:
+        try:
+            v = int( ch )
+            n += ch
+            if c:
+                split_t.append( ''.join( c ) )
+                c = ''
+        except ValueError:
+            c += ch
+            if n:
+                split_t.append( int( ''.join( n ) ) )
+                n = ''
+    if c:
+        split_t.append( ''.join( c ) )
+    if n:
+        split_t.append( int( ''.join( n ) ) )
+    return split_t
+
+def hinge_compare( hinge1, hinge2 ):
+    """
+    Compares items like 'chr10' and 'chrM' or 'scaffold2' and scaffold10' so that
+    first part handled as text but last part as number
+    """
+    split_hinge1 = hinge1.split( '\t' )
+    split_hinge2 = hinge2.split( '\t' )
+    # quick check if either hinge is empty
+    if not ''.join( split_hinge2 ):
+        if ''.join( split_hinge1 ):
+            return 1
+        elif not ''.join( split_hinge1 ):
+            return 0
+    else:
+        if not ''.join( split_hinge1 ):
+            return -1
+    # go through all parts of the hinges and compare
+    for i, sh1 in enumerate( split_hinge1 ):
+        # if these hinge segments are the same, just move on to the next ones
+        if sh1 == split_hinge2[ i ]:
+            continue
+        # check all parts of each hinge
+        h1 = split_nums( sh1 )
+        h2 = split_nums( split_hinge2[ i ] )
+        for j, h in enumerate( h1 ):
+            # if second hinge has no more parts, first is considered larger
+            if j > 0 and len( h2 ) <= j:
+                return 1
+            # if these two parts are the same, move on to next
+            if h == h2[ j ]:
+                continue
+            # do actual comparison, depending on whether letter or number
+            if type( h ) == int:
+                if type( h2[ j ] ) == int:
+                    if h > h2[ j ]:
+                        return 1
+                    elif h < h2[ j ]:
+                        return -1
+                # numbers are less than letters
+                elif type( h2[ j ] ) == str:
+                    return -1
+            elif type( h ) == str:
+                if type( h2[ j ] ) == str:
+                    if h > h2[ j ]:
+                        return 1
+                    elif h < h2[ j ]:
+                        return -1
+                # numbers are less than letters
+                elif type( h2[ j ] ) == int:
+                    return 1
+    # if all else has failed, just do basic string comparison
+    if hinge1 > hinge2:
+        return 1
+    elif hinge1 == hinge2:
+        return 0
+    elif hinge1 < hinge2:
+        return -1
+
+def hinge_sort( infile, outfile, hinge ):
+    """Given input file name, sorts logically (text vs. numeric) into provided output file name."""
+    hinge_locs = {}
+    bad_lines = []
+    fin = open( infile, 'rb' )
+    line = fin.readline()
+    while line.strip():
+        try:
+            hinge_parts = line.split( '\t' )[ :hinge ]
+            try:
+                hinge_locs[ '\t'.join( hinge_parts ) ].append( fin.tell() - len( line ) )
+            except KeyError:
+                hinge_locs[ '\t'.join( hinge_parts ) ] = [ fin.tell() - len( line ) ]
+        except ValueError:
+            bad_line.append( line )
+        line = fin.readline()
+    fin.close()
+    fin = open( infile, 'rb' )
+    fout = open( outfile, 'wb' )
+    hinge_locs_sorted = hinge_locs.keys()
+    hinge_locs_sorted.sort( hinge_compare )
+    for hinge_loc in hinge_locs_sorted:
+        locs = hinge_locs[ hinge_loc ]
+        for loc in locs:
+            fin.seek( loc )
+            fout.write( fin.readline() )
+    fout.close()
+    fin.close()
+
+def __main__():
+    parser = optparse.OptionParser()
+    parser.add_option( '-o', '--output', dest='output', help='The name of the output file' )
+    parser.add_option( '-1', '--input1', dest='input1', help='The name of the first input file' )
+    parser.add_option( '-2', '--input2', dest='input2', help='The name of the second input file' )
+    parser.add_option( '-g', '--hinge', dest='hinge', help='The "hinge" to use (the value to compare)' )
+    parser.add_option( '-c', '--columns', dest='columns', help='The columns to include in the output file' )
+    parser.add_option( '-f', '--fill_options_file', dest='fill_options_file', default=None, help='The file specifying the fill value to use' )
+    (options, args) = parser.parse_args()
+    hinge = int( options.hinge )
+    cols = [ int( c ) for c in str( options.columns ).split( ',' ) if int( c ) > hinge ]
+    inputs = [ options.input1, options.input2 ]
+    if options.fill_options_file == 'None':
+        inputs.extend( args )
+    elif len( args ) > 0:
+        inputs.extend( args )
+    fill_options = None
+    if options.fill_options_file != 'None' and options.fill_options_file is not None:
+        try:
+            fill_options = Bunch( **stringify_dictionary_keys( json.load( open( options.fill_options_file ) ) ) )
+        except Exception, e:
+            print 'Warning: Ignoring fill options due to json error (%s).' % e
+    if fill_options is None:
+        fill_options = Bunch()
+    if 'file1_columns' not in fill_options:
+        fill_options.file1_columns = None
+    if fill_options and fill_options.file1_columns:
+        fill_empty = {}
+        for col in cols:
+            fill_empty[ col ] = fill_options.file1_columns[ col - 1 ]
+    else:
+        fill_empty = None
+    assert len( cols ) > 0, 'You need to select at least one column in addition to the hinge'
+    delimiter = '\t'
+    # make sure all files are sorted in same way, ascending
+    tmp_input_files = []
+    input_files = inputs[:]
+    for in_file in input_files:
+        tmp_file = tempfile.NamedTemporaryFile()
+        tmp_file_name = tmp_file.name
+        tmp_file.close()
+        hinge_sort( in_file, tmp_file_name, hinge )
+        tmp_file = open( tmp_file_name, 'rb' )
+        tmp_input_files.append( tmp_file )
+    # cycle through files, getting smallest line of all files one at a time
+    # also have to keep track of vertical position of extra columns
+    fout = file( options.output, 'w' )
+    old_current = ''
+    first_line = True
+    current_lines = [ f.readline().rstrip( '\r\n' ) for f in tmp_input_files ]
+    last_lines = ''.join( current_lines )
+    last_loc = -1
+    while last_lines:
+        # get the "minimum" hinge, which should come first, and the file location in list
+        hinges = [ delimiter.join( line.split( delimiter )[ :hinge ] ) for line in current_lines ]
+        hinge_dict = {}
+        for i in range( len( hinges ) ):
+            if not hinge_dict.has_key( hinges[ i ] ):
+                hinge_dict[ hinges[ i ] ] = i
+        hinges.sort( hinge_compare )
+        hinges = [ h for h in hinges if h ]
+        current, loc = hinges[0], hinge_dict[ hinges[0] ]
+        # first output empty columns for vertical alignment (account for "missing" files)
+        # write output for leading and trailing empty columns
+        # columns missing from actual file handled further below
+        current_data = []
+        if current != old_current:
+            # fill trailing empty columns with appropriate fill value
+            if not first_line:
+                if last_loc < len( inputs ) - 1:
+                    if not fill_empty:
+                        filler = [ '' for col in range( ( len( inputs ) - last_loc - 1 ) * len( cols ) ) ]
+                    else:
+                        filler = [ fill_empty[ cols[ col % len( cols ) ] ] for col in range( ( len( inputs ) - last_loc - 1 ) * len( cols ) ) ]
+                    fout.write( '%s%s' % ( delimiter, delimiter.join( filler ) ) )
+                # insert line break before current line
+                fout.write( '\n' )
+            # fill leading empty columns with appropriate fill value
+            if loc > 0:
+                if not fill_empty:
+                    current_data = [ '' for col in range( loc * len( cols ) ) ]
+                else:
+                    current_data = [ fill_empty[ cols[ col % len( cols ) ] ] for col in range( loc * len( cols ) ) ]
+        else:
+            if loc - last_loc > 1:
+                if not fill_empty:
+                    current_data = [ '' for col in range( ( loc - last_loc - 1 ) * len( cols ) ) ]
+                else:
+                    current_data = [ fill_empty[ cols[ col % len( cols ) ] ] for col in range( ( loc - last_loc - 1 ) * len( cols ) ) ]
+        # now output actual data
+        split_line = current_lines[ loc ].split( delimiter )
+        # fill empties within actual line if appropriate
+        if fill_empty:
+            new_split_line = split_line[:]
+            split_line = []
+            for i, item in enumerate( new_split_line ):
+                col = i + 1
+                if not item:
+                    try:
+                        split_line.append( fill_empty[ i + 1 ] )
+                    except KeyError:
+                        split_line.append( item )
+                else:
+                    split_line.append( item )
+        # add actual data to be output below
+        if ''.join( split_line ):
+            for col in cols:
+                if col > hinge:
+                    # if this column doesn't exist, add the appropriate filler or empty column
+                    try:
+                        new_item = split_line[ col - 1 ]
+                    except IndexError:
+                        if fill_empty:
+                            new_item = fill_empty[ col ]
+                        else:
+                            new_item = ''
+                    current_data.append( new_item )
+            # grab next line for selected file
+            current_lines[ loc ] = tmp_input_files[ loc ].readline().rstrip( '\r\n' )
+            # write relevant data to file
+            if current == old_current and current_data:
+                fout.write( '%s%s' % ( delimiter, delimiter.join( current_data ) ) )
+            elif current_data:
+                fout.write( '%s%s%s' % ( current, delimiter, delimiter.join( current_data ) ) )
+            last_lines = ''.join( current_lines )
+        else:
+            last_lines = None
+        last_loc = loc
+        old_current = current
+        first_line = False
+    # fill trailing empty columns for final line
+    if last_loc < len( inputs ) - 1:
+        if not fill_empty:
+            filler = [ '' for col in range( ( len( inputs ) - last_loc - 1 ) * len( cols ) ) ]
+        else:
+            filler = [ fill_empty[ cols[ col % len( cols ) ] ] for col in range( ( len( inputs ) - last_loc - 1 ) * len( cols ) ) ]
+        fout.write( '%s%s' % ( delimiter, delimiter.join( filler ) ) )
+    fout.write( '\n' )
+    fout.close()
+    for f in tmp_input_files:
+        os.unlink( f.name )
+
+if __name__ == "__main__" : __main__()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/column_join.xml	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,260 @@
+<tool id="column_join" name="Column Join" version="1.1.0">
+  <description></description>
+  <command interpreter="python">
+    column_join.py
+        --output=$output
+        --input1=$input1
+        --input2=$input2
+        --hinge=$hinge
+        --columns=$columns
+        #if $fill_empty_columns.fill_empty_columns_switch == "fill_empty":
+            --fill_options_file=$fill_options_file
+        #end if
+        #for $f in $file_chooser:
+            ${f.input}
+        #end for
+  </command>
+  <inputs>
+    <param name="input1" type="data" format="tabular" label="Choose the first file for the join" />
+    <param name="hinge" type="data_column" data_ref="input1" multiple="false" numerical="false" label="Use this column and columns to left the 'hinge' (matching data for each join)" help="All columns to left of selected column (plus selected column) will be used. Select 2 for pileup" />
+    <param name="columns" type="data_column" data_ref="input1" multiple="true" numerical="false" label="Include these column" help="Multi-select list - hold the appropriate key while clicking to select multiple columns" />
+    <conditional name="fill_empty_columns">
+      <param name="fill_empty_columns_switch" type="select" label="Fill empty columns">
+        <option value="no_fill" selected="True">No</option>
+        <option value="fill_empty">Yes</option>
+      </param>
+      <when value="no_fill" />
+      <when value="fill_empty">
+        <conditional name="do_fill_empty_columns">
+          <param name="column_fill_type" type="select" label="Fill Columns by">
+            <option value="single_fill_value" selected="True">Single fill value</option>
+            <option value="fill_value_by_column">Values by column</option>
+          </param>
+          <when value="single_fill_value">
+            <param type="text" name="fill_value" label="Fill value" value="." />
+          </when>
+          <when value="fill_value_by_column">
+            <repeat name="column_fill" title="Fill Column">
+              <param name="column_number" label="Column" type="data_column" data_ref="input1" />
+              <param type="text" name="fill_value" value="." />
+            </repeat>
+          </when>
+        </conditional>
+      </when>
+    </conditional>
+    <param name="input2" type="data" format="tabular" label="Choose the second file for the join" />
+    <repeat name="file_chooser" title="Additional Input">
+      <param name="input" label="Additional input file" type="data" format="tabular" />
+    </repeat>
+  </inputs>
+  <configfiles>
+    <configfile name="fill_options_file">&lt;%
+import json
+%&gt;
+#set $__fill_options = {}
+#if $fill_empty_columns['fill_empty_columns_switch'] == 'fill_empty':
+    #if $fill_empty_columns['do_fill_empty_columns']['column_fill_type'] == 'single_fill_value':
+        #set $__start_fill = $fill_empty_columns['do_fill_empty_columns']['fill_value'].value
+    #else:
+        #set $__start_fill = ""
+    #end if
+    #set $__fill_options['file1_columns'] = [ __start_fill for i in range( int( $input1.metadata.columns ) ) ]
+    #if $fill_empty_columns['do_fill_empty_columns']['column_fill_type'] == 'fill_value_by_column':
+        #for column_fill in $fill_empty_columns['do_fill_empty_columns']['column_fill']:
+            #set $__fill_options['file1_columns'][ int( column_fill['column_number'].value ) - 1 ] = column_fill['fill_value'].value
+        #end for
+    #end if
+#end if
+${json.dumps( __fill_options )}
+    </configfile>
+  </configfiles>
+  <outputs>
+    <data name="output" format="tabular" />
+  </outputs>
+  <tests>
+    <test>
+      <param name="input1" value="column_join_in1.pileup" ftype="pileup" />
+      <param name="hinge" value="2" />
+      <param name="columns" value="1,2,3,4,5,7" />
+      <param name="fill_empty_columns_switch" value="fill_empty" />
+      <param name="column_fill_type" value="single_fill_value" />
+      <param name="fill_value" value="?" />
+      <param name="input2" value="column_join_in2.pileup" ftype="pileup" />
+      <param name="input" value="column_join_in3.pileup" ftype="pileup" />
+      <output name="output" file="column_join_out1.pileup" ftype="tabular" />
+    </test>
+    <test>
+      <param name="input1" value="column_join_in4.pileup" ftype="pileup" />
+      <param name="hinge" value="2" />
+      <param name="columns" value="1,2,3,4" />
+      <param name="fill_empty_columns_switch" value="no_fill" />
+      <param name="input2" value="column_join_in5.pileup" ftype="pileup" />
+      <param name="input" value="column_join_in6.pileup" ftype="pileup" />
+      <output name="output" file="column_join_out2.pileup" ftype="tabular" />
+    </test>
+<!--  This test is failing for an unclear reason (the column values do not get
+      passed into the script), but passes in the browser
+    <test>
+      <param name="input1" value="column_join_in7.pileup" ftype="tabular" />
+      <param name="hinge" value="2" />
+      <param name="columns" value="3,4,5" />
+      <param name="fill_empty_columns_switch" value="fill_empty" />
+      <param name="column_fill_type" value="fill_value_by_column" />
+      <param name="column_number" value="5" />
+      <param name="fill_value" value="X" />
+      <param name="input2" value="column_join_in8.pileup" ftype="tabular" />
+      <param name="input" value="column_join_in9.pileup" ftype="tabular" />
+      <output name="output" file="column_join_out3.pileup" ftype="tabular" />
+    </test>
+-->
+    <test>
+      <param name="input1" value="column_join_in10.pileup" ftype="pileup" />
+      <param name="hinge" value="1" />
+      <param name="columns" value="2,7" />
+      <param name="fill_empty_columns_switch" value="no_fill" />
+      <param name="input2" value="column_join_in11.pileup" ftype="pileup" />
+      <param name="input" value="column_join_in12.pileup" ftype="pileup" />
+      <output name="output" file="column_join_out4.pileup" ftype="tabular" />
+    </test>
+    <test>
+      <!-- Test for handling missing column -->
+      <param name="input1" value="column_join_in13.tabular" ftype="tabular" />
+      <param name="hinge" value="1" />
+      <param name="columns" value="5" />
+      <param name="fill_empty_columns_switch" value="fill_empty" />
+      <param name="column_fill_type" value="single_fill_value" />
+      <param name="fill_value" value="0" />
+      <param name="input2" value="column_join_in14.tabular" ftype="tabular" />
+      <param name="input" value="column_join_in15.tabular" ftype="tabular" />
+      <output name="output" file="column_join_out5.tabular" ftype="tabular" />
+    </test>
+  </tests>
+  <help>
+**What it does**
+
+This tool allows you to join several files with the same column structure into one file, removing certain columns if necessary. The user needs to select a 'hinge', which is the number of left-most columns to match on. They also need to select the columns to include in the join, which should include the hinge columns, too.
+
+Note that the files are expected to have the same number of columns. If for some reason the join column is missing (this only applies to the last column(s)), the tool attempts to handle this situation by inserting an empty item (or the appropriate filler) for that column on that row. This could lead to the situation where a row has a hinge but entirely empty or filled columns, if the hinge exists in at least one file but every file that has it is missing the join column. Also, note that the tool does not distinguish between a file missing the hinge altogether and a file having the hinge but missing the column (in both cases the column would be empty or filled). There is an example of this below.
+
+-----
+
+**General Example**
+
+Given the following files::
+
+  FILE 1
+  chr2    1    T    6    .C...,     I$$III
+  chr2    2    G    6    ..N..,     III@II
+  chr2    3    C    7    ..C...,    I$IIIII
+  chr2    4    G    7    .G....,    I#IIIII
+  chr2    5    G    7    ...N..,    IIII#BI
+  chr2    6    A    7    ..T...,    I$IDIII
+  chr1    1    C    1    ^:.        I
+  chr1    2    G    2    .^:.       $I
+  chr1    3    A    2    ..         I%
+  chr1    4    C    2    ..         I$
+  chr1    5    T    3    ..^:.      I#I
+  chr1    6    G    3    ..^:,      I#I
+
+  FILE 2
+  chr1    3    T    1    ^:.        I
+  chr1    4    G    2    .^:.       $I
+  chr1    5    T    2    ..         I%
+  chr1    6    C    3    ..^:.      III
+  chr1    7    G    3    ..^:.      I#I
+  chr1    8    T    4    ...^:,     I#II
+  chr2    77   C    6    .G...,     I$$III
+  chr2    78   G    6    ..N..,     III@II
+  chr2    79   T    7    ..N...,    I$IIIII
+  chr2    80   C    7    .G....,    I#IIIII
+  chr2    81   G    7    ...A..,    IIII#BI
+  chr2    82   A    8    ...G...,   I$IDIIII
+  chr2    83   T    8    .A.....N   IIIIIIII
+  chr2    84   A    9    ......T.   I$IIIIIII
+
+  FILE 3
+  chr1    1    A    1    .          I
+  chr1    2    T    2    G.         I$
+  chr1    3    C    2    .,         I@
+  chr1    4    C    3    ..N        III
+  chr1    42   C    5    ...N^:.    III@I
+  chr1    43   C    5    .N..^:.    IIIII
+  chr1    44   T    5    .A..,      IA@II
+  chr1    45   A    6    .N...^:.   IIIII$
+  chr1    46   G    6    .GN..^:.   I@IIII
+  chr1    47   A    7    ....^:..,  IIIII$I
+  chr2    73   T    5    .N..,      II$II
+  chr2    74   A    5    ....,      IIIII
+  chr2    75   T    5    ....,      IIIII
+  chr2    76   T    5    ....,      IIIII
+  chr2    77   C    5    ....,      IIIBI
+  chr2    78   T    5    ....,      IDIII
+
+To join on columns 3 and 4 combining on columns 1 and 2, columns 1-4 should be selected for the 'Include these columns' option, and column 2 selected for the 'hinge'. With these settings, the following would be output::
+
+  chr1    1    C    1              A    1
+  chr1    2    G    2              T    2
+  chr1    3    A    2    T    1    C    2
+  chr1    4    C    2    G    2    C    3
+  chr1    5    T    3    T    2
+  chr1    6    G    3    C    3
+  chr1    7              G    3
+  chr1    8              T    4
+  chr1    42                       C    5
+  chr1    43                       C    5
+  chr1    44                       T    5
+  chr1    45                       A    6
+  chr1    46                       G    6
+  chr1    47                       A    7
+  chr2    1    T    6
+  chr2    2    G    6
+  chr2    3    C    7
+  chr2    4    G    7
+  chr2    5    G    7
+  chr2    6    A    7
+  chr2    73                       T    5
+  chr2    74                       A    5
+  chr2    75                       T    5
+  chr2    76                       T    5
+  chr2    77             C    6    C    5
+  chr2    78             G    6    T    5
+  chr2    79             T    7
+  chr2    80             C    7
+  chr2    81             G    7
+  chr2    82             A    8
+  chr2    83             T    8
+  chr2    84             A    9
+
+**Example with missing columns**
+
+Given the following input files::
+
+  FILE 1
+  1   A
+  2   B   b
+  4   C   c
+  5   D
+  6   E   e
+
+  FILE 2
+  1   M   m
+  2   N
+  3   O   o
+  4   P   p
+  5   Q
+  7   R   r
+
+if we join only column 3 using column 1 as the hinge and with a fill value of '0', this is what will be output::
+
+  1   0   m
+  2   b   0
+  3   0   o
+  4   c   p
+  5   0   0
+  6   e   0
+  7   0   r
+
+Row 5 appears in both files with the missing column, so it's got nothing but fill values in the output file.
+
+  </help>
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in1.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,30 @@
+chrM	1	A	G	25	0	25	1	^:.	I
+chrM	2	C	T	25	0	25	2	.N	I$
+chrM	3	T	G	27	0	25	2	..	II
+chrM	4	G	C	36	0	25	3	.^:.^:.	II+
+chrM	5	T	A	38	0	25	3	...	III
+chrM	6	T	A	39	0	25	4	.N..	I$II
+chrM	7	G	C	43	0	25	5	...^:.^:.	IIIII
+chrM	8	A	C	46	0	25	6	.....^:.	IIIIII
+chrM	9	T	G	53	0	25	8	..G...^:.^:.	IIIII$II
+chrM	10	T	G	56	0	25	9	........^:.	IIIIIIIII
+chrM	11	C	G	57	0	25	10	.........^:.	IIIIIIIIII
+chrM	12	T	C	61	0	25	11	..........^:.	IIIIIIIIIII
+chrM	13	T	G	79	0	25	15	.........^:.^:.^:.^:.^:.^:.	IIIIIIIIIIII#II
+chrM	14	A	C	58	0	25	18	.......G.........^:.	BIIIIIII+IIIIIIIII
+chrM	15	G	A	87	0	25	19	..N...............^:.^:.	DIIIIII$(IIIIIIIIIII
+chrM	16	G	C	88	0	25	20	.........N..........	IIIIIIIIIIIIIIIIIIII
+chrM	17	A	C	88	0	25	20	.........G..........	9IIIIIIIIIIIIIIIIIII
+chrM	18	G	A	89	0	25	20	...A................	@IIIIIIIIIIIIIIIIIII
+chrM	19	G	T	58	0	25	20	....T.............GG	IIIIIIIIIIIIIIIIII'A
+chrM	20	T	C	55	0	25	20	.........C........C.	IIIIIIIIIIIIIII2II#$
+chrM	21	C	T	87	0	25	20	..........G.........	IIIIIIIIIIIIIIIIIIII
+chrM	22	C	A	87	0	25	20	..........N.........	IIIIIIIIIIIIIIAIIIII
+chrM	23	A	G	87	0	25	20	.....T..............	9IIIIIIIIII0IIIIIIII
+chrM	24	A	T	89	0	25	20	.....N..N...........	III$IIII"IIIIICII#II
+chrM	25	G	A	57	0	25	21	...A................^:.	A@.$IIIIIIIFIIIIIIIII
+chrM	26	C	G	58	0	25	22	...N....A.............	IIHIDII&IIIIII@IIIIII
+chrM	27	T	A	99	0	25	23	....................^:.^:.^:.	IE8IFIII9IIIIIIIIIIIIII
+chrM	28	A	C	99	0	25	24	..G.....................	1FIIIIIIIIIIIIIIIIDEIIII
+chrM	29	G	C	58	0	25	25	........C.........NN....	;IIIIII+HII=III$III""IIII
+chrM	30	T	T	65	0	25	25	...C....................^:.	;I?&IAI0IIIIIIIIIIIIIIIII
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in10.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,26 @@
+0610009D07Rik	2	1.41	1.41	-0.24	12/12	2	1
+1110002N22Rik	2	1.70	1.70	-0.06	10/12	2	1
+1110008L16Rik	3	1.73	1.73	-0.54	12/12	2	1
+1110054O05Rik	1	1.55	1.55	1.14	5/12	1	1
+Actg1	2	4.24	4.24	2.36	4/12	2	1
+Actl6a	2	1.55	1.55	1.00	10/12	1	1
+Actn1	1	3.46	3.46	3.17	1/12	1	1
+Actn4	1	3.46	3.46	3.17	1/12	1	1
+Bnc2	1	2.00	2.00	1.67	3/12	1	1
+Bub3	2	1.89	1.89	1.02	9/12	2	1
+Cad	4	4.90	4.90	3.09	2/12	1	1
+Calm1;Calm3;Calm2	2	2.83	2.83	2.57	3/12	1	1
+E130012A19Rik	2	5.66	5.66	1.50	3/12	2	1
+E2f6	2	3.39	3.39	1.80	5/12	2	1
+Gm12620	1	3.46	3.46	3.17	1/12	1	1
+Gm13092;LOC677017	1	1.15	1.15	0.29	9/12	1	1
+Gm14173;Rpl37a;Gm4149	1	3.00	3.00	1.37	4/12	2	1
+Gm14393;2210418O10Rik;Gm14296;Gm14401;RP23-330D3.5	1	3.46	3.46	3.17	1/12	1	1
+Gm189	1	1.20	1.20	0.16	10/12	2	1
+Sfrs7	1	1.71	1.71	0.18	7/12	2	1
+Sin3a	1	1.71	1.71	-0.12	7/12	2	1
+Ski	1	2.45	2.45	2.13	2/12	1	1
+Skil	1	2.00	2.00	1.03	3/12	1	1
+Tubb2c	1	2.00	2.00	1.67	3/12	1	1
+Tubb2c-ps1	1	12.00	12.00	3.17	1/12	2	1
+Zscan4f	2	1.70	1.70	1.00	10/12	2	1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in11.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,36 @@
+0610009D07Rik	3	1.73	1.73	1.15	12/12	2	1
+0610010K14Rik	1	1.41	1.41	0.96	6/12	1	1
+1110002N22Rik	3	2.08	2.08	0.74	10/12	2	1
+1110008L16Rik	1	1.00	1.00	-1.35	12/12	1	1
+Acta1	2	1.54	1.54	0.63	11/12	2	1
+Actb	1	1.33	1.33	0.00	9/12	2	1
+Actg1	1	3.00	3.00	0.87	4/12	2	1
+Actl6a	1	1.10	1.10	-0.33	10/12	1	1
+Actl6b	1	2.45	2.45	2.13	2/12	1	1
+Bnc2	1	2.00	2.00	1.67	3/12	1	1
+Bptf	1	3.46	3.46	3.17	1/12	1	1
+Brip1	1	1.22	1.22	-0.19	8/12	1	1
+Brms1l	1	12.00	12.00	3.17	1/12	2	1
+Btf3;Gm3531	1	2.00	2.00	1.67	3/12	1	1
+Bub3	3	2.00	2.00	2.13	9/12	1	1
+C330007P06Rik	1	2.45	2.45	2.13	2/12	1	1
+Cad	1	2.45	2.45	0.50	2/12	1	1
+Calm1;Calm3;Calm2	1	2.00	2.00	1.03	3/12	1	1
+Cbx1	2	3.39	3.39	2.24	5/12	2	1
+E2f6	1	2.40	2.40	0.53	5/12	2	1
+Eed	1	1.20	1.20	-0.47	10/12	2	1
+Gm10079	2	1.41	1.41	0.16	12/12	1	1
+Gm11230	2	1.48	1.48	1.21	11/12	1	1
+Gm13072;Trmt112	1	3.46	3.46	3.17	1/12	1	1
+Gm13092;LOC677017	1	1.33	1.33	0.29	9/12	2	1
+Gm14231	1	1.31	1.31	0.51	7/12	1	1
+Gm14456;Tpt1	1	2.00	2.00	1.67	3/12	1	1
+Gm15501;Rps8	1	1.55	1.55	1.14	5/12	1	1
+Gm189	1	1.20	1.20	0.16	10/12	2	1
+Sfrs11	3	1.73	1.73	1.15	12/12	2	1
+Sin3a	4	3.43	3.43	1.93	7/12	2	1
+Sirt7	1	2.00	2.00	1.67	3/12	1	1
+Skiv2l2	12	3.46	3.46	-0.72	12/12	2	1
+Tubb2b	4	2.00	2.00	0.49	12/12	2	1
+Zscan4e	1	1.41	1.41	0.63	6/12	1	1
+Zscan4f	2	1.70	1.70	1.00	10/12	2	1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in12.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,36 @@
+0610009D07Rik	4	2.00	2.00	2.54	12/12	2	1
+0610010K14Rik	1	1.41	1.41	0.96	6/12	1	1
+1110002N22Rik	2	1.70	1.70	-0.06	10/12	2	1
+1110008L16Rik	6	2.45	2.45	0.68	12/12	2	1
+1110037F02Rik	1	2.45	2.45	2.13	2/12	1	1
+1190005F20Rik	4	2.18	2.18	0.28	11/12	2	1
+Acot8	1	2.00	2.00	0.07	6/12	2	1
+Acta1	2	1.54	1.54	0.63	11/12	2	1
+Actb	2	1.89	1.89	1.35	9/12	2	1
+Actl6b	1	6.00	6.00	2.13	2/12	2	1
+Bend3	1	1.15	1.33	-0.51	9/12	1	1.33
+Bend5	1	3.46	3.46	3.17	1/12	1	1
+Brip1	2	1.73	1.73	0.58	8/12	1	1
+Btf3;Gm3531	1	4.00	4.00	1.67	3/12	2	1
+Bub3	1	1.33	1.33	-0.09	9/12	2	1
+C130039O16Rik	1	2.45	2.45	2.13	2/12	1	1
+C1d	1	1.73	1.73	0.87	4/12	1	1
+Caprin1	2	2.42	2.42	0.51	7/12	2	1
+Cbx3	2	1.54	1.54	0.75	11/12	2	1
+Eed	1	1.10	1.10	-0.47	10/12	1	1
+Efha1	1	3.46	3.46	3.17	1/12	1	1
+Exosc1	3	1.73	1.73	1.29	12/12	2	1
+Exosc10	25	5.00	5.00	1.03	12/12	2	1
+Gm189	2	1.70	1.70	2.12	10/12	2	1
+Gm3200	1	2.45	2.45	2.13	2/12	1	1
+Gm9855;Tdg	2	1.70	1.70	1.37	10/12	2	1
+Sfrs11	4	2.00	2.00	2.54	12/12	2	1
+Sfrs12	2	5.66	5.66	2.57	3/12	2	1
+Sin3a	1	1.31	1.31	-0.12	7/12	1	1
+Sirt7	1	2.00	2.00	1.67	3/12	1	1
+Skiv2l2	34	5.83	5.83	0.68	12/12	2	1
+Tubb2b	3	1.73	1.73	-0.10	12/12	2	1
+Tubb4	1	1.15	1.15	0.29	9/12	1	1
+Zscan4-ps2	1	12.00	12.00	3.17	1/12	2	1
+Zscan4e	2	2.83	2.83	2.12	6/12	2	1
+Zscan4f	2	1.70	1.70	1.00	10/12	2	1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in13.tabular	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,6 @@
+alpha	beta	gamma	delta
+1	A	I	a	i
+2	B	II	b	ii
+5	C	III	c
+7	D	IV	d	iii
+11	E	V	e	iv
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in14.tabular	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,11 @@
+alpha	beta	gamma	delta	epsilon
+1	AA	I2	aa	i2
+2	BB	II2	bb
+3	CC	III2	cc	ii2
+4	DD	IV2	dd	iii2
+6	EE	V2	ee	iv2
+7	FF	VI2	ff
+8	GG	VII2	gg	v2
+9	HH	VIII2	hh	vi2
+10	II	IX2	ii	vii2
+11	JJ	X2	jj	viii2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in15.tabular	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,11 @@
+alpha	beta	gamma
+2	BBB	II3	bbb	i3
+3	CCC	III3	ccc	ii3
+4	DDD	IV2	ddd	iii3
+5	EEE	V3	eee
+6	FFF	VI3	fff	iv3
+7	GGG	VII3	ggg	v3
+8	HHH	VIII3	hhh
+9	III	IX3	iii	vi3
+10	JJJ	X3	jjj	vii3
+11	LLL	XI3	lll
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in2.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,30 @@
+chrM	1	T	G	25	0	25	1	^:.	I
+chrM	2	C	A	25	0	25	1	.	I
+chrM	3	C	A	25	0	25	2	..	I+
+chrM	4	G	A	36	0	25	3	.^:.^:.	II+
+chrM	5	A	T	36	0	25	3	...	IDI
+chrM	6	A	G	36	0	25	4	..T.	IIBI
+chrM	7	C	G	42	0	25	5	...^:.^:.	ID@II
+chrM	8	T	T	45	0	25	6	.....^:.	III$BI
+chrM	9	C	A	51	0	25	8	......^:.^:.	$#III+III
+chrM	10	G	G	54	0	25	9	........^:.	III$III#I
+chrM	11	T	C	57	0	25	10	.........^:.	IIIDBIIIII
+chrM	12	A	T	60	0	25	11	..........^:.	IIIIIIIII$I
+chrM	13	A	T	78	0	25	18	..A.........^:.^:.^:.^:.^:.^:.	III#$IIIIII+IIDIIB
+chrM	14	G	A	56	0	25	19	........T.........^:.	BIIIIIII+IIII$IBIII
+chrM	15	G	A	87	0	25	20	..................^:.^:.	DIIIIIII(IIIIIIIIIII
+chr1	1	T	T	87	0	25	20	.............T......	IIIIIIIIIIII$DIIIIII
+chr1	2	A	T	87	0	25	20	......G.............	9IIIIIIIIIIIIICBIIII
+chr1	3	A	G	87	0	25	20	....A...............	@IIIIIIIII#IIII#IIII
+chr1	4	A	T	55	0	25	20	..................GG	IIIII@II$IIIIIIIII'A
+chr1	5	C	A	54	0	25	20	.......A..........C.	IIIIIIIIIDIIIII2II#$
+chr1	6	G	T	87	0	25	20	.............A......	IIIIIIIBIIIIII#IIIII
+chr1	7	A	C	87	0	25	20	..........C.........	IIIII+IIIIIIIIAIIIII
+chr1	8	G	A	87	0	25	20	....G.........T.....	9IIIIIIIIII0IIIIIIII
+chr1	9	A	T	87	0	25	20	........N...........	IIII$III"IIIIICIIIII
+chr1	10	G	A	57	0	25	21	...A................^:.	A@.$IIIIIIIFIIIIIIIII
+chr1	11	C	A	57	0	25	22	....G...A.............	IIHIDII&IIIIII#III$II
+chr1	12	T	A	99	0	25	24	.....................^:.^:.^:.	IE8IFIII9IIIIIIIIIIIIIII
+chr1	13	A	C	99	0	25	25	...N.....................	1FIIIIIIIIIIIIIIIIDEII$II
+chr1	14	G	C	55	0	25	25	....G..............NN....	;IIIBIII+HII=IIIIII""IIII
+chr1	15	T	G	68	0	25	28	...C.......N............^:.	;I?&IAI0IIIIIII@II#$II@II
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in3.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,30 @@
+chr1	1	G	G	25	0	25	1	^:.	I
+chr1	2	T	T	25	0	25	1	.	I
+chr1	3	T	T	27	0	25	1	.	I
+chr1	4	A	A	36	0	25	2	.^:.	I+
+chr1	5	A	A	36	0	25	3	...	III
+chr1	6	T	T	36	0	25	4	.N..	II#I
+chr1	7	G	G	42	0	25	5	...^:.^:.	IIIII
+chr1	8	T	T	45	0	25	6	.....^:.	IIIIII
+chr1	9	A	A	51	0	25	7	.....^:.^:.	IBIIIII
+chr1	10	G	G	54	0	25	9	........^:.	IIIIIIIII
+chr1	11	C	C	57	0	25	10	.........^:.	IIIIIIII"I
+chr1	12	T	T	60	0	25	11	..........^:.	IIIIIIDIIII
+chr1	13	T	T	78	0	25	17	...........^:.^:.^:.^:.^:.^:.	IIII"$IIIIIIIIIII
+chr1	14	A	A	56	0	25	18	.......G.........^:.	BIIIIIII+IIIIIIIII
+chr1	15	A	A	87	0	25	20	....N.............^:.^:.	DIII$III(IIIIIIIIIII
+chr4	1	T	T	90	0	25	20	..........N.........	IIIIIIIII$IIIIIIIIII
+chr4	2	A	A	87	0	25	20	.......C............	9IIIIIIIII"IIIIIIIII
+chr4	3	A	A	34	0	25	20	......G.............	@IIIIIIIIIIII#IIIIII
+chr4	4	T	T	55	0	25	21	...........N.......GG	IIIII@IIIIIIIIIIIII'A
+chr4	5	A	A	54	0	25	21	...N...............C.	IIII"IIIIIIIIIII2II#$
+chr4	6	T	T	87	0	25	21	................N....	IIIIIIII$IIIIIIIIIIII
+chr4	7	A	A	80	0	25	21	.....G...............	III$IIIIIIIIIIIAIIIII
+chr4	8	A	A	87	0	25	22	..N...................	9IIIIIIIIII0IIIII"$III
+chr4	9	A	A	87	0	25	22	.GG.......N...........	IIII$IIIII"IIIIICIIIII
+chr4	10	G	G	57	0	25	23	.....A................^:.	A@.$IIIIIIIIIFIIIIIIIII
+chr4	11	C	C	57	0	25	25	.......A.....GN..........	IIHIDII&III#IIIIIIIIIIIII
+chr4	12	A	A	99	0	25	26	.......................^:.^:.^:.	IE8IFIII9IIIIIII$IIIIIIIII
+chr4	13	A	A	99	0	25	27	........N..................	1FIIIIII$IIIIIIIIIIIIDEIIII
+chr4	14	G	G	55	0	25	28	..N...................NN....	;III$I#IIII+HII=IIIIII""IIII
+chr4	15	G	G	68	0	25	30	...C....N..G.................^:.	;I?&IAI0IIIIIII$#I$@IIIIIIIIII
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in4.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,25 @@
+chr2	1	T	6	.C...,	I$$III
+chr2	2	G	6	..N..,	III@II
+chr2	3	C	7	..C...,	I$IIIII
+chr2	4	G	7	.G....,	I#IIIII
+chr2	5	G	7	...N..,	IIII#BI
+chr2	6	A	7	..T...,	I$IDIII
+chr2	7	T	8	...C...,	IIIBD$II
+chr2	8	A	8	..A....,	IBI#IIII
+chr2	9	C	9	.GA..N..,	I$IBIII#I
+chr2	10	T	9	........,	I$II#IIII
+chr2	11	C	10	.>>..T...,	IIII@I$I$I
+chr2	12	G	10	.N..G....,	III$IIIIII
+chr2	13	A	11	....A..T..,	IIIIII#I@II
+chr1	1	C	1	^:.	I
+chr1	2	G	2	.^:.	$I
+chr1	3	A	2	..	I%
+chr1	4	C	2	..	I$
+chr1	5	T	3	..^:.	I#I
+chr1	6	G	3	..^:,	I#I
+chr1	7	C	4	.N.,	IIII
+chr1	8	A	4	...,	I$II
+chr1	9	T	5	..C.,	I#IDI
+chr1	10	G	5	N...,	IBII@
+chr1	11	A	5	.C..,	I$II#
+chr1	12	C	5	..N.,	I$III
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in5.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,25 @@
+chr1	42	T	1	^:.	I
+chr1	43	G	2	.^:.	$I
+chr1	44	T	2	..	I%
+chr1	45	C	3	..^:.	III
+chr1	46	G	3	..^:.	I#I
+chr1	47	T	4	...^:,	I#II
+chr1	48	A	4	.N.,	IIII
+chr1	49	C	5	....,	IIIII
+chr1	50	A	5	..G.,	IIIDI
+chr1	51	A	5	A...,	IBIII
+chr1	52	A	5	....,	IIII#
+chr1	53	G	5	..N.,	I$III
+chr2	77	C	6	.G...,	I$$III
+chr2	78	G	6	..N..,	III@II
+chr2	79	T	7	..N...,	I$IIIII
+chr2	80	C	7	.G....,	I#IIIII
+chr2	81	G	7	...A..,	IIII#BI
+chr2	82	A	8	...G...,	I$IDIIII
+chr2	83	A	8	...T...,	IIIBD$II
+chr2	84	T	8	..A....,	IBI#IIII
+chr2	85	G	8	.GA....,	IIBIII#I
+chr2	86	C	9	........,	I$II#IIII
+chr2	87	G	9	....T...,	IIIII$I$I
+chr2	88	G	10	.N..G....,	III$IIIIII
+chr2	89	G	10	...A..T..,	IIIII#I@II
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in6.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,25 @@
+chr1	42	C	1	^:.	I
+chr1	43	C	2	.^:.	II
+chr1	44	T	2	..	II
+chr1	45	A	3	..^:.	III
+chr1	46	G	4	...^:.	IIII
+chr1	47	A	5	....^:,	IIIII
+chr1	48	T	5	...N,	I#III
+chr1	49	G	5	....,	IIIII
+chr1	50	A	5	.G..,	IIIII
+chr1	51	G	5	....,	IIIII
+chr2	52	T	5	.N..,	II$II
+chr2	53	A	5	....,	IIIII
+chr2	54	T	5	....,	IIIII
+chr2	55	T	5	....,	IIIII
+chr2	56	C	5	....,	IIIBI
+chr2	57	T	5	....,	IDIII
+chr2	58	T	6	.N...,	IIIIII
+chr2	59	A	6	.....,	IIII$I
+chr3	60	C	6	...G.,	I#IIII
+chr3	61	T	6	..N..,	IIIIII
+chr3	62	C	6	...A.,	IIIIII
+chr3	63	C	7	.N....,	IIIIIII
+chr3	64	A	7	...G..,	IIIII$I
+chr3	65	T	7	...AA.,	IIIII@@
+chr3	66	A	7	....N.,	IIIIIII
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in7.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,32 @@
+chr2	1	T	6	.C...,	I$$III
+chr2	2	G	6	..N..,	III@II
+chr2	3	C	7	..C...,	I$IIIII
+chr2	4	G	7	.G....,	I#IIIII
+chr2	5	G	7	...N..,	IIII#BI
+chr2	6	A	7	..T...,	I$IDIII
+chr2	7	T	8	...C...,	IIIBD$II
+chr2	8	A	8	..A....,	IBI#IIII
+chr2	9	C	9	.GA..N..,	I$IBIII#I
+chr2	10	T	9	........,	I$II#IIII
+chr2	11	C	10	.>>..T...,	IIII@I$I$I
+chr2	12	G	10	.N..G....,	III$IIIIII
+chr2	13	A	11	....A..T..,	IIIIII#I@II
+chr2	14	G	11	..N.......	IICIBII@AII
+chr2	15	C	11	A.....NG..	IIIIIDIIIII
+chr2	16	T	11	...C.....G	I$IIIB@IIIC
+chr2	17	C	12	G......TN..	IIAII@IIII$I
+chr2	18	A	12	N......G..A	IIIBIII$IIII
+chr2	19	A	13	.......NN...	IIIIIIBIII$$@
+chr2	20	C	13	..GT.......N	IIIIABIIC$III
+chr1	1	C	1	^:.	I
+chr1	2	G	2	.^:.	$I
+chr1	3	A	2	..	I%
+chr1	4	C	2	..	I$
+chr1	5	T	3	..^:.	I#I
+chr1	6	G	3	..^:,	I#I
+chr1	7	C	4	.N.,	IIII
+chr1	8	A	4	...,	I$II
+chr1	9	T	5	..C.,	I#IDI
+chr1	10	G	5	N...,	IBII@
+chr1	11	A	5	.C..,	I$II#
+chr1	12	C	5	..N.,	I$III
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in8.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,23 @@
+chr1	42	T	1	^:.	I
+chr1	43	G	2	.^:.	$I
+chr1	44	T	2	..	I%
+chr1	45	C	3	..^:.	III
+chr1	46	G	3	..^:.	I#I
+chr1	47	T	4	...^:,	I#II
+chr1	48	A	4	.N.,	IIII
+chr1	49	C	5	....,	IIIII
+chr1	50	A	5	..G.,	IIIDI
+chr1	51	A	5	A...,	IBIII
+chr1	52	A	5	....,	IIII#
+chr1	53	G	5	..N.,	I$III
+chr2	77	C	6	.G...,	I$$III
+chr2	78	G	6	..N..,	III@II
+chr2	79	T	7	..N...,	I$IIIII
+chr2	80	C	7	.G....,	I#IIIII
+chr2	81	G	7	...A..,	IIII#BI
+chr2	82	A	8	...G...,	I$IDIIII
+chr2	83	A	8	...T...,	IIIBD$II
+chr2	84	T	8	..A....,	IBI#IIII
+chr2	85	G	8	.GA....,	IIBIII#I
+chr2	86	C	9	........,	I$II#IIII
+chr2	87	G	9	....T...,	IIIII$I$I
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_in9.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,25 @@
+chr1	43	C	2	.^:.	II
+chr3	65	T	7	...AA.,	IIIII@@
+chr1	44	T	2	..	II
+chr2	54	T	5	....,	IIIII
+chr1	45	A	3	..^:.	III
+chr1	47	A	5	....^:,	IIIII
+chr1	50	A	5	.G..,	IIIII
+chr1	51	G	5	....,	IIIII
+chr2	52	T	5	.N..,	II$II
+chr2	53	A	5	....,	IIIII
+chr3	60	C	6	...G.,	I#IIII
+chr3	61	T	6	..N..,	IIIIII
+chr1	49	G	5	....,	IIIII
+chr2	55	T	5	....,	IIIII
+chr2	56	C	5	....,	IIIBI
+chr2	58	T	6	.N...,	IIIIII
+chr2	59	A	6	.....,	IIII$I
+chr3	62	C	6	...A.,	IIIIII
+chr1	42	C	1	^:.	I
+chr3	63	C	7	.N....,	IIIIIII
+chr1	48	T	5	...N,	I#III
+chr1	46	G	4	...^:.	IIII
+chr3	64	A	7	...G..,	IIIII$I
+chr3	66	A	7	....N.,	IIIIIII
+chr2	57	T	5	....,	IDIII
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_out1.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,60 @@
+chr1	1	?	?	?	?	T	T	87	25	G	G	25	25
+chr1	2	?	?	?	?	A	T	87	25	T	T	25	25
+chr1	3	?	?	?	?	A	G	87	25	T	T	27	25
+chr1	4	?	?	?	?	A	T	55	25	A	A	36	25
+chr1	5	?	?	?	?	C	A	54	25	A	A	36	25
+chr1	6	?	?	?	?	G	T	87	25	T	T	36	25
+chr1	7	?	?	?	?	A	C	87	25	G	G	42	25
+chr1	8	?	?	?	?	G	A	87	25	T	T	45	25
+chr1	9	?	?	?	?	A	T	87	25	A	A	51	25
+chr1	10	?	?	?	?	G	A	57	25	G	G	54	25
+chr1	11	?	?	?	?	C	A	57	25	C	C	57	25
+chr1	12	?	?	?	?	T	A	99	25	T	T	60	25
+chr1	13	?	?	?	?	A	C	99	25	T	T	78	25
+chr1	14	?	?	?	?	G	C	55	25	A	A	56	25
+chr1	15	?	?	?	?	T	G	68	25	A	A	87	25
+chr4	1	?	?	?	?	?	?	?	?	T	T	90	25
+chr4	2	?	?	?	?	?	?	?	?	A	A	87	25
+chr4	3	?	?	?	?	?	?	?	?	A	A	34	25
+chr4	4	?	?	?	?	?	?	?	?	T	T	55	25
+chr4	5	?	?	?	?	?	?	?	?	A	A	54	25
+chr4	6	?	?	?	?	?	?	?	?	T	T	87	25
+chr4	7	?	?	?	?	?	?	?	?	A	A	80	25
+chr4	8	?	?	?	?	?	?	?	?	A	A	87	25
+chr4	9	?	?	?	?	?	?	?	?	A	A	87	25
+chr4	10	?	?	?	?	?	?	?	?	G	G	57	25
+chr4	11	?	?	?	?	?	?	?	?	C	C	57	25
+chr4	12	?	?	?	?	?	?	?	?	A	A	99	25
+chr4	13	?	?	?	?	?	?	?	?	A	A	99	25
+chr4	14	?	?	?	?	?	?	?	?	G	G	55	25
+chr4	15	?	?	?	?	?	?	?	?	G	G	68	25
+chrM	1	A	G	25	25	T	G	25	25	?	?	?	?
+chrM	2	C	T	25	25	C	A	25	25	?	?	?	?
+chrM	3	T	G	27	25	C	A	25	25	?	?	?	?
+chrM	4	G	C	36	25	G	A	36	25	?	?	?	?
+chrM	5	T	A	38	25	A	T	36	25	?	?	?	?
+chrM	6	T	A	39	25	A	G	36	25	?	?	?	?
+chrM	7	G	C	43	25	C	G	42	25	?	?	?	?
+chrM	8	A	C	46	25	T	T	45	25	?	?	?	?
+chrM	9	T	G	53	25	C	A	51	25	?	?	?	?
+chrM	10	T	G	56	25	G	G	54	25	?	?	?	?
+chrM	11	C	G	57	25	T	C	57	25	?	?	?	?
+chrM	12	T	C	61	25	A	T	60	25	?	?	?	?
+chrM	13	T	G	79	25	A	T	78	25	?	?	?	?
+chrM	14	A	C	58	25	G	A	56	25	?	?	?	?
+chrM	15	G	A	87	25	G	A	87	25	?	?	?	?
+chrM	16	G	C	88	25	?	?	?	?	?	?	?	?
+chrM	17	A	C	88	25	?	?	?	?	?	?	?	?
+chrM	18	G	A	89	25	?	?	?	?	?	?	?	?
+chrM	19	G	T	58	25	?	?	?	?	?	?	?	?
+chrM	20	T	C	55	25	?	?	?	?	?	?	?	?
+chrM	21	C	T	87	25	?	?	?	?	?	?	?	?
+chrM	22	C	A	87	25	?	?	?	?	?	?	?	?
+chrM	23	A	G	87	25	?	?	?	?	?	?	?	?
+chrM	24	A	T	89	25	?	?	?	?	?	?	?	?
+chrM	25	G	A	57	25	?	?	?	?	?	?	?	?
+chrM	26	C	G	58	25	?	?	?	?	?	?	?	?
+chrM	27	T	A	99	25	?	?	?	?	?	?	?	?
+chrM	28	A	C	99	25	?	?	?	?	?	?	?	?
+chrM	29	G	C	58	25	?	?	?	?	?	?	?	?
+chrM	30	T	T	65	25	?	?	?	?	?	?	?	?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_out2.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,65 @@
+chr1	1	C	1				
+chr1	2	G	2				
+chr1	3	A	2				
+chr1	4	C	2				
+chr1	5	T	3				
+chr1	6	G	3				
+chr1	7	C	4				
+chr1	8	A	4				
+chr1	9	T	5				
+chr1	10	G	5				
+chr1	11	A	5				
+chr1	12	C	5				
+chr1	42			T	1	C	1
+chr1	43			G	2	C	2
+chr1	44			T	2	T	2
+chr1	45			C	3	A	3
+chr1	46			G	3	G	4
+chr1	47			T	4	A	5
+chr1	48			A	4	T	5
+chr1	49			C	5	G	5
+chr1	50			A	5	A	5
+chr1	51			A	5	G	5
+chr1	52			A	5		
+chr1	53			G	5		
+chr2	1	T	6				
+chr2	2	G	6				
+chr2	3	C	7				
+chr2	4	G	7				
+chr2	5	G	7				
+chr2	6	A	7				
+chr2	7	T	8				
+chr2	8	A	8				
+chr2	9	C	9				
+chr2	10	T	9				
+chr2	11	C	10				
+chr2	12	G	10				
+chr2	13	A	11				
+chr2	52					T	5
+chr2	53					A	5
+chr2	54					T	5
+chr2	55					T	5
+chr2	56					C	5
+chr2	57					T	5
+chr2	58					T	6
+chr2	59					A	6
+chr2	77			C	6		
+chr2	78			G	6		
+chr2	79			T	7		
+chr2	80			C	7		
+chr2	81			G	7		
+chr2	82			A	8		
+chr2	83			A	8		
+chr2	84			T	8		
+chr2	85			G	8		
+chr2	86			C	9		
+chr2	87			G	9		
+chr2	88			G	10		
+chr2	89			G	10		
+chr3	60					C	6
+chr3	61					T	6
+chr3	62					C	6
+chr3	63					C	7
+chr3	64					A	7
+chr3	65					T	7
+chr3	66					A	7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_out3.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,70 @@
+chr1	1	C	1	^:.			X			X
+chr1	2	G	2	.^:.			X			X
+chr1	3	A	2	..			X			X
+chr1	4	C	2	..			X			X
+chr1	5	T	3	..^:.			X			X
+chr1	6	G	3	..^:,			X			X
+chr1	7	C	4	.N.,			X			X
+chr1	8	A	4	...,			X			X
+chr1	9	T	5	..C.,			X			X
+chr1	10	G	5	N...,			X			X
+chr1	11	A	5	.C..,			X			X
+chr1	12	C	5	..N.,			X			X
+chr1	42			X	T	1	^:.	C	1	^:.
+chr1	43			X	G	2	.^:.	C	2	.^:.
+chr1	44			X	T	2	..	T	2	..
+chr1	45			X	C	3	..^:.	A	3	..^:.
+chr1	46			X	G	3	..^:.	G	4	...^:.
+chr1	47			X	T	4	...^:,	A	5	....^:,
+chr1	48			X	A	4	.N.,	T	5	...N,
+chr1	49			X	C	5	....,	G	5	....,
+chr1	50			X	A	5	..G.,	A	5	.G..,
+chr1	51			X	A	5	A...,	G	5	....,
+chr1	52			X	A	5	....,			X
+chr1	53			X	G	5	..N.,			X
+chr2	1	T	6	.C...,			X			X
+chr2	2	G	6	..N..,			X			X
+chr2	3	C	7	..C...,			X			X
+chr2	4	G	7	.G....,			X			X
+chr2	5	G	7	...N..,			X			X
+chr2	6	A	7	..T...,			X			X
+chr2	7	T	8	...C...,			X			X
+chr2	8	A	8	..A....,			X			X
+chr2	9	C	9	.GA..N..,			X			X
+chr2	10	T	9	........,			X			X
+chr2	11	C	10	.>>..T...,			X			X
+chr2	12	G	10	.N..G....,			X			X
+chr2	13	A	11	....A..T..,			X			X
+chr2	14	G	11	..N.......			X			X
+chr2	15	C	11	A.....NG..			X			X
+chr2	16	T	11	...C.....G			X			X
+chr2	17	C	12	G......TN..			X			X
+chr2	18	A	12	N......G..A			X			X
+chr2	19	A	13	.......NN...			X			X
+chr2	20	C	13	..GT.......N			X			X
+chr2	52			X			X	T	5	.N..,
+chr2	53			X			X	A	5	....,
+chr2	54			X			X	T	5	....,
+chr2	55			X			X	T	5	....,
+chr2	56			X			X	C	5	....,
+chr2	57			X			X	T	5	....,
+chr2	58			X			X	T	6	.N...,
+chr2	59			X			X	A	6	.....,
+chr2	77			X	C	6	.G...,			X
+chr2	78			X	G	6	..N..,			X
+chr2	79			X	T	7	..N...,			X
+chr2	80			X	C	7	.G....,			X
+chr2	81			X	G	7	...A..,			X
+chr2	82			X	A	8	...G...,			X
+chr2	83			X	A	8	...T...,			X
+chr2	84			X	T	8	..A....,			X
+chr2	85			X	G	8	.GA....,			X
+chr2	86			X	C	9	........,			X
+chr2	87			X	G	9	....T...,			X
+chr3	60			X			X	C	6	...G.,
+chr3	61			X			X	T	6	..N..,
+chr3	62			X			X	C	6	...A.,
+chr3	63			X			X	C	7	.N....,
+chr3	64			X			X	A	7	...G..,
+chr3	65			X			X	T	7	...AA.,
+chr3	66			X			X	A	7	....N.,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_out4.pileup	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,65 @@
+0610009D07Rik	2	2	3	2	4	2
+0610010K14Rik			1	1	1	1
+1110002N22Rik	2	2	3	2	2	2
+1110008L16Rik	3	2	1	1	6	2
+1110037F02Rik					1	1
+1110054O05Rik	1	1				
+1190005F20Rik					4	2
+Acot8					1	2
+Acta1			2	2	2	2
+Actb			1	2	2	2
+Actg1	2	2	1	2		
+Actl6a	2	1	1	1		
+Actl6b			1	1	1	2
+Actn1	1	1				
+Actn4	1	1				
+Bend3					1	1
+Bend5					1	1
+Bnc2	1	1	1	1		
+Bptf			1	1		
+Brip1			1	1	2	1
+Brms1l			1	2		
+Btf3;Gm3531			1	1	1	2
+Bub3	2	2	3	1	1	2
+C1d					1	1
+C130039O16Rik					1	1
+C330007P06Rik			1	1		
+Cad	4	1	1	1		
+Calm1;Calm3;Calm2	2	1	1	1		
+Caprin1					2	2
+Cbx1			2	2		
+Cbx3					2	2
+E2f6	2	2	1	2		
+E130012A19Rik	2	2				
+Eed			1	2	1	1
+Efha1					1	1
+Exosc1					3	2
+Exosc10					25	2
+Gm189	1	2	1	2	2	2
+Gm3200					1	1
+Gm9855;Tdg					2	2
+Gm10079			2	1		
+Gm11230			2	1		
+Gm12620	1	1				
+Gm13072;Trmt112			1	1		
+Gm13092;LOC677017	1	1	1	2		
+Gm14173;Rpl37a;Gm4149	1	2				
+Gm14231			1	1		
+Gm14393;2210418O10Rik;Gm14296;Gm14401;RP23-330D3.5	1	1				
+Gm14456;Tpt1			1	1		
+Gm15501;Rps8			1	1		
+Sfrs7	1	2				
+Sfrs11			3	2	4	2
+Sfrs12					2	2
+Sin3a	1	2	4	2	1	1
+Sirt7			1	1	1	1
+Ski	1	1				
+Skil	1	1				
+Skiv2l2			12	2	34	2
+Tubb2b			4	2	3	2
+Tubb2c	1	1				
+Tubb2c-ps1	1	2				
+Tubb4					1	1
+Zscan4-ps2					1	2
+Zscan4e			1	1	2	2
+Zscan4f	2	2	2	2	2	2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/column_join_out5.tabular	Tue Apr 01 09:30:45 2014 -0400
@@ -0,0 +1,12 @@
+1	i	i2	0
+2	ii	0	i3
+3	0	ii2	ii3
+4	0	iii2	iii3
+5	0	0	0
+6	0	iv2	iv3
+7	iii	0	v3
+8	0	v2	0
+9	0	vi2	vi3
+10	0	vii2	vii3
+11	iv	viii2	0
+alpha	0	epsilon	0