У меня есть таблица, которая выглядит так (вкладка разделена):
Ron Rob rock bammy
m f m f
florida Atlanta florida texas
Эта таблица имеет порядок 5 * 512 и на основе данных строки 3, я хочу извлечь значения в строке1. например: я хочу иметь имена всех людей, живущих во Флориде и Техасе, в таблице из 2 столбцов и n числе строк.
Florida Ron
Florida Rock
Texas BAmmy
и так далее.
любые предложения для bash или PERL-вкладышей...
Заранее спасибо.
awk 'NR==1{for(i=1;i<=NF;i++)n[i]=$i}; NR==3{for(i=1;i<=NF;i++){if($i~/florida|texas/)print $i"\t"n[i];}}' yourFile
см. тест ниже:
kent$ echo "Ron Rob rock bammy
m f m f
florida Atlanta florida texas"|awk 'NR==1{for(i=1;i<=NF;i++)n[i]=$i}; NR==3{for(i=1;i<=NF;i++){if($i~/florida|texas/)print $i"\t"n[i];}}'
вывод
florida Ron
florida rock
texas bammy
РЕДАКТИРОВАТЬ
kent$ echo "Ron Rob rock bammy
m f m f
florida(8) Atlanta florida(8) texas(2;7)"|awk 'NR==1{for(i=1;i<=NF;i++)n[i]=$i}; NR==3{for(i=1;i<=NF;i++){if($i~/florida\(8\)|texas\(2;7\)/)print $i"\t"n[i];}}'
вывод:
florida(8) Ron
florida(8) rock
texas(2;7) bammy
Еще одно решение для Perl:
perl -ane 'push@c,@F}{print grep{/^(florida|atlanta)\t/i}map{"$c[$_+$#c/3*2+1]\t$c[$_]\n"}0..$#c/3'
Или как скрипт
#!/usr/bin/perl
use strict;
use warnings;
my (@data, @rows);
push @data, split/\s+/ while (<>);
for (0 .. $#data/3) {
my $name = $data[$_];
my $location = $data[$_+$#data/3*2+1];
push @rows, "$location\t$name\n" if $location =~ /^(florida|atlanta)$/i;
}
print join("", @rows);
с условием if внутри цикла вместо отдельного grep
.
Мой подход состоит в том, чтобы сгладить все три строки в один массив и использовать for (0.. $#data/3)
цикл над индексами, соответствующими именам из первой строки, и получить местоположение из столбца сопоставления с $data[$_+$#data/3*2+1]
.
#!/usr/bin/env perl
use strict;
use warnings;
my $pat = shift;
sub interleave($$){
my ($foo,$bar) = @_;
return map { ( $_ , shift @{$bar} ) } @{$foo};
}
my $n=0;
my(@p,%h);
while(<>){
chomp;
if($n%3==0){
@p = split /\t/, $_;
} elsif($n%3==2){
my @l = split /\t/, $_;
my %kv = interleave(\@p, \@l);
foreach my $k (keys %kv){
push(@{$h{$kv{$k}}}, $k);
}
}
$n++;
}
foreach my $loc (keys %h){
if(!defined $pat || $loc =~ /$pat/i){
foreach my $name (@{$h{$loc}}){
print ucfirst($loc), "\t", ucfirst($name), "\n";
}
}
}
А потом позвоните
perl extract.pl 'texas|florida' < data
Форма "Oneliner":
perl -ne 'BEGIN{$p=shift||"^";}chomp;if($n++%3!=1){unless(@p){@p=split/\t/,$_;next;}my %kv = map { ( $_ , shift @p ) } split(/\t/, $_);map { push(@{$h{$_}}, $kv{$_}); } keys %kv;}END{map{for my$nm(@{$h{$_}}){print ucfirst($_),"\t",ucfirst($nm),"\n";}}grep{/$p/i}keys%h;}' 'florida|texas' < data
Мне кажется, что это работа для Text :: CSV_XS. Это не очень хорошая идея, чтобы разделить на пробелы, так как многие, кажется, предполагая, как это будет не в состоянии ни на что, но упрощенных данных.
Код:
use strict;
use warnings;
use Text::CSV_XS;
my $csv = Text::CSV_XS->new( {
sep_char => "\t",
binary => 1,
});
# get array refs to each row, with appropriate name
# For larger data sets, using an array to hold the array refs would be better
my $name = $csv->getline(*DATA);
my $gender = $csv->getline(*DATA);
my $city = $csv->getline(*DATA);
for (keys @$city) { # lists the column numbers
if ($city->[$_] =~ /florida|texas/i) {
print "$city->[$_]\t$name->[$_]\n";
}
}
__DATA__
Ron Rob rock bammy
m f m f
florida Atlanta florida texas
Вывод:
florida Ron
florida rock
texas bammy
Здесь работает Perl-решение, но оно немного более сложное, что я бы хотел. Вероятно, вам лучше помещать эти данные в базу данных.
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
my %rows = (
name => 1,
location => 3,
);
my %location = map { $_ => 1 } qw[florida texas];
my @names;
while (<DATA>) {
next unless grep { $_ == $. } values %rows;
chomp;
if ($. == $rows{name}) {
@names = split;
}
if ($. == $rows{location}) {
my @locs = split;
for my $x (0 .. $#locs) {
if ($location{lc $locs[$x]}) {
say ucfirst $locs[$x]. "\t$names[$x]";
}
}
last;
}
}
__END__
Ron Rob rock bammy
m f m f
florida Atlanta florida texas