awk: 각 열의 각 고유 문자열의 총 개수와 해당 열 순서대로 출력

awk: 각 열의 각 고유 문자열의 총 개수와 해당 열 순서대로 출력

각 열의 각 uniq 문자열의 총 개수(합계)를 가져오고 해당 열 순서로 출력하려고 합니다.

다양한 전체 입력이 수천 개의 행과 열인 경우가 많기 때문에 강력한 awk 명령에서 이것을 사용해야 합니다.

나는 이것을 직접 시도했지만 운이 없었습니다. 나는 꽤 가깝다고 생각합니다. 분명히 작동하지 않지만 코드를 사용하는 곳은 다음과 같습니다.

awk -F ',' '{ for(N=1; N<=NF; N++) {{count[$N]++} END {for (word in count) print word, count[word]}}}'

위 코드에 대한 내 생각은 실행하면 지정된 단일 열에 대해 원하는 출력을 얻을 수 있다는 것입니다. 이제 열 2를 가정해 보겠습니다.

awk '{count[$2]++} END {for (word in count) print word, count[word]}'

하지만 각 열에 대해 이러한 유형의 출력이 필요합니다. 그래서 이것을 달성하기 위해 열을 반복하려고 시도했지만 실패했습니다.

다음은 몇 가지 샘플 데이터입니다.

입력 예:

M,M,M,M
N,N,N,N    
A,M,G,L
P,P,P,P
A,N,G,L    
P,N,P,L
A,A,A,A
C,C,C,C
A,M,G,C
L,L,L,L
G,G,G,G

해당 예상 출력:

M 1,M 2,M 1,M 1
N 1,N 2,N 1,N 1
A 3,A 1,A 1,A 1
P 1,P 1,P 1,P 1
L 1,L 1,L 1,L 3
G 1,G 1,G 3,G 1
C 1,C 1,C 1,C 1

출력을 설명하기 위해 예제 입력의 첫 번째 열에는 As가 3개 있고 다른 모든 문자는 각각 1개만 있으므로 해당 열의 출력은 다음과 같습니다.
M 1
N 1
A 3
P 1
L 1
G 1
C 1

이 코드를 작성했는데 작동하지만 이상적으로는 awk 명령에서 구현하고 싶습니다.

for i in $(seq $NumberOfColumns);do
ColumnOutput=$(awk -F ',' -v x=$i '{count[$x]++} END {for (word in count) print word, count[word]}' file)
TotalOutput=$(paste <(echo "$TotalOutput") <(echo "$ColumnOutput") -d ,)
done    
echo "$TotalOutput" | sed 's/^,//g'    

답변1

나는 이것이 도움이 될 것이라고 생각합니다:

$ awk -F"," '              
    NR==FNR { for (i=1;i<=NF;i++) {a[i,$i]+=1;b[$i]=$i} next }
    { for (i=1;i<=NF;i++)if(b[$1]) printf "%s %s,",$1,a[i,$1];else next; print ""; delete b[$1] }
' file file
M 1,M 3,M 1,M 1,
N 1,N 3,N 1,N 1,
A 4,A 1,A 1,A 1,
P 2,P 1,P 2,P 1,
C 1,C 1,C 1,C 2,
L 1,L 1,L 1,L 3,
G 1,G 1,G 4,G 1,

답변2

사용행복하다(이전 Perl_6)

~$ raku -e 'my @a = [Z] lines.map: *.split(","); my @b; \ 
            for ^@a -> $row  {                          \
                for ^$row.elems -> $col {    my %h;     \
                    %h{$_}++ for @a[$row]>>.[$col];     \
                    @b.push: %h.sort }};                \ 
            for ^@b>>.elems.max -> $j {                 \
                put @b.map({ $_.[$j] // (" " => "0") }).join: "," };'  file

또는 (더 쉽게):

~$ raku -e 'my @a = [Z] lines.map: *.split(","); my @b; \
            for ^@a -> $row {                           \
                my %h is BagHash = @a[$row];            \ 
                @b.push: %h.sort };                     \ 
            for ^@b>>.elems.max -> $j {                 \
                put @b.map({ $_.[$j] // (" " => "0") }).join: "," };'  file

다음은 Perl 계열의 프로그래밍 언어인 Raku로 작성한 답변입니다. Raku에는 유니코드에 대한 고급 지원 기능이 내장되어 있습니다. 위의 코드는 @서명된 배열 및 %서명된 해시(예: 사전/키-값 쌍) 와 같은 다양한 Perl 기능을 활용합니다 .

  • 먼저, 데이터를 한 줄씩 읽어서 lines각 데이터를 split쉼표로 구분합니다. 행과 열을 바꾸려면 [Z]연산자를 사용하고 데이터를 @a배열에 저장하십시오.

  • 다음으로 @a배열은 먼저 by, $row다음으로 반복 $col되어 각 $row셀에 대해 "키"( ) 가 해시(첫 번째 답변) 또는 BagHash(두 번째 답변) $_에 저장됩니다 . %h각 행이 구문 분석된 후 키/값 데이터가 @b배열에 저장됩니다.

  • 마지막으로 max키/값의 수를 결정하고(통과) 데이터를 출력합니다. 특정 열의 키가 정의되지 않은 곳에 0을 삽입하도록 주의합니다(OP는 여기에서 대신 사용할 수 있음).elemsput0"Nil"" "

입력 예(열의 키 개수가 같지 않음):

M,M,M,M
N,N,N,N
A,M,G,L
P,P,P,P
A,N,G,L
P,N,P,L
A,A,A,A
C,C,C,C
A,M,G,X
L,L,L,L
G,G,G,G

출력 예(키/값 쌍 \t탭으로 구분됨):

A  4,A  1,A  1,A  1
C  1,C  1,C  1,C  1
G  1,G  1,G  4,G  1
L  1,L  1,L  1,L  4
M  1,M  3,M  1,M  1
N  1,N  3,N  1,N  1
P  2,P  1,P  2,P  1
   0,   0,   0,X  1

https://docs.raku.org/type/BagHash
https://docs.raku.org
https://raku.org

관련 정보