【问题标题】:how to correctly merge tab delimited files如何正确合并制表符分隔的文件
【发布时间】:2016-06-10 18:17:32
【问题描述】:

如果我有以下两个文件:

00001    missing     affected          0.0132    case
00002    missing     not affected      0.042     control
00003    missing     affected          0.12      case
00004    present                                 case
00005    missing     affected          0.001     control

00001    A     406      R     case
00002    Q     612      B     case
00004    N/A   102            case                    
00005    C                    control

如何通过每个文件的第一列和最后一列连接这两个表,以便得到如下内容:

00001    missing     affected          0.0132    case    A     406      R
00002    missing     not affected      0.042     
00003    missing     affected          0.12
00004    present                                 case    N/A   102                              
00005    missing     affected          0.001     control       00005    C                    

请注意,第二个表中的 00003 缺失,并且第二个表中的第 2 行与表 1 中的 case 列不一致,因此这实际上是一个 SQL 等价于左连接。

谢谢。

【问题讨论】:

  • 请展示您的编码工作。
  • 您可以先为每个文件创建一个“人工”前导字段,以便简单地 joining 结果:awk '{print $1$NF,$0}'

标签: bash awk merge


【解决方案1】:

首先将输入中的字段分隔符更改为不同于空格的内容(因为空格在not affected 内)。所以让我们假设输入是这样的(merge1.csv):

00001;missing;affected;0.0132;case
00002;missing;not affected;0.042;control
00003;missing;affected;0.12;case
00004;present;;;case
00005;missing;affected;0.001;control

和(merge2.csv):

00001;A;406;R;case
00002;Q;612;B;case
00004;N/A;102; ;case
00005;C; ; ;control

现在

  1. 从第 1 列和第 5 列中创建一个排序键,并使用类似 <merge1.csv awk -v FS=';' -v OFS=';' '{print $1 "-" $5 ";" $0}' | sort -k1,1 的内容对其进行排序
  2. 在这样的连接命令中一起使用上述两个

    join -t';' -j1 -e "" -a 1  -o 1.2,1.3,1.4,2.6,2.3,2.4,2.5 <( <merge1.csv awk -v FS=';' -v OFS=';' '{print $1 "-" $5 ";"  $0}' | sort -k1,1 ) <( <merge2.csv awk -v FS=';' -v OFS=';' '{print $1 "-" $5 ";"  $0}' | sort -k1,1 )
    

    您想阅读选项-a -e-o 上的“加入”手册页

  3. 除了最后一行的最后两列之外,这或多或少会提供您想要的输出。

【讨论】:

  • 没有理由更改字段分隔符 - 制表符与分号一样合理且易于处理。只需使用BEGIN{FS=OFS="\t"}
  • @EdMorton 复制/粘贴提供的样本会导致空格,而不是制表符。因此,获得与空间不同的分隔符的步骤。 ; 有额外的好处,我们可以看到正在发生的事情,尤其是当某些列中存在缺失/空值时。
  • 啊,所以你的意思是他应该为了提供这个问题的示例输入/输出而改变它,而不是他应该在他的实际应用程序中改变它。好吧,够公平的。
【解决方案2】:

我不明白获得该输出的确切算法是什么(例如,为什么00005 是输出的最后一行中的倒数第二个字段?)并且研究an SQL equivalent of a left join 的含义超出了我的工资范围但希望这会让你接近:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR { a[$1,$NF] = $0; next }
{
    split(a[$1,$NF],b)
    $NF = b[5] OFS b[2] OFS b[3] OFS b[4]
    print
}

$ awk -f tst.awk file2 file1
00001   missing affected        0.0132  case    A       406     R
00002   missing not affected    0.042
00003   missing affected        0.12
00004   present                 case    N/A     102
00005   missing affected        0.001   control C

这样您就可以看到上面的选项卡在哪里排列:

$ awk -f tst.awk file2 file1 | column -s$'\t' -t
00001  missing  affected      0.0132  case     A    406  R
00002  missing  not affected  0.042
00003  missing  affected      0.12
00004  present                        case     N/A  102
00005  missing  affected      0.001   control  C

如果您编辑问题以包含您希望实现的任何算法的伪代码,那么我相信有人可以帮助您将其转换为 awk。

此外,如果 file1 中可能缺少关键字段但存在于 file2 中,则将该情况包含在您的示例输入/输出中,以便我们了解您希望如何处理。

【讨论】:

    【解决方案3】:

    你可以使用这个 awk:

    awk 'BEGIN{FS=OFS="\t"} {
       k = $1 FS $NF
    } FNR==NR {
       gsub("^" $1 FS "|" FS $NF "$", "")
       a[k] = $0
       next
    } {
       $NF = (k in a) ? $NF OFS a[k] : ""
    } 1' file2 file1 |
    column -s $'\t' -t
    

    column -s $'\t' -t 用于将输出格式化为表格格式。

    00001  missing  affected      0.0132  case     A    406  R
    00002  missing  not affected  0.042
    00003  missing  affected      0.12
    00004  present                        case     N/A  102
    00005  missing  affected      0.001   control  C
    

    【讨论】:

    • FWIW 你可以用gsub("^"$1 FS|FS $NF"$","");a[k]=$0 甚至gsub(/^[^\t]+\t|\t+[^\t]+/,""); a[k]=$0} 替换s=$0; sub($1 FS, "", s); sub(FS $NF, "", s); a[k]=s,如果$1 和$NF 中可能有RE 元字符。更重要的是 - SEP 是未定义的,所以你将这两个字段连接起来,这通常是“一件坏事”(ab ca bc -> abc)。您可以使用 SUBSEP 或 FS 代替 SEP。
    • 不客气,您确实应该对未定义的 SEP 做一些事情 - 要么使用已定义的东西,要么如果您真的想连接它们(不推荐)然后摆脱 SEP,因为它具有误导性.
    • 其实SEP 并不是真正需要的,即使FS 可以用作两个连接键之间的分隔符。
    • @anubhava 是的,我在第一条评论中说您可以使用 SUBSEP 或 FS。
    • @brucezepplin:很高兴它成功了。我已经稍微更新了它以在输出中产生更好的格式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    • 2011-05-30
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多