10000ごとにファイルを分割する(行ではない)


8

次のようなファイルがあります。

chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT    

2番目のフィールド(行ではなく番号の間隔)の10000間隔ごとにこのファイルを分割したいと思います。したがって、このファイルでは、最初の行(61336212の行)から61346211(61336212 + 9999)以下の行に分割し、次に61346212から61356211に分割し、以下同様に分割します。ご覧のとおり、2番目のフィールド/列の数値は「入力」されていません。

これを行う方法はありますか?


たとえば、61346211の後の次の数値が61346220である場合、出力の2番目のファイルが61346212または61346220で始まる範囲をカバーすることを期待しますか?
Joe Lee-Moyet、2015

2番目の範囲は61346212からカバーする必要があります。–アガトシア15
19:13

回答:


13
awk 'NR==1 {n=$2}
     {
       file = sprintf("file.%.4d", ($2-n)/10000)
       if (file != last_file) {
         close(last_file)
         last_file = file
       }
       print > file
     }'

記述しfile.0000file.0001...(数がいるint(($2-n)/10000)ところnである$2最初の行のため)。

他の方法で書き込みを停止すると、ファイルを閉じることに注意してください。数百のファイルを同時に開くと、同時に開くawkことができるファイルの数が制限に達します(GNU はその制限を回避できますが、パフォーマンスはすぐに低下します)。

これらの数は常に増加していると想定しています。


3
何が起こっているのか説明してもらえますか?
Fiximan 2015

ここで何が起こっているのか説明してもらえますか?また、以下のコメントのように、出力ファイル名の長さを一定にするために、file.1 file.2 .. file.100 .. file..2320ではなく、file.0000、file.0001などがあります。
アガトシア2015

1
@Fiximan、私はコードを言い換えることなく、これ以上説明できないと思います。どの部分が不明確だと思いますか?
ステファンChazelas

ファイル名の生成file = ...は理解できましたが、反復はどのように機能しますか?言う部分n = n + 10000lower_boundary <= $2 < upper_boundary部分もありません。一般的に、全体if (file != last_file) { close(last_file) ; last_file = file }が私のリーグから外れています
フィクシーマン

1
@Fixman、そうですね、これを言い換えるif (file != last_file)と、現在のファイルが前のファイルと同じでない場合は、前のファイルを閉じます(したがって、一度に1つのファイルのみを開いてください(保持する必要はありません)他のソリューションと同様にすべてオープン))
StéphaneChazelas 15

7

ワンライナーバージョンをハック。おそらく、このフォーラムよりもコードゴルフに適しています。これにより、split1、split2、split3などがファイル名として生成されます。

awk '{if($2>b+9999){a++;b=$2}print >"split" a}' file.txt

split001、split002、split003という名前の出力ファイルを作成するには、次の追加が必要sprintfです。

awk '{if($2>b+9999){a++;b=$2}print >sprintf("split%03d",a)}' file.txt

@StéphaneChazelasによって特定されたgawkの速度低下の問題を回避するには、perlを使用します。

perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt

1
この方法の場合、ファイル名を連続的にする方法はありますか?これにより、split1 .... split100 ... split1000が出力されますが、split00001 ... split 00100 .. split01000 ..?
アガトシア2015

1
もちろん、追加のsprintf魔法が追加されました。
2015

入力に0、9999、12000、19999、21000、22000がある場合、file1には0、9999が配置されますが、file2には12000、19999、21000が配置され、要件とは奇妙に思えます。
ステファンChazelas

1
これは、数百のファイルの後に同時に開くことができるファイルの数の制限に達することに注意してください(GNU awkはその制限を回避できますが、パフォーマンスはすぐに低下します)。
ステファンChazelas

1
うん。あなたが言及した問題に気づきました。
アガトシア2015

4
#!/bin/bash
first=$( head -n1 file | awk -F" +" '{print $2}' )
last=$( tail -n1 file | awk -F" +" '{print $2}' )
for (( i=$first ; i<=$last ; i=i+10000 )) ; do
   awk -v start=$i -v end=$(($i+10000)) 'BEGIN { FS == " +" } { if ( $2 >= start && $2 < end ) print $0 }' file \
   >> interval_"$i"_to_"$(( $i+10000 ))"
done

間隔を100に設定してテストします。

more inter*
::::::::::::::
interval_61336212_to_61346212
::::::::::::::
chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
::::::::::::::
interval_61336312_to_61346312
::::::::::::::
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT  

注:空の間隔で空のファイルを生成します。空のファイルを削除するには、次を追加します。

for file in interval* ; do
  if [ ! -s "$file" ] ; then
    rm "$file"
  fi
done

forループの各ステップでファイルを実行するため、最も効率的ではありません。


3

行カウントではなく計算だけを意味する場合:

awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file

入力に0、9999、12000、19999、21000、22000がある場合、file1には0、9999が配置されますが、file2には12000、19999、21000が配置され、要件とは奇妙に思えます。
ステファンChazelas

これは、数百のファイルの後に同時に開くことができるファイルの数の制限に達することに注意してください(GNU awkはその制限を回避できますが、パフォーマンスはすぐに低下します)。
ステファンChazelas

@StéphaneChazelas私はあなたを明確に理解しているのかわかりません。第三のファイルの使用9999で21000へのごたい場合は代わりに10000
コスタス

私の質問の理解から、OPは最初のファイルに0〜9999、2番目のファイルに10000〜19999の行を必要としています。
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.