Portál AbcLinuxu, 14. května 2025 10:30

Dotaz: Perl - Pole a třídění

27.11.2010 14:38 Radek Podskubka | skóre: 2
Perl - Pole a třídění
Přečteno: 429×
Odpovědět | Admin
Ahoj, potřebuju v perlu seřadit nějaké hodnoty za tím účelem, abych mohl určit jejich medián. Problém je v tom, že k těmto hodnotám přísluší ještě nějaké odchylky. Data ukládám do pole z texťáku, kde jsou v jednom sloupci hodnoty a ve druhém jejich chyby. A medián určuju pro jednotlivé dny (tedy v prvním sloupci figuruje ještě nějaké datum, ale to není podstatné, navíc je to vidět z kódu). V přiložený kód funguje dobře, v podstatě jen určuje medián příslušných hodnot. Ovšem já bych potřeboval do výstupu ještě k mediánu vytisknout odchylku, která k němu přísluší (tedy NE medián odchylek). Problém tedy nastává v okamžiku, kdy se provádí příkaz
@vars = sort @{$dailyvars{$_}};
Pokud bych měl tedy uloženy odchylky v podobném poli jako hodnoty, a tímto způsobem zamíchám s jejich pořadím,tak už nepřísluší například pátý prvek pole vars k pátému prvku pole odchylek. Principielně je snad řešení snadné, prostě kromě hodnot je třeba naskládat do pole @{dailyvars{1}} ještě odkazy na konkrétní prvky pole odchylek a na ty se potom odvolávat. Já jsem ale v Perlu doccela nováček a netuším jak tohle zapsat nebo mě nenapadá nějaké snažší řešení. Děkuji za každou radu.

Celý výpis kódu je zde
while (<>)
{
    if (/^#/) { next; }
    /([\d-]+) (\d+) ([\d\.]+) ([\d\.]+)/ or die "daily_median.pl: Error processing line: $_";
    if (! exists $dailyvars{$1}) { @{$dailyvars{$1}} = ( $3 ); $count{$1} = 1; }
    else { push @{$dailyvars{$1}}, $3; $count{$1} += 1; }
}
foreach (sort keys %dailyvars)
{
    @vars = sort @{$dailyvars{$_}};
    $n = $#vars + 1;
    if ($n % 2 == 1) {
	$median = $vars[($n - 1) / 2]; }
    else {
	$median = ($vars[$n / 2] + $vars[$n / 2 - 1]) / 2; }
    print $_ . " $median " . $count{$_} . "\n";
}

Řešení dotazu:


Nástroje: Začni sledovat (1) ?Zašle upozornění na váš email při vložení nového komentáře.

Odpovědi

Řešení 2× (Radek Podskubka (tazatel), pht)
27.11.2010 15:42 buff | skóre: 10 | blog: buff
Rozbalit Rozbalit vše Re: Perl - Pole a třídění
Odpovědět | | Sbalit | Link | Blokovat | Admin
Myslím, že Tvé řešení je špatně: sort v perlu defaultně třídí lexikograficky, nikoliv číselně.

K problému: pokud by při načítání byla odchylka např. v $4:
if (! exists $dailyvars{$1}) { @{$dailyvars{$1}} = ({var => $3, odchylka => $4}); $count{$1} = 1; }
else { push @{$dailyvars{$1}}, {var => $3, odchylka => $4}; $count{$1} += 1; }

...

@vars = sort { $a->{var} <=> $b->{var} } (@{$dailyvars{$_}});

...

$median = $vars[($n - 1) / 2]->{var};

A další změny analogicky.

Vtip je v tom, že do $dailyvars{$den} neukládáš jen hodnoty, ale ukazatele na hash, který má dva klíče: var a odchylka. Tím si podržíš ty přidružené hodnoty pospolu.

Doufám, že je to trochu srozumitelné, spěchám, tak to vysvětluju tak nějak narychlo a ne moc exaktně. Podívej se do manuálu na sort, tam uvidíš, že to, co dávám do prvních složených závorek, je funkce, která určuje, podle čeho se bude třídit.
27.11.2010 17:16 Radek Podskubka | skóre: 2
Rozbalit Rozbalit vše Re: Perl - Pole a třídění
Dík moc. To je určitě velmi efektivní řešení. Pomohlo mi to.
wamba avatar 27.11.2010 18:16 wamba | skóre: 38 | blog: wamba
Rozbalit Rozbalit vše Re: Perl - Pole a třídění

popř. něco takového

use 5.010;
use warnings;
use strict;

my %dailyvars;

while (<>) {
next if /^#/;    #ignoruj komentáře
/(?<date>[\d-]+) \d+ (?<var>[\d\.]+) (?<odchylka>[\d\.]+)/ #rozeber řádek
or die "daily_median.pl: Error processing line: $_";

push @{ $dailyvars{ $+{date} }{vars} },
$+{var};    #přidej hodnotu mezi hodnoty
push @{ $dailyvars{ $+{date} }{'odchylky'} },
$+{odchylka};    #přidej odchylku mezi odchylky
$dailyvars{ $+{date} }{count}++;    #přičti k počtu 1
}

foreach ( sort keys %dailyvars ) {      #pro všechny  dny (seřazené)
my $median;
my @vars = sort { $a <=> $b } @{ $dailyvars{$_}{vars} };  #srovnej hodnoty
my $n = @vars;                                            #počet  hodnot;

if ( $n % 2 == 1 ) {
$median = $vars[ ( $n - 1 ) / 2 ]
;    #vypočti median pro  sudý počet hondot
}
else {
$median = ( $vars[ $n / 2 ] + $vars[ $n / 2 - 1 ] )
/ 2;    #vypočti median pro lichý počet hodnot
}

#vypiš výsledek
say $_
. "\tmedian: $median "
. "\tpocet: $dailyvars{$_}{count} "
. "\todchylky:  @{ $dailyvars{$_}{odchylky} }";
}

This would have been so hard to fix when you don't know that there is in fact an easy fix.

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.