Ключ хеша как массив для получения минимального и максимального числа, если значения столбцов равны
У меня есть следующие данные:
miRNA17 70 105 dvex699824 12 233
miRNA17 21 60 dvex699824 42 20
miRNA17 55 89 dvex699824 6 40
miRNA18 58 85 dvex701176 119 92
miRNA17 66 105 dvex703815 35 75
miRNA17 31 71 dvex703815 43 83
miRNA17 39 79 dvex703815 43 83
miRNA2 28 56 dvex731981 313 286
miRNA17 10 70 dvex735428 142 203
miRNA17 29 91 dvex735428 213 152
miRNA17 66 105 dvex735668 163 125
Вопрос: если у меня есть эти 6 столбцов, мне нужно сгруппировать и распечатать в соответствии с этими правилами:
та же миРНК ## \ t независимо \ t независимо \ t та же самая dvex#### \t возьмите нижнюю \ t возьмите самую высокую
Например, это возможный вывод:
miRNA17 21 105 dvex699824 6 233
miRNA18 58 85 dvex701176 119 92
miRNA17 31 105 dvex703815 35 83
miRNA2 28 56 dvex731981 313 286
miRNA17 10 105 dvex735428 142 203
Как можно решить эту проблему с помощью хэш-ключей в виде массивов?
3 ответа
Perl скрипт:
use strict;
# Not shown... Parse the data file, stuff into an array of arrays.
my @data = (
[ 'miRNA17', 70, 105, 'dvex699824', 12, 233 ],
[ 'miRNA17', 21, 60, 'dvex699824', 42, 20 ],
[ 'miRNA17', 55, 89, 'dvex699824', 6, 40 ],
[ 'miRNA18', 58, 85, 'dvex701176', 119, 92 ],
[ 'miRNA17', 66, 105, 'dvex703815', 35, 75 ],
[ 'miRNA17', 31, 71, 'dvex703815', 43, 83 ],
[ 'miRNA17', 39, 79, 'dvex703815', 43, 83 ],
[ 'miRNA2', 28, 56, 'dvex731981', 313, 286 ],
[ 'miRNA17', 10, 70, 'dvex735428', 142, 203 ],
[ 'miRNA17', 29, 91, 'dvex735428', 213, 152 ],
[ 'miRNA17', 66, 105, 'dvex735668', 163, 125 ]
);
my %results;
foreach my $record (@data) {
my ($mirna, $col2, $col3, $dvex, $col5, $col6) = @$record;
$results{$mirna}{$dvex}{col2} = $col2; # don't care.
$results{$mirna}{$dvex}{col3} = $col3; # don't care.
$results{$mirna}{$dvex}{col5} = $col5
if not $results{$mirna}{$dvex}{col5} or $results{$mirna}{$dvex}{col5} > $col5;
$results{$mirna}{$dvex}{col6} = $col6
if not $results{$mirna}{$dvex}{col6} or $results{$mirna}{$dvex}{col6} < $col6;
}
foreach my $mirna (keys %results) {
foreach my $dvex (sort keys %{$results{$mirna}}) {
printf "%-8s %5d %5d %-10s %3d %3d\n",
$mirna, $results{$mirna}{$dvex}{col2}, $results{$mirna}{$dvex}{col3},
$dvex, $results{$mirna}{$dvex}{col5}, $results{$mirna}{$dvex}{col6};
}
}
1;
Выход:
miRNA2 28 56 dvex731981 313 286
miRNA17 55 89 dvex699824 6 233
miRNA17 39 79 dvex703815 35 83
miRNA17 29 91 dvex735428 142 203
miRNA17 66 105 dvex735668 163 125
miRNA18 58 85 dvex701176 119 92
Гораздо эффективнее обрабатывать файлы последовательно, не загружая сначала все в большой массив, когда это возможно. Вот как может выглядеть такое решение:
my @output_line = split / /, <IN_FILE>;
while (<IN_FILE>)
{
my @current_line = split / /, $_;
if ($current_line[0] ne $output_line[0])
{
printf OUT_FILE "%-8s %5d %5d %-10s %3d %3d\n", @output_line;
@output_line = @current_line;
}
else
{
$output_line[1] = $current_line[1] if ($current_line[1] < $output_line[1]);
$output_line[2] = $current_line[2] if ($current_line[2] > $output_line[2]);
$output_line[4] = $current_line[4] if ($current_line[4] < $output_line[4]);
$output_line[5] = $current_line[5] if ($current_line[5] > $output_line[5]);
}
}
printf OUT_FILE "%-8s %5d %5d %-10s %3d %3d\n", @output_line;
Предостережение: в вашем вопросе указано, что выходные строки должны иметь "тот же dvex####". Тем не менее, ваш пример выходных данных не показал это. Таким образом, я проигнорировал это требование. Однако вы можете легко ввести это требование, просто вставив другое условие в if
заявление.
Второе предостережение: этот подход также требует, чтобы линии, которые будут сгруппированы, располагались рядом друг с другом, как это было в ваших выборочных данных.
Это простой скрипт, который выдаст желаемый результат, хотя он делает больше, чем указано в ваших требованиях, так как он также проверяет мин / макс для столбцов 2 и 3.
Я использую List::Util, чтобы получить значения min/max, что является исключительно удобством. Модуль является ядром начиная с v5.7.3, поэтому он не должен представлять проблему. Использование Text::CSV целесообразно, но может не потребоваться, в зависимости от ваших данных. Предполагая, что в ваших столбцах нет пробелов без табуляции, можно избежать использования split
, который удалит зависимость модуля.
use strict;
use warnings;
use Text::CSV;
use List::Util qw(min max);
my $csv = Text::CSV->new({
sep_char => "\t",
eol => $/, # required for $csv->print
binary => 1,});
my %data;
my @order;
# *DATA and *STDOUT represent file handles, and can be replaced with
# any other file handle as you require. DATA is used here for simplicity.
#
while (my $row = $csv->getline(*DATA)) {
my ($mir, $dv) = @{$row}[0,3];
my $field = "$mir/$dv";
unless (defined $data{$field}) { # new fields are stored as-is
push @order, $field; # preserving original order of input
$data{$field} = $row;
next;
}
$data{$field}[1] = min($data{$field}[1], $row->[1]);
$data{$field}[2] = max($data{$field}[2], $row->[2]);
$data{$field}[4] = min($data{$field}[4], $row->[4]);
$data{$field}[5] = max($data{$field}[5], $row->[5]);
}
for my $field (@order) {
$csv->print(*STDOUT, $data{$field});
}
__DATA__
miRNA17 70 105 dvex699824 12 233
miRNA17 21 60 dvex699824 42 20
miRNA17 55 89 dvex699824 6 40
miRNA18 58 85 dvex701176 119 92
miRNA17 66 105 dvex703815 35 75
miRNA17 31 71 dvex703815 43 83
miRNA17 39 79 dvex703815 43 83
miRNA2 28 56 dvex731981 313 286
miRNA17 10 70 dvex735428 142 203
miRNA17 29 91 dvex735428 213 152
miRNA17 66 105 dvex735668 163 125
Выход:
miRNA17 21 105 dvex699824 6 233
miRNA18 58 85 dvex701176 119 92
miRNA17 31 105 dvex703815 35 83
miRNA2 28 56 dvex731981 313 286
miRNA17 10 91 dvex735428 142 203
miRNA17 66 105 dvex735668 163 125
Обратите внимание, что выходные данные не совпадают с вашими, потому что вы не смогли различить числа dvex для последней строки вашего образца ввода.