在文件之间匹配 2 个主要列;并在这些主列匹配时将其他列粘贴到输出文件中。保持第一个文件的行大小不变

在文件之间匹配 2 个主要列;并在这些主列匹配时将其他列粘贴到输出文件中。保持第一个文件的行大小不变

这是我大约 24 小时前提出的上一个问题的后续问题:在文件之间同时匹配两个主列,并在这些主列匹配时将补充列粘贴到输出文件中

G-Man 用一个有用的代码解决了这个问题,但我有一个后续问题。我已经接受了答案,因此第二篇文章......

我有 3 个文件,每个文件都有唯一数量的列,所有列均以制表符分隔,但某些列在 3 个文件之间共享。这是我想用来创建某种“聚合”文件的 3 个文件之间的共享列。

下表显示了这些文件的示例。基本上我想匹配文件之间的 MAIN1 和 MAIN2 列。三个文件之间的两列都必须匹配。

当两个文件之间的 MAIN1 和 MAIN2 匹配时,我想将 file2 中的列“minor8”添加到 file1 中表格的右侧。随后,当两个文件之间的 MAIN1 和 MAIN2 匹配时,我想在 file1 表的右侧添加 file3 中的“minor9”。因为“minor8”应该紧邻 file1 的最右列(列名称:“minor3”),所以我希望“minor9”紧邻“minor8”进入新的输出文件。输出文件给出了我理想的最终文件应该是什么样子的想法。

基本上这些是 3 个文件的示例(“选项卡”有点混乱)

文件1:

MAIN1   minor1  MAIN2   minor3
1  bla1    a    blabla1
1  bla2    b    blabla2
1  bla3    c    blabla3
2  bla4    a    blabla4
2  bla5    d    blabla5
3  bla6    e    blabla6
4  bla7    f    blabla7
5  bla8    a    blabla8
5  bla9    g    blabla9

文件2:

minor8  MAIN1   MAIN2
yes1    2   d
yes2    3   e
yes3    4   f
yes4    5   a
yes5    5   g
yes6    1   a
yes7    1   b
yes8    1   c
yes9    2   a

文件3:

MAIN1   MAIN2   minor9
5   a   sure1
5   g   sure2
1   a   sure3
1   b   sure4
1   c   sure5
2   a   sure6
2   d   sure7
3   e   sure8
4   f   sure9

所需的输出文件:

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1   bla1    a   blabla1 yes6    sure3
1   bla2    b   blabla2 yes7    sure4
1   bla3    c   blabla3 yes8    sure5
2   bla4    a   blabla4 yes9    sure6
2   bla5    d   blabla5 yes1    sure7
3   bla6    e   blabla6 yes2    sure8
4   bla7    f   blabla7 yes3    sure9
5   bla8    a   blabla8 yes4    sure1
5   bla9    g   blabla9 yes5    sure2

如前所述,G-Man 提供了一个有用的代码,它完全符合我的要求(请参阅上一篇文章)。我可能会问 G-Man(或其他有时间的人)一些关于我不太理解的代码中某些单独行的具体问题,但在那之前,我有后续问题。

G-Man 的代码能够重新创建上述 OUTPUT 文件,所以谢谢 G-Man!

后续问题:

我忘记提及的一件事是,代码无法执行(据我所知),如果文件之间的 MAIN1 和 MAIN2 列不匹配,它将从 file1 中删除行。这是我的错,因为我没有具体说明这一点。我的目标是拥有一个输出文件,其中 file1 中的任何行都不会被删除。

基本上 file1 是我的优先文件。无论该文件有多少行(接近一百万),这也是 OUTPUT 文件应该有的行数。如果没有列 MAIN1、MAIN2 匹配,则某些行的“minor8”和“minor9”列可以为空。但当“minor8”或“minor9”(或两者)存在“缺失/空”值时,我想保留 file1 的这些行。

我将尝试使用上述文件 2 和 3 的稍微不同的版本来说明这一点(因此 file1 保持不变)。

调整后的文件2(没有MAIN1,MAIN2组合:2,d):

minor8  MAIN1   MAIN2
yes2    3   e
yes3    4   f
yes4    5   a
yes5    5   g
yes6    1   a
yes7    1   b
yes8    1   c
yes9    2   a

调整后的文件3(没有MAIN1,MAIN2组合:5,a):

MAIN1   MAIN2   minor9
5   g   sure2
1   a   sure3
1   b   sure4
1   c   sure5
2   a   sure6
2   d   sure7
3   e   sure8
4   f   sure9

调整后的所需输出(即,对于 MAIN1、MAIN2 组合 2-d,minor8 列中为空值;对于 MAIN1、MAIN2 组合 5-a,minor9 列中为空值):

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1   bla1    a   blabla1 yes6    sure3
1   bla2    b   blabla2 yes7    sure4
1   bla3    c   blabla3 yes8    sure5
2   bla4    a   blabla4 yes9    sure6
2   bla5    d   blabla5     sure7
3   bla6    e   blabla6 yes2    sure8
4   bla7    f   blabla7 yes3    sure9
5   bla8    a   blabla8 yes4    
5   bla9    g   blabla9 yes5    sure2

我希望我的解释方式足够清楚。我发现表格的选项卡有点混乱。你们喜欢这样,还是喜欢我在视觉上理顺表格? (我可以想象,由此产生的唯一问题是,当您复制粘贴我的示例数据时,您将拥有不应该存在的其他选项卡......)

无论如何,我非常感谢你们的帮助。希望在不久的将来的某个时候,除了简单地寻求帮助之外,我还能为这个论坛做出贡献......

您对如何编辑 G-Man 的代码以使这成为可能有什么建议吗?或者,如果您对如何编写考虑到此附加要求的有用代码有完全不同的建议,请告诉我。

答案1

创建以下文件:

merge21:

开始 {
        FS =“\t”
        OFS =“\t”
}
NR==FNR { #文件2
        关键=$2“,”$3
        存在[键] = 1
        小8[键] =1 美元
        下一个
}
{#文件1
        键 = $1 "," $3
        if (present[key]) 打印 $1, $2, $3, $4,小调8[调]
        否则打印$1、$2、$3、$4、“-”
}

merge312:

开始 {
        FS =“\t”
        OFS =“\t”
}
NR==FNR { #文件3
        关键=$1“,”$2
        存在[键] = 1
        小9[键] =3 美元
        下一个
}
{#文件1 + 文件2
        键 = $1 "," $3
        if (present[key]) 打印 $1, $2, $3, $4,$5,未成年人9[关键]
        否则打印 $1, $2, $3, $4,5 美元, “-”
}

它们几乎完全相同;我已将差异加粗。现在输入命令

awk -f merge21 file2 file1 | awk -f merge312 file3 -

这假设您的关键字段都不包含逗号,并且您的数据都不包含连字符,但这实际上仅取决于存在一些数据中未出现的字符串。扩展它以支持更多列将是微不足道的;我希望这是显而易见的。这可以可以增强在一次运行中完成所有操作awk,但这会更复杂一些,并且(IMNSHO)不值得付出努力。

这会产生文件中数据的所谓“左外连接”;看INNER 和 OUTER 连接之间的区别在 Stack Overflow 上了解一些定义。 (“左外连接”在该问题的接受答案中定义为(解释为)“第一个表中的所有行,加上其他表中的任何公共行”。)

你的输出将是

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1       bla1    a       blabla1 yes6    sure3
1       bla2    b       blabla2 yes7    sure4
1       bla3    c       blabla3 yes8    sure5
2       bla4    a       blabla4 yes9    sure6
2       bla5    d       blabla5 -       sure7
3       bla6    e       blabla6 yes2    sure8
4       bla7    f       blabla7 yes3    sure9
5       bla8    a       blabla8 yes4    -
5       bla9    g       blabla9 yes5    sure2

并且,显然,您可以删除-字符与sed. (当然,如果您的真实数据实际上包含连字符,请选择一些未使用的字符或字符串作为缺失数据的占位符。)


笔记

  • FSOFS分别是输入字段分隔符和输出字段分隔符。 (显然IFS在 中毫无意义awk;这是我的一个错误。)您可能并不真正需要FS="\t"awk默认情况下将制表符识别为输入中的字段分隔符。 (它允许您拥有包含空格的字段,但您似乎对此不感兴趣。)  OFS="\t"很重要;因此,我可以说print $1, $2, $3, $4 并让输入字段成为输出它们之间有标签。如果我不说OFS="\t",它们将被空格分隔,除非我说print $1 "\t" $2 "\t" $3 "\t" $4,这是乏味的并且损害可读性。
  • 如果您对 MAIN1 和 MAIN2 给出了额外的约束 - 例如,它们始终只有一个字符,或者 MAIN1 始终是数字而 MAIN2 始终以字母开头 - 我就不需要,中的逗号 ( ) key。但你的第一个问题的原始版本没有显示这样的限制。考虑以下数据:

    MAIN1 ($2)         MAIN2 ($3)         badkey = $2 $3         goodkey = $2 "," $3
        2              34151                  234151                   2,34151
       23               4151                  234151                   23,4151
    

    如果我们不在键中包含一些不会出现在键字段(MAIN1 和 MAIN2)中的分隔符,我们可以key为不同的行获得相同的值。

  • 冒着吹毛求疵的风险,我不会“告诉 Linux”任何事情;我只是想告诉 Linux。我告诉你awk该怎么做。
  • 关于代码
    NR==FNR { # 文件3
            键 = $1 "," $2
            存在[键] = 1
            小9[键] = $3
            下一个
    }
    考虑 的倒数第七行file3,其中包含1 a sure3。显然我们有$1= 1$2=a$3= sure3,所以key= 1,a。  present[key] = 1意味着我设置present["1,a"]1一个标志来表明file3 一条1,a线;即=有一个minor9值。由于 中 没有行,没有被设置,因此代码的“”部分知道没有for = ,并且应该打印key1,a5,afile3present["5,a"]file1 + file2minor9key5,a-反而。这个名字present只是我随意选择的;它表明该1,a行是展示file3 (并且该5,a行不在)。习惯上用1“TRUE”来表示。
  • 您可以替换print $1, $2, $3, $4for (n=1; n<=4; n++) printf "%s\t", $n.您应该通过对最后一个字段使用 plain print (而不是)或通过执行来结束该行。您可以通过执行类似的操作进一步简化printfprintf "\n"

            for (n=1; n<=4; n++) printf "%s\t", $n
            if (present[key]) 打印minor8[key]
            否则打印“-”

请阅读awk(1), 这POSIX 规范awk, GNU Awk 用户指南,并查看awk.info了解更多信息。

相关内容