![]() |
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?? |
Re: Moving Average
hi,
scheint mir momentan nicht so die mathematische Hürde zu sein. Woran hapert es denn genau? |
Re: Moving Average
Hallo,
da die Zahlen im StringGrid als Zeichenketten vorliegen, musst du sie erst konvertieren:
Delphi-Quellcode:
Als Parameter strings kannst du z.B. StringGrid.Rows[iRow] oder StringGrid.Cols[iCol] übergeben.
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; Die Berechnung des gleitenden Durchschnitts ist dann wahrscheinlich nicht mehr dein Problem.
Delphi-Quellcode:
Getippt und nicht getestet.
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; Grüße vom marabu |
Re: Moving Average
Zitat:
zeig doch mal her, was du bereits hast, und wo es hackt ... |
Re: Moving Average
Ich tippe mal auf ein Mehrschleifenproblem wobei die 2.Schleife das Problem darstellt.
Delphi-Quellcode:
i : ist die Laufvariable für das Stringgrid
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; 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. |
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; |
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. |
Re: Moving Average
Sorry,
da war ein Fehler drin - ich habe ihn korrigiert. Hier noch die Variante ohne die Funktion Mean():
Delphi-Quellcode:
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.
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;
Delphi-Quellcode:
Für FixedRows > 0 müssen die Signaturen der Konvertierfunktionen noch um einen entsprechenden skip value erweitert werden.
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; Freundliche Grüße |
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. |
Re: Moving Average
Zitat:
|
Re: Moving Average
Ok Diese Frage ist geklärt.
Das Word gleich Integer ist wußte ich bis Dato nicht. Danke. |
Re: Moving Average
Word = 16 Bit ohne Vorzeichen
integer = 32 Bit mit Vorzeichen Word passt also immer in Integer. |
Re: Moving Average
Hallo,
Zitat:
Delphi-Quellcode:
Getippt und nicht getestet. Die Anpassung der Prozedur DataToStrings() überlasse ich dir zur Übung.
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; Zitat:
Freundliche Grüße |
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. |
Re: Moving Average
Hallo,
Zitat:
Zitat:
Oder anders ausgedrückt: Nur weil du etwas in Spalte 5 schreibst, wird die Spalte 5 nicht sichtbar. Gute Nacht |
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:
oder gibt es in der für mich als Laien sehr schwer durchschaubaren Rechenprozedur eine bessere Möglichkeit.
For i =1 to StrToInt(Edit1.Text)-1 do
JvSG1.Cells[4,i] :=''; Schönen Sonntag |
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:
Wenn die Rechenprozedur für einen Laien nicht durchschaubar ist, dann habe ich den Algorithmus wohl schlecht implementiert.
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; Nachdenkliche Sonntagsgrüße |
Re: Moving Average
Ich bedanke mich bei allen die geholfen haben insbesondere bei Marabu. Meine Frage ist beantwortet.
|
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; |
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 |
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. |
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 |
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 |
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:
Ich verzichte auf strings.Clear und überschreibe nur noch die notwendigen Zellen.
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; 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