правильно соединить два файла на основе двух общих столбцов

У меня есть два файла, которые яя пытаюсь объединить / объединить на основе столбцов1 а также2, Они выглядят примерно так, с (file158210 линии) намного короче, чемfile2815530 линии) и яЯ хотел бы найти пересечение этих двух файлов на основе полей1 а также2 в качестве индекса ::: I '

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) в любом из файлов, но это некажется, чтобы помочь. (Любое понимание того, почему это так, также приветствуется)

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

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
 nneonneo21 окт. 2012 г., 04:05
Можете ли вы привести несколько примеров, когда меняется первый столбец?
 Jonathan Leffler21 окт. 2012 г., 04:10
В ваших образцах данных нет строк, которые должны соответствовать ...
 Serge21 окт. 2012 г., 04:10
Или их нужно соединять построчно?
 suegene21 окт. 2012 г., 20:27
@JonathanLeffler: ты прав! Спасибо, что потратили время на пересмотр примеров, чтобы они работали правильно, я должен был подумать сделать это сам в оригинальном посте.
 Serge21 окт. 2012 г., 04:07
какие поля должны быть использованы для объединения строк?
 Ed Morton21 окт. 2012 г., 16:05
У вас должен быть один файл, созданный в DOS, и один в UNIX или что-то ещеЭто должны быть какие-то управляющие символы в конце строк в одном или обоих файлах, которые мешают выводу. Пытаться "кот -v " на обоих файлах, чтобы увидеть контрольные символы, и попробуйте dos2unix на обоих, чтобы исправить их.
 suegene21 окт. 2012 г., 20:26
@Serge: извинения за запоздалый ответ, файлы должны быть объединены на основе полей 1 и 2 (следовательно, в столбцах вопросов 1 и 2)

Ответы на вопрос(3)

Решение Вопроса
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

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

 Ed Morton26 мар. 2014 г., 17:54
Спасибо @fedorqui, теперь, если я смогу найти работу, где это 'на самом деле ценный :-).
 Steve21 окт. 2012 г., 06:08
+1 намного аккуратнее!
 Tedee1234521 окт. 2012 г., 16:56
Я новичок в awk. Не могли бы вы вкратце объяснить, что в свою очередь делает ваша команда?
 Tedee1234521 окт. 2012 г., 18:39
@ Ред МортонСпасибо, большое спасибо за разъяснения.
 Ed Morton21 окт. 2012 г., 14:41
Тогда у вас нет соответствующих строк в вашем входе. Решение работает для проблемы, которую вы описали. Или, может быть, выиспользуется старый, неработающий awk (/ usr / bin / awk в Solaris). Что вам говорит awk -version?
 fedorqui26 мар. 2014 г., 17:36
Хороший ответ! Поздравляю за то, что вы 20 КБ, полностью заслуженный за ваш гуру-ашм :)
 Ed Morton21 окт. 2012 г., 17:43
@ tedee12345: см. выше измененный ответ.
 Kent18 мар. 2013 г., 15:30
@ Edmorton Я узнал много трюков от вас. и этот ответ с полным объяснением заслуживаетnice answer знак! +1!
 Tedee1234521 окт. 2012 г., 16:01
Вы'верно, ваше решение работает. Я проверил ваш файл, который вы привели в качестве примера. Я'Я пытался подать пост выше. Извините за ошибку.
 suegene21 окт. 2012 г., 20:20
@ Edmorton Большое спасибо, и извините, выПравильно, что этот пример не был настолько хорош, я просто разместил заголовки обоих файлов в качестве примера того, как они выглядят, но выполнение кода в этих строках не привело бы к выводу, и я обязательно посмотрю это в будущем. Приносим свои извинения всем остальным!
 Tedee1234521 окт. 2012 г., 09:19
Это решение не работает. Я не печатаю.
 Ed Morton21 окт. 2012 г., 16:13
Нет проблем. Можно'не представляю, почему ОП счел целесообразным публиковать большие образцы входных файлов, которые не производят никакого вывода, а затем строку вывода, которая нет происходит в этом входе. Полагаю, я просто пытаюсь сделать это более сложным для тех из нас, кто пытается помочь.

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 сделать фактическое соединение. Отфильтруйте его выходные данные, чтобы заменить символ без пробела пробелом.

Это вывод из файлов, как в вопросе:

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
 Serge21 окт. 2012 г., 20:47
@suegene, пожалуйста, смотрите обновление тогда
 suegene21 окт. 2012 г., 21:08
@ Серж, я нене знаю достаточно хорошо, чтобы понять, чтоИдет, но ваш обновленный ответ не дает желаемого результата, он фактически увеличивает количество строк в любом из входных файлов. Я неНе знаю, поможет ли это, но поле 1 является символом, а поле 2 - числовым. Оба файла уже отсортированы по полю 1, а затем по номеру 2.
 Serge21 окт. 2012 г., 04:29
@JonathanLeffler Я не получил никакого ответа от ОП на мои вопросы, поэтому я просто предполагал, что объединение будет выполняться построчно, поэтому я выполняю объединение по нумерации строк, производимой cat
 Jonathan Leffler21 окт. 2012 г., 04:23
Объединение должно быть в столбцах 1 и 2, нене так ли? А такжеjoin кажется, работает только с одним столбцом.
 newtover11 апр. 2013 г., 12:55
о, где ты узнал об этих двух() <()? Это'потрясающе!
 Jonathan Leffler21 окт. 2012 г., 04:32
ХОРОШО - справедливо; Я нене думаю, чточто имел в виду ФП, но яя пропустил-n наcat команды (но потом я подписываюсь на школу в Нью-Джерси для разработкиcat а также 'cat вернулся из Беркли, размахивая флагами (перефразируя цитату Кена Томпсона) меня раздражает).
 Serge21 окт. 2012 г., 04:33
@JonathanLeffler Я даже могуне понимаю, почему он не удивлен, получив одну строку при условии, что данные и требование объединить в столбцах 1 & 2.

Вы можете использовать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

Ваш ответ на вопрос