欠落値を含む新しい行の挿入(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

これは素晴らしい作品です。しかし、問題は、160,000行を超える行を含む約600個のファイルに対してこれを行う必要があることです。したがって、ループソリューションがすべての行を検索するのに時間がかかりすぎます。私の質問は:これを行うためのより簡単な解決策はありますか?

答え1

joinここでは素晴らしい作品です。

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

join結合フィールドがそうでなければ文句を言うので、stderrを捨てました。語彙的ソートされました。

答え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

どこ

  • $HOME ディレクトリ( ~/nl.awk) に 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のバージョンが最後の値で失敗します。1つの文書、(joinソートは非常にトリッキーですが、3回使用するとsort必要に応じて動作します):

inlwmv afile 0

関連情報