String und ZahlenProbleme, glaube ich

String und ZahlenProbleme, glaube ich

am 15.06.2006 07:18:30 von Karsten Moldau

Hallo NG,
habe folgenden auszug aus meinem kleinen Skript:

#!/usr/bin/perl

@zeilen =3D ("");
open(datei, " while()
{
push(@zeilen,$_);
}
close(datei);


$a=3D0;
for(@zeilen)
{
@spalti=3Dsplit(/;/,$zeilen[$a],4);


$zeit[$a]=3D$spalti[0];$tempK[$a]=3D$spalti[2];$tempL[$a]=3D $spalti[3];$a++;
}



for($i=3D0;$i<=3D$a;$i++)
{

if($tempK[$i+1]>$tempK[$i] and $al==0){
$maxmin[$o]=3D$tempK[$i+1];
$al=3D0;
}
}


Irgendwie kommt er bei mir in die letzte Schleife nicht rein, und ich
habe gar keine Ahnung, wie ich das lösen kann.
Ich lese aus einer Datei eben ein paar zahlen ein, die ich dann in der
letzten schleife vergleiche,


wäre für jede erdenkliche Hilfe dankbar.

mfg
Karsten

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 09:34:29 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 07:18):

> Hallo NG,
> habe folgenden auszug aus meinem kleinen Skript:

Wirklich "Dein" Skript? Oder bist Du
nur damit konfrontiert worden?

> for($i=0;$i<=$a;$i++)
> {
>
> if($tempK[$i+1]>$tempK[$i] and $al==0){
> $maxmin[$o]=$tempK[$i+1];
> $al=0;
> }
> }

Scheinbar hast Du dich vertippt, Du bist
mit dem Finger bei $maxmin[$i] auf
$maxmin[$o] gerutscht.

Weiterhin sehe ich nicht den Sinn,
einen (bis dahin) undefinierten Wert
($al) numerisch zu testen. Das geht schief.

> Irgendwie kommt er bei mir in die letzte Schleife nicht
> rein, und ich habe gar keine Ahnung, wie ich das lösen kann.
> Ich lese aus einer Datei eben ein paar zahlen ein, die
> ich dann in der letzten schleife vergleiche,

Wenn Du sowas öfters machst, wäre die
Investition in ein Perl-Buch anzuraten.

Aber wenn nicht, dann können wir Dir
hier vielleicht Dein Programm innerhalb
von 10 Minuten zu einem "richtig guten"
Programm machen ;-)

Wir bräuchten dazu das Format (Beispiel)
der Input-Daten...

Viele Grüße

Mirco

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 09:54:04 von Karsten Moldau

> Wir bräuchten dazu das Format (Beispiel)
> der Input-Daten...


So sieht das Programm eigentlich vollständgi aus:

#!/usr/bin/perl
@zeilen =3D ("");
open(datei, " while()
{
push(@zeilen,$_);
}
close(datei);


$a=3D0;
for(@zeilen)
{
@spalti=3Dsplit(/;/,$zeilen[$a],4);

$zeit[$a]=3D$spalti[0];$tempK[$a]=3D$spalti[2];$tempL[$a]=3D $spalti[3];$a++;
}

$al=3D"0";
if($tempK[2]>$tempK[1]){
$al=3D"0";
}

$o=3D0;
for($i=3D1;$i<=3D$a;$i++)
{

if($tempK[$i+1]>$tempK[$i] and $al eq "0"){
$maxmin[$o]=3D$tempK[$i+1];
$al=3D0;
}

if($tempK[$i+1]<$tempK[$i] and $al eq "0"){
$maxmin[$o]=3D$tempK[$i];
$o++;
$al=3D1;
}

if($tempK[$i+1] eq $tempK[$i] ){
$maxmin[$o]=3D$tempK[$i];
}

if($tempK[$i+1]<$tempK[$i] and $al eq "1"){
$maxmin[$o]=3D$tempK[$i];
}

if($tempK[$i+1]>$tempK[$i] and $al eq "1"){

$maxmin[$o]=3D$tempK[$i-1];
$o++;
$al=3D0;
}
}

open(lll, ">reinschreiber.txt");
for(@maxmin)
{
print lll "$_\n";
}
close(lll);




die Datei Temepraturverlauf1.csv sieht folgendermassen aus:





01.01.1999;1,6;4,25;1,73
01.01.1999 00:10;1,5;4,23;1,74
01.01.1999 00:20;1,41;4,21;1,76
01.01.1999 00:30;1,32;4,19;1,77
01.01.1999 00:40;1,24;4,17;1,78
01.01.1999 00:50;1,16;4,14;1,8
01.01.1999 01:00;1,1;4,12;1,81
01.01.1999 01:10;1,05;4,1;1,83
01.01.1999 01:20;1,01;4,08;1,84
01.01.1999 01:30;0,97;4,05;1,86
01.01.1999 01:40;0,93;4,03;1,87
01.01.1999 01:50;0,87;4,01;1,89
01.01.1999 02:00;0,8;3,98;1,9
01.01.1999 02:10;0,71;3,96;1,92
01.01.1999 02:20;0,6;3,94;1,93
01.01.1999 02:30;0,49;3,92;1,95
01.01.1999 02:40;0,38;3,89;1,97
01.01.1999 02:50;0,28;3,87;1,99
01.01.1999 03:00;0,2;3,85;2,01
01.01.1999 03:10;0,15;3,82;2,02
01.01.1999 03:20;0,11;3,8;2,04
01.01.1999 03:30;0,1;3,77;2,06
01.01.1999 03:40;0,09;3,75;2,08
01.01.1999 03:50;0,1;3,72;2,1
01.01.1999 04:00;0,1;3,7;2,11
01.01.1999 04:10;0,1;3,67;2,13
01.01.1999 04:20;0,11;3,65;2,15
01.01.1999 04:30;0,12;3,62;2,16
01.01.1999 04:40;0,15;3,6;2,18
01.01.1999 04:50;0,21;3,58;2,2
01.01.1999 05:00;0,3;3,55;2,22
01.01.1999 05:10;0,43;3,53;2,23
01.01.1999 05:20;0,58;3,51;2,25
01.01.1999 05:30;0,74;3,49;2,27
01.01.1999 05:40;0,89;3,47;2,29
01.01.1999 05:50;1,01;3,46;2,31
01.01.1999 06:00;1,1;3,44;2,33
01.01.1999 06:10;1,14;3,42;2,35
01.01.1999 06:20;1,13;3,41;2,37
01.01.1999 06:30;1,09;3,39;2,39
01.01.1999 06:40;1,03;3,38;2,4
01.01.1999 06:50;0,96;3,36;2,42
01.01.1999 07:00;0,9;3,34;2,43
01.01.1999 07:10;0,85;3,33;2,45
01.01.1999 07:20;0,83;3,31;2,46
01.01.1999 07:30;0,81;3,29;2,48
01.01.1999 07:40;0,8;3,28;2,49
01.01.1999 07:50;0,8;3,26;2,5
01.01.1999 08:00;0,8;3,24;2,52
01.01.1999 08:10;0,8;3,23;2,53
01.01.1999 08:20;0,82;3,21;2,55
01.01.1999 08:30;0,85;3,2;2,56
01.01.1999 08:40;0,9;3,18;2,58
01.01.1999 08:50;0,98;3,17;2,59
01.01.1999 09:00;1,1;3,16;2,6
01.01.1999 09:10;1,26;3,15;2,62
01.01.1999 09:20;1,45;3,15;2,64
01.01.1999 09:30;1,64;3,15;2,67
01.01.1999 09:40;1,83;3,15;2,69
01.01.1999 09:50;1,99;3,16;2,7
01.01.1999 10:00;2,1;3,16;2,71
01.01.1999 10:10;2,16;3,17;2,7
01.01.1999 10:20;2,17;3,17;2,69
01.01.1999 10:30;2,17;3,17;2,67
01.01.1999 10:40;2,18;3,18;2,65
01.01.1999 10:50;2,21;3,19;2,63
01.01.1999 11:00;2,3;3,2;2,62
01.01.1999 11:10;2,46;3,21;2,6
01.01.1999 11:20;2,67;3,22;2,58
01.01.1999 11:30;2,92;3,24;2,57
01.01.1999 11:40;3,19;3,25;2,55
01.01.1999 11:50;3,46;3,28;2,52
01.01.1999 12:00;3,7;3,3;2,5
01.01.1999 12:10;3,91;3,32;2,47
01.01.1999 12:20;4,08;3,35;2,43
01.01.1999 12:30;4,23;3,37;2,4
01.01.1999 12:40;4,36;3,4;2,36
01.01.1999 12:50;4,48;3,43;2,32
01.01.1999 13:00;4,6;3,46;2,28
01.01.1999 13:10;4,72;3,48;2,24
01.01.1999 13:20;4,83;3,51;2,2
01.01.1999 13:30;4,93;3,54;2,16
01.01.1999 13:40;5,02;3,57;2,12
01.01.1999 13:50;5,07;3,59;2,07
01.01.1999 14:00;5,1;3,62;2,03
01.01.1999 14:10;5,09;3,64;2
01.01.1999 14:20;5,04;3,66;1,96
01.01.1999 14:30;4,97;3,68;1,92
01.01.1999 14:40;4,86;3,69;1,89
01.01.1999 14:50;4,74;3,71;1,86
01.01.1999 15:00;4,6;3,72;1,83
01.01.1999 15:10;4,45;3,73;1,8
01.01.1999 15:20;4,28;3,73;1,78
01.01.1999 15:30;4,09;3,74;1,76



und jetzt die erklärung was das ganze eigentlich soll:

in dieser Datei temper..csv
steht an der dritten stelle eine temperatur. diese ist mit lokalen
Minima und maxima herauszulesen und in der datei reinschreiber.txt
auszugeben.

sprich:

bei folgender temperatur verteilung :

1 2 3 4 4 3 2 1 0 0 0 0 -1 -2 -3 -4 -4 -2 1 2 3 5 5 2

ist quasi das interessante für mich : 1 dann 4 dann -4 dann 5

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 11:05:58 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 09:54):

>> Wir bräuchten dazu das Format (Beispiel)
>> der Input-Daten...
> So sieht das Programm eigentlich vollständgi aus:
> die Datei Temepraturverlauf1.csv sieht folgendermassen aus:

OK, ich habe es versprochen, also muss ich es
auch machen ;-)

Ich hänge mal das Programm an, was aus Deinen
Daten die maxima/Minima generiert. Die eigentliche
Extremasuche liegt in diesem Block:

Die eingelesenen Temperaturen befinden
sich hier bereits in @t, und zwar auf
numerisches Format (mit Punkt) umgewandelt.

Es gibt eine Hilfsfunktion, die einen Zahlenvergleich
durchführt und 1, 0 oder -1 liefert, man braucht dann
nur zu scheuen, wo das wechselt.

push @ext, map { # bestimme vorzeichenwechsel
my $s1 = signdiff( $t[ $_-1 ], $t[ $_ ] );
my $s2 = signdiff( $t[ $_ ], $t[ $_+1 ] );
$s2 != $s1 ? $t[$_] : ();
} 1..$#t-1;

Wenn die Vorzeichendifferenzen unterschiedlich sind
--> $s2 != $s1 ? $t[$_] : ();
wird der Wert $t[$_] verwendet, ansonsten nicht "()".

Falls das Minimum/Maximum "flach ist, werden hier
beide "Eckwerte" der/des flachen Sohle/Sattels
angegeben (falls erwünscht).

Dein .csv liefert also:

4.25 <- erster Wert immer drin
3.15
3.15
3.16
3.16
3.17
3.17
3.73
3.73
<- letzer Wert nicht drin, laut Deiner Anweisung

Viele Grüße

Mirco

----------------------------------------

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

use constant ZEIT => 1; # csv column index
use constant TEMP_K => 2; # csv column index
use constant TEMP_L => 3; # csv column index

my $infile = shift || 'Temperaturverlauf1.csv';
my $outfile = shift || 'reinschreiber.txt';

my @temps = read_temp_from_csv( $infile );
my @maxmin = scan_t_extrema( @temps );

write_maxima( $outfile, @maxmin );

#-------------------------------------------------#

sub scan_t_extrema {
my (@t) = @_;
my @ext = $t[0];
push @ext, map { # bestimme vorzeichenwechsel
my $s1 = signdiff( $t[ $_-1 ], $t[ $_ ] );
my $s2 = signdiff( $t[ $_ ], $t[ $_+1 ] );
$s2 != $s1 ? $t[$_] : ();
} 1..$#t-1;
return @ext;
}

sub read_temp_from_csv {
open (my $datei, '<', shift) or die "error open for reading, $!";

my @tlist = map { my
$tk = (split /;/, $_)[TEMP_K]; # read TEMP_K
$tk =~ tr/,/./; # change , to .
$tk
} <$datei>;
close $datei;
return @tlist;
}

sub write_maxima {
my($fname, @maxima) = @_;

open (my $datei, '>', $fname) or die "error open for writing, $!";
print $datei "$_\n" for @maxima;
close $datei;
}

sub signdiff {
my ($v1, $v2) = @_;
return 0 if $v1 == $v2;
return 1 if $v1 < $v2;
return -1;
}
__END__

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 11:42:40 von Mirco Wahab

Thus spoke Mirco Wahab (on 2006-06-15 11:05):

> Thus spoke Karsten Moldau (on 2006-06-15 09:54):
>>> Wir bräuchten dazu das Format (Beispiel)
>>> der Input-Daten...
>> So sieht das Programm eigentlich vollständgi aus:
>> die Datei Temepraturverlauf1.csv sieht folgendermassen aus:

Ich habe das Problem der flachen Minima/Maxima gelöst, in dem ich
einfach bei Delta-Werten von Null diese Punkte zum vorherigen
Anstiegsbereich zähle. Jetzt wurde man also den Anstiegs-
Vorzeichenwechsel /nach/ Talsohle/Sattel detektieren.

Dazu änderte ich die Funktion signdiff{). Dei csv hat jetzt
nur noch ein "Minimum" (bei 3.15) und eben den "Anfangswert".

Viele Grüße

Mirco


==> nochmal der ganze Kram, damit nichts durcheinanderkommt ;-)

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

use constant ZEIT => 0; # csv column index
use constant TEMP_K => 2; # csv column index
use constant TEMP_L => 3; # csv column index

my $infile = shift || 'Temperaturverlauf1.csv';
my $outfile = shift || 'reinschreiber.txt';

my @temps = read_temp_from_csv( $infile );
my @maxmin = scan_t_extrema( @temps );

write_maxima( $outfile, @maxmin );

#-------------------------------------------------#

sub scan_t_extrema {
my (@t) = @_;
my @ext = $t[0];
push @ext, map { # bestimme vorzeichenwechsel
my $s1 = signdiff( $t[ $_-1 ], $t[ $_ ] );
my $s2 = signdiff( $t[ $_ ], $t[ $_+1 ] );
$s2 != $s1 ? $t[$_] : ();
} 1..$#t-1;
return @ext;
}

sub read_temp_from_csv {
open (my $datei, '<', shift) or die "error open for reading, $!";
my @tlist = map { my
$tk = (split /;/, $_)[TEMP_K]; # read TEMP_K
$tk =~ tr/,/./; # change , to .
$tk
} <$datei>;
close $datei;
return @tlist;
}

sub write_maxima {
my($fname, @maxima) = @_;
open (my $datei, '>', $fname) or die "error open for writing, $!";
print $datei "$_\n" for @maxima;
close $datei;
}

my $last_diff = 0; # ignoriere Wechsel zu 0-Anstieg
sub signdiff {
my ($v1, $v2) = @_;
if ($v1 < $v2) { return $last_diff = 1; }
if ($v1 > $v2) { return $last_diff = -1; }
return $last_diff;
}
__END__

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 13:56:39 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 07:18):

> Irgendwie kommt er bei mir in die letzte Schleife nicht rein, und ich
> habe gar keine Ahnung, wie ich das lösen kann.
> Ich lese aus einer Datei eben ein paar zahlen ein, die ich dann in der
> letzten schleife vergleiche,

Ich hab mir jetzt nach dem
Mittagessen nochmal Dein altes
Programm angesehen und denke,
dass es überhaupt nicht gehen
kann (Hauptgrund) , wenn Du aus
..csv-Files mit Komma-Dezimaltrenner
einliest ("deutsche" Standardein-
stellung).

Dadurch macht Perl ständig
numerische Vergliche auf
string-Werten "3,5", "3,6"
usw, was nicht klappen kann.

Das Programm ist kurz genug,
um es einfach neu zu schreiben -
daher habe ich Dir erstmal meine
"Variante aufgedrückt" (ich hoffe,
Du verübelst mir das nicht ;-)

Übrigens, die Anstiegs-Wechsel-
bestimmung würdde auch richtig
"Perlisch" wahrscheinlich so aussehen:

sub scan_temp_extrema {
...
push @ext, map $t[$_],
grep DChng( @t[$_-1..$_+1] ),
1 .. @t-2;
...
}

D.h. eine Liste von Ziffern 1 bis N-2
wird durch grep geschickt, grep prüft
über die Funktion DChng() mit
einem 3-Element-Slice aus @t, ob
ein Anstiegswechsel stattfand
und signalisiert über 0 oder 1
an map, dass es diesen Wert aus @t
an die Ergebnisliste schicken soll
(oder nicht, bei 0).

Das nennt sich iirc 'decorate/undecorate check'.

Die D_Chng()-Funktion macht nun nichts
weiter als die bekannte signdiff()-Fkt.
über diese 3 Werte aufzurufen

sub DChng { # teste Anstieg-Aenderung innerhalb der 3 Werte
my @slc = @_; # slice -1 .. +1 holen, @slc[0,1,2]
return signdiff($slc [0], $slc [1]) - signdiff($slc[1], $slc[2]);
}

Also für mich war das ein sehr interessantes
Beispiel und die Möglichkeit, Perl auf einem
Gebiet anzuwenden, dass so ähnlich ist wie mein
eigenes ;-)

Viele Grüße

Mirco

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 15:17:20 von Karsten Moldau

Hallo Mirco,
erstmal vieln vieen dank für deine Hilfe.
Ich muss gestehen, dass es wichtig für mich ist, dass meine Version
läuft, womit ich nichts gegen deine sicher elegantere Version gesagt
haben will.
Aber bei deinen Versionen verstehe ich ehrlichgesagt garnichts, was
natürlich nur an mir liegt, das wäre so eigentlich mein erstes
Program gewesen in Perl.
Theoretisch bräuchte ich nur eine Hilfestellung, warum mein Programm
nicht läuft, wie ich es will.

> Dadurch macht Perl ständig
> numerische Vergliche auf
> string-Werten "3,5", "3,6"
> usw, was nicht klappen kann.

gibt es nicht eine Möglichkeit, mit dem ich einfach aus string Werten
numerische Werte machen kann?


> Die D_Chng()-Funktion macht nun nichts
> weiter als die bekannte signdiff()-Fkt.
> über diese 3 Werte aufzurufen

Es kann aber sein, dass 3 Werte nicht ausreichen, oder nicht?
zB. 1 2 3 4 4 4 4 4 4 4 3 oder 1 2 3 2 1 1 1 1 1 1 1 1 0 -1 0 1 1 0



>
> sub DChng { # teste Anstieg-Aenderung innerhalb der 3 Werte
> my @slc =3D @_; # slice -1 .. +1 holen, @slc[0,1,2]
> return signdiff($slc [0], $slc [1]) - signdiff($slc[1], $slc[2]);
> }
>
> Also für mich war das ein sehr interessantes
> Beispiel und die Möglichkeit, Perl auf einem
> Gebiet anzuwenden, dass so ähnlich ist wie mein
> eigenes ;-)




vielen dank nocheinmal für deine Mühe

Karsten

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 15:48:00 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 15:17):
Hallo Karsten

> Ich muss gestehen, dass es wichtig für mich ist, dass meine Version
> läuft, womit ich nichts gegen deine sicher elegantere Version gesagt
> haben will.

OK. Ich habe Deine version bereinigt, die
Umwandlung von, nach . durchgeführt und
an diesen Beitrag angehängt. Du hattest
noch ein paar Index-Fehler beim Feldzu-
griff (for($i=1; $i<=$na; $i++)) usw.

> das wäre so eigentlich mein erstes
> Program gewesen in Perl.

OK, sorry, das sehe ich ein.

> Theoretisch bräuchte ich nur eine Hilfestellung,
> warum mein Programm nicht läuft, wie ich es will.

OK. Also, immer mit
use warnings;
use strict;

anfangen, dann bemeckert
Perl schon einige Dinge,
die verkehrt sind.

Weiterhin hast du numerische
Vergleiche (<, >, ==) mit
String-Vergleichen (eq, lt, gt)
wild durcheinander gemischt.

> gibt es nicht eine Möglichkeit, mit dem ich einfach
> aus string Werten numerische Werte machen kann?
"Duck typing", wenn Du einen "String" numerisch
verwendest - und Perl ihn umwandeln _kann_, wandelt
es ihn auch (richtig) um.

>> Die D_Chng()-Funktion macht nun nichts
>> weiter als die bekannte signdiff()-Fkt.
>> über diese 3 Werte aufzurufen
>
> Es kann aber sein, dass 3 Werte nicht ausreichen, oder nicht?
> z.B. 1 2 3 4 4 4 4 4 4 4 3 oder 1 2 3 2 1 1 1 1 1 1 1 1 0 -1 0 1 1 0

Doch, die reichen aus, da ja via

my $last_diff = 0; # ignoriere Wechsel zu 0-Anstieg
sub signdiff {
my ($v1, $v2) = @_;
if ($v1 < $v2) { return $last_diff = 1; }
if ($v1 > $v2) { return $last_diff = -1; }
return $last_diff;
}

in $last_diff der letzte "Wechsel" gemerkt wird,
egal wie viele "gleiche" dazwischenliegen.

Viele grüße & viel Erfolg

Mirco


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

my @zeilen = ();
open(DATEI, " push @zeilen, ; # identisch mit while <..>
close DATEI;

my $na = 0; # NEVER EVER $a und $b "einfach so" benutzen!
my (@spalti, @zeit, @tempK, @tempL, @maxmin);
for (@zeilen) {
@spalti = split /;/, $zeilen[$na], 4;
$zeit [$na] = $spalti[0];
($tempK[$na] = $spalti[2]) =~ tr/,/./; # hier , nach . wandeln
$tempL[$na] = $spalti[3];
$na++;
}

my $al=0;
if( $tempK[2] > $tempK[1] ) {
$al = 0;
}

my $o=0;
for(my $i=0; $i<$na-1; $i++) {

if( $tempK[$i+1] > $tempK[$i] and $al == 0 ) {
$maxmin[$o] = $tempK[$i+1];
$al = 0;
}

if( $tempK[$i+1] < $tempK[$i] and $al == 0 ) {
$maxmin[$o] = $tempK[$i];
$o++;
$al = 1;
}

if( $tempK[$i+1] == $tempK[$i] ) {
$maxmin[$o] = $tempK[$i];
}

if( $tempK[$i+1] < $tempK[$i] and $al == 1 ) {
$maxmin[$o] = $tempK[$i];
}

if( $tempK[$i+1] > $tempK[$i] and $al == 1 ) {
$maxmin[$o] = $tempK[$i-1];
$o++;
$al = 0;
}
}

open (LLL, ">reinschreiber.txt");
for (@maxmin) {
print LLL "$_\n";
}
close LLL;

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 16:15:03 von Karsten Moldau

Vielen dank Mirco,
das Ding läuft, und sogar richtig, soweit ich es geschafft habe bei
520000 Daten etwas zu kontrolliern.

Das ist echt super, ich kann deine Version sogar lesen ;-)

ich hatte meine Version ein wenig von Java(ca. 6 Jahre her) und von
Selfhtml zusammengewürfelt.



danke nocheinmal

Karsten

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 17:15:04 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 16:15):

> das Ding läuft, und sogar richtig, soweit ich es geschafft habe bei
> 520000 Daten etwas zu kontrolliern.
> ich hatte meine Version ein wenig von Java(ca. 6 Jahre her) und von
> Selfhtml zusammengewürfelt.

Dafür ist Deine Version doch ziemlich gut
geworden - und war nur mit wenigen Ecken,
die leicht zu beseitigen waren, versehen.

Du sagst: >500,000 Werte? Wow! Warte mal
Wie lange läuft das Skript bei sowas?

Ich habe mal meine Version voll-
stängig von Feldern auf Feld-
referenzen umgestellt, würde das
in der Laufzeit einen Unterschied
machen? Ich würde denken - ja.
(Aber wissen tu ich es nicht).


(==> Anhang, "Version mit Referenzen")

Viele Grüße

Mirco

==>
#!/usr/bin/perl
use strict;
use warnings;
use constant TEMP_K => 2; # csv column index

my (@Temp, @Extr); # only used as references here
my $infile = shift || 'Temperaturverlauf1.csv';
my $outfile = shift || 'reinschreiber.txt';

read_temp_from_csv( $infile, \@Temp ) and print "temp reading done\n";
scan_temp_extrema( \@Temp, \@Extr ) and print "extrema check done\n";
write_maxima( $outfile, \@Extr ) and print "all [0-$#Temp] done\nOK\n";

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #

sub scan_temp_extrema {
my($rt, $re)= @_; # [r]eferenz zu [t]emperatur, [e]xtremwert
push @$re, $rt->[0]; # add ersten[0] @t-Wert
push @$re, map { $rt->[$_] }
grep { SlopeChg( @$rt[$_-1 .. $_+1] ) }
1 .. @$rt-2;
push @$re, $rt->[-1]; # add letzten[-1] @t-Wert
return scalar @$re;
}

sub read_temp_from_csv {
my($fname, $rt) = @_; # [r]eferenz zu [t]emperatur
open (my $datei, '<', $fname) or die "error open for reading, $!";
@$rt = map { my
$tk = (split /;/)[TEMP_K]; # read TEMP_K
$tk =~ tr/,/./; # change , to .
$tk; # return value from map{}
} <$datei>;
close $datei;
return scalar @$rt; # liefere Anzahl zurueck
}

sub write_maxima {
my($fname, $re) = @_; # [r]eferenz zu [e]xtremwert
open (my $datei, '>', $fname) or die "error open for writing, $!";
print $datei "$_\n" for @$re;
close $datei;
}

sub SlopeChg { # bestimme Anstieg-Aenderung innerhalb der 3 Werte
my @tv = @_; # slice -1 .. +1 holen, @slc[0,1,2]
return signdiff($tv[0], $tv[1]) - signdiff($tv[1], $tv[2]);
} # liefere 0 oder 1 (bei Anstiegsaenderung)

my $last_diff = 0; # ignoriere Wechsel zu Anstieg delta==0
sub signdiff {
my ($v1, $v2) = @_;
if ($v1 < $v2) { return $last_diff = 1; }
if ($v1 > $v2) { return $last_diff = -1; }
return $last_diff; # falls delta==0, liefere vorhergehenden Anstieg
}

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 17:25:53 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 16:15):

> das Ding läuft, und sogar richtig, soweit ich es geschafft habe bei
> 520000 Daten etwas zu kontrolliern.
> ich hatte meine Version ein wenig von Java(ca. 6 Jahre her) und von
> Selfhtml zusammengewürfelt.

Dafür ist Deine Version doch ziemlich gut
geworden - und war nur mit wenigen Ecken,
die leicht zu beseitigen waren, versehen.

Du sagst: >500,000 Werte? Wow! Warte mal
Wie lange läuft das Skript bei sowas?

Ich habe mal meine Version voll-
stängig von Feldern auf Feld-
referenzen umgestellt, würde das
in der Laufzeit einen Unterschied
machen? Ich würde denken - ja.
(Aber wissen tu ich es nicht).

(==> Anhang, "Version mit Referenzen")

Viele Grüße

Mirco

==>
#!/usr/bin/perl
use strict;
use warnings;
use constant TEMP_K => 2; # csv column index

my (@Temp, @Extr); # only used as references here
my $infile = shift || 'Temperaturverlauf1.csv';
my $outfile = shift || 'reinschreiber.txt';

read_temp_from_csv( $infile, \@Temp ) and print "temp reading done\n";
scan_temp_extrema( \@Temp, \@Extr ) and print "extrema check done\n";
write_maxima( $outfile, \@Extr ) and print "all [0-$#Temp] done\nOK\n";

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #

sub scan_temp_extrema {
my($rt, $re)= @_; # [r]eferenz zu [t]emperatur, [e]xtremwert
push @$re, $rt->[0]; # add ersten[0] @t-Wert
push @$re, map { $rt->[$_] }
grep { SlopeChg( @$rt[$_-1 .. $_+1] ) }
1 .. @$rt-2;
push @$re, $rt->[-1]; # add letzten[-1] @t-Wert
return scalar @$re;
}

sub read_temp_from_csv {
my($fname, $rt) = @_; # [r]eferenz zu [t]emperatur
open (my $datei, '<', $fname) or die "error open for reading, $!";
@$rt = map { my
$tk = (split /;/)[TEMP_K]; # read TEMP_K
$tk =~ tr/,/./; # change , to .
$tk; # return value from map{}
} <$datei>;
close $datei;
return scalar @$rt; # liefere Anzahl zurueck
}

sub write_maxima {
my($fname, $re) = @_; # [r]eferenz zu [e]xtremwert
open (my $datei, '>', $fname) or die "error open for writing, $!";
print $datei "$_\n" for @$re;
close $datei;
}

sub SlopeChg { # bestimme Anstieg-Aenderung innerhalb der 3 Werte
return signdiff($_[0], $_[1]) - signdiff($_[1], $_[2]);
} # liefere 0 oder 1 (bei Anstiegsaenderung)

my $last_diff = 0; # ignoriere Wechsel zu Anstieg delta==0
sub signdiff {
if ($_[0] < $_[1]) { return $last_diff = 1; }
if ($_[0] > $_[1]) { return $last_diff = -1; }
return $last_diff; # falls delta==0, liefere vorhergehenden Anstieg
}

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 17:31:50 von Karsten Moldau

> > das Ding läuft, und sogar richtig, soweit ich es geschafft habe bei
> > 520000 Daten etwas zu kontrolliern.
> > ich hatte meine Version ein wenig von Java(ca. 6 Jahre her) und von
> > Selfhtml zusammengewürfelt.

The Mistake devil hat sich eingeschlichen eine Null zuviel!

Kann ich eigentlich die Einzelnen Elemente vom Vektor @maxmin
untereinader addieren uns subtrahieren?

mfg
KArsten

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 18:02:52 von Wolf Behrenhoff

Karsten Moldau schrieb:
> und jetzt die erklärung was das ganze eigentlich soll:
>
> in dieser Datei temper..csv
> steht an der dritten stelle eine temperatur. diese ist mit lokalen
> Minima und maxima herauszulesen und in der datei reinschreiber.txt
> auszugeben.

Was ich an den bisher geposteten Lösungen überhaupt nicht verstehe, ist
der Grund, warum die Datei überhaupt zunächst ganz eingelesen werden
muss (besonders, wo jetzt gesagt wird, dass mehrere hunderttausend
Datensätze zu bearbeiten sind). Man braucht sich doch immer nur die
vorherige Zahl und die Richtung zu merken.

Also zum Beispiel so (nur, dass die sub "get" angepasst werden muss, zum
Beispiel, durch ein Einlesen aus einer Datei)

use strict;
use warnings;

my @zahlen = qw(5 6 7 7 7 8 7 6 7 8 0 9 8 7 1 1 2);
sub get { shift @zahlen }

#zum Anfang "falschrum" einlesen, damit man sich ein $last=$new spart
my $new = get(); print "$new ";
my $last = get();
my $steigend = $last - $new > 0 ? 1:0;

while (defined($new=get())) {
if ($steigend) {
if ($new - $last < 0) {
print "$last ";
$steigend = 0;
}
} else {
if ($new - $last > 0) {
print "$last ";
$steigend = 1;
}
}
$last=$new;
}

Wolf

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 21:19:03 von Mirco Wahab

Thus spoke Wolf Behrenhoff (on 2006-06-15 18:02):

> Was ich an den bisher geposteten Lösungen überhaupt nicht verstehe, ist
> der Grund, warum die Datei überhaupt zunächst ganz eingelesen werden
> muss (besonders, wo jetzt gesagt wird, dass mehrere hunderttausend
> Datensätze zu bearbeiten sind). Man braucht sich doch immer nur die
> vorherige Zahl und die Richtung zu merken.

Hmmmmmm, vielleicht weil das, meiner Meinung nach ...

- nicht das Problem ist (File-IO zu Speicher ist
"heutzutage" bis 100MB od. 200MB imho kein Thema),

- den Vorteil der klaren logschen Gliederung
im Programm mit sich bringt, auch wenn man
die Daten nur sequentiell braucht,

- gar nicht anders geht, wenn man Interpedenzen
innerhalb der Daten betrachten will (wobei der
geschilderte Fall imho "an der Grenze" liegt).

> Also zum Beispiel so (nur, dass die sub "get" angepasst
> werden muss, zum Beispiel, durch ein Einlesen aus einer Datei)
>
> use strict;
> use warnings;
>
> my @zahlen = qw(5 6 7 7 7 8 7 6 7 8 0 9 8 7 1 1 2);
> sub get { shift @zahlen }

Machen wir doch folgendes. Wir konstruieren uns einen
adäquaten Test-Set, deren "Lösung" wir kennen, sagen
wir 100x 2pi-Durchläufe einer sin()-Funktion (also
200 Extremwerte, jeweils 1,-1...), wobei jeder 2pi-
Sektor 10000 Punkte bekommt.

Das sind 1,000,000 Werte - dazu noch ein
paar andere Spalten, um das OP-Problem zu
treffen (ergibt bei mir eine 35.3 MB csv-Datei).

Anstatt Fussbad zu gucken, setzen wir uns hin
und schreiben ein kleines Programm, dass diese
200 Extrema möglichst schnell findet.

(Mein zuletzt gepostetes Programm braucht mind. 3 min,
also viel zu langsam - das Einlesen selbst dauert
aber ca. 4 Sekunden).

Morgen treffen wir uns wieder hier und schauen mal.
(Finde ich interessant und praxisrelevant.)

Viele Grüße

Mirco

==> testset.pl
#!/usr/bin/perl
use strict;
use warnings;
use Math::Trig;

my $fname = shift || 'sintab.csv';
open (my $fh, '>', $fname) or die "can't open $!";

my ($n_seq, $per_seq, $format) = (100, 10000/(2*pi), "%s;%d;%g;%g\n");

printf "=> sin tab: %d blocks a %d entries\n", $n_seq, $per_seq*(2*pi);
print $fh map {
sprintf $format, time, $_, sin($_/$per_seq), $_/$per_seq;
} 0 .. $per_seq*$n_seq*(2*pi);
close $fh;

printf "done (N=%d)\n", $per_seq*$n_seq*(2*pi);
__END__

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 21:21:02 von Mirco Wahab

Thus spoke Karsten Moldau (on 2006-06-15 17:31):

>>[520,000]
>
> The Mistake devil hat sich eingeschlichen eine Null zuviel!

OK, wäre trotzdem im Bereich des Machbaren ...

> Kann ich eigentlich die Einzelnen Elemente vom Vektor @maxmin
> untereinader addieren uns subtrahieren?

Wie meinst Du das genau?

$maxmin[i] = $maxmin[j] + $maxmin[k];

oder sowas?



Viele Grüße

Mirco

Re: String und ZahlenProbleme, glaube ich

am 15.06.2006 23:25:44 von Wolf Behrenhoff

Mirco Wahab schrieb:
> Thus spoke Wolf Behrenhoff (on 2006-06-15 18:02):
>
>> Was ich an den bisher geposteten Lösungen überhaupt nicht verstehe, ist
>> der Grund, warum die Datei überhaupt zunächst ganz eingelesen werden
>> muss (besonders, wo jetzt gesagt wird, dass mehrere hunderttausend
>> Datensätze zu bearbeiten sind). Man braucht sich doch immer nur die
>> vorherige Zahl und die Richtung zu merken.
>
> Hmmmmmm, vielleicht weil das, meiner Meinung nach ...
>
> - nicht das Problem ist (File-IO zu Speicher ist
> "heutzutage" bis 100MB od. 200MB imho kein Thema),

Doch, das ist ein Thema. Vielleicht nicht auf einem Einzelplatzrechner,
wo man gerade nichts anderes zu tun hat. Aber wenn man das Programm mit
konstantem Speicherbedarf schreiben kann, warum sollte man dann O(n)
Speicherbedarf wollen?

> - den Vorteil der klaren logschen Gliederung
> im Programm mit sich bringt, auch wenn man
> die Daten nur sequentiell braucht,

Ich sehe kein Problem dabei, da geht nichts kaputt. Ich betrachte das
ganze ja als Stream und habe eine Funktion get, die auch
getNaechstesElement heißen könnte.

> - gar nicht anders geht, wenn man Interpedenzen
> innerhalb der Daten betrachten will (wobei der
> geschilderte Fall imho "an der Grenze" liegt).

Hier liegt nichts an der Grenze - dies ist eindeutig ein Beispiel, wo
man nicht alles auf einmal einlesen sollte.

>> Also zum Beispiel so (nur, dass die sub "get" angepasst
>> werden muss, zum Beispiel, durch ein Einlesen aus einer Datei)
>
> Machen wir doch folgendes. Wir konstruieren uns einen
> adäquaten Test-Set, deren "Lösung" wir kennen, sagen
> wir 100x 2pi-Durchläufe einer sin()-Funktion (also
> 200 Extremwerte, jeweils 1,-1...), wobei jeder 2pi-
> Sektor 10000 Punkte bekommt.
>
> Morgen treffen wir uns wieder hier und schauen mal.
> (Finde ich interessant und praxisrelevant.)

Also - das ist doch nun wirklich kein Problem, mein Programm anzupassen!
Du musst nur die Zeile mit @zahlen = ... löschen und die Funktion get
durch folgendes ersetzen:

sub get { (split /;/,<>)[2] }

Fertig. Aufruf:
perl temp.pl < sintab.csv
Dauert bei mir 10 Sekunden, Speicherverbrauch: 2 MB.

Wolf

Re: String und ZahlenProbleme, glaube ich

am 16.06.2006 01:15:35 von Wolf Behrenhoff

Wolf Behrenhoff schrieb:

[Mein Script]
> Dauert bei mir 10 Sekunden, Speicherverbrauch: 2 MB.

Nachtrag:
Dein Script aus braucht bei mir
nicht nur über 300 MB RAM, sondern auch 8:45! Das ist der 150-fache
Speicherverbrauch und die 52-fache Zeit verglichen mit meinem Script!

Ein kleiner weiterer Unterschied: irgendwo im Thread hattest du oder der
OP gesagt, dass die letzte Zahl nicht dazugehört, die erste aber schon.
Das ist bei mir der Fall, bei dir wird dagegen auch die letzte Zahl
ausgegeben. (OK, das ist eine Kleinigkeit.)

Wolf

Re: String und ZahlenProbleme, glaube ich

am 16.06.2006 09:58:40 von Mirco Wahab

Thus spoke Wolf Behrenhoff (on 2006-06-16 01:15):

Guten Morgen,

> Dein Script aus braucht bei mir
> nicht nur über 300 MB RAM, sondern auch 8:45! Das ist der 150-fache
> Speicherverbrauch und die 52-fache Zeit verglichen mit meinem Script!

OK, ich hab mal hier eben auf einem Athlon 2500 getestet
und komme auf folgende Werte:

- - - - - - - - - - - - - - - - - - - - - - - - -
$> uname -a
Linux (null) 2.6.16.13-4-default #1 Wed May 3 04:53:23 UTC 2006 i686 athlon i386 GNU/Linux

$> cat /proc/cpuinfo | grep AMD
vendor_id : AuthenticAMD
model name : AMD Athlon(tm) XP 2500+

- - - - - - - - - - - - - - - - - - - - - - - - -
1) mirco-old (read to array)

$> time perl temp-mw.pl sintab.csv

real 0m13.102s
user 0m12.497s
sys 0m0.532s

(top/virt: ca. 230MB)

- - - - - - - - - - - - - - - - - - - - - - - - -
2) op-original (read to array)

$> time perl temp-op.pl sintab.csv

real 0m14.426s
user 0m13.885s
sys 0m0.516s

(top/virt: ca. 230MB)

- - - - - - - - - - - - - - - - - - - - - - - - -
3) wolf (read/process by line)

$> time perl temp-wb.pl < sintab.csv > out.txt

real 0m5.368s
user 0m5.280s
sys 0m0.084s

(top/virt: ca. 4,5MB)

- - - - - - - - - - - - - - - - - - - - - - - - -
4) mirco-new (slurp + regex)

$> time perl temp-rg.pl < sintab.csv > out.txt

real 0m6.956s
user 0m6.760s
sys 0m0.184s

(top/virt: ca. 73MB)

- - - - - - - - - - - - - - - - - - - - - - - - -

> Ein kleiner weiterer Unterschied: irgendwo im Thread hattest du oder der
> OP gesagt, dass die letzte Zahl nicht dazugehört, die erste aber schon.
> Das ist bei mir der Fall, bei dir wird dagegen auch die letzte Zahl
> ausgegeben. (OK, das ist eine Kleinigkeit.)

Warte mal, das konnte man doch bei so einem "overdesigned"-tem
Programm wie meinem "explizit" einstellen:
...
sub scan_temp_extrema {
push @$re, $rt->[0]; # add ersten[0] @t-Wert
push @$re, map { $rt->[$_] }
...
push @$re, $rt->[-1]; # add letzten[-1] @t-Wert
...


Aber als vorläufiges Resúme:

Dein Algorithmus, die "running direction" on the fly
beim Einlesen mitzuschneiden ist schon sehr gut und
in diesem Falle sicher die optimale Lösung. Ich sehe
das ein ;-)

Die Frage ist nur, braucht man weitere Interpedenzen
innerhalb der Daten oder nicht - wie ich schon mit
meinem Hinweis "liegt an der Grenze" festgestellt
haben wollte (an der Grenze liegt noch "innerhalb"
der Grenze).

Ich habe Deine Idee mit dem Direction-change aufgegriffen
und in eine Regex(!) als code-Assertion eingebaut, die
ich über den 'geslurpten' Datensatz schicke.

Hier bei mir ist das bei besagtem Datensatz von 10^6 Zeilen
auch bereits in 6,9 sek durch (kommt also nicht an Deine
5,3 sek ran).

Trotzdem vielen Dank an die Beteiligten, hat mir sehr viel
gebracht (musste einige Ansichten revidieren)

Viele Grüße

Mirco

==> (slurp + regex)

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

my ($upw, $old) = (0, 0); # trickse rum wegen startwert
my $rg = qr/ [^;]+?; [^;]+?; ([^;]+?); # zeit;???;temp_k;(temp_l)\n
(?{
if( $upw && $1-$old < 0 or !$upw && $1-$old > 0 ) {
$upw = 1-$upw; # richtung gewechselt!
print "$old\n"; # letzten wert ausgeben
}
$old = $1;
}) /x; # regex fuer suche vorbereiten

$_ = do { local $/; <> }; # gesamte datei "slurpen" => $_
1 while /$rg/g; # regex anwenden