저는 약 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"
}