最初の行に基づいて列を並べ替える方法は?


12

非常に大きなデータセット(1000行と700000列)の列を並べ替える必要があります。例として、私の列はcol1 col4 col3 col2のようにランダムに配置され、ソートする必要があります。

私はいくつかのコマンドを試しましたが、成功しませんでした。

例:

ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

この例では、ドットは、列と行がたくさんあることを意味します。繰り返しますが、列を次のようにソートする必要があります。

ID M1 M2 M3 M4 M5 M6 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

ありがとうございました


データセットの数行で例を追加できますか?
jcbermu

あなたの期待される結果は最初の行だけがソートされており、他の値は同じままです、なぜですか?
-RomanPerekhrest

実際には、列に従う必要があり、例の間違いでした。ごめんなさい
LLVerardo

列全体を最初の行に基づいてソートする必要があります。
-LLVerardo

2
転置、最初の列で並べ替え、転置して戻します。
桂佐藤

回答:


10

GNU datamashおよびGNUの場合sort

datamash transpose -t ' ' -H <file_in.csv | sort -V | datamash transpose -t ' ' -H >file_out.csv

これは、「かなり小さい」データに対しては正常に機能します。ファイルで動作する場合と動作しない場合があります。

編集:転置なしの以下のソリューションは、リソースをあまり使用しないはずです。


1
RSコマンドはに軽い代替かもしれないdatamash例えばrs -T < file_in.csv | sort | rs -T -C' 'rsDebianベースのシステムでパッケージとして利用可能であるべき)
steeldriver

2
FWIW rs(「データ配列の再構成」)は、一部のBSDの基本システムで利用可能です。
クサラナナンダ

6
perl -pale '
   $. == 1 and
   @I = map  { $_->[1] }
        sort { $a->[0] <=> $b->[0] }
        map  { [ $F[$_] =~ /^M(\d+)$/, $_ ] } 1..$#F;
   $_ = "@F[0, @I]";
' yourlargefile

  1. 最初の行ではM、よく知られているを使用して、先頭にある数字の後にある数値部分を使用して、2番目...最後の列を数値的にソートしますSchwartzian maneuver。これにより、インデックスが並べ替えられ、列が数値順にソートされます(M1、M2、M3、...)
  2. 残っているのは、これらのインデックスを使用@Iして、@F要素です。
  3. 二重引用符で囲まれた形式で配列を割り当てると、要素がスペースで区切られた文字列に変換されます。
  4. -pPerlのへのオプションは、自動印刷の可能$_内容は、-l追加するものnewline

6

perlモジュールSort :: Naturallyを使用する

入力データ

ID M2 M5 M8 M1 M3 M9 M700000
A1 m1,2 m1,5 m1,8 m1,1 m1,3 m1,9 m1,7000000
A2 m2,2 m2,5 m2,8 m2,1 m2,3 m2,9 m2,7000000
A3 m3,2 m3,5 m3,8 m3,1 m3,3 m3,9 m3,7000000
A1000 m1000,2 m1000,5 m1000,8 m1000,1 m1000,3 m1000,9 m1000,7000000
perl -MSort::Naturally -lane '
  if ($. == 1) {
    @indices = (0, map  { $_->[0] }
                   sort { ncmp($a->[1], $b->[1]) }
                   map  { [$_, $F[$_]] }
                   1..$#F
               );
    $, = " ";
  }
  print @F[@indices]
' test.data

出力

ID M1 M2 M3 M5 M8 M9 M700000
A1 m1,1 m1,2 m1,3 m1,5 m1,8 m1,9 m1,7000000
A2 m2,1 m2,2 m2,3 m2,5 m2,8 m2,9 m2,7000000
A3 m3,1 m3,2 m3,3 m3,5 m3,8 m3,9 m3,7000000
A1000 m1000,1 m1000,2 m1000,3 m1000,5 m1000,8 m1000,9 m1000,7000000

+1は最もエレガントなものであり、列名に特定のプレフィックスが余りに想定されていない、ワンパスソリューションです。
アリエル

4

rsユーティリティがインストールされている場合、これを行うことができます。

rs -c' ' -T | {
    stdbuf -i0 sed "1q"
    sort -V
} | rs -C' ' -T

またはすべてを1行で:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • 最初 rsは入力データを転置します(スペースで区切られたフィールドを使用)
  • コマンドグループ:
    • sed最初の行を読み取り、出力してから終了し、パイプの残りの部分はそのままにしrsます。 入力バッファリングをオフにすることにより、最初の改行のみを読み取り、それ以上の読み取りstdbufを行わsedないようにするために必要です
    • sort残りの行
  • 2番目rsは、結果のストリームを元の形式に置き換えます。

rsMacOSにはデフォルトでインストールされます。Linuxシステムでは、インストールする必要があります-例えば

sudo apt install rs

警告:stdbufおよびsorts -VオプションはGNU固有であるため、変更されていないMacOSでは動作しません。


0

GNUをお持ちの場合awk、これを試すことができます:

NR == 1 {
    for (i = 2; i <= NF; i++) {
        columns[substr($i, 2)] = i;
    }
    count = asorti(columns, sorted, "@ind_num_asc");
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" M%s", sorted[i]);
        indx[i] = columns[sorted[i]];
    }
    print "";
    next;
}
{
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" %s", $(indx[i]));
    }
    print "";
}

0

Pythonの場合:

from csv import DictReader, DictWriter
with open('in_file.csv') as infile, open('out_file.csv') as outfile:
  reader = DictReader(infile)
  writer = DictReader(outfile, fieldnames=sorted(reader.fieldnames))
  writer.writerows(reader)

0

あなたがこれを良い答えとみなしたかどうかはわかりませんが、...

この問題を解決するためにデータベースを使用しないのはなぜですか?データセットを一時テーブルとしてインポートしてから、

SELECT column1、column2、... column-n FROM my_temp_table

必要に応じて、別のフィルターまたは変換を使用できます。その後、必要に応じて出力を再フォーマットできます。

これらのタスクはすべて、bashスクリプトとしてプログラムし、パイプを使用して出力をチェーンすることができます。

時々、コマンド間の出力の進行状況を確認するために「pv」コマンドを使用しました。

データセットをインポートするには、Pentaho Data Integrationを使用してETLをプログラムできます。


0

たぶんこれもあなたを助けるかもしれません。

  1. まず、ファイルの転置を使用できます(/programming/1729824/an-efficient-way-to-transpose-a-file-in-bashのいずれか)
  2. sortコマンドで最初の列をソートします。
  3. もう一度転置します。

例:

$ echo "ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln" | awk '
{ 
    for (i=1; i<=NF; i++)  {
        a[NR,i] = $i
    }
}
NF>p { p = NF }
END {    
    for(j=1; j<=p; j++) {
        str=a[1,j]
        for(i=2; i<=NR; i++){
            str=str" "a[i,j];
        }
        print str
    }
}' | sort -n | awk '
{ 
    for (i=1; i<=NF; i++)  {
        a[NR,i] = $i
    }
}
NF>p { p = NF }
END {    
    for(j=1; j<=p; j++) {
        str=a[1,j]
        for(i=2; i<=NR; i++){
            str=str" "a[i,j];
        }
        print str
    }
}'
ID M1 M2 M3 M5 .....M7000000 M8 M9
Animal1 1 1 0 0 .....1 2 2
Animal2 0 0 1 1 .....0 2 1
Animal3 1 2 2 1 .....0 0 1
.       
.       
.       
.       
Animaln    
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.