TAChart 2 Reihen

Rund um die LCL und andere Komponenten
Antworten
JosefKoller
Beiträge: 115
Registriert: Sa 17. Mär 2007, 10:53

TAChart 2 Reihen

Beitrag von JosefKoller »

Hallo,

wie stell ich den in einem Chart (Bar) mehrere Datenreihen per Code dar?

Beispiel

Reihe 1 Reihe 2
12 8
24 1
33 14
2 100
... ...

Eine Reihe bring ich hin, aber mit einer 2. Reihe klappt es einfach nicht.
chart1.ClearSeries;
Bars := TBarSeries.Create(Chart1);
for Count := 1 to 10 do
begin
Bars.AddXY(count, WorksheetGrid.Worksheet.ReadAsNumber(count,1), WorksheetGrid.Worksheet.ReadAsUTF8Text(count,0), Colours[1]);
Bars.Marks.Distance:= 5;
//Bars.Marks.Style:=smsValue;
bars.Legend.Order:=count;
bars.Legend.Format:=WorksheetGrid.Worksheet.ReadAsUTF8Text(0,1)
end;
Chart1.AddSeries(Bars);
Besten Dank.

Josef

wp_xyz
Beiträge: 5153
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart 2 Reihen

Beitrag von wp_xyz »

Nebeneinander oder gestapelt? Im ersten Fall brauchst du zwei Series, im zweiten Fall eine einzige Series mit einem zweiten y-Wert. Mehr dazu in http://wiki.lazarus.freepascal.org/TACh ... _BarSeries und http://wiki.lazarus.freepascal.org/TACh ... _BarSeries

JosefKoller
Beiträge: 115
Registriert: Sa 17. Mär 2007, 10:53

Re: TAChart 2 Reihen

Beitrag von JosefKoller »

Hallo,

vielen Dank.

Jetzt häng ich an einer Durchschnittsdarstellung der Werte.

ich hab diesen Code nach der Erzeugung der 2 Bars hinzugefügt.
calc :=TCalculatedChartSource.Create(chart1);
calc.AccumulationMethod:= camAverage;
calc.AccumulationDirection:=cadBackward;
calc.Origin:= bars.Source;
Irgendwie komm ich da aber nicht weiter. Muß den dieses calc nicht auch ähnlich wie Chart1.AddSeries(calc) zugewiesen werden?
Was fehlt denn da noch,um den durchschnitt anzuzeigen?

Danke.

Josef

Wie bring ich den eine Linie des Durchschnitts

wp_xyz
Beiträge: 5153
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart 2 Reihen

Beitrag von wp_xyz »

TChartSeries bezieht die Daten immer aus einer TChartSource. Wenn du einer Series Daten per Series.AddXY hinzufügst, wird eine interne ListChartSource verwendet. Du kannst aber jederzeit einen anderen ChartSource-Typ verwenden, indem du diese ChartSource der Eigenschaft "Source" der Series zuweist.

Um die Durchschnittslinie zu zeichnen, musst du dafür dem Chart eine weitere Series hinzufügen, ich würde eine TListSeries nehmen (--> LineSeries). In die "Source" dieser Series hängst du deine TCalculatedChartSource, also

Code: Alles auswählen

 
  LineSeries.Source := calc;
 
Achtung aber: TCalculatedChartSource berechnet einen "moving average" über "AccumulationRange" Datenpunkte (--> Chart-Analyse von Finanzdaten...). Um wirklich eine horizontale Linie zu erhalten, musst du wahrscheinlich den AccumulationRange größer als die Anzahl deiner Datenpunkte setzen - hab gerade kein Projekt zur Hand, an dem ich diese Aussage testen könnte.

Oder du berechnest dir in einem Drei-Zeiler den Durchschnitt selbst und weist diesen Wert der "Position" einer TConstantLine-Series zu.

[EDIT]
In (lazarus)/components/tachart/demo/chartsource ist ein Beispiel für einen "Moving average". Eine horizontale Linie, die per Augenmaß dem Durchschnitt entspricht, erhält man mit den Einstellungen: "AccumulationRange" sehr groß, und "AccumulationDirection" = cadCenter.

JosefKoller
Beiträge: 115
Registriert: Sa 17. Mär 2007, 10:53

Re: TAChart 2 Reihen

Beitrag von JosefKoller »

Besten Dank.

Das mit dem "selbst Rechnen" schaut so aus, wie ich es mir vorstelle.
sumavg:=sumavg / count;
avgLine:=TASeries.TConstantLine.Create(chart1);
avgLine.Position:=sumavg;
avgLine.pen.Color:=Colours[1];
avgLine.pen.Width:=5;
avgLine.Pen.Style:=psDot;
avgLine.Legend.Format:='AVG '+ WorksheetGrid.Worksheet.ReadAsUTF8Text(0,1);
chart1.AddSeries(avgLine);
Dann dachte ich mir, es gibt ja auch in der Unit math etliche Funktionen, z. b. mean().

Hab dann folgendes probiert:
funkmean:= TFuncSeries.Create(chart1);
funkmean.Pen.Style:=psDot;
funkmean.Pen.Width:=3;
funkmean.OnCalculate := @MyFunc;
funkmean.Legend.Format:='Mean';
funkmean.Active:=True;
chart1.AddSeries(funkmean);
und in MyFunc
procedure TMainForm.MyFunc(const AX: Double; out AY: Double);
begin
AY := mean(AX);
end;
Schaut aber nicht toll aus, soll heißen, es wird eine Linie von 0 nach 10 gezogen. siehe Bild.

Haste da vielleicht auch noch einen tipp, wie man diese funkseries richtig anwendet?

Danke.

Josef
Dateianhänge
laz_mean.jpg

wp_xyz
Beiträge: 5153
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart 2 Reihen

Beitrag von wp_xyz »

Die TFuncSeries ist gedacht, mathematische Funktionen zu zeichen, im OnCalculate-Event-Handler gibt man an, wie sich y (AY) aus x (AX) berechnet. Für ein etwas komplexeres Beispiel kannst du dir dieses Tutorial anschauen: http://wiki.lazarus.freepascal.org/TACh ... ion_Series. Weil die FuncSeries für jeden beliebigen Punkt berechnet wird, kann man reinzoomen, ohne dass Stufen sichtbar werden (was bei einer TLineSeries geschehen würde, bei der die Funktion für diskrete Werte ausgerechnet wird).

Zu deinem Problem: Du wendest die Mean-Function nur auf den einen Wert AX an - der Mittelwert eines Datenpunktes ist aber der Wert selber. Daher erhältst du für jeden x-wert als y-Wert den x-Wert, d.h. die y-Werte sind gleich den x-Werten. Wenn die y-Werte im Array yData[0..n-1] stehen, dann müsstest du stattdessen Mean mit dem Argument yData aufrufen:

Code: Alles auswählen

 
procedure TMainForm.MyFunc(const AX: Double; out AY: Double);
begin
  AY := Mean(yData);
end; 
Das wäre aber ungeschickt, da der Mittelwert für jeden x-Wert neu berechnet wird, obwohl er sich ja gar nicht ändert.

Für einen einfachen Mittelwert ist das Overkill. Nimm doch stattdessen die TConstantLine Series.

In deinem Code fehlt noch, wie sumavg berechnet wird - du musst einfach alle Datenpunkte durchlaufen und die y-Werte addieren. Falls, wie oben schon erwähnt, alle y-Werte in einem Array stehen, kannst du auch die Mean-Funktion aus der Unit math verwenden; wenn nicht, ist diese Funktion nutzlos.

Noch ein Hinweis: Wenn sich etwas an deinen Daten ändert, musst du bei der ConstantLine Series den Mittelwert neu berechnen und im Feld Position der Series entsprechend neu eintragen. Bei einer TCalculatedChartSource ist das nicht nötig, denn diese bekommt mit, wenn Daten sich ändern.

[EDIT]
Übrigens, falls du die Balkengruppen an jeder Achsenmarkierung zentriert haben willst: Die Breite jedes Balkens ist bestimmt durch BarWidthPercent (bezogen auf den Abstand der Mittelpunkte der Balkengruppen). Die Mitte jedes Balkens liegt bei BarOffsetPercent. Bei zwei Balken pro Gruppe muss BarWidthPercent kleiner sein als 50%, denn 2x50%=100% --> die Balkengruppen stoßen zusammen. Um etwas Abstand zwischen den Balkengruppen zu lassen, nimm z.B. BarWidthPercent = 40%. Um die Hälfte dieser Zahl musst du die 1.Balkengruppe nach links (BarOffsetPercen = 20) und die 2. Balkengruppe nach rechts (BarOffsetPercent = +20) verschieben, so dass der Schwerpunkt der Balkengruppe bei der Achsenmarkierung liegt.

Noch ein Hinweis: Falls du den grauen Hintergrund des Titels nicht willst, kannst du auch Chart.Title.Brush.Style = fsClear verwenden (--> Transparenter Hintergrund)

Antworten