web-dev-qa-db-ja.com

特定の列でフィールド値が同じである行のグループを並べ替える

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

1 bob A
1 jim B
1 Kate A
1 Nancy C
1 bill A
1 Jason A
2 James B
2 fill B
2 cake C
2 lucky C
2 Lucy A
2 lily B

各1および2グループ内の列3でデータを並べ替えるにはどうすればよいですか?出力は次のようになります。

1 bob A
1 Kate A
1 bill A
1 Jason A
1 jim B
1 Nancy C
2 Lucy A
2 James B
2 fill B
2 lily B
2 cake C
2 lucky C

Kateは、入力ではこの順序であるため、出力ではbillの前に表示されることに注意してください。

列1の値が大きく、1、2から2000になるので、特定の値だけでなく、行番号を比較しながらawkprintについて考えていました。

1
Mavis

sortファイルを最初の列で数値的に、3番目の列で辞書式に:

sort -s -k1n,1 -k3,3 file

注意: -sはPOSIX仕様の拡張です

6
guest

GNU sort for -s次に @ guest's solution を参照してください。それ以外の場合は、cat + sort + cutを使用します。

$ cat -n file | sort -k2,2n -k4,4 -k1,1n | cut -f2-
1 bob A
1 Kate A
1 bill A
1 Jason A
1 jim B
1 Nancy C
2 Lucy A
2 James B
2 fill B
2 lily B
2 cake C
2 lucky C
2
Ed Morton

配列の各行を収集します。行の最初の単語が前の最初の単語と同じでない場合は、3番目の単語でソートされた配列を出力します。単純なsortがその仕事をすることができるとき、これはやや上を行くかもしれません。以下は、質問に示されている形式とは異なる入力ファイルを考慮していません。

gawk:

BEGIN {ors=ORS; ORS=""; PROCINFO["sorted_in"]="@ind_str_asc"}

$1!=r {
    output()
    delete a
    r=$1
}
{
    a[$3]=a[$3] $0 ors
}

END {
    output()
}

function output() {
    for (i in a)
        print a[i]
}

python:

import fileinput, operator
r=''; a=[]
def out():
    for p in sorted(a,key=operator.itemgetter(2)):
        print(' '.join(p))

for line in fileinput.input():
    x = line.rstrip().split()
    if r!=x[0]:
        r=x[0]
        if a:
            out()
            del a[:]
    a.append(x)
out()

Perl:

Perl -lae 'sub out {foreach(sort keys %a) {print $a{$_}}} BEGIN {$ors=$\;$\=""}
    if ($F[0] ne $r) {$r=$F[0]; out; %a=()}
    $a{$F[2]}=$a{$F[2]}.$_.$ors; END{out}'
1
user413047

これがあなたが望んでいたawkソリューションです。 (具体的には、gawk [GNU awk]です。これは、POSIX awkでは機能しません。)

_awk '
        function dump() {
                PROCINFO["sorted_in"] = "@ind_str_asc"
                for (arg3 in group) {
                        PROCINFO["sorted_in"] = "@ind_num_asc"
                        for (line_num in group[arg3]) {
                                print group[arg3][line_num]
                        }
                        PROCINFO["sorted_in"] = "@ind_str_asc"
                }
        }
        {
                if ($1 != saved_arg1) {
                        dump()
                        delete group
                        saved_arg1 = $1
                }
                group[$3][NR] = $0
        }
        END {
                dump()
        }
    '
_

主な作業は途中から始まります。各行について、その_$1_値が最新の値と異なる場合は、新しいグループに参加していることを意味します。前のグループのデータをダンプし(つまり、出力に書き込みます)、前のグループの保存済みデータを削除してから、新しい_$1_値を記憶します。

次に、いずれの場合も、現在の行をgroup配列に追加します。これは、_$3_値とNR(行番号)でインデックス付けされた2次元配列です。したがって、たとえば、サンプル入力の最初の6行については、次のようになります。

_group["A"][1] = "1 bob A"
group["B"][2] = "1 jim B"
group["A"][3] = "1 Kate A"
group["C"][4] = "1 Nancy C"
group["A"][5] = "1 bill A"
group["A"][6] = "1 Jason A"
_

7行目に_$1_ = _2_が表示されたら、dump関数(プログラムの先頭で定義)を呼び出します。 for (arg3 in group)は、_arg3_をABCの順に設定します。次に、_arg3_ = Aの場合、ループfor (line_num in group[arg3])(つまり、for (line_num in group["A"])は_line_num_を_1_、_3_、_5_、_6_の順で、印刷します。

_1 bob A
1 Kate A
1 bill A
1 Jason A
_

他の_$3_値についても同様です。他の_$1_値についても同様です。