【问题标题】:Join loop on multiple files, filling empty fields加入多个文件的循环,填充空白字段
【发布时间】:2017-11-16 17:21:14
【问题描述】:

我想在第 1 列加入 28 个文件,在每次迭代加入时保留两个文件的所有结果,并用 0 填充空列。总共有两列。前 3 个文件的预期输入和输出:

文件1

a   1       
b   2       
c   1       
d   4       

文件2

a   2       
b   3   


        

文件3

c   2       
d   2       
e   1       
        

加入

a   1   2   0
b   2   3   0
c   1   0   2
d   4   0   2
e   0   0   1

我已经在我的实际文件上使用管道写出了整个连接,它可以工作,但显然很混乱。为了清楚起见,我在这里用省略号缩短了它:

join -t $'\t' -a 1 -a 2 -e '0' -o 0 1.2 2.2 -1 1 -2 1 *D3-E-N-1*/*matrix.txt.cut *D3-E-N-2*/*matrix.txt.cut | join -t $'\t' -a 1 -a 2 -e '0' -o 0 1.2 1.3 2.2 -1 1 -2 1 - *D3-E-N-3*/*matrix.txt.cut | join -t $'\t' -a 1 -a 2 -e '0' -o 0 1.2 1.3 1.4 2.2 -1 1 -2 1 - *D3-E-N-4*/*matrix.txt.cut | join -t $'\t' -a 1 -a 2 -e '0' -o 0 1.2 1.3 1.4 1.5 2.2 -1 1 -2 1 - *D3-E-N-5*/*matrix.txt.cut | ... > final.matrix.txt

我知道这可以循环添加新字段,但我是编程新手,循环不是我的强项。我试过这个(从这里bash join multiple files with empty replacement (-e option)):

i=3
orderl='0,1.2'
orderr=',2.2'
for k in UNITAS*/*seq_cut
do
    if [ -a final.results ]
    then
        join -a1 -a2 -e "0" -o "$orderl$orderr" final.results $k  > tmp.res
        orderl="$orderl,1.$i"
        i=$((i+1))
        mv tmp.res final.results
    else
        cp $k final.results
    fi
done

这给了我错误

[:参数太多

[:参数太多

cp:覆盖'final.results'?

是否有人对递归连接文件的脚本或更适合此任务的程序有建议?

非常感谢!

【问题讨论】:

    标签: bash loops unix join


    【解决方案1】:

    只需使用 R,您就可以根据需要更改所需的扩展名:

    以下是我用作示例的文件:

    f1.txt

    a 1
    b 4
    c 6
    e 3
    

    f2.txt

    c 1
    d 4
    f 5
    z 3
    

    f3.txt

    a 1
    b 4
    c 5
    e 7
    g 12
    

    R 代码:

    #!/bin/env/Rscript
    
    ext='.ext' #can alter this to desired extension
    files <- list.files(pattern=ext) #get name of files in a directory
    listOfFiles <- lapply(files, function(x){ read.table(x, row.names=1) } )
    
    #The big reduction of all the files into a table
    tbl <- Reduce(function(...) data.frame(merge(..., all = T, by = 0), row.names=1), listOfFiles)
    
    tbl[is.na(tbl)] <- 0 #set all NA vals to 0
    colnames(tbl) <- files #set the columns to the corresponding filenames (optional)
    tbl #print out the table
    

    输出:

      f1.ext f2.ext f3.ext
    a      1      0      1
    b      4      0      4
    c      6      1      5
    d      0      4      0
    e      3      0      7
    f      0      5      0
    g      0      0     12
    z      0      3      0
    

    【讨论】:

    • 效果很好,谢谢!
    【解决方案2】:

    我很惊讶你的管道能正常工作;我自己无法让它工作。此外,您的输入文件和输出文件似乎不匹配。但除此之外:

    Join 只会连接两列:一个键和一个字段。这意味着您需要某种方式将键与字段分开,否则您将丢失以前连接的列。

    示例: f1:

    a   1
    b   2
    c   1
    d   4
    

    f2:

    a   2
    b   3
    

    f3:

    c 2 d 2 e 1

    $ join -a1 -a2 -e "0" -t'       ' -o "0,1.2,2.2" f1 f2
    a   1   2
    b   2   3
    c   1   0
    d   4   0
    $ join -a1 -a2 -e "0" -t'       ' -o "0,1.2,2.2" f1 f2 > f4
    $ join -a1 -a2 -e "0" -t'       ' -o "0,1.2,2.2" f4 f3
    a   1   0
    b   2   0
    c   1   2
    d   4   2
    e   0   1
    

    这不是你想要的。

    您可以将第一个选项卡更改为 ;并加入然后改回来,但如果后面的文件引入了新的键,它会给你半行来表示第一个文件中不存在的键。

    将文件添加到仅使用键的连接将创建一个带有零的列,该列也必须被删除。

    您可能有一些通配符来列出所有文件,在我的示例中可能是 f?,但您的可能是 file.**.cols 等。尝试使用带有 ls 的通配符当然。

    所以,把它们放在一起:

    #first make a key-file k0
    cat f?  | cut -f1  | sort -u > k0
    # change the separator to ';' and back
    for f in f? ; do
        sed 's/\t/;/' k0 > t0
        sed 's/\t/;/' $f > t1
        join -a1 -a2 -e "0" -t';' -o "0,1.2,2.2" t0 t1 | sed 's/;/\t/g' > k0
    done
    # remove the '0' column from the key-file
    sed 's/\t0\t/\t/' k0
    

    或者看看 awk。

    【讨论】:

    • 感谢您的 cmets。上面的三个文件只是一个例子,因为我实际上有很多文件要加入。我编写的命令将所有文件上的连接连接在一起,每次添加新字段(1.2 1.3 1.4 等),这样它们就不会丢失。我需要的是一个循环,为任意数量的文件增加和添加这些。
    • 编辑了答案以显示如何在通配符中循环文件名。
    猜你喜欢
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 2019-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多