插入包含缺失值的新行 (NA)

插入包含缺失值的新行 (NA)

如果缺少值,我想在文本文件中插入新行。例如,我有以下文本文件 (A.txt),其中缺少第 5 行。此外,由于文件应有 12 行,因此第 11-12 行也丢失了。

1 2.30
2 3.01
3 3.22
4 3.34
6 3.01
7 2.90
8 2.99
9 3.00
10 3.02

我的预期输出如下。对于缺失的情况,应添加一行并注明编号和 NA。如您所见,这在第 5、11 和 12 行如期发生:

1 2.30
2 3.01
3 3.22
4 3.34
5 NA
6 3.01
7 2.90
8 2.99
9 3.00
10 3.02
11 NA
12 NA

我可以使用以下脚本来做到这一点:

f1=/my-directory/
echo "new file" > "$f1"/newfile.txt  

for i in {1..12}; do
l=$(awk '{print $1}' /"$f1"/A.txt | grep -wE ^$i /"$f1"/A.txt)
if grep --quiet -wE ^$i /"$f1"/A.txt; then echo "$l" >> "$f1"/newfile.txt; else echo "$i NA" >> "$f1"/newfile.txt; fi

done

这很好用。但问题是我需要对包含超过 160000 行的大约 600 个文件执行此操作。因此,循环解决方案将花费太多时间搜索所有行。我的问题是:是否有更简单的解决方案可以做到这一点?

答案1

join在这里效果很好:

join -a 1 -o 0,2.2 -e NA  <(seq 12)  A.txt  2>/dev/null

我扔掉了 stderr,因为join如果连接字段不是,则会抱怨从词汇上来说已排序。

答案2

您可以使用awk脚本来执行此操作:

awk '{ while (NR + shift < $1) { print (NR + shift) " NA"; shift++ }; print } END { shift++; while (NR + shift < 13) { print (NR + shift) " NA"; shift++ } }' /tmp/test1

将产生所需的输出/tmp/test1(将其替换为您要处理的每个文件)。

以更易读的形式:

#!/usr/bin/awk -f
{
    while (NR + shift < $1) {
        print (NR + shift) " NA"
        shift++
    }
    print
}
END {
    shift++
    while (NR + shift < 13) {
        print (NR + shift) " NA"
        shift++
    }
}

将其保存为文件,例如fill-missing,使其可执行,然后您可以简单地运行

./fill-missing /tmp/test1

该脚本处理每一行,并跟踪 中当前行号的预期增量shift。因此,对于每一行,如果当前调整的行与该行中的第一个数字不匹配,它将打印适当的行号,然后NA增加增量;一旦行号匹配,它就会打印当前行。在该过程结束时,它会打印达到 12 所需的所有缺失行。

答案3

awk 文件

BEGIN { i=1 ; }
function upto(x) { while (i<x) printf "%d NA\n",i++ ;}
 { if ( $1 == i ) print ; upto($1) ; i++ ;}
END { upto(final+1) ;}

被称为与

awk -f nl.awk -v final=12 /tmp/test1

你的整个循环

cd /my/directory
ls | while read f
do
      awk -f ~/nl.awk -v final=12 $f > /an/other/dir/$f
done

在哪里

  • 你把 awk 程序放在你的 $HOME 目录中 ( ~/nl.awk)

答案4

Glenn Jackman 的bash方法join进行了一些调整,该函数从输入文件中获取计数,并使用该计数作为默认值:

# Usage: inlwmv file [ missing_value [ extra_lines ] ]
#      if unset, missing_value="NA", and extra_lines=0
inlwmv() { join -a 1  -o 0,2.2  -e "${2:-NA}" \
           <(seq $((${3:-0} + $(tail -n 1 "$1" | cut -d ' ' -f1))) | sort -k 1b,1)  \
           <(sort -k 1b,1 "$1") | \
           sort -g ; }

对于OP问题:

inlwmv A.file "" 2

为了这个重复的问题Jackman 版本在最后一个值上失败一份文件,(join对于排​​序非常挑剔,但使用sort三次可以使其按要求运行):

inlwmv afile 0

相关内容