Печатать подстроки каждый символ - PullRequest
0 голосов
/ 26 июня 2018

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

input="file.txt"
awk '{print substr($1,1,21)}' $input


awk '{print substr($1,2,21)}' $input

, которые дают мне следующий вывод, соответственно.

AATAAGGTGCCTGATTAAA-G   
ATAAGGTGCCTGATTAAA-GG

Входной файл содержит около 17 тыс. Символов, и мне удалось попробовать выполнить цикл for для подсчета символов и выполнить приведенную выше команду в цикле for, например:

count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
   do
awk '{print substr($1,$num,21)}' $input
   done

Но это возвращает пустые выходные данные. Я также хотел запускать его как bash-скрипты с вводом и размером подстрок и выходного файла, указанных в командной строке, например:

script.sh input_file.txt 21 output.txt

И я попробовал это, но это тоже не сработало.

  input=$1
  kmer=$2
  output=$3
  count=`wc -c ${input} |cut -d' ' -f1`
  for num in `seq ${count}`
    do
 awk '{print substr($1,$num,$kmer)}' $input > $output
  done

Какие-нибудь советы, что я делаю не так? Я довольно новичок в awk ...

Ответы [ 5 ]

0 голосов
/ 27 июня 2018

Заботиться о записи в Perl?

#! /bin/env perl

use strict;
use warnings;

my $data;
my $offset = 0;
my $window = shift or die "Use: $0 {windowSize} [ < ] infile [ > outfile ]\n";

{ local $/;
  $data = <>;
}

print "$_\n" while $_ = substr $data, $offset++, $window;

exit;

Может сжиматься до одной строки, но даже при использовании строгих и предупреждений & c ...

$: wc -c src
17000 src

$: time ./slide 21 src
!"#$%&'()*+,-./012345
"#$%&'()*+,-./0123456
#$%&'()*+,-./01234567
$%&'()*+,-./012345678

. , ,

WXYZ[\
XYZ[\
YZ[\
Z[\
[\
\

real    0m0.029s
user    0m0.004s
sys     0m0.021s
0 голосов
/ 27 июня 2018

Относительно вашей конкретной проблемы, фрагмент:

awk '{print substr($1,$num,21)}' $input

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

pax$ num=42 && echo '$num'
$num
pax$ num=42 && echo "$num"
42

Следовательно, $num будет не заменено значением переменной оболочки.

Как также видно выше, вы можете использовать двойные кавычки, которые позволят разрешить расширение, но тогда вам нужно экранировать $1 до , чтобы предотвратить его расширение. Обычно мне проще превратить переменные оболочки в переменные awk, например:

awk -vnum=$num '{print substr($1,num,21)}' $input

Следующий фрагмент показывает это в действии:

pax$ num=42 && awk 'END{print $num}' </dev/null

pax$ num=42 && awk -v num=$num 'END{print num}' </dev/null
42

Однако 17 000 вызовов внешней программы будут довольно неэффективными, вам лучше скомпилировать что-нибудь или, если вам нужно использовать сценарии, это можно сделать полностью в самом bash. Приведенный ниже код показывает, как вы можете это сделать, важный бит находится в блоке time ( ), все остальное - просто настройка тестовых данных, синхронизация и очистка.

# Create test data.

(
    for i in {1..1000} ; do
        echo -n "abcdefghijklmnop-"
    done
) >inputdata.txt

# Time the execution.

time (
    char17k="$(cat inputdata.txt)"
    echo ${#char17k}
    for ((i = 0; i < ${#char17k}; i++)) ; do
        echo ${char17k:i:21}
    done

)

# Clean up.

rm -rf inputdata.txt

В моей системе это завершается примерно через десять секунд. Время, затрачиваемое на 17 000 awk вызовов, примерно в три раза больше, даже без выполнения какой-либо полезной работы:

pax$ time (for in in {1..17000} ; do awk '{}' </dev/null ; done )
real    0m30.649s
user    0m5.196s
sys     0m4.848s

Конечно, вы можете получить даже больше скорости, дав awk делать все работу. Замена содержимого блока time ( ) в приведенном выше коде на:

awk '{for (i = 1; i < length($0); i++) {print substr($0, i, 21)}}' inputdata.txt

дает гораздо более впечатляющее (около десятой доли секунды):

real    0m0.121s
user    0m0.008s
sys     0m0.016s
0 голосов
/ 26 июня 2018

Вы также можете сделать это с помощью GNU sed следующим образом:

echo -n "123456789" | sed -r ':loop h;s/.//3g;p;x; s/.//; t loop'
12
23 
34
45
56
67
78
89 
9

3g - это размер "скользящего окна" + 1.

для обработки данных в файле вместо STDIN, просто укажите его после команды sed:

sed -r ':loop h;s/.//3g;p;x; s/.//; t loop' myfile
0 голосов
/ 27 июня 2018
$ echo {1..9} | tr -d ' ' |   # create test data
  awk -v len=3 '{n=length($0); for(i=1;i<=n-len+1;i++) print substr($0,i,len)}'

123
234
345
456
567
678
789
0 голосов
/ 26 июня 2018
#!/usr/bin/env bash 

input=$1
kmer=$2
output=$3

data=$(<"$input")

for ((i=0;i<${#data};i++)); do
    echo "${data:i:kmer}"
done > "$output"

Используется только расширение подстроки , цитата из руководства:

${parameter:offset:length}

Это называется расширением подстроки. Расширяется до length символов значения parameter начиная с символа, указанного offset.


Использование gawk:

awk -v num="$kmer" '{for(i=1;i<=length($0);i++) print substr($0,i,num)}' "$input" > "$output"

Это намного более быстрое решение. Разница в скорости значительна: протестировано на 17 тыс. Символов и окне из 30 символов: ~ 10 с для первого решения, ~ 0,01 с для второго решения.

...