使用bash合并具有缺失值的两个数据表

时间:2014-12-17 01:26:17

标签: bash text merge

我正在寻找一个可以合并包含表格的两个文件的脚本。 这些列是单个样品的细菌计数,而行包含细菌的名称。我不能对它们进行排序和合并,因为有些细菌只出现在一个文件中而不出现在另一个文件中。如果是这种情况,我想用零填充行。

以下是一个例子:

档案1

Header                         S1    S2    S3    S4
Acetobacterium submarinus     1350  1000   1541 1541
Abiotrophia defectiva         100   110    112  166
Acetobacterium tundrae         2     1      0     0

文件2

Header                         S5    S6     S7    S8
Acholeplasma cavigenitalium   100    90    88    120
Acetobacterium woodii          2     3      4     0
Acetobacterium submarinus     500   600    400   480

结果文件应该(按字母顺序排序)

Header                         S1    S2    S3    S4    S5    S6     S7    S8
Abiotrophia defectiva         100   110    112  166     0     0     0      0
Acetobacterium submarinus     1350  1000   1541  1541  500   600    400   480
Acetobacterium tundrae         2     1      0     0     0     0      0     0
Acetobacterium woodii          0     0      0     0     2     3      4     0
Acholeplasma cavigenitalium    0     0      0     0    100    90    88    120

有什么想法吗?

我知道粘贴功能可以通过第一列合并文件,但我不确定如何处理丢失的物种。

更新 这是两个示例数据集。列号与原始数据集中的相同,我只是缩短了行数。

https://www.dropbox.com/s/h46nwjwwfdyzwqr/Class_Level_Aggregate_Counts-1.csv?dl=0 https://www.dropbox.com/s/x8wtdxl45bej729/Class_Level_Aggregate_Counts-2.csv?dl=0

2 个答案:

答案 0 :(得分:0)

您应该将join-a 1 2-e '0'-o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5'选项一起使用:

join -a 1 -a 2 -e '0' -1 1 -2 1 -o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5' -t $'\t' file1 file2 > joinedfile

由于join需要排序输入,并且您希望标题行位于顶部,您必须排除第一行然后排序:

sed -n '2,$p' file1unsorted | sort >file1
sed -n '2,$p' file2unsorted | sort >file2

之后,对已排序的文件运行上面的join命令(另请注意指定列分隔符的-t - 我假设您有Tab - 分隔文件。

单独加入标题:

head -1 file1unsorted | join -1 1 -2 1 -o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5' -t $'\t' - <(head -1 file2unsorted) >headerfile

然后&#34;重新组装&#34;你的最终文件(将新标题添加到文件的其余部分):

cat headerfile joinedfile >resulfile

更新

关于join对列数的依赖性(如果你的文件有更多的列):是的,在某种程度上存在依赖性。确切地说,列号用于-1-2选项(两者的值均为1,这是您要加入的相应文件中的列号;显然,只要您加入第一列,它就不依赖于总列数。列号也用在-o选项中,用于指定输出格式(即哪些列和输出顺序,格式为&#34;文件#.column#&#34;,均来自1,用于连接的列具有&#34; 0&#34;)的特殊语法。我们在示例中指定的格式实际上是默认格式(首先是列连接,然后是第一个文件中的所有其余列,后面是第二个文件的所有其他列),但遗憾的是我们仍然无法省略此选项由于-e选项需要它(它可能不在您的join版本中,因此请尝试省略-o部分,看看会发生什么。)

答案 1 :(得分:0)

有时,老式的强力方法可以在很难将数据塞入单个功能的地方工作。以下Bash脚本读取两个数据文件,逐步将它们操作到tmp文件(在/ tmp中)排序,然后将值读入数组,最后对于每个唯一名称,如果数据存在于S1 - S8值,则将它们组合在一起两个文件,否则用0 s填充缺失值。 trap函数会在退出时删除临时文件。该文件评论很好,以帮助解释逻辑。 注意这是一个Bash解决方案(主要由于替代测试运营商),但可以轻松适应其他shell环境。如果您有疑问,请告诉我们:

#!/bin/bash

## simple error/usage function
function usage {
    errno=${2:-0}
    if test -n "$1" ; then
        printf "\n %s\n" "$1"
    fi
cat >&2 <<TAG

  Merge two bacterial count data files zeroing non-common columns.

  Usage:  ./${0//*\//} file_1 file_2

TAG
    exit $((errno))
}

## semi-random tmp file timestamp 'mmdd????'
function tstamp {
    local rd=$(date +%N)
    printf "%s" "$(date +%m%d)${rd:4:4}"
}

## trap function - cleanup temp files
function cleanup {
    rm "$tfn1"
    rm "$tfn2"
    rm "$tfnall"
    rm "$tfnuniq"
}

## respond to help
test "$1" = "-h" -o "$1" = "--help" && usage

## validate input files
test -z "$1" && usage "error: insufficient input." 1
test -z "$2" && usage "error: insufficient input." 1
test -r "$1" || usage "error: invalid input, file not readable '$1'" 1
test -r "$2" || usage "error: invalid input, file not readable '$2'" 1

## assign temp file names
tfn1="/tmp/bmrg_$(tstamp).tmp"      # temp file_1 (sorted w/o header)
tfn2="/tmp/bmrg_$(tstamp).tmp"      # temp file_2 (sorted w/o header)
tfnall="/tmp/bmrg_$(tstamp).tmp"    # concatenated $tfn1 $tfn2
tfnuniq="/tmp/bmrg_$(tstamp).tmp"   # uniq records $tfn1 $tfn2

## create $tfn1 $tfn2 & validate
tail -n+2 "$1" | sort > "$tfn1"
tail -n+2 "$2" | sort > "$tfn2"

test -f "$tfn1" || usage "error: failed to create tmp file '${tfn1}'" 1
test -f "$tfn2" || usage "error: failed to create tmp file '${tfn2}'" 1

## set trap for cleanup on exit
trap cleanup EXIT

## read names from $tfn1
while read -r name || test -n "$name" ; do
    name1+=( "$name" )
done <<<"$(cut -c -30 "$tfn1")"
unset name

## read names from $tfn2
while read -r name || test -n "$name" ; do
    name2+=( "$name" )
done <<<"$(cut -c -30 "$tfn2")"
unset name 

## concatenate $tfn1 $tfn2
printf "%s\n" "${name1[@]}" > "$tfnall"
printf "%s\n" "${name2[@]}" >> "$tfnall"

## get unique names
sort -u "$tfnall" > "$tfnuniq"

## read $tfn1 values into separate arrays
while read -r v1 v2 v3 v4 || test -n "$v4" ; do
    s1+=( "$v1" )
    s2+=( "$v2" )
    s3+=( "$v3" )
    s4+=( "$v4" )
done <<<"$(cut -c 31- "$tfn1")"

## read $tfn2 values into separate arrays
while read -r v5 v6 v7 v8 || test -n "$v8" ; do
    s5+=( "$v5" )
    s6+=( "$v6" )
    s7+=( "$v7" )
    s8+=( "$v8" )
done <<<"$(cut -c 31- "$tfn2")"

printf "Header                         S1    S2    S3    S4    S5    S6    S7    S8\n"

## for each unique name in $tfnuniq
while read -r name || test -n "$name" ; do

    ## test if found in name1, print values by index, else print 0's
    found=0
    for ((i=0; i < ${#name1[@]}; i++)); do
        test "${name1[i]}" = "$name" && { found=1; break; }
    done
    if test "$found" -eq 1 ; then
        printf "%-30s%-6s%-6s%-6s%-6s" "$name" "${s1[i]}" "${s2[i]}" "${s3[i]}" "${s4[i]}"
    else
        printf "%-30s%-6s%-6s%-6s%-6s" "$name" "0" "0" "0" "0"
    fi

    ## test if found in name2, print values by index, else print 0's
    found=0
    for ((i=0; i < ${#name2[@]}; i++)); do
        test "${name2[i]}" = "$name" && { found=1; break; }
    done
    if test "$found" -eq 1 ; then
        printf "%-6s%-6s%-6s%-6s\n" "${s5[i]}" "${s6[i]}" "${s7[i]}" "${s8[i]}"
    else
        printf "%-6s%-6s%-6s%-6s\n" "0" "0" "0" "0"
    fi

done <"$tfnuniq"

exit 0

<强>输出:

$ bash bactmerge.sh dat/bact1.txt dat/bact2.txt
Header                         S1    S2    S3    S4    S5    S6    S7    S8
Abiotrophia defectiva         100   110   112   166   0     0     0     0
Acetobacterium submarinus     1350  1000  1541  1541  500   600   400   480
Acetobacterium tundrae        2     1     0     0     0     0     0     0
Acetobacterium woodii         0     0     0     0     2     3     4     0
Acholeplasma cavigenitalium   0     0     0     0     100   90    88    120

注意:脚本依赖于问题中提供的数据文件的间距。如果您的数据文件与发布的不同(例如由于制表符/空格转换),您可以调整上面cut命令提供的值。