Составление пар слов на основе одного столбца - PullRequest
0 голосов
/ 28 мая 2018

Я хочу составить пары слов на основе третьего столбца (идентификатора).Мой файл похож на этот пример:

A ID.1
B ID.2
C ID.1
D ID.1
E ID.2
F ID.3  

Результат, который я хочу получить:

A C ID.1
A D ID.1
B E ID.2
C D ID.1

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

Я попробовал этот код, который работает хорошо, но требует много времени (и я не знаю, есть ли избыточности):

counter=2
cat filtered_go_annotation.txt | while read f1 f2; do 
tail -n +$counter go_annotation.txt | grep $f2 | awk '{print "'$f1' " $1}'; 
((counter++))
done > go_network2.txt

Хвост используется для удалениястрока, когда она прочитана.

Ответы [ 7 ]

0 голосов
/ 28 мая 2018

Если ваш ввод большой, может быть быстрее решить его пошагово, например:

# Create temporary directory for generated data
mkdir workspace; cd workspace

# Split original file
awk '{ print $1 > $2 }' ../infile

# Find all combinations
perl -MMath::Combinatorics \
     -n0777aE              \
     '
       $c=Math::Combinatorics->new(count=>2, data=>[@F]);
       while(@C = $c->next_combination) { 
         say join(" ", @C) . " " . $ARGV
       }
     ' *

Вывод:

C D ID.1
C A ID.1
D A ID.1
B E ID.2
0 голосов
/ 28 мая 2018

Еще один awk, использующий переопределение $0.Это делает решение RomanPerekhrest немного короче:

{a[$2]=a[$2] FS $1}
END { for(i in a) { $0=a[i]; for(j=1;j<NF;j++)for(k=j+1;k<=NF;++k) print $j,$k,i} }
0 голосов
/ 28 мая 2018

в два этапа

$ sort -k2 file > file.s
$ join -j2 file.s{,} | awk '!(a[$2,$3]++ + a[$3,$2]++){print $2,$3,$1}'

A C ID.1
A D ID.1
C D ID.1
B E ID.2
0 голосов
/ 28 мая 2018

Perl

решение с использованием обратного отслеживания регулярных выражений

perl -n0777E '/^([^ ]*) (.*)\n(?:.*\n)*?([^ ]*) (\2)\n(?{say"$1 $3 $2"})(?!)/mg' foo.txt
  • flags see perl -h.
  • ^([^ ]*) (.*)\n: соответствует строке, по крайней мере, с одним пробеломпервая группа захвата с левой стороны от первого пробела, вторая группа захвата с правой стороны.
  • (?:.*\n)*?: сопоставляет (без захвата) 0 или более строк, чтобы сначала попытаться выполнить следующий шаблон, прежде чем сопоставить больше строк.
  • ([^ ]*) (\2)\n: аналогично первому совпадению с использованием обратной ссылки \2 для сопоставления строки с тем же ключом.
  • (?{say"$1 $3 $2"}): код для отображения захваченных групп
  • (?!): чтобы соответствие не возвращалось.

Обратите внимание, что оно может быть немного сокращено

perl -n0777E '/^(\S+)(.+)[\s\S]*?^((?1))(\2)$(?{say"$1 $3$2"})(?!)/mg' foo.txt
0 голосов
/ 28 мая 2018

Интересно, сработает ли это (в GNU awk):

$ awk '
($2 in a) && !($1 in a[$2]) {  # if ID.x is found in a and A not in a[ID.X]
    for(i in a[$2])            # loop all existing a[ID.x] 
        print i,$1,$2          # and output combination of current and all previous matching
}
{
    a[$2][$1]                  # hash to a
}' file
A C ID.1
A D ID.1
C D ID.1
B E ID.2
0 голосов
/ 28 мая 2018

С GNU awk для sorted_in и истинных многомерных массивов:

$ cat tst.awk
{ vals[$2][$1] }
END {
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (i in vals) {
        for (j in vals[i]) {
            for (k in vals[i]) {
                if (j != k) {
                    print j, k, i
                }
            }
            delete vals[i][j]
        }
    }
}

$ awk -f tst.awk file
A C ID.1
A D ID.1
C D ID.1
B E ID.2
0 голосов
/ 28 мая 2018

Awk решение:

awk '{ a[$2] = ($2 in a? a[$2] FS : "") $1 }
     END {
         for (k in a) {
             len = split(a[k], items);
             for (i = 1; i <= len; i++)
                 for (j = i+1; j <= len; j++)
                     print items[i], items[j], k 
         }
     }' filtered_go_annotation.txt

Выход:

A C ID.1
A D ID.1
C D ID.1
B E ID.2
...