"Einfache" Statistik erfassen, Speichern & Anzeigen
-
Hallo Leute ...
Ich möchte in meinem Programm mehrere Statistik Funktionen anbieten.
Für den Überblick:
Das Programm bietet eine Lernfunktion, also eine Art Quiz bei dem jede Menge Fachfragen abgefragt werden. Schwierigkeitsgrad & Fachbereiche sind natürlich frei wählbar.
Zur Statistik:
Folgende Funktionen halte ich für sinnvoll, da das ganze später ja auch einen ernsten Zweck erfüllen soll:
- aktuelle Statistik ( beim laufenden Quiz - richtige / falsche Antworten)
- Statistik nach Fachbereichen ( Worin bin ich stark, worin schlecht ?)
- Langzeitstatistik (Übungstage und Tendenz)Punkt 1 & 3 habe ich bereits "mit meinen Möglichkeiten" versucht umzusetzen. Verwendet habe ich dazu zedGraph.
Folgende Variablen verwende ich:
richtigBeantwortet
falschBeantwortet
Fachbereich
Datum
Schwierigkeitwelche dann irgendwann via dieser "gebasteleten" Funktion gespeichert wird:
public bool saveStatistik(BenutzerInfoKlasse IBenutzerInfoKlasse, int richtig, int falsch, DateTime datum, int schwierigkeit) { if (IBenutzerInfoKlasse.benutzername == null) return false; try { StreamWriter sr = new StreamWriter(System.IO.Path.Combine(IBenutzerInfoKlasse.benutzerdatenpfad, IBenutzerInfoKlasse.benutzername + ".sta"), true); sr.WriteLine("<datum>" + datum.ToShortDateString () +"</datum>"+"<richtig>" + richtig.ToString () + "</richtig>" + "<falsch>" + falsch.ToString () + "</falsch>" + "<schwierigkeit>" + schwierigkeit.ToString () + "</schwierigkeit>"); sr.Close(); return true; } catch (Exception e) { ShowErrorMessage(e); return false; } }
Und via:
protected void TloadStatistiken(BenutzerInfoKlasse IBenutzerInfoKlasse) { DiskIO IDiskIO = new DiskIO(); List<String[]> rohdaten = IDiskIO.loadStatistik(IBenutzerInfoKlasse); List<String> bewertungsdatum = new List<String>(); List<statistikstruct> structlist = new List<statistikstruct>(); List<statistikstruct> structlisttemp = new List<statistikstruct>(); String[] buffer; int richtig, falsch; //prüfen ob Statistik Daten bestehen if (rohdaten == null || rohdaten.Count < 1) { IQuizPanel.IHauptprogramm.status.Text = "Es wurden keine Statistikdaten gefunden."; return; } foreach (String [] set in rohdaten) { buffer = (String[])set; String datum = buffer[0]; richtig = Convert.ToInt16(buffer[1]); falsch = Convert.ToInt16(buffer[2]); if (bewertungsdatum.Contains(datum)) { //Datum bereits vorhanden foreach (statistikstruct stat in structlist) { if (stat.datum == datum) { richtig = stat.richtig + richtig; falsch = stat.falsch + falsch; statistikstruct Istatistikstruct = new statistikstruct(); Istatistikstruct.datum = datum; Istatistikstruct.richtig = richtig; Istatistikstruct.falsch = falsch; structlisttemp.Add(Istatistikstruct); structlist.Remove(stat); break; } } } else { //Datum noch nicht vorhanden statistikstruct Istatistikstruct = new statistikstruct(); Istatistikstruct.datum = datum; Istatistikstruct.richtig = richtig; Istatistikstruct.falsch = falsch; structlist.Add(Istatistikstruct); bewertungsdatum.Add(datum); continue; } } //Daten sind in der Liste vollständig foreach (statistikstruct s in structlisttemp) structlist.Add (s); statistikdaten = structlist; zeitpunkte = bewertungsdatum; }
...so geladen:
public List<String[]> loadStatistik(BenutzerInfoKlasse IBenutzerInfoKlasse) { List<String[]> daten = new List<string[]>(); try { StreamReader r = new StreamReader(System.IO.Path.Combine(IBenutzerInfoKlasse.benutzerdatenpfad, IBenutzerInfoKlasse.benutzername + ".sta"), true); String[] trennzeichen = new String[] { "<datum>", "</datum>", "<richtig>", "</richtig>", "<falsch>", "</falsch>" }; String readbuffer; //für den Streamreader while (!r.EndOfStream) { readbuffer = ""; readbuffer = r.ReadLine(); //Trennt die TAGS von den Daten in ein String Array daten.Add(readbuffer.Split(trennzeichen, StringSplitOptions.RemoveEmptyEntries)); } } catch (Exception) { //MessageBox.Show("Fehler bei DiskIO - loadStatistik"); return null; } return daten; }
Ich gebe zu, alles nicht das wahre. Wie würdet ihr das lösen ?
-
Also wenn du unbedingt mit dem StreamWriter und StreamReader arbeiten willst, dann pack die bitte in ein "using-Block".
So wie das aussieht willst du ja XML Speichern. Dafür gibt es bereits Klassen in .NET die dich dabei ein wenig unterstützen.
Schau dir dazu mal, XmlWriter, XDocument und Linq-To-Xml an.
-
Okay. Manchmal verstehe ich im nachhinein selber nicht mehr was ich mache. Bestimmt 80 % meines Progis verarbeiten auf irgendeine Weise XML mit den von dir angesprochenen Klassen.
Habe alles mal umgenudelt, wobei die Daten nun wie folgt als XML Document gespeichert und geladen werden:
<?xml version="1.0"?> <Statistik> <Datum>26.01.2011<richtig>0</richtig><falsch>8</falsch><Schwierigkeit>0</Schwierigkeit><Fachbereich>5</Fachbereich></Datum> <Datum>26.01.2011<richtig>3</richtig><falsch>1</falsch><Schwierigkeit>0</Schwierigkeit><Fachbereich>5</Fachbereich></Datum> </Statistik>
Wobei ich nun wieder bei meiner Fragestellung wäre, wobei ich die "aktuelle Statistik" gerne erstmal bei Seite lassen würde, da diese nicht auf die XML Daten zugreift.
Wie verarbeite ich nun die Daten ? Alles in ein Array oder direkt aus dem XMLDocument verarbeiten ? Und nutze ich wirklich zedGraph ? Ich möchte das ganze gerne ziemlich übersichtlich haben, vielleicht nen kleines UserControl / Form an der linken oberen Ecke des Desktops. zedGraph ist bei kleiner Skalierung schlecht lesbar, finde ich. Was würdet ihr nutzen ?
-
Guten Morgen
Brauche nocheinmal eure Hilfe:
Beim laden der Statistik Form wird dies aufgerufen:
private void InitChartAktuell(ZedGraphControl zgc) { myPane = zgc.GraphPane; myPane.CurveList.Clear(); //Titel myPane.Title.Text = "Statistik " + IQuizPanel.IHauptprogramm.IBenutzerInfo.benutzername; myPane.Title.FontSpec.Size = 20; //X Labels myPane.XAxis.Title.FontSpec.Size = 15; myPane.YAxis.Title.Text = "Anzahl"; myPane.YAxis.Title.FontSpec.Size = 15; try { //XLabels string[] XLabels = { "Übungstag: " + DateTime.Now.ToShortDateString () }; myPane.XAxis.Type = AxisType.Text; myPane.XAxis.Scale.TextLabels = XLabels; myPane.XAxis.Scale.FontSpec.Size = 15; //Y Labels myPane.YAxis.Type = AxisType.Exponent; myPane.YAxis.Scale.FontSpec.Size = 15; myPane.YAxis.Scale.FontSpec.IsBold = true; //Refresh zgc.AxisChange(); zgc.ZoomOutAll(myPane); zgc.Refresh(); } catch (Exception) { } }
Via Menü hat der User die Möglichkeit die anderen Statistiken anzeigen zu lassen:
private void aktuelleStatistikToolStripMenuItem_Click(object sender, EventArgs e) { if (activChart == 0) { UpdateChartAktuell(zedGraphControl1); return; } LangzeitanzeigenToolStripMenuItem.Enabled = true; AktuellanzeigenToolStripMenuItem2.Enabled = false; FachbereichanzeigenToolStripMenuItem1.Enabled = true; SchwierigkeitToolStripMenuItem.Enabled = true; InitChartAktuell(zedGraphControl1); activChart = 0; } private void langzeitstatistikAnzeigenToolStripMenuItem_Click(object sender, EventArgs e) { if (activChart == 1) return; //evtl. neu zeichnen ? myPane.CurveList.Clear(); LangzeitanzeigenToolStripMenuItem.Enabled = false; AktuellanzeigenToolStripMenuItem2.Enabled = true; FachbereichanzeigenToolStripMenuItem1.Enabled = true; SchwierigkeitToolStripMenuItem.Enabled = true; InitChartLangzeit(zedGraphControl1); activChart = 1; } usw...
Hier der Code zur Langzeitstatistik:
public void InitChartLangzeit(ZedGraphControl zgc) { if (XMLStatistik == null) return; if (structlist == null) structlist = new List<statistikstruct>(); zgc.Invalidate(); zgc.GraphPane.GraphObjList.Clear(); List<String> zeitpunkte = new List<string>(); //Alle Daten (Datums) in eine Liste holen foreach (XmlNode dateNode in XMLStatistik.DocumentElement.ChildNodes) if (zeitpunkte.Contains (dateNode.FirstChild.InnerText) == false) zeitpunkte.Add (dateNode.FirstChild.InnerText); foreach (String zeitpunkt in zeitpunkte) { statistikstruct s = new statistikstruct(); s.datum = zeitpunkt; foreach (XmlNode node in XMLStatistik.DocumentElement.ChildNodes) { //Wenn Datum übereinstimmt - richtig und falsch zusammenzehlen if (node.FirstChild.InnerText != zeitpunkt) continue; else { s.richtig = s.richtig + Convert.ToInt16(node.ChildNodes[1].InnerText); s.falsch = s.falsch + Convert.ToInt16(node.ChildNodes[2].InnerText); } } structlist.Add(s); } myPane = zgc.GraphPane; //Needed for redrawing the chart, to remove old curves //myPane.CurveList.Clear(); // Set the title and axis labels myPane.Title.Text = "Langzeitstatistik " + IQuizPanel.IHauptprogramm.IBenutzerInfo.benutzername; myPane.XAxis.Title.Text = "Datum der Bewertung"; myPane.YAxis.Title.Text = "Falsch / Richtig"; // Make up some data points string[] labels = zeitpunkte.ToArray(); List<double> ri = new List<double>(); List<double> fa = new List<double>(); foreach (statistikstruct s in structlist) { ri.Add(s.richtig); fa.Add(s.falsch); } double[] y2 = ri.ToArray(); double[] y = fa.ToArray(); // Generate a red bar with "Curve 1" in the legend BarItem myCurve = myPane.AddBar("Falsch", null, y, Color.Red); // Fill the bar with a red-white-red color gradient for a 3d look myCurve.Bar.Fill = new Fill(Color.Red, Color.White, Color.Red); // Generate a blue bar with "Curve 2" in the legend myCurve = myPane.AddBar("Richtig", null, y2, Color.LimeGreen); // Fill the bar with a Blue-white-Blue color gradient for a 3d look myCurve.Bar.Fill = new Fill(Color.LimeGreen, Color.White, Color.LimeGreen); // Draw the X tics between the labels instead of at the labels myPane.XAxis.MajorTic.IsBetweenLabels = true; // Set the XAxis to Text type myPane.XAxis.Type = AxisType.Text; // Set the XAxis labels myPane.XAxis.Scale.TextLabels = labels; //myPane.XAxis.Scale.FontSpec.Size = 10; // Set the bar type to stack, which stacks the bars by automatically accumulating the values myPane.BarSettings.Type = BarType.Stack; // Calculate the Axis Scale Ranges zgc.AxisChange(); zgc.ZoomOutAll(myPane); zgc.Refresh(); }
Das Problem ist nun, das bei einem Wechsel von einer Statistik zur anderen, die alte Statistik nicht gelöscht, sondern die neue angehängt wird. Wie umgehe ich das ?