объединить 2 строки на основе одинаковых значений столбца

У меня есть файл, как показано ниже.

47196436 47723284 name1 1.77273 42672249 52856963 name2 1.06061 52856963 430695 name2 1.16667 55094959 380983 name3 1.55613 17926380 55584836 name4 1.02461 3213456 34211 name4 1.11 54321 34211 name4 1.23 

Первые 2 столбца соответствуют основным ключам в моей таблице. Я пытаюсь объединить строки таким образом, что если есть одно и то же имя, все ключи будут в одной строке.

Я пытаюсь получить вывод как,

 47196436 47723284 name1 42672249 52856963 430695 name2 55094959 380983 name3 17926380 55584836 3213456 34211 54321 name4 

Я смог добиться этого частично, используя команду ниже.

 awk '{ x[$3]=x[$3] " " $2; } END { for (k in x) print k,x[k] >"OUTPUT1"; }' ccc.txt 

Однако это не дает мне результат правильно. Мне нужна помощь в дальнейшем изменении указанной команды.

4 Solutions collect form web for “объединить 2 строки на основе одинаковых значений столбца”

Решение perl :

 $ perl -ane '$h{$F[2]} .= " ".$F[0]." ".$F[1]; END { for $k (sort keys %h) { print $_," " for grep {!$seen{$_}++} split(" ",$h{$k}); print "$k\n"; } }' file 47196436 47723284 name1 42672249 52856963 430695 name2 55094959 380983 name3 17926380 55584836 3213456 34211 54321 name4 

Ужасно, но, похоже, делает работу

 awk '$3 != prev {if (NR != 1) print prev; prev=$3; delete a}; !($1 in a){a[$1]++; printf "%s ", $1}; !($2 in a){a[$2]++; printf "%s ", $2}; END {print prev}' ccc.txt 47196436 47723284 name1 42672249 52856963 430695 name2 55094959 380983 name3 17926380 55584836 3213456 34211 54321 name4 

Вот еще один подход Perl:

 $ perl -ane 'foreach(@F[0..1]){$k{$F[2]}{$_}++} END{ foreach $v (sort keys(%k)){ print "$_ " foreach(keys(%{$k{$v}})); print "$v\n" }; } ' file 

Это дает:

 47723284 47196436 name1 42672249 430695 52856963 name2 380983 55094959 name3 34211 55584836 17926380 54321 3213456 name4 

объяснение

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

 #!/usr/bin/perl ## This is the hash that will store our values. my %k; ## Read through the input file line by line ## saving each line as $line. This is what the -n ## switch to perl means, only there each line is saved ## in the special variable $_. while (my $line=<>) { ## Split the line into the @F array. This is ## what the -a switch does. #chomp($line); my @F=split(/\s+/,$line); ## Populate the %k hash that we defined at the beginning. ## This is a hash of hashes, it looks like this: ## $hash{key1}{key2}=value ## In this case, we are saying: ## $hash{3rd field}{1st field}=1 ## $hash{3rd field}{2nd field}=1 ## This just serves to add the 1st and 2nd fields ## to the list of fields for this $F[2] (the 3rd field, the name). ## A side effect of this is that hash keys are unique so duplicates ## are automatically removed. $k{$F[2]}{$F[0]}=1; $k{$F[2]}{$F[1]}=1; } ## We have now finished processing the file ## (this is the END{} block above), so let's print. ## This saves the keys of the hash %k in the @names array ## sorted alphabetically. my @names=(sort keys(%k)); ## Go through each of the names, saving ## them as $name foreach my $name (@names) { ## Now, iterate through the values associated ## with the current $name. These are saved as the ## keys of the hash %k{$name} foreach my $value ( (keys(%{$k{$name}})) ){ print "$value "; } ## Now print the name as well print "$name\n"; } 

Сценарий выше делает то же самое, что и один вкладыш, который я разместил, он просто расширен, чтобы использовать более четкий синтаксис.

Если вы не возражаете против использования gawk >= 4.0 , это (что почти так же, как у terdon ) приведет к желаемому результату, с дополнительным именем и порядком клавиш:

 NF { Names[$3][$1] = 1; Names[$3][$2] = 1; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; # if you want `Name` ordered for (Name in Names) { PROCINFO["sorted_in"] = "@ind_num_asc"; # if you want `Key` ordered for (Key in Names[Name]) { printf("%s ", Key); } print Name; } } 

дает:

 47196436 47723284 name1 430695 42672249 52856963 name2 380983 55094959 name3 34211 54321 3213456 17926380 55584836 name4 
  • как очистить свободные строки / столбцы после строки ваших данных
  • Присоедините две таблицы, основанные на значениях, не связанных друг с другом, в выбранном столбце
  • Почему мой Perl не играет хорошо с Unicode?
  • Как заменить все точки с запятой после первого?
  • Упрощение однострочного вызова с помощью sed и нескольких вызовов awk
  • Выполнять команды Perl с определенного IP-адреса?
  • Как использовать sed, чтобы заменить все остальное другой строкой, кроме шаблона?
  • библиотека perl getopts.pl
  • Индекс увеличения в файле
  • Ошибка при попытке вызова find из perl
  • Необходимо проанализировать таблицу двойного входа с двумя парами
  • Linux и Unix - лучшая ОС в мире.