Ускорить сценарий, который определяет, все ли столбцы в строке одинаковы или нет

Мне нужно ускорить сценарий, который по существу определяет, являются ли все «столбцы» для каждой строки одинаковыми, а затем записывает новый файл, содержащий один из идентичных элементов или «no_match». Файл разделен запятыми, состоит из около 15 000 строк и содержит различное количество «столбцов».

Например:

1-69 4-59,4-59,4-59,4-61,4-61,4-61 1-46,1-46 4-59,4-59,4-59,4-61,4-61,4-61 6-1,6-1 5-51,5-51 4-59,4-59 

Пишет новый файл:

 1-69 no_match 1-46 no_match 6-1 5-51 4-59 

Удаление второй и четвертой строк, поскольку они содержат не идентичные столбцы.

Вот мой далеко не элегантный сценарий:

 #!/bin/bash ind=$1 #file in num=`wc -l "$ind"|cut -d' ' -f1` #number of lines in 'file in' echo "alleles" > same_alleles.txt #new file to write to #loop over every line of 'file in' for (( i =2; i > same_alleles.txt else echo "no_match" >> same_alleles.txt fi done #END 

В настоящее время сценарий занимает около 11 минут, чтобы выполнить все ~ 15 000 строк. Я не совсем уверен, как ускорить это (честно говоря, я удивлен, что смог даже заставить его работать). Любое выбитое время было бы фантастическим. Ниже приведен fragment из 100 строк, которые можно использовать:

 allele 4-39 1-46,1-46,1-46 4-39 4-4,4-4,4-4,4-4 3-23,3-23,3-23 3-21,3-21 4-34,4-34 3-33 4-4,4-4,4-4 4-59,4-59 3-23,3-23,3-23 1-45 1-46,1-46 3-23,3-23,3-23 4-61 1-8 3-7 4-4 4-59,4-59,4-59 1-18,1-18 3-21,3-21 3-23,3-23,3-23 3-23,3-23,3-23 3-30,3-30-3 4-39,4-39 4-61 2-70 4-38-2,4-38-2 1-69,1-69,1-69,1-69,1-69 1-69 4-59,4-59,4-59,4-61,4-61,4-61 1-46,1-46 4-59,4-59,4-59,4-61,4-61,4-61 6-1,6-1 5-51,5-51 4-59,4-59 1-18 3-7 1-69 4-30-4 4-39 1-69 1-69 4-39 3-23,3-23,3-23 4-39 2-5 3-30-3 4-59,4-59,4-59 3-21,3-21 4-59,4-59 3-9 4-59,4-59,4-59 4-31,4-31 1-46,1-46 1-46,1-46,1-46 5-51,5-51 3-48 4-31,4-31 3-7 4-61 4-59,4-59,4-59,4-61,4-61,4-61 4-38-2,4-38-2 3-21,3-21 1-69,1-69,1-69 3-23,3-23,3-23 4-59,4-59 3-48 3-48 1-46,1-46 3-23,3-23,3-23 3-30-3,3-30-3 1-46,1-46,1-46 3-64 3-73,3-73 4-4 1-18 3-7 1-46,1-46 1-3 4-61 2-70 4-59,4-59 5-51,5-51 3-49,3-49 4-4,4-4,4-4 4-31,4-31 1-69 1-69,1-69,1-69 4-39 3-21,3-21 3-33 3-9 3-48 4-59,4-59 4-59,4-59 4-39,4-39 3-21,3-21 1-18 

Мой сценарий занимает ~ 7 секунд, чтобы завершить это.

 $ awk -F, '{ for (i=2; i<=NF; ++i) if ($i != $1) { print "no_match"; next } print $1 }' file 1-69 no_match 1-46 no_match 6-1 5-51 4-59 

Извините, но я даже не посмотрел на ваш код, слишком много всего происходило. Когда вы обнаружите, что три раза вызываете awk в теле цикла с одними и теми же данными, вам придется искать другие способы сделать это более эффективно. Кроме того, если вы используете awk , вам не нужен grep и cut как awk легко сможет выполнить свои задачи (хотя в этом случае они не нужны).

Приведенный выше скрипт awk читает строку через запятую и сравнивает каждое поле с первым. Если какой-либо из тестов завершается неудачей, no_match строка no_match и сценарий продолжается со следующей строки. Если цикл завершается (без нахождения несоответствия), печатается первое поле.

Как скрипт:

 #!/usr/bin/awk -f BEGIN { FS = "," } { for (i=2; i<=NF; ++i) if ($i != $1) { print "no_match" next } print $1 } 
  • FS - это разделитель поля ввода, также устанавливаемый с помощью опции -F в командной строке. awk разделит каждую строку на этом символе, чтобы создать поля.
  • NF - количество полей в текущей записи («столбцы в строке»).
  • $i ссылается на поле i: th в текущей записи, где i может быть переменной или константой (как в $1 ).

Связанные с:

  • Почему использование цикла оболочки для обработки текста считается плохой практикой?

СУХАЯ вариация:

 #!/usr/bin/awk -f BEGIN { FS = "," } { output = $1 for (i=2; i<=NF; ++i) if ($i != output) { output = "no_match" break } print output } 

Awk – это полноценный язык программирования. Вы уже используете это. Но не используйте его только для простых задач с несколькими вызовами в строке, используйте его для всей задачи. Используйте разделитель полей в awk, не используйте cut. Сделайте полную обработку в awk.

 awk -F',' ' { eq=1; for (i = 2; i <= NF; i++) if ($1 != $i) eq=0; print eq ? $1 : "no_match"; } ' $1 

С помощью perl List::MoreUtils , оценивая distinct элементы / uniq в скалярном контексте:

 perl -MList::MoreUtils=distinct -F, -lne ' print( (distinct @F) > 1 ? "no_match" : $F[0]) ' example 1-69 no_match 1-46 no_match 6-1 5-51 4-59 

Вы можете сделать это, используя редактор sed , как показано ниже:

 sed -e ' s/^\([^,]*\)\(,\1\)*$/\1/;t s/.*/NOMATCH/ ' input.csv 

Здесь мы полагаемся на regex для умножения себя и достижения конца строки. Если он может это сделать, завершите его первым полем, в противном случае мигает NOMATCH .

Объяснение:

Вот что происходит в моей голове, когда я вижу этот pbm:
Думайте о comma-separated fields как о stones разных цветов. И представьте их, можно ли их расположить в ряд как повторение первого камня, с запятой перед ними.

Что-то вроде:

STONEA ,STONEA ,STONEA ,STONEA ... all the way to end of line

Теперь с точки зрения терминологии регулярных выражений это становится:

^ (STONEA) (,\1) (,\1) (,\1) ... all the way to end of line

^ (STONEA) (,\1)* $

Выход:

 1-69 NOMATCH 1-46 NOMATCH 6-1 5-51 4-59