저는 3천만에서 4천만 개의 데이터 항목 사이에 있는 많은 데이터를 가지고 있습니다. 우리는 이 파일들을 처리하여 다른 인터페이스 팀으로 보내야 합니다.
아래는 우리가 받은 파일 형식입니다.
c1 c2 c3 c4 c5 c6
A B C D 5 s
A B C D 4 s
A B E F 5 s
A B E F 8 S
C D E F 9 S
출력 파일의 모든 열을 인쇄해야 합니다. 이는 GPRS 사용과 관련되므로 그룹화해야 합니다.c1-c4그런 다음 모든 것이 일치하면 요약해야 합니다.c5을 클릭하고 파일의 모든 내용을 인쇄합니다.
다음은 샘플 출력 파일입니다.
c1 c2 c3 c4 c5 c6
A B C D 9 s
A B E F 13 s
C D E F 9 s
또한 이 작업 흐름은 Unix 스크립트보다 Perl에서 훨씬 빠르게 실행된다고 들었습니다.
답변1
perl
@terdon의 답변과 유사하지만 더 나은 형식의 출력을 제공하는 또 다른 솔루션은 다음과 같습니다.
$ perl -alne '
(print && next) if $. == 1;
$h{"@F[0..3]"}{s} += $F[4];
$h{"@F[0..3]"}{t} = $F[5];
END {
for (keys %h) {
printf "%-4s%-4s%-4s%-4s%-4s%-4s",split(" ",$_),$h{$_}{s},$h{$_}{t};
printf "\n";
}
}' file
c1 c2 c3 c4 c5 c6
A B E F 13 S
A B C D 9 s
C D E F 9 S
답변2
도구 선택 관련: 일반적으로 도구가 전문화될수록 속도가 빨라집니다. 따라서 , , 등이 포함된 파이프라인은 , tr
, 등 보다 빠른 경향이 있습니다 . 그러나 물론 그것은 작업에 따라 많이 달라집니다. Perl이 더 빠르다는 내용을 읽었다면 잘못 읽었을 수도 있습니다. 또는 비교는 한 번에 한 줄을 처리하는 쉘 루프에 대한 것입니다(수백만 줄이 있는 파일의 경우 느릴 수 있음).cut
grep
sort
sed
awk
perl
python
ruby
병합할 행이 연속적인 형식으로 입력한 경우 awk가 좋은 선택입니다(sed에서는 추가를 수행할 합리적인 방법이 없습니다).
awk -v OFS='\t' ' # use tabs to separate output fields
NR==1 {print; next} # keep the first line intact
function flush () { # function to print a completed sum
if (key != "") print previous, sum, more;
sum=0
}
{key = $1 OFS $2 OFS $3 OFS $4} # break out the comparison key
key!=previous {flush()} # if the comparison key has changed, print the accumulated sum
{previous=key; sum+=$5; more=$6} # save the current line
END {flush()} # print the last
'
행이 연속되지 않은 경우 정렬을 통해 연속적으로 만들 수 있습니다. 일반적인 sort
구현은 고급 언어로 데이터 구조를 조작하는 것보다 고도로 최적화되고 빠릅니다.
sort | awk …
이는 열 구분 기호가 항상 탭과 같이 일관적이라고 가정합니다. 그렇지 않은 경우 입력을 사전 처리하여 그렇게 하거나 sort -k1,1 -k2,2 -k3,3 -k4,4
구분 기호에 관계없이 이러한 특정 필드를 비교하는 데 사용하세요.
답변3
이는 시작하는 데 도움이 될 수 있습니다.
perl -ane '$h{"@F[0 .. 3]"} += $F[4] }{ print "$_ $h{$_}\n" for keys %h' input-file
수행할 작업을 지정하지 않았기 때문에 마지막 열은 인쇄되지 않습니다. 또한 헤더 행을 올바르게 처리하지 못하지만 쉽게 수정할 수 있습니다.
답변4
내가 올바르게 이해했다면 다음과 같은 것을 원할 것입니다.
$ perl -lane 'if($.>1){$k{"@F[0..3]"}{sum}+=$F[4]; $k{"@F[0..3]"}{last}=$F[5]}
else{print "@F"}
END{
foreach (keys(%k)){ print "$_ $k{$_}{sum} $k{$_}{last}"}
}' file
c1 c2 c3 c4 c5 c6
C D E F 9 S
A B E F 13 S
A B C D 9 s
이렇게 하면 열 정렬이 유지되지 않습니다. 이것이 문제인지는 모르겠습니다. 그러나 헤더를 올바르게 처리하고 필요한 출력을 생성합니다.
설명하다
perl -lane
:-l
각 문자열의 끝에서 개행 문자를 제거하고 이를 각print
명령문에 추가합니다. 각 입력 줄을 공백의 필드로 분할a
하고 이러한 필드를 배열에 저장합니다@F
.n
방법입력 파일을 한 줄씩 읽고 아래 스크립트를 적용하십시오.-e
.
다음은 주석 처리된 스크립트 형식의 동일한 한 줄입니다.
#!/usr/bin/env perl
## This is the equivalent of perl -ne
## in the one-liner. It iterates through
## the input file.
while (<>) {
## This is what the -a flag does
my @F=split(/\s+/);
## $. is the current line number.
## This simply tests whether we are on the
## first line or not.
if ($.>1) {
## @F[0..3] is an array slice. It holds fields 1 through 4.
## The slice is used as a key for the hash %k and the 5th
## field is summed to $k{slice}{sum} while the last column is
## saved as $k{slice}{last}.
$k{"@F[0..3]"}{sum}+=$F[4]; $k{"@F[0..3]"}{last}=$F[5];
}
## If this is the first line, print the fields.
## I am using print "@F" instead of a simple print
## so that all lines are formatted in the same way.
else {
print "@F\n";
}
}
## This is the same as the END{} block
## in the one liner. It will be run after
## the whole file has been read.
## For each of the keys of the hash %k
foreach (keys(%k)){
## Print the key ($_, a special variable in Perl),
## the value of $k{$key}{sum} (the summed values),
## and the last column.
print "$_ $k{$_}{sum} $k{$_}{last}\n"
}