Правильно объединить два файла на основе 2-х столбцов

У меня есть два файла, которые я пытаюсь объединить/объединить на основе столбцов 1 и 2. Они выглядят примерно так: файл1 (58210 строк) намного короче, чем file2 (815530 строк), и я бы хотел найти пересечение этих двух файлов на основе полей 1 и 2 в качестве индекса:

file1:

2L      25753   33158
2L      28813   33158
2L      31003   33158
2L      31077   33161
2L      31279   33161
3L      32124   45339
3L      33256   45339
...

file2:

2L      20242   0.5     0.307692307692308
2L      22141   0.32258064516129        0.692307692307692
2L      24439   0.413793103448276       0.625
2L      24710   0.371428571428571       0.631578947368421
2L      25753   0.967741935483871       0.869565217391304
2L      28813   0.181818181818182       0.692307692307692
2L      31003   0.36    0.666666666666667
2L      31077   0.611111111111111       0.931034482758621
2L      31279   0.75    1
3L      32124   0.558823529411765       0.857142857142857
3L      33256   0.769230769230769       0.90625
...

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

awk 'FNR==NR{a[$1$2]=$3;next} {if($1$2 in a) print}' file1 file2 | wc -l
awk 'FNR==NR{a[$1$2]=$3;next} {if($1$2 in a) print}' file2 file1 | wc -l

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

Как я могу просто объединить файлы так, чтобы только строки файла2, имеющие соответствующие столбцы 1 и 2 в файле1, печатались с добавлением столбца 3 файла1, чтобы выглядеть примерно так:

2L      25753   0.967741935483871       0.869565217391304    33158
2L      28813   0.181818181818182       0.692307692307692    33158
2L      31003   0.36    0.666666666666667    33158
2L      31077   0.611111111111111       0.931034482758621    33161
2L      31279   0.75    1    33161
3L      32124   0.558823529411765       0.857142857142857    45339
3L      33256   0.769230769230769       0.90625    45339

Ответ 1

awk 'NR==FNR{a[$1,$2]=$3;next} ($1,$2) in a{print $0, a[$1,$2]}' file1 file2

Облик:

$ cat file1
2L      5753   33158
2L      8813   33158
2L      7885   33159
2L      1279   33159
2L      5095   33158
$
$ cat file2
2L      8813    0.6    1.2
2L      5762    0.4    0.5
2L      1279    0.5    0.9
$
$ awk 'NR==FNR{a[$1,$2]=$3;next} ($1,$2) in a{print $0, a[$1,$2]}' file1 file2
2L      8813    0.6    1.2 33158
2L      1279    0.5    0.9 33159
$

Если это не то, что вы хотите, просим пояснить и, возможно, опубликовать несколько более типичных примеров ввода/вывода.

Прокомментированная версия вышеуказанного кода предоставит запрошенное объяснение:

awk ' # START SCRIPT

# IF the number of records read so far across all files is equal
#    to the number of records read so far in the current file, a
#    condition which can only be true for the first file read, THEN 
NR==FNR {

   # populate array "a" such that the value indexed by the first
   # 2 fields from this record in file1 is the value of the third
   # field from the first file.
   a[$1,$2]=$3

   # Move on to the next record so we don't do any processing intended
   # for records from the second file. This is like an "else" for the
   # NR==FNR condition.
   next

} # END THEN

# We only reach this part of the code if the above condition is false,
# i.e. if the current record is from file2, not from file1.

# IF the array index constructed from the first 2 fields of the current
#    record exist in array a, as would occur if these same values existed
#    in file1, THEN
($1,$2) in a {

   # print the current record from file2 followed by the value from file1
   # that occurred at field 3 of the record that had the same values for
   # field 1 and field 2 in file1 as the current record from file2.
   print $0, a[$1,$2]

} # END THEN

' file1 file2 # END SCRIPT

Надеюсь, что это поможет.

Ответ 2

Если вы хотите присоединиться к файлам по строкам, используйте следующую команду:

join -o 1.2,1.3,2.4,2.5,1.4 <(cat -n file1) <(cat -n file2)

По мере обновления вопроса:

join -o 1.1,2.2,2.3,1.2 <(sed 's/[[:space:]]\+/@/' file1|sort) \
    <(sed 's/[[:space:]]\+/@/' file2|sort)|sed 's/@/\t/'

Сначала замените первый разделитель в каждой строке некоторым несимметричным символом и отсортируйте оба входных файла. Затем используйте join, чтобы выполнить фактическое соединение. Отфильтруйте свой вывод, чтобы заменить пробел char на пробел.

Это вывод из файлов, о которых идет речь:

xyz]$ join -o 1.1,2.2,2.3,1.2 <(sed 's/[[:space:]]\+/@/' file1|sort) \
<(sed 's/[[:space:]]\+/@/' file2|sort)|sed 's/@/\t/'

2L  25753 0.967741935483871 0.869565217391304 33158
2L  28813 0.181818181818182 0.692307692307692 33158
2L  31003 0.36 0.666666666666667 33158
2L  31077 0.611111111111111 0.931034482758621 33161
2L  31279 0.75 1 33161
3L  32124 0.558823529411765 0.857142857142857 45339
3L  33256 0.769230769230769 0.90625 45339

Ответ 3

Вы можете использовать команду join, но вам нужно создать одно поле объединения в каждой таблице данных. Предполагая, что у вас есть значения, отличные от 2L в столбце 1, этот код должен работать независимо от отсортированного или несортированного характера двух входных файлов:

tmp=${TMPDIR:-/tmp}/tmp.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15

awk '{print $1 ":" $2, $0}' file1 | sort > $tmp.1
awk '{print $1 ":" $2, $0}' file2 | sort > $tmp.2

join -o 2.2,2.3,2.4,2.5,1.4 $tmp.1 $tmp.2

rm -f $tmp.?
trap 0

Если у вас есть bash и "подстановка процесса", или если вы знаете, что данные уже отсортированы соответствующим образом, вы можете упростить обработку.


Я не совсем уверен, почему ваш код не работал, но я, вероятно, использовал бы a[$1,$2] для индексов; это даст вам меньше проблем, если некоторые из ваших значений столбца 1 являются чистыми числами и поэтому могут быть смущены при объединении столбцов 1 и 2. Именно поэтому сценарии "создания ключа" awk использовали двоеточие между полями.


С пересмотренными файлами данных, как показано:

file1

2L      5753   33158
2L      8813   33158
2L      7885   33158
2L      7885   33159
2L      1279   33158
2L      5095   33158
2L      3256   33158
2L      5372   33158
2L      7088   33161
2L      5762   33161

file2

2L      5095    0.666666666666667       1
2L      5372    0.5     0.925925925925926
2L      5762    0.434782608695652       0.580645161290323
2L      5904    0.571428571428571       0.869565217391304
2L      5974    0.434782608695652       0.694444444444444
2L      6353    0.785714285714286       0.84
2L      7088    0.590909090909091       0.733333333333333
2L      7885    0.714285714285714       0.864864864864865
2L      7902    0.642857142857143       0.810810810810811
2L      8263    0.833333333333333       0.787878787878788

(Не изменяется от вопроса.)

Выход

2L 5095 0.666666666666667 1 33158
2L 5372 0.5 0.925925925925926 33158
2L 5762 0.434782608695652 0.580645161290323 33161
2L 7088 0.590909090909091 0.733333333333333 33161
2L 7885 0.714285714285714 0.864864864864865 33158
2L 7885 0.714285714285714 0.864864864864865 33159