다음과 같은 데이터가 있습니다.
111 5
111 6
111 1
222 8
222 9
222 1
222 3
555 9
555 7
555 6
의 각 값에 대해 $1
가능하다면 AWK를 사용하여 $2
해당 값에 대한 모든 값의 중앙값을 얻고 싶습니다.$1
원하는 출력:
111 5 5
111 6 5
111 1 5
222 8 5.5
222 9 5.5
222 1 5.5
222 3 5.5
555 9 7
555 7 7
555 6 7
$1
여기서 5는 5, 6, 1( == 값 ) 의 중앙값이고 111
, 5.5는 8, 9, 1, 3의 중앙값입니다.
답변1
모든 UNIX 시스템의 모든 쉘에서 sort+awk를 사용하십시오.
$ cat tst.awk
$1 != prev { if (NR>1) prt(); prev=$1 }
{ vals[++cnt] = $2 }
END { prt() }
function prt( i,med) {
med = (vals[int((cnt+1)/2)] + vals[int((cnt/2)+1)]) / 2
for (i=1; i<=cnt; i++) {
print prev, vals[i], med
}
cnt = 0
}
$ sort -k1,1n -k2,2n file | awk -f tst.awk
111 1 5
111 5 5
111 6 5
222 1 5.5
222 3 5.5
222 8 5.5
222 9 5.5
555 6 7
555 7 7
555 9 7
$2
위 코드는 모든 현재 값을 $1
명명된 배열에 저장 vals[]
한 다음 $1
값이 변경되거나 파일의 끝에 도달하면 호출하여 prt()
해당 배열의 중앙값을 계산하고 명명된 변수에 저장한 med
다음 루프에 인쇄합니다. s 및 의 $1
모든 관련 추가 사항 .$2
med
출력 행은 위에서 재정렬되었습니다. 이것이 문제라면 먼저 행을 장식하여 원래 순서를 유지한 다음 위에서 sort+awk를 수행한 다음 다시 원래 순서로 정렬하고 마지막으로 장식을 취소할 수 있습니다.
GNU awk가 있고 키 값이 이미 정렬되어 있는 경우 함수 asort()
내에서 호출 할 수 있으므로 이전에 prt()
호출할 필요가 없습니다 . 정렬이 없으면 모든 것을 배열에 저장한 다음 END 부분에서 정렬할 수 있습니다. 그러나 그림에서 볼 수 있듯이 먼저 호출하는 것이 가장 명확하고 단순하며 효율적이며 이식성이 뛰어납니다.sort
awk
sort
답변2
데이터가 이미 첫 번째 열에서 사전순으로 정렬되어 있고 프로세스 대체를 이해하는 셸을 사용하고 있다고 가정합니다 <(...)
.
$ join file <( datamash -W groupby 1 median 2 <file )
111 5 5
111 6 5
111 1 5
222 8 5.5
222 9 5.5
222 1 5.5
222 3 5.5
555 9 7
555 7 7
555 6 7
명령 결과를 사용하여 파일의 첫 번째 필드에서 관계형 JOIN 작업을 수행합니다.
datamash -W groupby 1 median 2 <file
이 명령은 첫 번째 필드의 값으로 그룹화된 두 번째 필드의 각 값 집합의 중앙값을 계산합니다. 이 옵션을 사용하면 GNU가 입력을 공백으로 구분된 것으로 처리하도록 -W
합니다 .datamash
이 작업의 결과는
111 5
222 5.5
555 7
원하는 결과를 얻으려면 이를 첫 번째 필드의 원본 데이터와 연결하세요.
데이터가 첫 번째 필드에서 아직 정렬되지 않은 경우:
join <( sort -k 1,1n file ) <( datamash -s -W groupby 1 median 2 <file )
첫 번째 필드가 동일한 행이 서로 상대적으로 재정렬되지 않도록 하려면 명령에서 다음을 sort file
사용하는지 확인하세요.안정적인정렬 알고리즘. 대부분의 구현에서는 이를 수행하기 위해 sort
비표준 옵션을 사용할 수 있습니다 .-s
프로세스 교체가 없는 쉘의 경우:
데이터는 다음과 같이 정렬됩니다.
datamash -W groupby 1 median 2 <file | join file -
데이터를 정렬해야 합니다.
sort -o file.sorted -k 1,1n file datamash -W groupby 1 median 2 <file.sorted | join file.sorted - rm -f file.sorted
답변3
GNU awk를 사용하여 asort() 함수를 실행하면 데이터 정렬 여부에 관계없이 입력 파일을 첫 번째 처리에서 두 번 처리해야 하며 각 Id 값을 하나로 그룹화한 다음 각각의 중앙값을 계산합니다. 그룹, 두 번째로 파일을 처리할 때 계산된 중앙값을 마지막 열로 인쇄합니다.id
대량:
awk -v sep=, 'NR==FNR{ id[$1]=($1 in id ? id[$1] sep : "") $2; next }
FNR==1 {
for(x in id){
ln=split(id[x], tmp, sep); asort(tmp)
id[x]=(ln%2? tmp[int(ln/2)+1]: (tmp[ln/2]+ tmp[ln/2+1])/2 )
}
}
{ print $0, id[$1] }' infile infile