按列合并两个文件,缺少条目时添加 0

按列合并两个文件,缺少条目时添加 0

我有两个文件,如下所示。我试图找出一种方法来创建一个主文件,其中包含文件 1 和文件 2 第二列中的所有条目,方法是在文件 2 中缺少条目的地方添加 0。所有这些文件都是制表符分隔的。我尝试了join命令,但无法让它工作。

  • 的例子File1
    orange
    banana
    berry
    cherry
    strawberry
    
  • 示例File2
    orange   1   
    banana   2   
    cherry   1   
    
  • 所需输出
    Output    Value
    orange     1
    banana     2
    berry      0
    cherry     1
    strawberry 0
    

我尝试过的:

join File1 File2 |less

答案1

$ join -a 1 -e 0 -o 0,2.2 <(sort File1) <(sort File2)
banana 2
berry 0
cherry 1
orange 1
strawberry 0

这用于join在文件之间执行关系 JOIN 操作。这需要对两个文件进行排序,这就是为什么我们在每个进程替换中对它们进行排序(如果您愿意,您显然可以对数据进行预排序)。该命令将列出第一个输入文件 ( ) 中的所有行,并用( )-a 1替换缺失的字段。输出中的字段将是连接字段(默认情况下每个文件中的第一个字段,并写入选项的参数中)和第二个文件中的第二个字段 ( )。0-e 00-o2.2

优点:快速(特别是当数据已经排序时)并且内存效率高。
缺点:重新排序数据。


要保留原始的顺序File1,您可以使用awk

$ awk 'NR == FNR { key[$1] = $2; next } { $2 = ($1 in key) ? key[$1] : 0 }; 1' File2 File1
orange 1
banana 2
berry 0
cherry 1
strawberry 0

这会将 的第一列读取File2为关联数组中的键key,将第二列读取为其关联值。

File1被读取时(NR不再等于),如果有与第一列对应的键,FNR我们将第二列设置为数组中的值,或者如果没有这样的键,则将第二列设置为数组中的值。key0

您可以通过滥用算术上下文中未初始化值为零的事实来稍微缩短代码:

$ awk 'NR == FNR { key[$1] = $2; next } { $2 = 0+key[$1] }; 1' File2 File1
orange 1
banana 2
berry 0
cherry 1
strawberry 0

Pro:输出按照 排序File1
缺点:数据File2存储在内存中(只有在读取大量行时才真正重要)。

答案2

我不知道有什么命令可以做到这一点,但可以编写脚本:

while IFS='' read -r l1; do
  grep "^${l1}" File2 || echo -e "${l1}\t0"
done < <(cat File1)

答案3

可以使用awk,但它需要缓冲File2(所以如果你有巨大的文件可能会达到限制,尽管通常不太可能)。

awk 'BEGIN{FS=OFS="\t"}
     !mainfile{val[$1]=$2;next}
     {if ($1 in val) {$2=val[$1]} else {$2=0}} 1' File2 mainfile=1 File1

其工作原理如下:

  • 在处理任何内容之前,我们将输入和输出字段分隔符设置为 TAB。
  • 在处理第一个输入文件(File2在本例中)时,通过未初始化的变量来指示mainfile,我们只需将每个“水果”的“值”记录在关联数组中val。之后,我们立即跳过处理到下一个输入行(并跳过只应在处理时应用的部分File1)。
  • 在处理下一个文件之前,awk将首先评估mainfile=1要设置mainfile为的语句,嗯,1.
  • 一旦mainfile设置,第一条规则将被忽略,我们只处理第二条规则。在这里,我们检查是否有一个“值”映射到第 1 列中的条目。如果是,我们将该值用于第 2 列,否则我们将第 2 列设置为0
  • 看似偏离1规则块之外的内容指示awk打印当前行,包括所做的任何修改。

答案4

听起来像是可以用 Python 快速实现的东西

#!/usr/bin/env python3
"""
Overly grande solution to the problem posed in the question.
"""
import sys

# Check whether the user specified two arguments to the script
if not len(sys.argv) == 3:
  sys.stderr.write(f"Usage: {sys.argv[0]} File1 File2")
  sys.exit(-1)

# Empty dictionary
dictionary = {}

# Fill the dictionary with zero-entries from the first file
with open(sys.argv[1]) as file:
  for line in file:
   dictionary[line.strip()] = 0

# Replace the zero entries when found in second file
with open(sys.argv[2]) as file:
  for line in file:
    entry, value = line.split()
    dictionary[entry] = int(value)

# print the result table
print("Output\tValue")
for key, value in dictionary.items():
  # We're using the format string syntax to give the count
  # field a constant length of 8, so that things are nicely
  # right-aligned.
  print(f"{key}\t{value:8}")

相关内容