web-dev-qa-db-ja.com

‌最初の列をIDとして、各グループで最小値の行を選択するにはどうすればよいですか?

私はこのようなファイルを持っています:

1   7.8e-12  
1   7.8e-12  
1   1.0e-11   
2   9.3e-13    
2   3.5e-12 
2   3.5e-10
2   3.1e-9         
3   3.0e-11    
3   3.0e-11     
3   1.7e-08   

列1のすべての値について、列2に最小値を持つ「すべての行」を選択し、列1でグループ化します。したがって、必要な出力は次のとおりです。

 1   7.8e-12  
 1   7.8e-12
 2   9.3e-13
 3   3.0e-11    
 3   3.0e-11 

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

4
Anna1364

1つのアプローチは、昇順で並べ替え、各col1の最初のcol2値を書き留め、現在のcol2値がそれと等しい場合に出力することです。

sort -k1,1n -k2,2g file | awk '!a[$1] {a[$1] = $2} $2 == a[$1]'
1   7.8e-12
1   7.8e-12
2   9.3e-13
3   3.0e-11
3   3.0e-11
6
steeldriver

これは、科学的記数法で数値を処理する必要があります。

awk '
    NR == FNR {
        if (!($1 in min) || $2 < min[$1])
            min[$1] = $2
        next
    }
    $2 == min[$1]
' file file

ファイルを2回処理します。1回目は各キーのmin値を見つけ、次にそのmin値の行を出力します。

5
glenn jackman

GNUツールを使用すると、値を複製する必要があるため複雑になります!

grep -F "$(datamash -W -g1 min 2 <infile | \
sed 's/\([^\.][1-9]\)e/\1.0e/')" <(sed 's/ \+/\t/' infile)

最小値のみが関係する場合は、以下でdatamashで十分です。

datamash -W -g1 min 2 <infile
1
αғsнιη

テキスト処理ベースの回答をまとめるために、PostgreSQLでこれを行う方法を次に示します。

まず、ファイルを前処理してCSVに変換し、インポートを簡単にします。

awk -v OFS=, '$1=$1' file.txt > file.csv

次に、次のようにPostgreSQLで一時テーブルを作成します。

create temp table x (id int, bignum float);

CSVをそれにコピーします。

\copy x from file.csv with (format csv)

そして、必要な結果について一時テーブルにクエリを実行します。

select id, bignum
from (
  select
    *,
    rank() over (partition by id order by bignum)
      as rank
  from x
) as sqlrequiresthisalias
where rank = 1;

結果:

 id | bignum  
----+---------
  1 | 7.8e-12
  1 | 7.8e-12
  2 | 9.3e-13
  3 |   3e-11
  3 |   3e-11
(5 rows)
1
Wildcard

GNU awk解決策:

awk 'BEGIN{ PROCINFO["sorted_in"] = "@val_num_asc" }
     { a[$1][++c] = $2 }
     END{ 
         for (i in a) { 
             prev = 0;
             for (j in a[i]) { 
                 v = a[i][j]; if (prev && v != prev) continue; 
                 print i, v; prev = v 
             }
         }
     }' file

出力:

1 7.8e-12
1 7.8e-12
2 9.3e-13
3 3.0e-11
3 3.0e-11
0
RomanPerekhrest