фильтровать файл по уникальному и наибольшему значению; объединить два массива в хэш

#arrays #perl #hash

#массивы #perl #хэш

Вопрос:

Мне нужно извлечь по уникальному роду (первая часть названия вида) в одном столбце, но с наибольшим числом в другом столбце в файле CSV при наличии кратных одного и того же имени.

Итак, если у вас несколько родов (одно и то же имя), тогда возьмите наибольшее число в последнем столбце, чтобы выбрать, которое будет представлять этот род.

Я извлек информацию в массивы, но у меня возникли проблемы с объединением двух для выбора. Я использовал https://perlmaven.com/unique-values-in-an-array-in-perl чтобы помочь, но мне нужно включить наибольшее число в последний столбец, когда ситуация с тем же родом.

 use strict;
use warnings;

open taxa_fh, '<', "$ARGV[0]" or die qq{Failed to open "$ARGV[0]" for input: $!n};

open match_fh, ">$ARGV[0]_genusLongestLEN.csv" or die qq{Failed to open for output: $!n};my @unique;

my %seen;
my %hash;

while ( my $line = <taxa_fh> ) {
   chomp( $line );
   my @parts = split( /,/, $line );
   my @name = split( / /, $parts[3]);
   my @A = $name[0];
   my @B = $parts[5];
   @seen{@A} = ();
   my @merged = (@A, grep{!exists $seen{$_}} @B);
   my @merged = (@A, @B);
   @hash{@A} = @B;
   print "$linen";
}

close taxa_fh;
close match_fh;
  

Пример ввода:

 AB179735.1.1711,AB179735.1.1711,278983,Eucyrtidium hexagonatum,0,1600
AB179736.1.1725,AB179736.1.1725,278986,Pterocorys zancleus,0,1763
AB181888.1.1758,AB181888.1.1758,281609,Protoperidinium crassipes,0,1700
AB181890.1.1709,AB181890.1.1709,281610,Protoperidinium denticulatum,0,1800
AB181892.1.1738,AB181892.1.1738,281611,Protoperidinium divergens,0,1800
AB181894.1.1744,AB181894.1.1744,281612,Protoperidinium leonis,0,1500
AB181899.1.1746,AB181899.1.1746,281613,Protoperidinium pallidum,0,1600
AB181902.1.1741,AB181902.1.1741,261845,Protoperidinium pellucidum,0,1750
AB181904.1.1734,AB181904.1.1734,281614,Protoperidinium punctulatum,0,1599
AB181907.1.1687,AB181907.1.1687,281615,Protoperidinium thorianum,0,1600
AB120001.1.1725,AB120001.1.1725,244960,Gyrodinium spirale,0,1500
AB120002.1.1725,AB120002.1.1725,244961,Gyrodinium fusiforme,0,1800
AB120003.1.1724,AB120003.1.1724,244962,Gyrodinium rubrum,0,1700
AB120004.1.1723,AB120004.1.1723,244963,Gyrodinium helveticum,0,1500
AB120309.1.1800,AB120309.1.1800,4442,Camellia sinensis,0,1700
  

Требуемый результат:

 AB179735.1.1711,AB179735.1.1711,278983,Eucyrtidium hexagonatum,0,1600
AB179736.1.1725,AB179736.1.1725,278986,Pterocorys zancleus,0,1763
AB181890.1.1709,AB181890.1.1709,281610,Protoperidinium denticulatum,0,1800
AB120002.1.1725,AB120002.1.1725,244961,Gyrodinium fusiforme,0,1800
AB120309.1.1800,AB120309.1.1800,4442,Camellia sinensis,0,1700
  

Комментарии:

1. Почему выбран Protoperidinium denticulatum, а не Protoperidinium divergens ? У них обоих 1800 в последнем столбце.

2. Мне нужен только один для представления этого рода. Итак, если у меня одинаковое число, я просто выбираю случайным образом между двумя или более.

3. Всегда ли виды одного рода соседствуют?

4. Нет, не всегда.

Ответ №1:

 use Text::CSV_XS qw( );

my $csv = Text::CSV_XS->new({
   auto_diag   => 2,
   binary      => 1,
   quote_space => 0,
});

my %by_genus;
while ( my $row = $csv->getline(*ARGV) ) {
   my ($genus) = split(' ', $row->[3]);
   $by_genus{$genus} = $row
      if !$by_genus{$genus}
      || $row->[5] > $by_genus{$genus}[5];
}

$csv->say(select(), $_) for values(%by_genus);
  

Ответ №2:

Правильное присвоение имен переменным делает код более читаемым:

 #! /usr/bin/perl
use warnings;
use strict;

my %selected;
while (<>) {
    my ($species, $value) = (split /,/)[3, 5];
    my $genus = (split ' ', $species)[0];
    if ($value > ($selected{$genus}{max} || 0)) {
        $selected{$genus}{max} = $value;
        $selected{$genus}{line} = $_;
    }
}
for my $genus (keys %selected) {
    print $selected{$genus}{line};
}
  

Порядок выходных строк случайный.

Комментарии:

1. Это предполагает, что последнее поле всегда будет неотрицательным. (Возможно, все в порядке, но вы должны были упомянуть об этом!)

2. Это обрабатывает только подмножество CSV.

3. Да, опасность угадать спецификацию по образцу входных данных.

Ответ №3:

Вы также можете использовать командную строку Perl

 perl -F, -lane ' ($g=$F[3])=~s/(^S ).*/$1/; if( $mx{$g}<$F[-1]) 
   { $kv{$g}=$_;$mx{$g}=$F[-1] } END { print $kv{$_} for(keys %kv) } ' file
  

с заданными входными данными в cara.txt файл, вывод которого

 $ perl -F, -lane ' ($g=$F[3])=~s/(^S ).*/$1/; if( $mx{$g}<$F[-1]) 
       { $kv{$g}=$_;$mx{$g}=$F[-1] } END { print $kv{$_} for(keys %kv) }  ' cara.txt
AB179736.1.1725,AB179736.1.1725,278986,Pterocorys zancleus,0,1763
AB179735.1.1711,AB179735.1.1711,278983,Eucyrtidium hexagonatum,0,1600
AB120309.1.1800,AB120309.1.1800,4442,Camellia sinensis,0,1700
AB120002.1.1725,AB120002.1.1725,244961,Gyrodinium fusiforme,0,1800
AB181890.1.1709,AB181890.1.1709,281610,Protoperidinium denticulatum,0,1800

$
  

Ответ №4:

Не вычурно, но выполняет свою работу

 #!/usr/bin/perl

use strict;

my @data = `cat /var/tmp/test.in`;

my %genuses = ();


foreach my $line ( @data ) {
  chomp($line);
  my @splitline = split(',', $line);
  my $genus = $splitline[3];
  my $num = $splitline[5];
  my ( $name, $extra ) = split(' ', $genus);
  if ( exists $genuses{$name}->{'num'} ) {
    if ( $genuses{$name}->{'num'} < $num ) {
      $genuses{$name}->{'num'} = $num;
      $genuses{$name}->{'line'} = $line;
    }
    else {
        next;
    }
  }
  else {
    $genuses{$name}->{'num'} = $num;
    $genuses{$name}->{'line'} = $line;
 }
}

foreach my $genus ( %genuses ) {
  print "$genuses{$genus}->{'line'}";
  print "n";
}
  

Вывод:

 [root@localhost tmp]# ./test.pl
AB179736.1.1725,AB179736.1.1725,278986,Pterocorys zancleus,0,1763
AB179735.1.1711,AB179735.1.1711,278983,Eucyrtidium hexagonatum,0,1600
AB120309.1.1800,AB120309.1.1800,4442,Camellia sinensis,0,1700
AB120002.1.1725,AB120002.1.1725,244961,Gyrodinium fusiforme,0,1800
AB181890.1.1709,AB181890.1.1709,281610,Protoperidinium denticulatum,0,1800
  

Не вижу очевидного метода, с помощью которого вы сортируете свои выходные данные

Комментарии:

1. Практически идентично ответу @choroba, вплоть до ненужной обработки только подмножества CSV.