보시다시피 탭으로 구분된 데이터로 약 4,000개의 행과 10개의 열이 있는 파일이 있습니다.
파일의 두 번째 열에는 다양한 조직이 기록됩니다.
samples tissue_s tissue_e tissue_d tissue_category tissue_visa sex study tissue_f age
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
samples6 stomach CNS ear CNS CNS male 1 ear 365
samples7 stomach CNS ear CNS CNS male 1 ear 365
samples8 stomach CNS ear CNS CNS male 1 ear 365
samples9 stomach CNS ear CNS CNS male 1 ear 365
...
...
10번 이상 나타나는 모든 조직 정보를 인쇄할 수 있기를 바랍니다.
하지만 이렇게 해서 중간 파일을 생성하는 것은 비효율적이라고 생각합니다. 좀 더 간결하고 효율적인 방법이 있나요?
cat file | awk '{print $2}' | awk '{a[$0]++}END{for(i in a){if(a[i] > 10){print i}}}' > tmp.txt
grep -wFf tmp.txt file.txt > resule.txt
답변1
한 가지 방법은 입력 파일을 두 번 처리하는 것입니다.
awk -F'\t' -v frq=10 -v colId=2 '
NR==FNR{ count[$colId]++; next }
count[$colId] >frq
' infile infile
참고: 사용자 정의 awk 변수는 레코드가 출력되어야 하는 대상 컬럼 ID에 있는 요소의 최소 반복 빈도를 설정하고 지정하는 frq
데 사용됩니다 .colId
또 다른 방법은 입력 파일을 처리하는 것입니다.한 번그리고 오직몇 줄 버퍼링ipnut 데이터가 다음과 같이 두 번째 필드에 정렬되어 있는 경우:
awk -F'\t' -v frq=10 -v colId=2 '
function prnt() { if(c>frq) printf("%s", buf); buf=c="" }
prev!=$colId{ prnt() }
{ c++; prev=$colId; buf = buf $0 ORS }
END{ prnt() }' infile
두 번째 필드에서 정렬되지 않은 경우 먼저 정렬한 다음 awk에 전달합니다.
<infile sort -t$'\t' -k2,2 |
awk -F'\t' -v frq=10 -v colId=2 '
function prnt() { if(c>frq) printf("%s", buf); buf=c="" }
prev!=$colId{ prnt() }
{ c++; prev=$colId; buf = buf $0 ORS }
END{ prnt() }'
답변2
사용행복하다(이전 Perl_6)
~$ raku -e 'my %h; do for lines.skip() {%h.push: .words.[1] => .words}; \
for %h.kv -> $k,@v {(put $k; .put for @v) if @v.elems > 4};' file
Perl 계열 언어인 Raku를 사용해 보는 데 관심이 있을 수도 있습니다. 한 가지 장점은 다른 언어 조합을 사용하는 동료와 데이터를 교환할 경우 내장된 고급 유니코드 지원입니다.
위의 내용은 해시를 선언하고 (두 번째 열)을 키로, (모든 열)을 값으로 사용하여 해시를 %h
자동으로 잘라냅니다 lines
( 헤더 행을 ping). skip
해시에는 중복 키가 존재할 수 없으므로 두 번째 열에 있는 각 개별 조직 아래에 행이 추가됩니다. 모든 행이 처리된 후 해시는 배열 내의 스칼라 키와 값에 입력됩니다. 인쇄만 합니다(예: OP 샘플 입력의 4줄 이상).push
.words.[1]
.words
%h
%h.kv
$k
@a
@v.elems > 4
입력 예:
samples tissue_s tissue_e tissue_d tissue_category tissue_visa sex study tissue_f age
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
samples6 stomach CNS ear CNS CNS male 1 ear 365
samples7 stomach CNS ear CNS CNS male 1 ear 365
samples8 stomach CNS ear CNS CNS male 1 ear 365
samples9 stomach CNS ear CNS CNS male 1 ear 365
출력 예(위 코드):
ear
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
필요에 맞게 출력을 조정하는 것은 매우 쉽습니다. put $k;
별도의 "조직" 헤더를 원하지 않으면 통화를 중단하세요. 또한 탭으로 구분된 줄을 재구성하도록 @a
줄 출력이 변경되었습니다 ..join("\t").put for @v
\t
위의 답변에서는 각 열 항목에 공백이 없다고 가정합니다. 공백을 분할( 또는 분할하지 않음)하는 .words
것이 좋은 생각이기 때문입니다. \t
각 열 항목이 공백으로 구분된 단일 요소가 될 것이라고 보장할 수 없는 경우 .split("\t")
대신 사용하세요. 이들을 하나로 합치면(위와 동일한 출력이 제공되지만 이제 탭으로 구분됩니다):
~$ raku -e 'my \%h; do for lines.skip() {\%h.push: .split("\t").[1] => .split("\t")}; \
for \%h.kv -> $k,@v {($k.put; .join("\t").put for @v) if @v.elems > 4};' file