元のファイルの列ヘッダーに基づいてファイルを別々のファイルに分割する方法は?

元のファイルの列ヘッダーに基づいてファイルを別々のファイルに分割する方法は?

最初の行の情報に基づいて、ファイルを複数のファイルに分割したいと思います。たとえば、次のようになります。

入力する:

1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 0 2 0 2

希望の出力:

output1.txt

02202020
02101011
02101011

output2.txt

2022002
1022002
1022002

output3.txt

220111
220000
220000

output4.txt

202000200202
202001200202
202001200202

出力30.txt

0202
0202
0202

答え1

$ awk '
    NR == 1 {
        for (i=1; i<=NF; i++) {
            output[i] = "output" $i ".txt"
            files[output[i]] = 1
        }
        next
    }
    {
        for (i=1; i<=NF; i++)  printf "%s", $i > output[i]
        for (file in files)    print ""        > file
    }
' input.filename

$ for f in output*.txt; do echo $f; cat $f; done
output1.txt
02202020
02101011
02101011
output2.txt
2022002
1022002
1022002
output3.txt
220111
220000
220000
output30.txt
00202
00202
00202
output4.txt
2020002
2020012
2020012

ヘッダー行には32個のフィールドがあり、他の行には33個のフィールドがあります。この問題を最初に解決する必要があります。

答え2

Perlスクリプト。

$in代わりにファイル名を設定するgenome.txtか、名前を引数として渡します。

スクリプト名を指定しcounter.plて実行権限を付与してから実行してください。./counter.pl

chmod 755 counter.pl
./counter.pl

または

chmod 755 counter.pl
./counter.pl genome.txt

counter.pl:

#!/usr/bin/perl

use strict;
use warnings;

my $in = $ARGV[0] || 'genome.txt'; # input file name

open (my $F, '<', $in) or die "Cannot open input file $!";
my $n = 0;
my %fd = ();
my @fd = ();

while (<$F>) {
        # trim
        s/^\s+//;
        s/\s+$//;
        next if (!$_); # Skip empty lines
        my @x = split(/\s+/, $_);
        # 1st line, open files
        if ( ! $n++)  {
           my $fd = 0;
           for (@x) {
              open ($fd{$_}, '>', "output$_.txt") 
                or die ("Cannot open file $!")
                  if (!exists($fd{$_}));
              $fd[$fd++] = $_;
           }
        }
        else { # Write data
           die ("Should have " . ($#fd+1) . " entries on line $n")
             if ($#x != $#fd);
           for (0 .. $#x) {
              print {$fd{$fd[$_]}} ($x[$_]);
           }
           print {$fd{$_}} ("\n") for (keys %fd);
        }
}

close $fd{$_} for (keys %fd);
close $F;
# the end

1行あたりの固定語数(時々32、例えば33)。

このバージョンはすべての列バリアントに対応できますが、すべての行の単語数が同じでなければなりません。die単語数が異なる場合やファイルを開くことができない場合は、エラー(行)が表示されます。

ファイル名($in)を調整するだけです。

入力ファイル:(最後に追加0を削除)

1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2

出力1.txt

02202020
02101011
02101011

出力2.txt

2022002
1022002
1022002

出力30.txt

0202
0202
0202

出力3.txt

220111
220000
220000

出力4.txt

2020002
2020012
2020012

答え3

さて、楽しいです。組み込み機能に大きく依存する純粋なBashバージョン(要求時)です。読む単語を配列に送信してファイルに保存します。ファイルは output001.txt ....output030.txt 形式になっています。 @ringOが修正したデータファイルを使用してテストしました。テストされていませんが、非常に大きなファイルでは、他のファイルよりも時間とリソースを節約できます。

データ:

1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2

源泉:

#!/usr/bin/env bash

# genome : to sort genome data sets according to patterns of the first (header)
# line of the file.  Data must be space delimited.  No dependencies.
#
# Usage:
#
#                      ./genome "data.txt" 

# global arrays
sc=(  )             # array of set element counts
sn=(  )             # array of set id numbers

# output_file "set id"

# change the output pattern and digit output width as required - default
# pattern is output.txt and digit width of three : output000.txt
output_file(){
    # format concept: pattern000.txt
    local op='output.txt'     # output pattern
    local ow=3                # output width: 3 => 000
    printf "%s%0${ow}d.%s" "${op%%.*}" "$1" "${op##*.}"
}

# define_sets "input.txt"

# identify sets - get elements count and sets id numbers from file
# header.
define_sets(){
    # declare and initialize
    local a an b c n
    read -r c < "$1"
    read -r a b <<< "$c"
    n=0; sn=( $a )

    # recurse header, identify sets
    until [[ -z $b ]]
    do
        n=$((n+1))
        an=$a
        read -r a b <<< "$b"
        [[ $an == $a ]] || { sn+=( $a ); sc+=( $n ); n=0; }
    done
    n=$((n+1))
    sc+=( $n )
}

# reset_files

# optional function, clears file data, otherwise data is appended to existing
# output files.
reset_files(){
    for s in ${sn[@]}
    do
        > "$(output_file "$s")"
    done
}

# extract_data "input.txt"

# use defined sets to extract data from the input file and send it to required
# output files. Uses nested 'while read' to bypass file header as data is saved.
extract_data(){
    local a c n s fn da=( )
    while read -a da
    do
        while read -a da
        do
            a=0 n=0
            for s in ${sc[@]}
            do
                c="$(echo "${da[@]:$a:$s}")" # words => string
                echo "${c// /}" >> "$(output_file "${sn[$n]}")"  # save
                n=$((n+1))
                a=$((a+s))
            done
        done
    done < "$1"
}

define_sets "$1"    # get data set structure from header
reset_files         # optional, clears and resets files
extract_data "$1"   # get data from input file and save

# end file

データ出力:

$ cat output001.txt 
02202020
02101011
02101011

$ cat output002.txt 
2022002
1022002
1022002

$ cat output003.txt 
220111
220000
220000

$ cat output004.txt 
2020002
2020012
2020012

$ cat output030.txt 
0202
0202
0202

答え4

楽しみにして他の解決策もあります。

awk '{ for (i=1; i<=NF;i++){
          if (NR==1) { file[i]=$i }
          if (NR!=1) { f="output" file[i]   ".txt";
                       g="output" file[i+1] ".txt";
                       printf("%s%s",$i,f==g?OFS:ORS)>>f;
                       close(f);
                      }
          }
      }' file

無制限のフィールドが必要な場合に変更し?OFS:てください?"":

ペアのない値を受け取るデフォルトファイルはですoutput.txt。このファイルは、最初の行の列数が処理される次の行と一致しない場合に値を受け取ります。すべてが正しい場合は空でなければなりません。スクリプトを実行した後もまだ存在する場合は、どこかに問題があります。

関連情報