Объединение 2 файлов на основе одного столбца
Я хотел бы объединить два файла. Я просмотрел предыдущие вопросы и ответы, но ни один из них не соответствовал моему желаемому результату.
У меня есть два файла, разделенных запятыми, разной длины, file1.csv
а также file2.csv
,
Мне нужно объединить эти файлы на основе их первого поля. Если первое поле из file1.csv
присутствует в file2.csv
, то соответствующая строка из file2.csv
следует добавить к этому из file1.csv
, Если 1-го поля нет, то строка из file1.csv
должны быть напечатаны и no match
добавлен к нему.
file1.csv
(4 колонки):
Contig_Spider_Gland_98_1_1,>Contig_Spider_Gland_98_1_1 [1169 - 963] (REVERSE SENSE),MQGHRRKLATPRQRAPRKERQRALLLRLQWRIGLQPCSRRNKSLDRKNIYWRYLVEYGSWKGRTHISDV,C#
Contig_Spider_Gland_98_7_3,>Contig_Spider_Gland_98_17965_1 [90 - 278],MADVEKTSCCTETKECCKDETCCENGQGACHTGKEECKDTCHKKACGCKAGEDCKCSDGKCGC,CC#CC#CC#C#C#C#C#C#C#C#C#C#
file2.csv
(7 столбцов):
Contig_Spider_Gland_98_1_1, SignalP-4.1, SIGNAL, 1, 22, 0.808, YES
Contig_Spider_Gland_98_8_2, SignalP-4.1, SIGNAL 1, 20, 0.877, YES
Желаемый результат:
Contig_Spider_Gland_98_1_1,>Contig_Spider_Gland_98_1_1 [1169 - 963] (REVERSE SENSE),MQGHRRKLATPRQRAPRKERQRALLLRLQWRIGLQPCSRRNKSLDRKNIYWRYLVEYGSWKGRTHISDV,C#,Contig_Spider_Gland_98_1_1, SignalP-4.1, SIGNAL, 1, 22, 0.808, YES
Contig_Spider_Gland_98_7_3,>Contig_Spider_Gland_98_17965_1 [90 - 278],MADVEKTSCCTETKECCKDETCCENGQGACHTGKEECKDTCHKKACGCKAGEDCKCSDGKCGC,CC#CC#CC#C#C#C#C#C#C#C#C#C#,no match
3 ответа
Это должно сделать это:
awk -F, -vOFS=, '(NR==FNR){a[$1]=$0; next}
{
if(a[$1]){print $0,a[$1]}
else{print $0,"no match"}
}' file2.csv file1.csv
объяснение
awk -F, -vOFS=,
: бежатьawk
, настройка входа (-F
) и вывод (-vOFS=,
) разделители полей для,
,(NR==FNR){a[$1]=$0; next}
:NR
а такжеFNR
являются специальными переменными, которые означают текущий номер строки и текущий номер строки текущего файла соответственно. При передаче более одного имени файла оба будут равны только во время чтения первого файла. Таким образом, это означает, что "при чтении первого файла сохраняйте каждую строку в массиве, ключом которого является первое поле, и переходите к следующей строке".if(a[$1]){print $0,a[$1]}
: мы сейчас во втором файле. Если 1-е поле текущей строки было также в 1-м файле, выведите текущую строку и строку из первого файла.else{print $0,"no match"}
: если 1-го поля не было в первом файле, выведите текущую строку и "нет совпадения"
Обратите внимание, что я прохожу file2.csv
в качестве первого файла и file1.csv
как второй. Это связано с тем, что один из двух файлов должен храниться в памяти, поэтому лучше хранить самый маленький из двух.
Это должно сделать (сокращено (R) и исправлено (TM) тердоном):
#!/usr/bin/perl
use strict;
use warnings;
@ARGV==2 || die;
open(my $file1, $ARGV[0]) || die("Could not open \"$ARGV[0]\": $!");
open(my $file2, $ARGV[1]) || die("Could not open \"$ARGV[1]\": $!");
$"=","; #" (this comment exists only to prevent syntax hilighting from breaking)
while(my $l1 = <$file1>) {
chomp($l1);
my @f1 = split(",", $l1);
if(my $l2 = <$file2>) {
chomp($l2);
my @f2 = split(",", $l2);
if($f1[0] eq $f2[0]) {
print("@f1,@f2\n");
}
else {
push(@f1, "no_match");
seek($file2, -length($l2), 1);
print("@f1\n");
}
}
else {
push(@f1, "no_match");
print("@f1\n");
}
}
close($file1);
close($file2);
exit;
Поскольку файлы отсортированы, "file1.csv" является надмножеством "file2.csv", и в обоих файлах нет повторяющихся строк:
- Сравнивает следующие строки в обоих файлах;
- Если первое поле строки из "file1.csv" совпадает с первым полем строки из "file2.csv", добавляет строку из "file2.csv" к строке из "file1.csv" (через запятую) и печатает сгенерированную строку; в противном случае добавляет поле "no_match" к строке из "file1.csv", возвращает одну строку назад в "file2.csv" и печатает измененную строку из "file1.csv";
- Если "file2.csv" не имеет больше строк, добавляет поле "no_match" к строке из "file1.csv" и печатает измененную строку из "file1.csv".
Другой вариант - использовать соединение:
join -t, file1.csv file2.csv -a 1 -o auto -e 'no match'
Если входные файлы еще не отсортированы, вы можете сделать это за один раз:
join -t, <(sort file1.csv) <(sort file2.csv) -a 1 -o auto -e 'no match'
Exlanation
-t,
устанавливает разделитель полей-a 1
убедитесь, что напечатаны все негодные строки-o auto
устанавливает формат вывода-e 'no match'
заменяет отсутствующие поля ввода на "нет совпадения"<(list)
Оператор связывает выходные данные команд сортировки с именованными каналами, которые затем используются в качестве файлов для команды соединения (называемой "Подстановка процессов").