条件付きで行を数字に置き換える

条件付きで行を数字に置き換える

約1,100万個の小さなファイルを含むディレクトリがあります。次のようになります。

wa_filtering_DP15_good_pops_snps_file_1
wa_filtering_DP15_good_pops_snps_file_2
.
.
.
wa_filtering_DP15_good_pops_snps_file_11232111

各ファイルには、以下のように2行と315列のみがあります。

1   0   0   0   0   0   0   0   0   0   1   2   1   
0   0   0   0   0   0   0   0   0   0   0   0   0

各ファイルを繰り返し、2行の各列に0の値がある場合は、それを9に置き換えて、次の結果を得ます。

1   9   9   9   9   9   9   9   9   9   1   2   1   
0   9   9   9   9   9   9   9   9   9   0   0   0

誰かが私がこれを行う方法を見つけるのを助けることができますか?ありがとう

答え1

awk解決策は次のとおりです。

awk '{split($0,ary1,/[ ]+/); getline x; split(x,ary2,/[ ]+/); 
    for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}} 
END{for (r=1;r<=NF;r++) printf ("%d ", ary1[r]); printf"\n"; 
    for (z=1;z<=NF;z++) printf ("%d ", ary2[z]); printf"\n"}' infile

説明する:

  • split($0,ary1,/[ ]+/);ary1:最初の行を読み取り、配列間に1つ以上のスペース区切り文字を使用して配列に分割します。

  • getline x; split(x,ary2,/[ ]+/);:2行目を変数として読み、配列に分割xしますary2

  • for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}}: 2 つのフィールド値の合計が次の場合、配列のary1各インデックスを繰り返します。i若い(真の条件でトリガされます!(0)。) 次に、2 つのフィールドの値を次に設定します。if(1)9

  • for (r=1;r<=NF;r++) printf ("%d ", ary1[r]); printf"\n";ary1:次に各配列の最終値と次の行を印刷しますary2


約1,100万個のファイル全体に適用するには、FILENAME.out現在読み取る入力ファイル名を表すFILENAME形式に変更を保存するだけですawk

awk '{split($0,ary1,/[ ]+/); getline x; split(x,ary2,/[ ]+/); 
    for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}} 
END{for (r=1;r<=NF;r++) printf ("%d ", ary1[r])>FILENAME".out"; printf"\n">FILENAME".out"; 
    for (z=1;z<=NF;z++) printf ("%d ", ary2[z])>FILENAME".out"
}' wa_filtering_DP15_good_pops_snps_file_{1..11232111}

答え2

楽しんでこれはRuby

ruby -e '
    data = File.readlines(ARGV.shift)
               .map {|line| line.split.map(&:to_i)}
               .transpose
               .map {|(a,b)| (a==0 && b==0) ? [9,9] : [a,b]}
               .transpose
               .each {|row| puts row.join(" ")}
' file
1 9 9 9 9 9 9 9 9 9 1 2 1
0 9 9 9 9 9 9 9 9 9 0 0 0

すべてのファイルを置き換えるには:

ruby -e '
    require "tempfile"
    require "pathname"
    Pathname.new("/path/to/your/files/").each_child do |pathname|
        next unless pathname.file?
        temp = Tempfile.new(pathname.basename.to_s)
        filename = pathname.to_s
        File.readlines(filename)
            .map {|line| line.split.map(&:to_i)}
            .transpose
            .map {|(a,b)| (a==0 && b==0) ? [9,9] : [a,b]}
            .transpose
            .each {|row| temp.puts row.join(" ")}
        temp.close
        File.link filename, filename+".bak"
        File.rename temp.path, filename
    end
'

答え3

これは、純粋なawkソリューションと比較して、数百万のファイルに対して遅くなる可能性がある代替手段です。

同様のアプローチを使用して行を列に置き換えることができます。

$ cat file1
1   0   0   0   0   0   0   0   0   0   1   2   1   
0   0   0   0   0   0   0   0   0   0   0   0   0

$ paste -d'-' <(head -n1 file1 |tr -s ' ' '\n') <(tail -n1 file1 |tr -s ' ' '\n')
1-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
1-0
2-0
1-0

0-0その後、すべての項目を単純なsedに置き換え、9-9出力を一時変数に保存できます。

$ f1=$(sed 's/0-0/9-9/g' <(paste -d'-' <(head -n1 file1|tr -s ' ' '\n') <(tail -n1 file1 |tr -s ' ' '\n')))
$ echo "$f1"
1-0
9-9
9-9
9-9
9-9
9-9
9-9
9-9
9-9
9-9
1-0
2-0
1-0

これで、列から行に復元できます。たとえば、次のようになります。

$ awk -F'-' 'NR==FNR{printf "%s ",$1;p=1;next}p{printf "\n";p=0}{printf "%s ",$2}END{printf "\n"}' <(echo "$f1") <(echo "$f1")
1 9 9 9 9 9 9 9 9 9 1 2 1  
0 9 9 9 9 9 9 9 9 9 0 0 0  

>file1最後のawkコマンドの最後に追加してfile1新しい内容で上書きすることもできます。

残りの唯一のことは、すべてのファイルを繰り返すことです。これはbashループを介して行うことができます。

for f in ./wa_filtering_DP15_good_pops_snps_file_*;do
  f1=$(sed 's/0-0/9-9/g' <(paste -d'-' <(head -n1 "$f"|tr -s ' ' '\n') <(tail -n1 "$f" |tr -s ' ' '\n')))
  awk -F'-' 'NR==FNR{printf "%s ",$1;p=1;next}p{printf "\n";p=0}{printf "%s ",$2}END{printf "\n"}' <(echo "$f1") <(echo "$f1") #>"$f" #uncomment >"$f" to overwrite the files...
done

答え4

最初のバリエーション:

単一ファイルの場合:

datamash -W transpose < input.txt | sed 's/0\t0/9\t9/' | datamash transpose

多くのファイルに対してループで同じことを行います。

for i in *; do datamash -W transpose < "$i" |
sed 's/0\t0/9\t9/' |
datamash transpose > "new_$i"; done

このループは、「new_」というプレフィックスが付いた各ファイルに対して変更された新しいファイルを生成します。その後、古いファイルをすべて削除し、ファイル名からプレフィックス "new_"を削除できます。

2番目の変形:

これは単一ファイルのソリューションです。複数のファイルの場合は、前のバリエーションで示したようにループを使用します。

tr '\n' '\t' < input.txt |
awk '{
    num = NF / 2;
    for(up = 1; up <= NF; up++) {
        if(up <= num) {
            low = num + up;
            if(!$up && !$low) {
                $up = 9;    
                $low = 9;
            }
        }

        printf "%s\t", $up;

        if(up % num == 0) 
            print "";
    }
}'

説明する

  1. tr '\n' '\t' < input.txt- 2本のワイヤを一緒に接続します。
  2. awk
    • 最初の行の要素と2番目の行の隣接要素の両方を確認します。たとえば、次のようになります。1そして3162そして第317話サムそして318、すぐに。
    • 両方の要素がある場合0、次のように変更されます。9
    • フィールドを順番に印刷する -1、2、3、4...628、629、630
    • 要素数が行内の要素数の倍数になるたびに、新しい行が追加されます。

入力する

1   0   0   0   0   0   0   0   0   0   1   2   1
0   0   0   0   0   0   0   0   0   0   0   0   0

出力

1   9   9   9   9   9   9   9   9   9   1   2   1
0   9   9   9   9   9   9   9   9   9   0   0   0

関連情報