열의 요소 발생 빈도를 기준으로 행을 선택하는 방법

열의 요소 발생 빈도를 기준으로 행을 선택하는 방법

보시다시피 탭으로 구분된 데이터로 약 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

https://docs.raku.org
https://raku.org

관련 정보