CSV 파일(7억 라인 이상)을 읽으려면 Unix 명령이 필요합니다. 예는 다음과 같습니다.
A, 10
B, 11
C, 12
A, 10
B, 12
D, 10
A, 12
C, 12
이 명령은 첫 번째 열의 발생 횟수를 계산한 다음 두 번째 열의 개별 발생 횟수를 계산하고 첫 번째 열의 항목별로 출력을 그룹화합니다. 출력은 다음과 같습니다.
A, 3, 2
B, 2, 2
C, 2, 1
D, 1, 1
답변1
출력의 처음 두 열을 얻으려면 다음을 수행하십시오.
$ cut -d, -f1 <file | sort | uniq -c | awk -vOFS=, '{ print $2, $1 }'
A,3
B,2
C,2
D,1
원본 파일의 첫 번째 열을 추출하여 정렬하고 중복 항목 수를 계산합니다. 마지막으로 awk
열을 바꾸고 그 사이에 쉼표를 삽입하십시오.
마지막 열에는
$ sort -u <file | cut -d, -f1 | uniq -c | awk -vOFS=, '{ print $1 }'
2
2
1
1
이렇게 하면 원본 데이터가 정렬되고 중복된 데이터가 삭제됩니다. 그런 다음 첫 번째 열과 중복 횟수를 추출합니다.저것계산됩니다. 마지막으로 awk
개수만 추출됩니다.
다음을 사용 bash
하고 결합하십시오 paste
.
$ paste -d, <( cut -d, -f1 <file | sort | uniq -c | awk -vOFS=, '{ print $2, $1 }' ) \
<( sort -u <file | cut -d, -f1 | uniq -c | awk -vOFS=, '{ print $1 }' )
A,3,2
B,2,2
C,2,1
D,1,1
데이터를 미리 정렬하면 이 속도가 약간 더 짧아지고 훨씬 빨라질 수 있습니다.
$ sort -o file file
$ paste -d, <( cut -d, -f1 <file | uniq -c | awk -vOFS=, '{ print $2, $1 }' ) \
<( uniq <file | cut -d, -f1 | uniq -c | awk -vOFS=, '{ print $1 }' )
A,3,2
B,2,2
C,2,1
D,1,1
답변2
나는 Perl one-liner를 사용하여 이 문제를 해결할 수 있는지 확인하고 싶었고 그것을 알아낼 수 있었습니다.
$ perl -F, -ane '$lcnt{$F[0]}++; $ccnt{$F[0]}{$F[1]}++; \
END { print "$_, $lcnt{$_}, " . (keys %{ $ccnt{$_} }) . "\n" for sort keys %lcnt }' \
file
A, 3, 2
B, 2, 2
C, 2, 1
D, 1, 1
무너지다
파일을 통해 반복
이 문장은 매우 복잡해 보이지만, 일단 분해해보면 실제로는 매우 간단합니다. Perl의 핵심 메커니즘은 다음과 같습니다.
$ perl -F, -ane '...; END { ... }' file
이는 Perl에게 파일을 가져오고 file
이를 반복하면서 -F,
구분 기호를 사용하여 자동으로 분할하고, 완료되면 END {..}
블록을 한 번 실행하고 종료하도록 지시합니다.
예를 들어:
$ perl -F, -ane 'print "arg1: $F[0] arg2: $F[1]"; END { print "DONE\n" }' file
arg1: A arg2: 10
arg1: B arg2: 11
arg1: C arg2: 12
arg1: A arg2: 10
arg1: B arg2: 12
arg1: D arg2: 10
arg1: A arg2: 12
arg1: C arg2: 12
DONE
노트:Perl의 자동 분할 기능은 자동으로 열을 배열에 넣습니다 . 여기서는 요소 1과 2, &를 @F
사용하고 있습니다 .$F[0]
$F[1]
물건을 세어보세요
다음으로 해야 할 일은 입력의 개별 비트를 계산하는 것입니다. 이를 위해 Perl의 해싱 기능을 활용하겠습니다. 우리는 2, 를 사용할 것 %lcnt
입니다 %ccnt
.
노트:Perl에서 가장 짜증나는 것 중 하나는 해시를 정의할 때와 해시에 액세스할 때 기호를 전환하는 것입니다. 액세스하면 에서 로 전환되지만 %lcnt
다른 $lcnt["A"]
방향으로 전환됩니다.
$lcnt{$F[0]}++; $ccnt{$F[0]}{$F[1]}++;
%lcnt
- 첫 번째 열의 문자 수%ccnt
- 두 번째 열의 개수에 액세스하기 위한 2개 좌표의 2D 해시가 포함되어 있습니다.
노트:이런 식으로 사물을 세는 것은 우리가 숫자를 세는 방식만으로 독특한 기능을 수행할 수 있게 해준다.
%lcnt
예를 들어 해시 내용을 확인해 보겠습니다 .
$ perl -F, -ane '$lcnt{$F[0]}++; $ccnt{$F[0]}{$F[1]}++; \
END { print "key: $_\n" for sort keys %lcnt }' file
key: A
key: B
key: C
key: D
각 해시를 보려면 다음을 수행하세요.
$ perl -F, -ane '$lcnt{$F[0]}++; $ccnt{$F[0]}{$F[1]}++; \
END { print "key: $_ val: $lcnt{$_}\n" for sort keys %lcnt }' file
key: A val: 3
key: B val: 2
key: C val: 2
key: D val: 1
노트:$lcnt{$F[0]}++
여기서 우리는 모든 노력이 완료되었음을 볼 수 있습니다계산파일을 반복하고 각 문자를 해시에 추가합니다 %lcnt
.
이게 결말이야
퍼즐의 마지막 조각은 수집된 모든 정보를 유용한 방식으로 표시하는 것입니다. 이를 위해 다음에서 사용합니다 END {...}
.
print "$_, $lcnt{$_}, " . (keys %{ $ccnt{$_} }) . "\n" for sort keys %lcnt
그러면 키 목록을 반복하여 %lcnt
다음 줄이 인쇄됩니다.
$_, $lcnt{$_}, " . (keys %{ $ccnt{$_} }) . "\n"
위의 구조를 확인하기 어려울 경우 보다 일반적인 구조는 다음과 같습니다.
A, 3, 2
^--- (keys %{ $ccnt{$_} }) ## count of unique columns for each character ($_)
^------ $lcnt{$_} ## count of each character
^--------- $_ ## character
그러면 문자( $_
), 해당 문자의 개수 값( $lcnt{$_}
) 및 두 번째 열의 각 문자에 대한 고유 값 개수가 포함된 행이 생성됩니다.
인용하다
답변3
입력 데이터가 있는 sqlite3
명령줄에서 작은 스크립트를 실행합니다 .input.csv
sqlite3 -batch <<EOF
.mode csv
CREATE TABLE data (letter TEXT, number INTEGER);
.import input.csv data
SELECT letter, COUNT(*) AS lcount, COUNT(DISTINCT number) AS dcount
FROM data
GROUP BY letter ;
EOF
이것은 다음과 같이 작동합니다
$ bash query.sqlite
A,3,2
B,2,2
C,2,1
D,1,1
답변4
datamash -t, -s -g 1 count 1 countunique 2 < input.txt
입력하다
A, 10
B, 11
C, 12
A, 10
B, 12
D, 10
A, 12
C, 12
산출
A,3,2
B,2,2
C,2,1
D,1,1