как присвоить значения интервалам с перекрывающимися областями?

Чтобы получить файлы вне репозитория, мне пришлось использовать git ls-files -o:

$ git ls-files -o
test/outside-1.txt
outside.txt
-1
03.04.2019, 20:43
3 ответа

Вы можете сделать это с помощью Perl, используя следующий метод:

$ perl -lane '
   my($id, $uniq_id, $lower, $upper) = @F;
   $h{$id}{$uniq_id}{MIN} = $lower;
   $h{$id}{$uniq_id}{MAX} = $upper;
   push @{$order{$id}}, $uniq_id;
   }{
   while(<STDIN>) {
      chomp;
      my($id, $number) = split;
      print join "\t", $id, $number,
       join(",", grep { $h{$id}{$_}{MIN} < $number and $h{$id}{$_}{MAX} > $number } @{$order{$id}})
         || qw/NA/;;
   }
' data.intervals < data.posiitons

Выход:

1  4     NA
1  19    g1,g2,g3
1  36    NA
1  49    NA
1  90    g6,g7
2  1     NA
2  20    g9
2  89    NA
2  93    g11
2  120   g11

Работы:

  • Сначала прочитайте файл интервалов и создайте структуру данных хэш-ключей на основе идентификатора, уникального идентификатора и содержащих конечные точки диапазона.
  • Хэш %orderхранит порядок, в котором уникальные идентификаторы встречались для целей воспроизведения в том же порядке. OTW, порядок хэшей случайный.
  • Затем прочитайте файл position и сначала распакуйте каждую запись (или строку )и поместите их в скаляры $id и $number.
  • grepдолжен выбрать уникальные идентификаторы, которые удовлетворяют ограничению числа, находящегося в диапазоне. В противном случае передается "NA".
2
28.01.2020, 05:08

Это то, для чего вы могли бы рассмотреть возможность использования небольшой базы данных -, используя, например, csvsqlиз csvkit (, который также предоставляет удобную csvformatутилиту ).

Например, предположим, что ваши данные находятся на вкладке -в отдельных файлах с именами intervalsи positionsи с использованием диалекта по умолчанию sqlite:

csvsql --tabs --query '
SELECT id,number,group_concat(id_uniq) AS "assigned1" 
FROM positions JOIN intervals USING(id)
WHERE number >= numberA AND number <= numberB
GROUP BY id,number ORDER BY id,number
' positions intervals | csvformat --out-tabs
id  number  assigned1
1   19  g1,g2,g3
1   90  g6,g7
2   20  g9
2   93  g11
2   120 g11

Несколько сложнее получить N/Aзаписи, :для этого вы можете соединить исходную positionsтаблицу с результатами и найти NULLзначения поля assigned1:

csvsql --tabs --query '
SELECT id,number,IFNULL(assigned1,"NA") assigned1 FROM positions 
LEFT JOIN (
  SELECT id,number,group_concat(id_uniq) AS "assigned1" 
  FROM positions JOIN intervals USING(id) 
  WHERE number >= numberA AND number <= numberB
  GROUP BY id,number
) USING(id,number) ORDER BY id,number 
' positions intervals | csvformat --out-tabs
id  number  assigned1
1   4   NA
1   19  g1,g2,g3
1   36  NA
1   49  NA
1   90  g6,g7
2   1   NA
2   20  g9
2   89  NA
2   93  g11
2   120 g11
1
28.01.2020, 05:08

Предполагая, что оба файла были отсортированы с помощью sort -b, вы можете составить все возможные комбинации каждой строки в двух файлах с одинаковым идентификатором с помощью

join ranges.txt positions.txt

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

Это дало бы для заданных данных

1 g1 5 20 4
1 g1 5 20 19
1 g1 5 20 36
1 g1 5 20 49
1 g1 5 20 90
1 g2 6 29 4
1 g2 6 29 19
1 g2 6 29 36
[...] (in total 55 lines)

Здесь у вас есть идентификатор, «уникальный идентификатор», два значения диапазона и позиция, которую вы хотите проверить.

Это может быть проанализировано программой awk:

join ranges.txt positions.txt |
awk '!($1 SUBSEP $5 in count) { count[$1,$5]=0 }
     $5 >= $3 && $5 <= $4 && ++count[$1,$5]
     END {
         for (i in count)
             if (count[i] == 0) {
                 split(i,s,SUBSEP)
                 print s[1], s[2], "NA"
             }
     }'

Это позволит отслеживать видимые идентификаторы и позиции в качестве ключей в массиве count. Значение будет содержать количество раз, когда позиция помещалась в диапазон. Нам нужно это, чтобы иметь возможность сказать «эта позиция не не была найдена ни в одном диапазоне».

Если текущая строка в выводе joinсодержит позицию в 5-м поле, которая находится в диапазоне полей 3 и 4, этот счетчик увеличивается (и строка выводится ).

Это выводит все строки в выводе join, которые соответствуют позициям в пределах диапазонов. Блок ENDобрабатывает несопоставленные позиции, перебирая массив countи распечатывая информацию, которую вы запросили в вопросе, в формате, который вы запрашивали там, когда значение countравно нулю.

Для заданных данных это дает

1 g1 5 20 19
1 g2 6 29 19
1 g3 17 35 19
1 g6 70 95 90
1 g7 87 93 90
2 g9 10 33 20
2 g11 90 132 93
2 g11 90 132 120
2 89 NA
1 36 NA
1 4 NA
2 1 NA
1 49 NA

Чтобы свернуть эти данные на основе "уникального идентификатора", мы могли бы передать их через другую awkпрограмму. (Я избегаю сохранения всего этого в массиве в той жеawkпрограмме, так как это может быть довольно много данных ).

awk '$NF == "NA" { print; next }
                 { key = $1 SUBSEP $NF }
     key == prev { group = group "," $2; next }
                 { if (group != "") print id, pos, group; id = $1; pos = $NF; group = $2; }
     END         { if (group != "") print id, pos, group }'

Это проходит через любую строку, последний столбец которой NA, так как они уже имеют правильный формат. Затем он создает «ключ» из идентификатора и позиции. Если этот ключ такой же, как и для предыдущей строки, «уникальный идентификатор» добавляется к строке с именем groupс запятой в качестве разделителя.

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

Собрав все это вместе и вспомнив, что оба входных файла должны быть отсортированы, мы получим

join ranges.txt positions.txt |
awk '!($1 SUBSEP $5 in count) { count[$1,$5]=0 }
     $5 >= $3 && $5 <= $4 && ++count[$1,$5]
     END {
         for (i in count)
             if (count[i] == 0) {
                 split(i,s,SUBSEP)
                 print s[1], s[2], "NA"
             }
     }' |
awk '$NF == "NA" { print; next }
                 { key = $1 SUBSEP $NF }
     key == prev { group = group "," $2; next }
                 { if (group != "") print id, pos, group
                   prev = key; id = $1; pos = $NF; group = $2; }
     END         { if (group != "") print id, pos, group }'

Результатом этого является

1 19 g1,g2,g3
1 90 g6,g7
2 20 g9
2 93 g11
2 89 NA
1 36 NA
1 4 NA
2 1 NA
1 49 NA
2 120 g11

который, за исключением порядка, идентичен тому, что вы ожидали. Чтобы исправить порядок, передайте это через sort -k1,1n -k2,2n.

-1
28.01.2020, 05:08

Теги

Похожие вопросы