Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Moving Average (https://www.delphipraxis.net/119366-moving-average.html)

fl63 25. Aug 2008 14:34


Moving Average
 
Hallo DPler


Heute eine Frage an alle Mathematiker unter Euch.
Wie Programmiert man einen laufenden Durchschnitt:
zB. Durchschitt aus drei Werten erster Durchschitt //kein Problem
Durchschnitt aus dem 2. 3. und 4.Wert zweiter Durchschnitt
Durchschnitt aus dem 3. 4. und 5.Wert

usw.
Also unten einen Wert hinzu und oben einen Fallenlassen.
Soweit kein Problem aber wie programmiere ich soetwas in einem Stringgrid??

angos 25. Aug 2008 15:30

Re: Moving Average
 
hi,

scheint mir momentan nicht so die mathematische Hürde zu sein. Woran hapert es denn genau?

marabu 25. Aug 2008 17:36

Re: Moving Average
 
Hallo,

da die Zahlen im StringGrid als Zeichenketten vorliegen, musst du sie erst konvertieren:

Delphi-Quellcode:
uses
  Math,
  Types;

procedure DataToStrings(data: TDoubleDynArray; strings: TStrings; const fmt: string = '%f');
var
  i: Integer;
begin
  strings.Clear;
  for i := Low(data) to High(data) do
    strings.Add(Format(fmt, [Data[i]]));
end;

function StringsToData(strings: TStrings; default: Double = 0): TDoubleDynArray;
var
  i: Integer;
begin
  SetLength(Result, strings.Count);
   for i := Low(Result) to High(Result) do
     Result[i] := StrToFloatDef(strings[i], default);
end;
Als Parameter strings kannst du z.B. StringGrid.Rows[iRow] oder StringGrid.Cols[iCol] übergeben.

Die Berechnung des gleitenden Durchschnitts ist dann wahrscheinlich nicht mehr dein Problem.
Delphi-Quellcode:
uses
  Math,
  Types;

function SimpleMovingAverage(data: TDoubleDynArray; n: Word): TDoubleDynArray;
var
  i: Integer;
  temp: TDoubleDynArray;
begin
  SetLength(Result, Length(data));
  for i := Low(data) to High(data) do
  begin
    if i < n
      then temp := Copy(data, 0, Succ(i)) // was: i (off by one)
      else temp := Copy(data, Succ(i - n), n);
    Result[i] := Mean(temp);
  end;
end;
Getippt und nicht getestet.

Grüße vom marabu

grenzgaenger 25. Aug 2008 21:52

Re: Moving Average
 
Zitat:

Zitat von fl63
Hallo DPler


Heute eine Frage an alle Mathematiker unter Euch.
Wie Programmiert man einen laufenden Durchschnitt:
zB. Durchschitt aus drei Werten erster Durchschitt //kein Problem
Durchschnitt aus dem 2. 3. und 4.Wert zweiter Durchschnitt
Durchschnitt aus dem 3. 4. und 5.Wert

usw.
Also unten einen Wert hinzu und oben einen Fallenlassen.
Soweit kein Problem aber wie programmiere ich soetwas in einem Stringgrid??

und wo liegt das problem? :glaskugel:


zeig doch mal her, was du bereits hast, und wo es hackt ...

fl63 26. Aug 2008 11:24

Re: Moving Average
 
Ich tippe mal auf ein Mehrschleifenproblem wobei die 2.Schleife das Problem darstellt.

Delphi-Quellcode:
procedure TForm1.Button6Click(Sender: TObject);
Var i,k,Summe :Integer;
  Durchschnitt:Extended;
begin
  Summe := 0;
    For i := 0 to SG1.RowCount-1 do
       begin
           k := 0;
           For k := k - (StrToInt(Edit2.Text)) to (SG1.RowCount-1 - StrToInt(Edit2.Text)) do
             begin
             If k <= 0 do
               Summe := Summe + StrToInt(SG1.Cells[1,k]);
             If k = 0 do
               Summe := Summe + StrToInt(SG1.Cells[1,k]);
             If k >= 0 do
               Summe := Summe + StrToInt(SG1.Cells[1,k]);

             if k = StrToInt(Edit2.Text) then break;
             end;
           Durchschnitt := Summe / StrToInt(Edit2.Text);
           SG1.Cells[3,k] := FloatToStr(Durchschnitt);
       end;
end;
i : ist die Laufvariable für das Stringgrid
k : Edit2.Text Die Glättungsvariable(Durchschnitt) je höher desto geringer der Einfluß der einzelnen Meßgröße.

Die Konvertierung erfolgt doch durch InttoStr, FloattoStr usw.

angos 26. Aug 2008 12:42

Re: Moving Average
 
Hi,

schau dir das mal an. musst du dann nur noch für dich abändern

Delphi-Quellcode:
var
  ir, ic, i, iDurchschnitt: Integer;
  val1, val2, val3: String;
begin

  for ir := 0 to grd.RowCount - 1 do
  begin
    for ic := 0 to grd.RowCount - 1 do
    begin
      if ((ir = 0) and (ic = 0))
        or ((ir = grd.RowCount-1) and (ic=grd.ColCount-1)) then Continue;

      if ic = 0 then
        val1 := grd.Cells[grd.ColCount-1, ir-1]
      else
        val1 := grd.Cells[ic-1, ir];

      val2 := grd.Cells[ic, ir];

      if ic = grd.ColCount-1 then
        val3 := grd.Cells[0, ir+1]
      else
        val3 := grd.Cells[ic+1, ir];

   
      idDurchschnitt := (InttoStr(val1) + InttoStr(val2) + InttoStr(val3)) / 3;

    end;
  end;
end;

fl63 26. Aug 2008 15:48

Re: Moving Average
 
Bei der function simple moving average von Marabu komme ich irgendwie nicht sehr weit.
Meine clickprocedure liefert irgenwelche Fehler, jedenfalls kein gleitenden Durchschnitt.

marabu 26. Aug 2008 17:53

Re: Moving Average
 
Sorry,

da war ein Fehler drin - ich habe ihn korrigiert.

Hier noch die Variante ohne die Funktion Mean():

Delphi-Quellcode:
function SimpleMovingAverage(data: TDoubleDynArray; n: Word): TDoubleDynArray;
var
  i: Integer;
  dSum: Double;
begin
  SetLength(Result, Length(data));
  dSum := 0;
  for i := Low(data) to High(data) do
  begin
    if i >= n then
      dSum := dSum - data[i - n];
    dSum := dSum + data[i];
    if i < n
      then Result[i] := dSum / Succ(i)
      else Result[i] := dSum / n;
  end;
end;
Denke bitte daran, dass der Code nur die Strukturierung des Problems durch Prozeduren und Funktionen zeigen soll. Getestet habe ich mit einem StringGrid ohne FixedRows.

Delphi-Quellcode:
const
  FMT = '%.1f';

procedure TDemoForm.InitButtonClick(Sender: TObject);
var
  i: Integer;
begin
  with StringGrid do
    for i := FixedRows to Pred(RowCount) do
      Cells[FixedCols, i] := Format(FMT, [Random * 100]);
end;

procedure TDemoForm.CalcButtonClick(Sender: TObject);
var
  data, sma: TDoubleDynArray;
begin
  with StringGrid do
  begin
    data := StringsToData(Cols[FixedCols]);
    sma := SimpleMovingAverage(data, 3);
    DataToStrings(sma, Cols[Succ(FixedCols)], FMT);
  end;
end;
Für FixedRows > 0 müssen die Signaturen der Konvertierfunktionen noch um einen entsprechenden skip value erweitert werden.

Freundliche Grüße

fl63 27. Aug 2008 16:41

Re: Moving Average
 
Hallo Marabu,

Was ist ein Skip Value??
Deine Proceduren laufen einwandfrei, dennoch habe ich ein paar Fragen:

1. n ist als Word deklariert Ich möchte eine Variable StrtoWord(edit1.text). StrToWord aus der Unit IdTrivialFTPBase funktioniert nicht. Also wie bekomme ich Edit1.text in Word konvertiert?
Vielleicht liegt es an der falschen Unit, sie ist aus der Delphi-Hilfe.

2. Die Ausgabe der Ergebnisse müssen ein paar Spalten weiter angezeigt werden.
FixedCol Problem. Wenn ich Unter CalcButtonClick DatatoStrings(sma(Cols[4]..) eingebe erscheinen gar keine Ergebnisse in der Spalte.

Bitte Hilfe.

DeddyH 27. Aug 2008 16:48

Re: Moving Average
 
Zitat:

Zitat von fl63
Also wie bekomme ich Edit1.text in Word konvertiert?

Mit StrToInt.

fl63 27. Aug 2008 16:58

Re: Moving Average
 
Ok Diese Frage ist geklärt.
Das Word gleich Integer ist wußte ich bis Dato nicht.
Danke.

DeddyH 27. Aug 2008 17:18

Re: Moving Average
 
Word = 16 Bit ohne Vorzeichen
integer = 32 Bit mit Vorzeichen

Word passt also immer in Integer.

marabu 27. Aug 2008 19:10

Re: Moving Average
 
Hallo,

Zitat:

Zitat von fl63
... Was ist ein Skip Value?? ...

ein Parameter mit dem du die FixedRows oder FixedCols übergehen kannst:

Delphi-Quellcode:
function StringsToData(strings: TStrings; skip: Word = 0; default: Double = 0): TDoubleDynArray;
var
  i: Integer;
begin
  SetLength(Result, strings.Count - skip);
   for i := Low(Result) to High(Result) do
     Result[i] := StrToFloatDef(strings[i + skip], default);
end;
Getippt und nicht getestet. Die Anpassung der Prozedur DataToStrings() überlasse ich dir zur Übung.

Zitat:

Zitat von fl63
... Die Ausgabe der Ergebnisse müssen ein paar Spalten weiter angezeigt werden. FixedCol Problem. Wenn ich Unter CalcButtonClick DatatoStrings(sma(Cols[4]..) eingebe erscheinen gar keine Ergebnisse in der Spalte. ...

Hat denn dein Grid genügend Spalten? Der Spalten-Index 4 bezeichnet die fünfte Spalte ...

Freundliche Grüße

fl63 28. Aug 2008 14:10

Re: Moving Average
 
Hallo Marabu,

Index 4 ist nur wahllos herausgegriffen.

Das Grid hat im Moment 3 Spalten, wobei Spalte B (Excel Notation) die zu berechnende Spalte ist.
Das Ergebnis mit deinen Funktionen erscheint in Spalte C und überschreibt damit andere Werte.
Mir bleibt dabei nur Spalte C in sichere Entfernung per MoveCol zu verschieben, daraufhin das RechenErgebnis in eine Spalte zB. D ebenfalls zu verschieben und die alte Spalte C wiederherzustellen.
Für die Berechnung des 5er, 10er und 20er Durchschnitts brauche ich 3 Ergebnisspalten (D,E,F).

Du siehst die Zahl der Spalten ist annähernd unbegrenzt.

marabu 29. Aug 2008 20:19

Re: Moving Average
 
Hallo,

Zitat:

Zitat von fl63
... Das Grid hat im Moment 3 Spalten ...

und ich hatte dich extra noch gefragt:

Zitat:

Zitat von marabu
... Hat denn dein Grid genügend Spalten? Der Spalten-Index 4 bezeichnet die fünfte Spalte ...

Du kannst zwar beliebige Spalten addressieren, angezeigt werden aber nur die Spalten mit Index < ColCount.
Oder anders ausgedrückt: Nur weil du etwas in Spalte 5 schreibst, wird die Spalte 5 nicht sichtbar.

Gute Nacht

fl63 31. Aug 2008 12:56

Re: Moving Average
 
Hallo Marabu,
Mein Fehler, habe die Test-Datei auf zehn LeerSpalten erweitert und siehe da es funktioniert.
Bleibt die letzte Frage.
Bei einem 5er Durchschnitt erscheinen in den ersten vier Zeilen Ergebnisse die da nicht hingehören.
Das erste richtige Ergebnis ist in der fünften Zeile, wie bekomme ich die ersten vier Zeilen gelöscht:
Mein Vorschlag :
Delphi-Quellcode:
For i =1 to StrToInt(Edit1.Text)-1 do
JvSG1.Cells[4,i] :='';
oder gibt es in der für mich als Laien sehr schwer durchschaubaren Rechenprozedur eine bessere Möglichkeit.

Schönen Sonntag

marabu 31. Aug 2008 13:33

Re: Moving Average
 
Hallo,

dein Ansatz hat etwas von "Tipp-Ex", ich würde den Fehler lieber an der Stelle beseitigen, wo er entsteht.

Ich führe die 0 als einen speziellen Wert ein, und passe die Routinen so an, dass Berechnung und Ausgabe für die ersten n-1 Werte unterbleibt:

Delphi-Quellcode:
procedure DataToStrings(data: TDoubleDynArray; strings: TStrings; const fmt: string = '%f');
var
  i: Integer;
begin
  strings.Clear;
  for i := Low(data) to High(data) do
    if IsZero(Data[i])
      then strings.Add('') // Null-Werte ausblenden
      else strings.Add(Format(fmt, [Data[i]]));
end;

function SimpleMovingAverage(data: TDoubleDynArray; n: Word): TDoubleDynArray;
var
  i: Integer;
  dSum: Double;
begin
  SetLength(Result, Length(data));
  dSum := 0;
  for i := Low(data) to High(data) do
  begin
    if i >= n then
      dSum := dSum - data[i - n];
    dSum := dSum + data[i];
    if Succ(i) < n
      then Result[i] := 0  // keine Berechnung für die ersten n-1 Werte
      else Result[i] := dSum / n;
  end;
end;
Wenn die Rechenprozedur für einen Laien nicht durchschaubar ist, dann habe ich den Algorithmus wohl schlecht implementiert.

Nachdenkliche Sonntagsgrüße

fl63 2. Sep 2008 11:45

Re: Moving Average
 
Ich bedanke mich bei allen die geholfen haben insbesondere bei Marabu. Meine Frage ist beantwortet.

fl63 3. Sep 2008 13:54

Re: Moving Average
 
Winzigen Fehler entdeckt:
Delphi-Quellcode:
function SimpleMovingAverage(data: TDoubleDynArray; n: Word): TDoubleDynArray;
var
  i: Integer;
  dSum: Double;
begin
  SetLength(Result, Length(data));
  dSum := 0;
  for i := Low(data) to High(data) do
  begin
     if i >= n then
      dSum := dSum - data[i - n];
    dSum := dSum + data[i];
    if Succ(i) <= n // hier FEHLER ausgebessert
      then Result[i] := 0  // keine Berechnung für die ersten n-1 Werte
      else Result[i] := dSum / n;
  end;
end;

marabu 3. Sep 2008 15:57

Re: Moving Average
 
Hallo,

die Bedeutung von "Succ(i) < n" hatte ich mit einem Kommentar herausgestellt. Willst du wirklich den ersten möglichen Wert ignorieren?

Auch wenn ich das nicht glauben mag, noch ein Hinweis: "i < n" ist gleichwertig zu "Succ(i) <= n", aber viel klarer.

Freundliche Grüße

fl63 4. Sep 2008 10:45

Re: Moving Average
 
Hallo Marabu,
Bei der Version ohne = erscheint bei meinem Testdurchschnit(5er) bereits in der vierten Zeile ein Ergebnis. Wie wir uns bereits geeinigt haben muß bei einem 5er Durchschnitt das erste Ergebnis in der 5ten Zeile stehen.
Bei der Vesion mit = funktioniert alles korrekt.

Leider habe ich noch immer Probleme den Skip Wert in die Procedure Data to Strings einzufügen. Ich komme dabei zu keinem vernünftigen Ergebnis.
Vielleicht hilfst du mir noch mal dabei.

marabu 4. Sep 2008 17:30

Re: Moving Average
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

ich habe dir eine Demo zum Studium angehängt. Mit dem Einfügen des Gleichheitszeichens korrigierst du nach meiner Einschätzung einen Fehler, den du an anderer Stelle machst.

Freundliche Grüße

fl63 6. Sep 2008 11:49

Re: Moving Average
 
Hallo Marabu,
Also den Skip so einzubinden hätte ich in 1000Jahren nicht geschafft. Die Hoffnung, daß durch die korrekte Einbindung des Skips die Spaltenüberschrift erhalten bleibt ist leider dahin. Ich werd sie dann wohl nachträglich wieder hinzufügen. Dennoch Danke für Deine Hilfe.
Wochenendgrüße fl63

marabu 6. Sep 2008 14:25

Re: Moving Average
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

die Demo aus meinem vorigen Beitrag sollte dir nur verdeutlichen, dass das von dir eingefügte Gleichheitszeichen keine Korrektur meines Codes ist, sondern ein Symptom deines eigenen Codes kuriert. Die Erhaltung von Spalten- und Zeilenüberschriften zeigt eine weitere Demo im Anhang. Den gewünschten Effekt erreiche ich durch kleine Änderungen an der Prozedur DataToStrings():

Delphi-Quellcode:
procedure DataToStrings(data: TDoubleDynArray; strings: TStrings;
    skip: Word = 0; const fmt: string = '%f');
var
  i: Integer;
begin
  for i := Low(data) to High(data) do
    if IsZero(Data[i])
      then strings[i + skip] := ''
      else strings[i + skip] := Format(fmt, [Data[i]]);
end;
Ich verzichte auf strings.Clear und überschreibe nur noch die notwendigen Zellen.

Freundliche Grüße


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:16 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz