Autor Beitrag
Federball-Fridolin
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 30.08.16 11:08 
Hallihallo,
ich hab eine zufällige Anzahl Zahlen, diese können auch zufällige Werte haben (0-1Mio).
Die Zahlen sollen nun eingeteilt werden.
Beispiel:
Gegebene Zahlen: 12, 15, 12460, 24890
Diese müssten jetzt in 3 "Gruppen" eingeteilt werden:
20, 15000, 25000
also Zahlen die nah zusammen sind sollen in eine Gruppe.
Das Aufrunden ist bereits programmiert.
Vielen Dank im Voraus, falls jemand eine Idee hat :)
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Di 30.08.16 11:48 
Was heißt denn "nah zusammen"? Und woran scheitert es?

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Federball-Fridolin Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 30.08.16 11:56 
Tut mir leid, es fällt mir schwer das zu beschreiben.

Das "wie nah" kommt darauf an, wie groß die Spanne zwischen größter und kleinster Zahl ist. Bei einem unterschied von 20 000 fallen die Gruppen deutlich größer aus, als bei einer Spanne von nur 100.
Genauere Beschreibung: Es handelt sich um eine Legende einer Weltkarte. In jedem Land befinden sich verschieden viele Objekte (die Anzahl ist erst während der Laufzeit bekannt). Nun werden die Länder mit
wenigen Objekten heller gefärbt als die anderen. In der Legende wird gezeigt, wie die Anzahl der Objekte mit der Farbe "zusammenhängt" (sprich dunkelrot z.B. 10000 Objekte). Wenn es aber jetzt nur angenommen 3 Länder mit Objekten gibt und in einem sind 16, in dem nächsten 14506 und im letzten 150640. So muss ich jetzt nicht alle Werte zwischen 14506 und 150640 darstellen, sondern nur 3 Werte:
20 (16 gerundet), 15000(gerundet), 160000 (gerundet).

Moderiert von user profile iconChristian S.: Beiträge zusammengefasst

Gibt es jedoch 20 Länder mit Objekten, die alle zwischen 100 und 1000 liegen, müsste ich wieder mehr Werte darstellen.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 30.08.16 12:09 
- Nachträglich durch die Entwickler-Ecke gelöscht -
t.roller
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 118
Erhaltene Danke: 34



BeitragVerfasst: Di 30.08.16 12:57 
Vielleicht hat unser Mathematiker eine Lösung.
Ich vermute: LOGARITHMUS.
Federball-Fridolin Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 30.08.16 12:59 
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
Dim bereichsGroesse As Double = roundUp(anzahl.Max / anzahl.Count)
            bereichTemp = bereichsGroesse
            Do
                If anzahl.Max > 100 Then
                    For j As Integer = 0 To anzahl.Count - 1
                        If anzahl(j) > bereichsGroesse - bereichTemp And anzahl(j) <= bereichsGroesse Then
                            roundUp(bereichsGroesse)
                            colorizer.RangeStops.Add(bereichsGroesse)
                            j = anzahl.Count - 1
                        End If
                    Next
                Else
                    colorizer.RangeStops.Add(bereichsGroesse)
                End If
                farbePruefen.Add(bereichsGroesse)
                bereichsGroesse += bereichTemp
            Loop While (Not bereichsGroesse - bereichTemp > anzahl.Max)


Beispielwerte: 10, 600, 3000
Jetzt bekomme ich als Einteilung 340, 680, 3000
jedoch ist die 340 viel zu hoch und es müsste 20 angezeigt werden, das liegt daran, dass ich irgendwie die Schrittweite defnieren muss (bereichsGroesse) und ich weiß nicht, wie ich das anstellen soll.

Bei dem Beispielcode werden die Bereiche ausgelassen, in denen sich kein Wert befindet.
anzahl -> Liste mit den Werten
colorizer -> meine Legende
farbePruefen -> Liste um Farbe in Legende zu prüfen
Federball-Fridolin Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 30.08.16 14:19 
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Guten Tag Federball-Fridolin,

mein Vorschlag wäre, errechne dir die Summe aller Objekte und bilde daraus den prozentualen Anteil der Objekte in den jeweiligen Ländern.
Der Anteil bestimmt demnach die Helligkeit der Farbe.


Ich muss leider ganze Zahlen in der Legende anzeigen, aber sonst natürlich eine gute Idee.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 30.08.16 16:12 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Federball-Fridolin Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mi 31.08.16 06:41 
Die Farbverteilung habe ich bereits gelöst, es geht um die Zahlen, die in der Legende angezeigt werden.
Trotzdem vielen Dank für deine Hilfe :)
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 31.08.16 10:22 
- Nachträglich durch die Entwickler-Ecke gelöscht -
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mi 31.08.16 11:43 
@Frühlingsrolle
Was du vorschlägst ist eine lineare Einteilung, was bei den Werten die uns der TE gegeben hat (10, 600, 3000) aber nicht aussagekräftig ist.

@TE
Was mir spontan einfällt, wäre der k-Means Clusteralgorithmus. Eine fertige Implementierung findest du im Accord.NET Framework.

Der k-Means-Algorithmus gruppiert Objekte anhand ihrer Merkmalsvektoren. Die Merkmalsvektoren werden anhand einer bestimmten Distanzfunktion einer der k Gruppen zugeordnet.

Das klingt erst mal kompliziert, ist aber in deinem Fall ganz einfach:

Du hast gesagt, du hast eine Liste von Zahlen. Das sind deine Objekte, die du gruppieren willst, sprich eine Zahl ist ein Objekt in deiner Ausgansmenge.
Jedes Objekt (also jede Zahl in deiner Liste) besitzt einen Merkmalsvektor. Der Merkmalsvektor dient dazu, die relevanten Eigenschaften eines Objekts in numerischer Form darzustellen. Einfach gesagt: der Merkmalsvektor macht aus deinem Objekt eine Zahl, mit der man rechnen kann. Da dein Objekt aber schon eine Zahl ist, kannst du diese genauso als Merkmalsvektor verwenden. D.h. dein Objekt ist gleichzeit dein Merkmalsvektor und ist einfach nur eine Zahl.
Die Distanzfunktion berechnet dann den Abstand deines Merkmalsvektors zu dem Clusterschwerpunkt. In deinem Fall bedeutet dass einfach nur: |i-m| wobei i die aktuelle Zahl aus deiner Liste und m der aktuelle Mittelwert deiner Gruppe ist.
Der Gruppenschwerpunkt ist in deinem Fall der Mittelwert der Gruppe.

Wie funktioniert der Algorithmus?

Das k in k-Means steht für die Anzahl der Gruppen, die du am Ende haben willst. Wenn du also deine Liste in drei Gruppen aufteilen willst, ist k=3.
Der Algorithmus sucht sich zu Beginn 3 zufällige Werte aus deiner Liste und benutzt diese als Startpunkte (bei 3 Gruppen wäre in deinem Fall 0, Mittelwert der Liste und das Maximum am Besten). Diese 3 Punkte (oder in deinem Fall: diese 3 Zahlen) sind jetzt deine Gruppenschwerpunkte. Nun nimmt der Algorithmus jede Zahl aus deiner Liste, und berechnet, welcher Gruppenschwerpunkt am nächsten ist und fügt diese Zahl zu der Gruppe hinzu. Nachdem er sie hinzugefügt hat, berechnet er den Schwerpunkt der entsprechenden Gruppe neu, indem er den Mittelwert aller Zahlen in deiner Gruppe nimmt. Dieses Verfahren sorgt dafür, dass die Gruppenschwerpunkte in deinem Wertebereich (also vom kleinsten bis zum größten Wert in deiner Liste) rumwandern und somit eng zusammen liegende Zahlen in einer Gruppe landen, auch wenn der Startpunkt nicht optimal gewählt wurde.

Am Ende spuckt dir der Algorithmus dann deine k (bei uns 3) Gruppen aus. Du musst dann nur noch von jeder Gruppe den Mittelwert berechnen und diesen dann auf eine "schöne" Zahl runden.

Alternativ zum k-Means-Algorithmus könnte man auch ein Histogramm aus deiner Liste erstellen und bei den Tiefpunkten aufteilen. Aber diese Methode ist (glaube ich zumindest) störanfälliger und außerdem habe ich jetzt keine Zeit das Thema weiter auszuholen.

Hoffe das hilft dir weiter.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 31.08.16 13:03 
- Nachträglich durch die Entwickler-Ecke gelöscht -
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mi 31.08.16 13:51 
Also von einer linearen Abhängigkeit wie du sie beschreibst (Farbe zu Höhenmeter) kann ich hier nichts lesen.
Was ich bisher gelesen und verstanden habe ist folgendes:
- TE hat x verschiedene Objekte (Länder)
- Jedem Objekt ist eine Zahl zugeornet (Die Anzahl der Objekte in einem Land), diese ist der Markmalsverktor.
- "Naheliegende" Merkmalsvektoren sollen gruppiert werden (daher k-Means)
- Jeder Gruppe wird eine Farbe zugeordnet

Er schreibt nirgendwo, dass die Farben linear berechnet werden, lediglich, dass Merkmalsvektoren mit kleineren Werten heller dargestellt werden sollen. Das kann aber linear, logarithmisch, polynomiell, exponentiell oder stufenweise über eine Zuordnungstabelle passieren.

Nehmen wir mal an, er hat diese Werte: (100, 120, 400, 220, 312, 320, 20209, 59033, 30283, 40222, 1000, 1500, 1100, 2300). Das entspräche 14 Ländern. Wenn er jetzt durchweg linear färben würde (und zwar nur in der Helligkeit, wie er es ja auch schreibt), hätte man eine Auflösung von (59033-100)/255=231 zu 1 im Alphakanal. Das wiederum bedeutet deine erste Gruppe (100, 120, 400, 220, 312, 320) hätte einen Alphawert von 255 oder 254. Die nächste Gruppe (1000, 1500, 1100, 2300) hätte einen Wert von 251 bis 245. Die letzte Gruppe (20209, 59033, 30283, 40222) hätte dann Werte von 167 bis 0. Lineare zuordnung würde hier einfach keinen Sinn machen, weil die ersten beiden Gruppen sich optisch kaum unterscheiden würden, die Letzte dann aber das halbe Wertespektrum unter sich aufteilt.

Bei k-Means hingegen, kann man zusammenliegende Werte erkennen (z.B. in 3 Gruppen, je nach dem wie viele Stufen du in der Legende anzeigen möchtest). Und dann die Helligkeitsstufen entsprechend der Gruppenanzahl aufteilst, also 255 / (3 - 1) = 127, hast du für die erste Gruppe einen Aplhawert von 255, die Zweite hat 128 und die Dritte 0.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 31.08.16 14:34 
- Nachträglich durch die Entwickler-Ecke gelöscht -
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Do 01.09.16 00:34 
Dann warten wir mal ab was der TE meint.

Ich habe mal eine Implementierung des KMeans gemacht (allerdings in C#), falls jemand damit spielen möchte:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tests
{
  class Program
  {
    static void Main(string[] args)
    {
      while (true)
      {
        Console.WriteLine("Enter some numbers (comma or space seperated):");
        string input = Console.ReadLine();

        if (string.IsNullOrEmpty(input))
          return;

        string[] numbers = input.Split(new[] {","" "", "}, StringSplitOptions.RemoveEmptyEntries);
        List<int> values = new List<int>();
        foreach (string number in numbers)
        {
          int i;
          if (int.TryParse(number, out i))
            values.Add(i);
        }

        Console.WriteLine();
        Console.WriteLine("How many groups should be generated?");
        input = Console.ReadLine();

        if (string.IsNullOrEmpty(input))
          return;

        int k;

        if (!int.TryParse(input, out k))
          return;

        KMeansResult result = KMeans(values, k);

        Console.WriteLine();
        Console.WriteLine("Result:");
        for (int i = 0; i < result.K; i++)
        {
          StringBuilder sb = new StringBuilder();
          sb.Append($"[Group {i}]: ");

          int[] group = result.GetGroup(i);

          foreach (int value in group)
            sb.Append($"{value}, ");

          sb = sb.Remove(sb.Length - 22);
          sb.Append($" (center: {result.GetGroupCenter(i):0.###})");

          Console.WriteLine(sb.ToString());
        }

        Console.WriteLine();
        Console.WriteLine();
      }

      Console.ReadLine();
    }

    private static KMeansResult KMeans(IEnumerable<int> values, int k)
    {
      List<int> source = values.OrderBy(v => v).ToList();
      double[] groupCenters = new double[k];
      int step = source.Count / k;
      
      for (int i = 0; i < k; i++)
        groupCenters[i] = source[i * step + step / 2];

      int[][] result = new int[k][];

      List<int>[] groups = new List<int>[k];

      for (int i = 0; i < groups.Length; i++)
        groups[i] = new List<int>();
      
      while (source.Count > 0)
      {
        int feature = source[0];
        source.RemoveAt(0);

        int nearestGroupIndex = 0;
        double minValue = double.MaxValue;

        for (int i = 0; i < groups.Length; i++)
        {
          double delta = Math.Abs(feature - groupCenters[i]);
          if (delta < minValue)
          {
            minValue = delta;
            nearestGroupIndex = i;
          }
        }

        groups[nearestGroupIndex].Add(feature);
        double groupSum = 0;

        for (int i = 0; i < groups[nearestGroupIndex].Count; i++)
          groupSum += groups[nearestGroupIndex][i];

        groupCenters[nearestGroupIndex] = groupSum / groups[nearestGroupIndex].Count;
      }

      for (int i = 0; i < k; i++)
        result[i] = groups[i].ToArray();

      return new KMeansResult(groupCenters, result, k);
    }
  }

  class KMeansResult
  {
    public int K { get; }

    double[] groupCenters;
    int[][] values;

    public KMeansResult(double[] groupCenters, int[][] values, int k)
    {
      this.groupCenters = groupCenters;
      this.values = values;
      K = k;
    }

    public int[] GetGroup(int index)
    {
      return values[index];
    }

    public double GetGroupCenter(int index)
    {
      return groupCenters[index];
    }
  }
}

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 01.09.16 07:40 
- Nachträglich durch die Entwickler-Ecke gelöscht -