Фильтрация текстового файла на основе значений в некоторых строках с помощью awk

#python #shell #awk #sed

#python #оболочка #awk #sed

Вопрос:

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

Например, мой текстовый файл выглядит так

 #EVM predictionEVM prediction: Mode:STANDARD S-ratio: 2.52 11043-11477 orient(-) score(1246.00)
11477   11043   single- 4   6   {SNAP_model.scaffold6_size143996-snap.2;SNAP

#EVM prediction: Mode:STANDARD S-ratio: 1.00 20968-21183 orient( ) score(432.00)
20968   21183   single  1   3   {GeneID_mRNA_scaffold6_size143996_6;GeneID}

#EVM prediction: Mode:STANDARD S-ratio: 1.00 21940-22362 orient(-) score(846.00)
22362   21940   single- 4   6   {GeneID_mRNA_scaffold6_size143996_7;GeneID}

#EVM prediction: Mode:STANDARD S-ratio: 12.32 33363-34677 orient( ) score(21500.00)
33363   33495   initial     1   1   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus}
33496   33611   INTRON          {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33612   33741   internal    2   2   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33742   33842   INTRON          {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33843   34677   terminal    3   3   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus}

#EVM prediction: Mode:STANDARD S-ratio: 2.41 46394-48564 orient(-) score(9677.00) noncoding_equivalent(4012.03) raw_noncoding(7194.39) offset(3182.36) 
46879   46394   terminal-   4   6   {GeneID_mRNA_scaffold6_size143996_13;GeneID}
47512   46880   INTRON          {GeneID_mRNA_scaffold6_size143996_13;GeneID}
48256   47513   internal-   4   6   {GeneID_mRNA_scaffold6_size143996_13;GeneID}
48366   48257   INTRON          {Augustus_model.g41.t1;Augustus}
48429   48367   internal-   4   6   {Augustus_model.g41.t1;Augustus}
48510   48430   INTRON          {Augustus_model.g41.t1;Augustus}
48564   48511   initial-    4   6   {Augustus_model.g41.t1;Augustus}
 

Теперь я хочу извлечь записи со счетом больше 1000. Я хочу удалить вторую и третью записи, которые имеют sccore-432 score(432.00) и score-846 score(846.00)

Я написал код awk

 awk -F '[()]' '{if ($4 > 1000) print $0}' input.out
 

но в качестве вывода он выдает только первую строку. т.е.

 #EVM predictionEVM prediction: Mode:STANDARD S-ratio: 2.52 11043-11477 orient(-) score(1246.00)
#EVM prediction: Mode:STANDARD S-ratio: 12.32 33363-34677 orient( ) score(21500.00)
#EVM prediction: Mode:STANDARD S-ratio: 2.41 46394-48564 orient(-) score(9677.00) noncoding_equivalent(4012.03) raw_noncoding(7194.39) offset(3182.36) 
 

Но я хочу извлечь полную запись, соответствующую счету, превышающему 1000.
Пожалуйста, помогите извлечь полную запись

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

1. Почему у этого вопроса есть python тег?

Ответ №1:

Вы можете использовать это awk с пустой RS match функцией and:

 awk -v RS= 'match($0, /score([^)] )/) amp;amp; substr($0, RSTART 6, RLENGTH-7) 0 > 1000 {ORS = RT; print}' file

#EVM predictionEVM prediction: Mode:STANDARD S-ratio: 2.52 11043-11477 orient(-) score(1246.00)
11477   11043   single- 4   6   {SNAP_model.scaffold6_size143996-snap.2;SNAP

#EVM prediction: Mode:STANDARD S-ratio: 12.32 33363-34677 orient( ) score(21500.00)
33363   33495   initial     1   1   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus}
33496   33611   INTRON          {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33612   33741   internal    2   2   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33742   33842   INTRON          {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus},{ev_type:GeMoMa/ID=model.scaffold6_size143996.rna-XM_007036272.2_R0;GeMoMa}
33843   34677   terminal    3   3   {SNAP_model.scaffold6_size143996-snap.3;SNAP},{GeneID_mRNA_scaffold6_size143996_10;GeneID},{Augustus_model.g38.t1;Augustus}

#EVM prediction: Mode:STANDARD S-ratio: 2.41 46394-48564 orient(-) score(9677.00) noncoding_equivalent(4012.03) raw_noncoding(7194.39) offset(3182.36)
46879   46394   terminal-   4   6   {GeneID_mRNA_scaffold6_size143996_13;GeneID}
47512   46880   INTRON          {GeneID_mRNA_scaffold6_size143996_13;GeneID}
48256   47513   internal-   4   6   {GeneID_mRNA_scaffold6_size143996_13;GeneID}
48366   48257   INTRON          {Augustus_model.g41.t1;Augustus}
48429   48367   internal-   4   6   {Augustus_model.g41.t1;Augustus}
48510   48430   INTRON          {Augustus_model.g41.t1;Augustus}
48564   48511   initial-    4   6   {Augustus_model.g41.t1;Augustus}
 

Более читаемая версия:

 awk -v RS= '
match($0, /score([^)] )/) amp;amp; substr($0, RSTART 6, RLENGTH-7) 0 > 1000 {
   ORS = RT
   print
}' file
 

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

1. Hii, спасибо за помощь. Я попробовал код. Он работает довольно круто с тестовым файлом. Но когда я попробовал использовать исходный файл (с 60 000 записей), он не работает. Даже не показал никакой ошибки.

2. Ответ может быть основан на предоставленных образцах данных. Если вы загрузите какой-либо другой образец, для которого он не работает, я могу проверить его со своей стороны.

Ответ №2:

РЕДАКТИРОВАТЬ: согласно комментарию OP, добавляя отредактированный код здесь.

 awk '
/^#EVM/{ found=="" }
match($0,/score([0-9] .[0-9] )/){
  found=1
  val=substr($0,RSTART,RLENGTH)
  gsub(/.*(|)$/,"",val)
  if(val 0>1000){ print; next }
}
found
' Input_file
 


Не могли бы вы попробовать следующее. Написано и протестировано с показанными примерами в GNU awk .

 awk '
match($0,/score([0-9] .[0-9] )/){
  val=substr($0,RSTART,RLENGTH)
  gsub(/.*(|)$/,"",val)
  if(val 0>1000){ print }
}' Input_file
 

Объяснение: добавление подробного объяснения выше.

 awk '                                  ##Starting awk program from here.
match($0,/score([0-9] .[0-9] )/){   ##Using match function to match regex of (digits DOT digits ) in current line.
  val=substr($0,RSTART,RLENGTH)        ##Creating sub string of matched regex above and storing it to val here.
  gsub(/.*(|)$/,"",val)               ##Globally substituting everything till ( and ) at last of line with NULL in val here.
  if(val 0>1000){ print }              ##If val is greater than 1000 then print line.
}' Input_file                          ##Mentioning Input_file name here.
 

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

1. Hii, спасибо за вашу помощь. Он работает с точки зрения фильтрации на основе оценки. Но этот код выдает тот же результат, что и раньше. Он печатает только первую строку каждой отфильтрованной записи

2. @SanthoshHegde, не могли бы вы, пожалуйста, уточнить подробнее по этому вопросу?

3. В вышеупомянутом текстовом файле 6 записей. Из которого я должен извлечь только записи со счетом> 1000. Это значение оценки для каждой записи находится в строке, начинающейся с #. Из приведенного выше примера я хочу извлечь 1-ю, 2-ю и 3-ю записи. Здесь запись означает не только строку, начинающуюся с #. Нижестоящие строки до следующего # принадлежат предыдущей записи. Но когда я запускаю код, фильтрация происходит, но я не получаю полную запись. Я получаю только строки с символом #. Я также хочу получить нижестоящие строки, если оценка превышает 1000.

4. @SanthoshHegde, если я правильно понял, не могли бы вы, пожалуйста, проверить мой код редактирования и дать мне знать, поможет ли это вам?

5. Я попробовал отредактированный код. Это не фильтрация по баллам. Он выдает на выходе то же, что и на входе

Ответ №3:

С GNU sed:

 sed -E '/score([1-9][0-9]{3,}/, /^ *$/!d' file
 

Ответ №4:

 awk '{split($8,k,"[()]")} k[2]> 1000' RS= ORS='nn' input
 

Если значение RS равно пустой строке, awk будет обрабатывать пустую строку в качестве разделителя записей. Мы устанавливаем ORS на две новые строки, чтобы в выходных данных сохранялись пустые строки. Это решение просто разбивает 8-е поле на круглые скобки. Согласно описанию проблемы, ожидается, что это поле будет строкой формы score(N) (если это условие на входе не выполняется, следует добавить некоторую проверку ошибок). Разделяя по этому файлу, мы получаем N k[2], поэтому мы просто проверяем, превышает ли это значение 1000. Когда это так, применяется правило печати записи по умолчанию.

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

1. Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.