같은 표의 첫 번째 열에 주어진 항목을 기준으로 아래 표의 열에 나열된 수치 값을 합산하고 싶습니다. 표의 내용은 다음과 같습니다.
10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
예상 결과는 다음과 같습니다(두 번째 및 마지막 열로 그룹화된 데이터).
12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F
Linux에서 awk를 사용하여 이 출력을 얻으려면 어떻게 해야 합니까?
답변1
$ cat tst.sh
#!/usr/bin/env bash
awk '
BEGIN { FS=OFS="," }
$2 != vals[2] {
if ( NR>1 ) {
prt()
}
split($0,vals)
next
}
{
for ( i=1; i<=NF; i++ ) {
if ( $i+0 == $i ) {
vals[i] += $i
}
}
}
END {
prt()
}
function prt( i) {
for (i=1; i<=NF; i++) {
printf "%s%s", vals[i], (i<NF ? OFS : ORS)
}
}
' "${@:--}"
$ ./tst.sh file
12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F
입력 파일이 아직 두 번째 필드로 그룹화되지 않은 경우(게시한 입력 예에서와 같이) 다음을 변경하십시오.
awk '...' "${@:--}"
이와 관련하여:
sort -t',' -k2,2 "${@:--}" | awk '...'
답변2
두 번째 열을 키로 사용하고 출력하는 동안 레코드 순서를 유지하면서 이 작업을 수행할 수 있습니다.
awk -F, -v OFS=, '!seen[$2]++{ recNr++ }
{ for(i=1; i<=NF; i++)
if(i!=2 && i!=NF)
sumCol[recNr, i, $2]+= $i
else
sumCol[recNr, i, $2]= $i (i==NF?ORS:"")
}
END{ for (key in sumCol){
if(sumCol[key]!=""){
recNumbr++; sep=""
split(key, tmp, SUBSEP)
for(j=1; j<=NF; j++){
printf ("%s", sep sumCol[recNumbr, j, tmp[3]])
sep=OFS
delete sumCol[recNumbr, j, tmp[3]]
}
}
}
}' infile
답변3
GNU 사용 datamash
:
$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
4,Chennai,5,6,7,8,9,0,6,F
12,Mumbai,1,7,7,0,10,7,59,M
이는 datamash
열 1과 열 3~9를 합산하고 입력을 열 2와 10의 조합으로 그룹화합니다.
datamash
그룹화된 열이 출력에서 먼저 출력되므로 두 번째 단계를 수행하여 원래 datamash
순서로 재정렬합니다.
출력은 그룹화 열을 기준으로 정렬되므로 Chennai
이전에 입력됩니다 Mumbai
. 원본 데이터가 이미 정렬되어 있는 경우 -s
명령에서 제거합니다.
다른 예시:
$ cat file
10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M
$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
8,Chennai,10,12,14,16,18,0,12,F
4,Chennai,5,6,7,8,9,0,6,M
12,Mumbai,1,7,7,0,10,7,59,M
답변4
사용행복하다(이전 Perl_6)
~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join("\t"), as => {$_.split(",").[ 0,2..*-2 ][*;*]}); \
for %class.kv -> $k,$v {say $k => $v.elems > 1 ?? [Z+] $v<> !! $v[*;*]};' file
OP는 이 문제를 해결하기 위해 Perl 언어 계열을 사용하는 것을 고려할 수 있습니다. 위의 내용은 Raku를 사용하는 한 가지 방법만을 나타냅니다. 즉, 두 번째 및 마지막 열( 쉼표 뒤의 색인) 을 lines
읽고 편집합니다. 이 열 정보는 분류자에도 유지되는 경우 중복되므로 이 매개변수는 구성 요소에서 숫자가 아닌 두 개의 열을 제거하는 데 사용됩니다 . 데이터는 해시에 저장됩니다 .classify
[1, *-1]
split
key
value
as
classify
value
%class
여기에서 쌍은 키/값 구성 요소 %class
로 분할되고 인쇄 되며 Raku의 삼항 연산자를 사용하여 테스트되어 여러 요소가 포함되어 있는지 확인합니다 . 여러 요소가 발견되면 열이 합산되어 사용됩니다 (컨테이너화되지 않은 다음 요소별 합산). 요소가 하나만 있는 경우 열이 제거됩니다 ( 합산되지 않고 평면화만 됨).kv
key
value
elems
put
[Z+] $v<>
put
$v[*;*]
입력 예:
10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M
출력 예(탭으로 구분 keys
):
Chennai M => (4 5 6 7 8 9 0 6)
Chennai F => (8 10 12 14 16 18 0 12)
Mumbai M => (12 1 7 7 0 10 7 59)
Raku에서는 쉼표로 구분된 출력이 확실히 가능하지만 아래 답변에서는 단순화를 위해 두 개의 "그룹화" 열을 열 1과 열 2로 추상화합니다.
~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join(","), as => {$_.split(",").[ 0,2..*-2 ][*;*]}); \
for %class.kv -> $k,$v {put $k ~","~ ($v.elems > 1 ?? [Z+] $v<> !! $v[*;*]).join(",")};' file
Chennai,F,8,10,12,14,16,18,0,12
Mumbai,M,12,1,7,7,0,10,7,59
Chennai,M,4,5,6,7,8,9,0,6
마지막으로 더 넓은 샘플 입력 데이터 세트를 제공한 @Kusalananda에게 감사드립니다.
https://docs.raku.org/routine/classify
https://docs.raku.org/언어/operators#index-entry-operator_ternary
https://raku.org