Как исключить строки в файле на основе диапазона значений, взятых из второго файла - PullRequest
1 голос
/ 06 марта 2020

У меня есть файл со списком диапазонов значений:

2    4
6    9
13   14

и второй файл, который выглядит следующим образом:

HiC_scaffold_1  1   26
HiC_scaffold_1  2   27
HiC_scaffold_1  3   27
HiC_scaffold_1  4   31
HiC_scaffold_1  5   34
HiC_scaffold_1  6   35
HiC_scaffold_1  7   37
HiC_scaffold_1  8   37
HiC_scaffold_1  9   38
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  13  39
HiC_scaffold_1  14  39
HiC_scaffold_1  15  42

, и я хотел бы исключить строки из файла 2, где значение столбца 2 находится в пределах диапазона, определенного в файле 1. Идеальным выводом будет:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

Я знаю, как извлечь отдельный диапазон с помощью awk:

awk '$2 == "2", $2 == "4"' file2.txt

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

Ответы [ 3 ]

3 голосов
/ 06 марта 2020

Это один awy:

$ awk '
NR==FNR {                           # first file
    min[NR]=$1                      # store mins and maxes in pairs
    max[NR]=$2
    next
}
{                                   # second file
    for(i in min)                   
        if($2>=min[i]&&$2<=max[i])
            next
}1' ranges data

Вывод:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

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

$ awk '
NR==FNR {                       # ranges file
    for(i=$1;i<=$2;ex[i++]);    # each value in the range goes to exclude hash
    next
}
!($2 in ex)' ranges data        # print if not found in ex hash
1 голос
/ 07 марта 2020

Если ваши диапазоны не велики:

$ cat tst.awk
NR==FNR {
    for (i=$1; i<=$2; i++) {
        bad[i]
    }
    next
}
!($2 in bad)

$ awk -f tst.awk file1 file2
HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42
0 голосов
/ 06 марта 2020

sedception

Если второй столбец file2.txt всегда равен индексу его строки, вы можете использовать sed для сокращения строк. Если это не ваш случай, обратитесь к пункту awkception .

sed $(sed 's/^\([0-9]*\)[[:space:]]*\([0-9]*\)/-e \1,\2d/' file1.txt) file2.txt

Где file1.txt содержит ваши диапазоны, а file2.txt - сами данные.

По сути, он создает вызов sed, который объединяет список выражений -e i,jd, что означает удаление строк между строкой ith и строкой jth .

В вашем примере sed 's/^\([0-9]*\)[[:space:]]*\([0-9]*\)/-e \1,\2d/' file1.txt выдаст -e 2,4d -e 6,9d -e 13,14d, который представляет собой список выражений для вызова sed на file2.txt.

В конце он вызовет:

sed -e 2,4d -e 6,9d -e 13,14d file2.txt

Эта команда удаляет все строки между 2-м и 4-м и все строки между 6-м и 9-м и все строки между 13-м и 14-м.

Очевидно, что она не работает если второй столбец file2.txt не не соответствует индексу собственной строки.

awkception

awk "{$(awk '{printf "if ($2>=%d && $2<=%d) next\n", $1, $2}' file1.txt)}1" file2.txt

Это решение работает, даже если второй столбец не соответствует индексу его строки.

Метод использует awk для создания программы awk, точно так же, как sed создал sed выражений в решении sedception .

В конце это вызовет:

awk '{
if ($2>=2 && $2<=4) next
if ($2>=6 && $2<=9) next
if ($2>=13 && $2<=14) next
}1' file2.txt

Следует отметить, что это решение значительно медленнее, чем sed.

...