сценарий оболочки (с циклом) для преобразования списка строк одну за другой

#bash #shell #awk #grep

#bash #оболочка #awk #grep

Вопрос:

У меня есть текстовый файл с большими данными (более 100 000 строк) в этом формате:

 0.000197239;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=CLCNKA;GeneDetail.refGene=.;ExonicFunc
0.00118343;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=CLCNKA;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.00276134;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=CLCNKA;GeneDetail.refGene=.;
0.0607495;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=CLCNKA;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.00670611;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=XDH;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.000197239;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=XDH;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.000394477;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=GRK4;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.0108481;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=GRK4;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.000394477;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=GRK4;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
0.0108481;AN=192;NS=2535;ANNOVAR_DATE=2015-12-14;Func.refGene=exonic;Gene.refGene=GRK4;GeneDetail.refGene=.;ExonicFunc.refGene=nonsynonymous_SNV;
 

Теперь каждая строка содержит имя гена, например, в начальных 4 строках есть CLCNKA gene . Я использую grep команду для подсчета частоты каждого имени гена в этом файле данных, как:

 grep -w "CLCNKA" my_data_file | wc -l
 

В отдельном файле содержится около 300 генов, которые необходимо искать в приведенном выше файле данных. Может ли какой-нибудь эксперт написать простой shell script с циклом, чтобы брать имя гена из списка по одному и сохранять его частоту в отдельном файле. Итак, выходной файл будет выглядеть следующим образом:

 CLCNKA    4
XDH    2
GRK4    4
 

Комментарии:

1. это больше работа для awk

2. Итак, не могли бы вы предложить awk команду для этого, я awk также редактирую тег.

3. теперь, когда он помечен правильно, вам, вероятно, поможет какой-нибудь эксперт по awk.

Ответ №1:

Вы нас запутали. Я и некоторые другие думаю, что все, что вам нужно, это количество каждого гена в файле, поскольку это то, что ваш ввод / вывод и некоторые из ваших описательных текстовых состояний ( count the frequency of each gene name in this data file ), которые будут такими:

 $ awk -F'[=;]' '{cnt[$11]  } END{for (gene in cnt) print gene, cnt[gene]}' file
GRK4 4
CLCNKA 4
XDH 2
 

в то время как все остальные думают, что вам нужно количество определенных генов, которые существуют в другом файле, поскольку это то, что указано в вашей теме, предлагаемом алгоритме и остальной части вашего текста.

Если все остальные правы, вам понадобится эта настройка, чтобы сначала прочитать файл «genes» и подсчитать только те гены в «файле», которые были перечислены в «genes»:

 awk -F'[=;]' 'NR==FNR{genes[$0]; next} $11 in genes{cnt[$11]  } END{for (gene in cnt) print gene, cnt[gene]}' genes file
GRK4 4
CLCNKA 4
XDH 2
 

Ваш пример не помогает, поскольку он даст тот же результат при любой интерпретации ваших требований, поэтому отредактируйте свой вопрос, чтобы уточнить, чего вы хотите. В частности, если есть гены, которые вы НЕ хотите подсчитывать, то включите строки, содержащие их, во входной выборке.

Комментарии:

1. спасибо за твою заботу, дорогая. Да, второй случай верен, я хотел прочитать «гены» из отдельного файла, затем выполнить поиск в «файле данных», показанном выше, и вычислить встречаемость генов. Выходные данные будут представлять собой отдельный файл, как указано выше.

Ответ №2:

awk ваш друг

 awk '{sub(/^.*Gene.refGene=/,"");sub(/;.*$/,"");
     genelist[$0]  }END{for(i in genelist){print i,genelist[i]}}' file
 

Вывод

 GRK4 4
CLCNKA 4
XDH 2
 

Примечание: это может не дать вам частоту имен генов в том порядке, в котором они отображаются в файле. Я думаю, что это не является обязательным требованием.

Ответ №3:

Это также можно сделать в чистом bash, используя функцию ассоциативного массива для подсчета частот:

 #!/bin/bash

# declare assoc array
declare -A freq

# split stdin input csv
for gene in $(cut -d ';' -f 6|cut -d = -f 2);do
    let freq[$gene]  
done

# loop over array keys
for key in ${!freq[@]}; do
    echo ${key} ${freq[$key]}
done
 

Ответ №4:

Более простое решение, основанное на команде uniq:

 #!/bin/bash

cut -d ';' -f 6|cut -d = -f 2|sort|uniq -c|while read -a kv;do
    echo  ${kv[1]} ${kv[0]}
done
 

Комментарии:

1. Нет необходимости в цикле и нет необходимости в 2 cut секундах : awk -F'[;=]' '{print $11}' file | sort | uniq -c | awk '{print $2, $1}' . Не предполагая, что вы должны делать это вместо 1 команды awk, конечно

Ответ №5:

Вот однострочный:

 sed "s/.*Gene.refGene=//;s/;.*//" test | sort | uniq -c | awk '{print $2,$1}'
 

sed — удалит все из строки, кроме имени гена
sort будет выполнять сортировку по имени
uniq -c — будет подсчитываться количество повторов гена
awk с выводом swap uniq (по умолчанию это: шаблон подсчета)

Ответ №6:

Для сохранения порядка предоставленный входной файл сортируется так, как указано в примере:

 $ perl -lne '
($g) = /Gene.refGene=([^;] )/;
if($g ne $p amp;amp; $. > 1)
{
    print "$pt$c";
    $c = 0;
}
$c  ; $p = $g;
END { print "$pt$c" }' ip.txt
CLCNKA  4
XDH     2
GRK4    4
 

Если нет, используйте переменную hash для увеличения имени гена, используемого в качестве ключа, и массива для хранения порядка ключей

 $ perl -lne '
($k) = /Gene.refGene=([^;] )/;
push(@o, $k) if !$h{$k}  ;
END { print "$_t$h{$_}" foreach (@o) }' ip.txt
CLCNKA  4
XDH     2
GRK4    4
 

Ответ №7:

если вы ищете только список генов, неэффективный, но простой способ

 read g; do echo -n $g " "; grep -c $g file; done < genes
 

предполагая, что ваши гены перечислены по одному в файле genes.

Если ваша файловая структура исправлена, более эффективная версия будет

 awk 'NR==FNR{genes[$1];next} 
            {sub(/Gene.refGene=/,"",$6)} 
 $6 in genes{count[$6]  } 
         END{for(g in count) print g,count[g]}' genes FS=';' file