Pokaż wyniki 1 do 10 z 10

Temat: Perl - problem z implementacją pochodnej

  1. #1
    Zarejestrowany
    Jul 2009
    Skąd
    Kraków
    Postów
    11

    Domyślnie Perl - problem z implementacją pochodnej

    Poszukuję najlepszego sposobu na znalezienie ekstremów lokalnych z pewnego zbioru danych. Próbowałem zrobić to poprzez "ręczne" porównywanie wartości sąsiadujących, ale to za mało. Jestem (prawie) fizykiem, więc najbardziej naturalnym sposobem szukania ekstremów dla mnie jest proste matematyczne zerowanie się pierwszej pochodnej. Problem w tym, że nie wiem jak to wszystko zaimplementować w Perlu. Nigdy tego nie robiłem, a pomoce, które znalazłem w sieci są niewystarczające (znalazłem jakiś przykład w C na numeryczną pochodną, ale to dla mnie za mało). Poza tym, plik który obrabiam ma troszkę syfków, więc sam nie wiem jak i gdzie najlepiej wpleść to wszystko w mój kod, żeby w ogóle hulało. Zresztą zobaczcie sami:

    Kod:
    #!/usr/bin/perl -w
    open (INP,"@ARGV[0]")||die "bladzik";
    @WE=<INP>; #przypisanie wejscia do tablicy
    close(INP);
    `rm -f @ARGV[0]_*_max`;
    $l=0; #ustawienie licznika wierszy
    foreach(@WE){ #dla kazdego elementu tablicy @WE - sluzy do przechodzenia pomiedzy kolejnymi wierszami pliku wejsciowego
        $l++;
        if ($l>16){ #dla wierszy od 17 w dol (tylko interesujace nas dane)
        @[email protected]_m3;@[email protected]_m2;@[email protected]_m1;@[email protected]_m0; #5 kolejnych wierszy
        s/,/./g;
        @l_m0=split(/;/,$_); #tworzy tablice zlozona z kolejnych elementow w danym wierszu (regex: ";")
        for($i=1;$i<=8;$i++){ #przejscie po kolejnych krzywych
    	if (@l_m4[$i+4]<@l_m3[$i+4] and @l_m3[$i+4]<@l_m2[$i+4] and @l_m2[$i+4]>@l_m1[$i+4] and @l_m1[$i+4]>@l_m0[$i+4]){ #warunek na maksimum (porownanie kolejnych wierszy) dla @l_m2
    	    @in[$i][email protected][$i]+1; #numer kolejnego maksimum
    	    @last[$i][email protected][$i];
    	    $_=`date +%s -d \"@l_m2[0] @l_m2[1]\"`; #przypisanie daty, pobranej z 2 pierwszych elementow @l_m2 (w formacie timestamp)
    	    s/\n//;
    	    @now[$i]=$_; #data w ts przypisana do kolejnego (i-tego) elementu @now
    	    if (@last[$i]==0){@last[$i][email protected][$i];}
    	    open (OUT,">>@ARGV[0]_".$i."_max")||die"blad"; #otwiera plik wyjsciowy
    	    print OUT @in[$i]." "[email protected][$i]." ".(@now[$i][email protected][$i])." \n"; #zapisuje do OUT tablice z numerem cyklu, timestampem oraz roznica czasu (kolejne wiersze)
    	    close(OUT);
    	}
        }
        }
    }
    Powyżej oczywiście początek programu, odpowiadający za obróbkę pliku i szukanie maksimów. Poniżej załączam plik, na którym pracuję, żeby zainteresowani wiedzieli w czym się babrzę.

    I co o tym wszystkim sądzicie? Jak to najefektywniej zmodyfikować? A może jest jeszcze jakiś inny "informatyczny" sposób szukania ekstremów? (Szczerze, powiem wam, że to zadanie jest baaardzo specyficzne i ja tu wcale niekoniecznie mam znaleźć wszystkie ekstrema, lecz - UWAGA! - mam wyłuskać ekstrema, które będą widoczne na wykresie sporządzonych z tych danych za pomocą excela. Nie, nie pytajcie - sam sobie tak życia nie utrudniłem. Ktoś spotkał się kiedyś z podobnym zadaniem?)
    Załączone Pliki Załączone Pliki

  2. #2
    Zarejestrowany
    Jun 2006
    Skąd
    rand(.eu)
    Postów
    8,748

    Domyślnie

    ... computing - please wait ...

    Musze sie wgryzc nieco w logike co to ma dac w sumie, bo liczenie pochodnych w Perlu mozna zrobic albo recznie albo gotowymi modulami... problem tylko (dla mnie jako nie-matematyka) ktore podejscie wybrac aby miec optymalne rozwiazanie.

    Daj mi nieco czasu, dam znac co i jak bym zrobil. Twoj problem bardzo mi sie podoba, jest dosc trudny (zwlaszcza dla nie-matematyka) i mozna sie niezle pobawic w kodzie, wiec dodatkowo rozwija programistycznie. AFK - ide czytac...

    EDIT:
    1. Tak na szybko - nie ma takiego czegos jak @ARGV[0] - jest $ARGV[0] i uzywanie 'rm -f' z taka notacja jest cholernie sliskie!
    2. odwolujac sie do elementu tablicy stosujemy $ a nie @ - konsekwentnie uzywasz zlego zapisu, ten kod nie ma prawa dzialac!
    3. Przypisania dla 5 kolejnych wierszy @l_mN nie maja sensu bo wszystkie te wartosci sa 'undef'...
    4. proponuje dodac na poczatku 'use strict; use warnings;' - zobaczysz ile bledow jest w tym kodzie i gdzie zmienne nie sa podstawiane - to dobra praktyka, ktora poczatkowo jest upierdliwa jak diabli ale oszczedza niesamowite ilosc czasu gdy cos nie dziala i trzeba robic debug kodu...
    5. w petli wewnatrz 'if ($l>16) ...' przypisujesz 5 wierszy - dla kazdego obrotu petli liczysz po nastepnych 5 wierszach? czyli wiersz 20 + 5 linii dalej? jakos nie widze tego w kodzie... chyba zla logika albo totalnie zly zapis - spoko - poprawi sie i wyjasni

    niedlugo dodam wiecej bo zaczynam lapac sie w kodzie i logice problemu...

    EDIT 2:
    Widze teraz dopiero ze w logice jest 'srednia ruchoma'... albo mi sie bardzo wydaje ze tak jest. Wyjasnij mi prosze logike tego dzialania bo nie moge tego wylapac z kodu - tam nie ma wiekszej logiki chyba. Widze mniej wiecej co probujesz zrobic ale nie rozumiem sensu i co porownujesz do czego :-(
    Jesli dobrze rozumiem to w rekordach nie interesuja Cie pola jak data i godzina, numer kolejny ani pole kontrolne. Interesuja Cie sekcja 'Rekordy' i tylko wartosci pol od 01 do 08 - na nich musisz zrobic porownania.

    30min myslenia pozniej - ... szukasz potencjalnych ekstremow (tutaj max) w podanych odczytach sprawdzajac wartosci kolumn 01 do 08 dla zakresu <n-2, n+2> pomiarow od n czyli 2 w prawo i 2 w lewo (lub gora/dol jak kto woli). Jesli n jest maksimum w zakresie to zapisujesz gdzie wystapilo, tak?

    Jesli odpowiesz mi na to pytanie to sadze ze mam dla Ciebie kod, ktory jest napisany bardziej po perlowemu i skomentowany gdzie trzeba abys mogl zobaczyc co, jak i dlaczego zrobilem tak a nie inaczej :-)

    EDIT 3:
    Sprawdz prosze czy wyniki ktore uzyskalem sa poprawne (zalacznik) - tyle mi wyszlo dla zalaczonych przez Ciebie danych. Do tego poprawilem literufki powyzej :P
    Załączone Pliki Załączone Pliki
    Ostatnio edytowane przez TQM : 07-28-2009 - 00:05
    ctrl-alt-del.cc - soft reset site for IT admins and other staff :-)

  3. #3
    Zarejestrowany
    Jul 2009
    Skąd
    Kraków
    Postów
    11

    Domyślnie

    Cytat Napisał TQM Zobacz post

    Daj mi nieco czasu, dam znac co i jak bym zrobil. Twoj problem bardzo mi sie podoba, jest dosc trudny (zwlaszcza dla nie-matematyka) i mozna sie niezle pobawic w kodzie, wiec dodatkowo rozwija programistycznie. AFK - ide czytac...
    Cieszę się, że Cię zainteresował.

    1. Tak na szybko - nie ma takiego czegos jak @ARGV[0] - jest $ARGV[0] i uzywanie 'rm -f' z taka notacja jest cholernie sliskie!
    2. odwolujac sie do elementu tablicy stosujemy $ a nie @ - konsekwentnie uzywasz zlego zapisu, ten kod nie ma prawa dzialac!
    Masz racje - to błędy, które pozostały z okresu jak jeszcze tego nie wiedziałem. Powinienem to wszystko poprawić, ale Perl jest zbyt inteligentny - nie wyrzuca błędu w takim wypadku, tylko warningi...
    30min myslenia pozniej - ... szukasz potencjalnych ekstremow (tutaj max) w podanych odczytach sprawdzajac wartosci kolumn 01 do 08 dla zakresu <n-2, n+2> pomiarow od n czyli 2 w prawo i 2 w lewo (lub gora/dol jak kto woli). Jesli n jest maksimum w zakresie to zapisujesz gdzie wystapilo, tak?
    Dokładnie tak.
    Ostatnio edytowane przez Vigl : 07-28-2009 - 13:41

  4. #4
    Zarejestrowany
    Jun 2006
    Skąd
    rand(.eu)
    Postów
    8,748

    Domyślnie

    Ok, to teraz powiedz czy wyniki ktore dostalem sie zgadzaja Kod mam gotowy ale zanim go wrzuce chce wiedziec ze wyniki sie zgadzaja :P
    ctrl-alt-del.cc - soft reset site for IT admins and other staff :-)

  5. #5
    Zarejestrowany
    Jul 2009
    Skąd
    Kraków
    Postów
    11

    Domyślnie

    Niestety nie Powinniśmy otrzymać dla kolejnych krzywych:
    1. - 65 max
    2. - 66
    5. - 65
    6. - 65
    W pozostałych ekstremów brak. Wiem, że to dziwaczne. A na dodatek zleceniodawca stwierdził, że powinienem zrobić to z zerowym marginesem błędu. Lekko niewykonalne jak dla mnie...

    Ale mimo wszystko będę bardzo wdzięczny za Twój kod i oczywiście dalsze rady.

  6. #6
    Zarejestrowany
    Jun 2006
    Skąd
    rand(.eu)
    Postów
    8,748

    Domyślnie

    Czyli na dobra sprawe nie interesuje nas kazde ekstremum/wierzcholek tylko wartosc maksymalna w zestawie i minimalna... czy moze kazdy punkt gdzie wykres przechodzi z czesci rosnacej (liniowo mowiac a>0) do czesci malejacej?

    To co mam pokazuje gdzie funkcja zmienia kierunek z dodatniego na ujemny wiec wszystkie wierzcholki w przebiegu.
    ctrl-alt-del.cc - soft reset site for IT admins and other staff :-)

  7. #7
    Zarejestrowany
    Jul 2009
    Skąd
    Kraków
    Postów
    11

    Domyślnie

    Interesuje nas każde ekstremum widoczne "gołym okiem" w Excelu. Na wykresie o dużej rozdzielczości. Ciężko mi to inaczej określić, bo właśnie tak "sprecyzował" to zleceniodawca.

  8. #8
    Zarejestrowany
    Jun 2006
    Skąd
    rand(.eu)
    Postów
    8,748

    Domyślnie

    No to w takim razie przy tej definicji 'dokladnosci' to co ja pokazalem jest dobre - gdzie linia laczaca wyniki 2 odczytow zmienia kierunek z rosnacego na malejacy tam masz 'ekstremum'

    Moze 'zleceniodawca' chce przemyslec jeszcze raz specyfikacje? Gdybys mial to opisane funkcja matematyczna (a nie danymi empirycznymi z aparatury) to mialbys ekstrema/max w tych wlasnie punktach. Zaraz wrzuce kod - mam na drugim kompie.

    EDIT:
    no to lecimy z kodem

    Kod:
    #!/usr/bin/perl
    use strict;
    use warnings;
    use Time::Local;
    
    # tutaj bedziemy miec komplet danych - lista list - efektywnie 2-wymiarowa :-)
    my @dane;
    
    my $INFILE = $ARGV[0] || 'curves.txt';  # jesli nie mamy podanego pliku to domyslnie bedzie curves.txt
    open (IN,$INFILE) || die "Nie moge otworzyc pliku $INFILE: $!\n";
    chomp(my @input = <IN>);   #wczytaj plik i usun znaki konca linii
    close (IN);
    
    # nie potrzebujemy licznika linnii bo to nie ma sensu  - po prostu sprawdzamy czy linia ma odpowiedni format
    # to co nie pasuje pomijamy, to co pasuje mielimy matematycznie :-]
    foreach (@input) {
    	next if !/^\d{2}-\d{2}-\d{2};\d{2}:\d{2}:\d{2};/;  # pomijamy te bez daty i godziny na poczatku linii
    	s/,/./g;  # zmieniamy notacje
    	s/\s+//g;  # wywalamy spacje
    	my @tmp = split/;/;   # tutaj mamy kolejne pola z obrabianej linii
    	# najpierw przekonwertujmy date i godzine na timestamp, polaczymy w ten sposob pierwsze dwa pola uzywajac
    	# timelocal($sec,$min,$hour,$mday,$mon,$year)
    	my $timestamp = timelocal(reverse(split(/:/,$tmp[1])), reverse(split(/-/,$tmp[0])));
    	# pola 4 do 11 to nasze dane w kolumnach 01 do 08 ale reszte pol chcemy zostawic, pozbywajac sie tylko kolumn 
    	# ktore nie maja nazw i zawieraja L i H - cokolwiek by to nie bylo, tniemy razem z data i godzina bo to juz mamy
    	for (1..4) { shift @tmp };
    	# skladamy calosc w jedna liste/tabele dopisujac ja na koncu macierzy - doslownie listy list (2-wymiarowa macierz)
    	unshift @tmp, $timestamp;
    	$dane[++$#dane] = \@tmp;
    	# jesli wszystko poszlo jak trzeba to powinnismy miec 1 nowy wiersz zawierajacy timestamp, dane, numer kolejny, pole kontrolne
    }
    
    # teraz mozemy sprawdzic jak nasze dane wygladaja w postaci przyjaznej dla oka
    # zakomentuj ponizsze 2 linie jesli nie chcesz tego widziec :)
    #use Data::Dumper;
    #print Dumper \@dane;
    #
    
    for my $kolumna (1..8) {
    	# lecimy kolumnami... 0 to timestamp, 1 do 8 to dane, 9 i 10 to ostatnie 2 pola
    	my $poprzednia = 0;
    	my $ostatnie_max_indeks = 0;
    	# profilaktycznie kasujemy stary plik z wynikami
    	unlink sprintf("max_%02d_%s", $kolumna, $INFILE);
    	# teraz jazda z danymi
    	for (my $wiersz = 0; $wiersz<=$#dane; $wiersz++) {
    		if ($poprzednia > $dane[$wiersz]) {
    			# jesli wartosc aktualna jest mniejsza od poprzedniej to poprzednia linia ma lokalne maksimum
    			# to mozna rozbudowac na sprawdzanie wiecej niz 1 linii i w ten sposob 'wygladzic' wyniki
    			open (OUT, ">>", sprintf("max_%02d_%s", $kolumna, $INFILE)) || die "Nie moge otworzyc pliku do zapisu: $!\n";
    			print OUT "$dane[$wiersz-1][9] $dane[$wiersz-1][0] ", $dane[$wiersz-1][0] - $dane[$ostatnie_max_indeks][0], "\n";
    			close (OUT);
    			$ostatnie_max_indeks = $wiersz-1;
    		}
    		$poprzednia = $dane[$wiersz];
    	}
    }
    Ostatnio edytowane przez TQM : 07-28-2009 - 16:53
    ctrl-alt-del.cc - soft reset site for IT admins and other staff :-)

  9. #9
    Zarejestrowany
    Jul 2009
    Skąd
    Kraków
    Postów
    11

    Domyślnie

    Dziękuję za wszystkie rady.
    Od tamtej pory niewiele zajmowałem się tym problemem, zdawało się, że zlecenie stało się nieaktualne, więc zrobiłem sobie wakacje. Właściwie wczoraj tchnęło mnie, żeby spróbować to dokończyć - trochę pracy w to włożyłem (głównie było to pocenie się nad tym - niestety jestem w tym marny - każda Twoja rada okazywała się bardziej efektowna niż kilka godzin mojego grzebania po sieci celem nabycia jakichś umiejętności), więc już nawet jeżeli nie mam mieć z tego tytułu żadnych profitów, może lepiej się jeszcze trochę pomęczyć i zostawić ten rozdział należycie zamknięty.
    W każdym razie na szybko próbowałem popracować nad Twoim kodem - efektywnie zaimplementować go do mechanizmów wczytywania, rysowania, zapisywania, etc. - ale nawet to mi nie za bardzo wyszło. Poniżej wklejam więc kod (większość i clue jest oczywiście Twoja, stąd też pozostawiam ją w miarę w oryginalnej formie):
    Kod:
    #!/usr/bin/perl
    use strict;
    use warnings;
    use Time::Local;
    
    # tutaj bedziemy miec komplet danych - lista list - efektywnie 2-wymiarowa 
    my @dane;
    
    my $INFILE = $ARGV[0] || 'curves.txt';  
    open (IN,$INFILE) || die "Nie moge otworzyc pliku $INFILE: $!\n";
    chomp(my @input = <IN>);   #wczytaj plik i usun znaki konca linii
    close (IN);
    
    foreach (@input) {
    	next if !/^\d{2}-\d{2}-\d{2};\d{2}:\d{2}:\d{2};/;  # pomijamy niepotrzebne linie
    	s/,/./g;  # zmieniamy notacje
    	s/\s+//g;  # wywalamy spacje
    	my @tmp = split/;/;   
    	my $timestamp = timelocal(reverse(split(/:/,$tmp[1])), reverse(split(/-/,$tmp[0])));
    	for (1..4) { shift @tmp };
    	unshift @tmp, $timestamp;
    	$dane[++$#dane] = \@tmp;
    	
    }
    
    #use Data::Dumper;
    #print Dumper \@dane;
    
    
    for my $kolumna (1..8) {
    	# lecimy kolumnami... 0 to timestamp, 1 do 8 to dane, 9 i 10 to ostatnie 2 pola
    	my $poprzednia = 0;
    	my $ostatnie_max_indeks = 0;
    	# profilaktycznie kasujemy stary plik z wynikami
    	unlink sprintf("max_%02d_%s", $kolumna, $INFILE);
    	# teraz jazda z danymi
    	for (my $wiersz = 0; $wiersz<=$#dane; $wiersz++) {
    		if ($poprzednia > $dane[$wiersz]) {
      	                open (OUT,">>$ARGV[0]_".$i."_max")||die"blad";
    			#open (OUT, ">>", sprintf("max_%02d_%s", $kolumna, $INFILE)) || die "Nie moge otworzyc pliku do zapisu: $!\n";
    			print OUT "$dane[$wiersz-1][9] $dane[$wiersz-1][0] ", $dane[$wiersz-1][0] - $dane[$ostatnie_max_indeks][0], "\n";
    			close (OUT);
    			$ostatnie_max_indeks = $wiersz-1;
    		}
    		$poprzednia = $dane[$wiersz];
    	}
    }
    foreach(`ls -1 $ARGV[0]_*_max`){
    s/\n//;
    $nazwa=$_;
    open (OUT,">$nazwa.gp")||die"blad"; #otwiera wyjsciowy plik gnuplota
    
    open (INPUT, "$nazwa.skala");
    $_=<INPUT>;
    s/\n//g;
    $RANGE=$_;
    close(INPUT);
    
    print OUT "set terminal png size 1800,1000\nset output \"$nazwa.png\" \nset yrange [0:$RANGE] \nplot \"$nazwa\" using 1:3\n"; #w gnuplocie - okreslenie formatu (png) i pliku wyjsciowego
    close(OUT);
    `gnuplot $nazwa.gp && rm -f $nazwa.gp`; #gnuplot tworzy wykresy
    }
    To jest całe programicho. Błąd pojawia się od 50. linii, przy zmiennej "$nazwa". Troszkę tam namieszałem ze zmienną domyślną, stało się to za bardzo skomplikowane (prościej mi nie wychodziło, już nie pamiętam dlaczego), a nie łapię się już jak można by to sprowadzić do jakiejś normalnej, działającej formy (i w tym miejscu też wychodzą umiejętności - a raczej ich brak, - minęło trochę czasu i gubię się we (prawie) własnym kodzie). Ten błąd to : "Global symbol (...) requires explicit package name at (...)" - nie za bardzo w ogóle to rozumiem, a tym bardziej nie wiem jak to się ma do tych linii (i w zasadzie skąd on się bierze).

    Jak będziesz miał chęci rzucić na to okiem i coś podpowiedzieć, to będę dźwięczny raz jeszcze.
    Oczywiście jak komuś jeszcze nasuną się jakieś myśli zarówno na temat tego kodu jak i ogólnie idei i wykonania programu, to nie pogardzę podpowiedziami.

  10. #10
    Zarejestrowany
    Jun 2006
    Skąd
    rand(.eu)
    Postów
    8,748

    Domyślnie

    Kod:
    $nazwa=$_;
    tu jest blad... powinno byc
    Kod:
    my $nazwa=$_;
    bo uzywany modulu 'strict' i on wymusza poprawne deklarowanie zmiennych ale za to daje nam bardzo wiele jak przychodzi do debugowania bledow
    ctrl-alt-del.cc - soft reset site for IT admins and other staff :-)

Podobne wątki

  1. [Perl] Kurs Perl - cz.1 - wprowadzenie i podstawy
    By TQM in forum Perl/Python/TCL/Prolog
    Odpowiedzi: 22
    Autor: 10-27-2014, 00:50
  2. Odpowiedzi: 18
    Autor: 07-14-2009, 11:55
  3. Odpowiedzi: 31
    Autor: 05-20-2008, 17:01
  4. Problem z Petla Perl
    By szpuni in forum Perl/Python/TCL/Prolog
    Odpowiedzi: 11
    Autor: 07-03-2006, 15:23

Zasady Postowania

  • Nie możesz zakładać nowych tematów
  • Nie możesz pisać wiadomości
  • Nie możesz dodawać załączników
  • Nie możesz edytować swoich postów
  •  
Subskrybuj