Daten Clustern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

Liebes Forum, komme nicht wirklich weiter mit folgender Sache:
Die Daten sehen so aus:

Code: Alles auswählen

3     14 16 18
3     13 17 18
3     12 17 16
4     12 16 16
4     10 16 13
5      8  15 19
5      6  15 19
5      6  16 18
Was ich benötige ist eine Liste, die so aussieht:

Code: Alles auswählen

[ [3, [14 16 18], [13,17,18], [12,17,16]], [4, [12 16 16], [10, 16, 13]], [5, [8 15 19], [6 15 19], [6 16 18]] ]
Also so eine Art Reduktion, wobei alle Elemente mit demselben ersten Index, 3, 4, 5, zusammengefasst werden. Also in der neuen Liste wäre das erste Element eines Elements jeweils der Index, und die weiteren Elemente die Dreier-Tupel. Sieht jemand eine einfach Lösung? Wäre echt froh um irgendwelche Hinweise. Die Anzahl Dreier-Tuple zu einem Index gehörend ist variabel.
[url=http://www.proandkon.com]proandkon.com[/url]
problembär

Vorschlag:

Code: Alles auswählen

#!/usr/bin/env python
# coding: iso-8859-1

a = """3     14 16 18
3     13 17 18
3     12 17 16
4     12 16 16
4     10 16 13
5      8  15 19
5      6  15 19
5      6  16 18"""

while "  " in a:
    a = a.replace("  ", " ")

b = a.split("\n")

d = {}
for i in b:
    c = i.split(" ")
    for u in range(len(c)):
        c[u] = int(c[u])
    ind = c[0]
    if not d.has_key(ind):
        d[ind] = []
    d[ind].append(c[1:])
out = []
dkeys = d.keys()
dkeys.sort()
for i in range(len(dkeys)):
    out.append([])
    out[i].append(dkeys[i])
    for u in d[dkeys[i]]:
        out[i].append(u)
print out
;)

Wie leicht das Ineinanderschachteln mehrerer und verschiedener Arten von Listen und Dictionaries in Python ist, beeindruckt mich immer wieder. Versuch' das mal in Perl!
BlackJack

@problembär: Mich beeindruckt wie kompliziert und umständlich Du einfache Sachen aufschreiben kannst. Lass die Finger von Python… :roll:
problembär

Nö, dafür isses doch.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Meine Lösung:

Code: Alles auswählen

In [1]: s = '''3     14 16 18
   ...: 3     13 17 18
   ...: 3     12 17 16
   ...: 4     12 16 16
   ...: 4     10 16 13
   ...: 5      8  15 19
   ...: 5      6  15 19
   ...: 5      6  16 18'''

In [2]: from collections import defaultdict

In [3]: res = defaultdict(list)

In [4]: for line in s.splitlines():
   ...:     k, v = line.split(None, 1)
   ...:     res[k].append(v.split())
   ...: 

In [5]: print res
------> print(res)
defaultdict(<class 'list'>, {'3': [['14', '16', '18'], ['13', '17', '18'], ['12', '17', '16']], '5': [['8', '15', '19'], ['6', '15', '19'], ['6', '16', '18']], '4': [['12', '16', '16'], ['10', '16', '13']]})
Etwas einfacher und kürzer…

//Edit: @problembär … wtf
the more they change the more they stay the same
BlackJack

@problembär: Wie bitte? Wofür ist was? Python um damit total unpythonischen Quelltext zu schreiben?
problembär

BlackJack hat geschrieben:@problembär: Wie bitte? Wofür ist was? Python um damit total unpythonischen Quelltext zu schreiben?
Python bietet Integer, Strings, Listen und Dictionaries usw. an und bestimmte Operationen, die man darauf anwenden kann, um Daten zu verarbeiten und so in die gewünschte Form zu bringen. Genau das habe ich gemacht. Ohne Zusatzmodule, stattdessen reines built-in-Python. Wer meint, das sei nicht pythonisch, hat sich vielleicht selbst zu weit von den Grundlagen der Sprache entfernt.
BlackJack

@problembär: Pythonisch ist Code ohne solche komischen, völlig unnötigen Verrenkungen zu schreiben. Selbst ohne das `defaultdict` kann man deutlich einfacheren Quelltext schreiben der das Problem löst. Alleine das ``for i in range(obj): obj``-Anti-Pattern disqualifiziert Dich schon. Dein Quelltext sieht aus als wenn Du nicht darüber nachgedacht hast was Du da tust, oder aber dass das Absicht war es möglichst umständlich zu machen.
0x1cedd1ce
User
Beiträge: 31
Registriert: Sonntag 3. Oktober 2010, 12:21

Code: Alles auswählen

a = """3     14 16 18
3     13 17 18
3     12 17 16
4     12 16 16
4     10 16 13
5      8  15 19
5      6  15 19
5      6  16 18"""

out_dict = {}

for line in [l.split() for l in a.splitlines()]:
    if line[0] not in out_dict:
        out_dict[line[0]] = []
    out_dict[line[0]].append(line[1:])
print(out_dict)
Selbe Ausgabe wie bei Dav1d nur als normales dict.
BlackJack

Eine Lösung ohne `defaultdict` muss sich nur an zwei Stellen geringfügig von einer mit unterscheiden. Dort wo das Wörterbuch erstellt wird und dort wo ein Wert hinzugefügt wird:

Code: Alles auswählen

    # index2triples = defaultdict(list)
    index2triples = dict()
    for row in (map(int, line.split()) for line in data.splitlines()):
        index, triple = row[0], row[1:]
        # index2triples[index].append(triple)
        index2triples.setdefault(index, []).append(triple)
    result = map(list, sorted(index2triples.items()))
    print result
Sofern man allerdings nicht unbedingt mit so etwas uraltem wie Python 2.4 kompatibel sein muss, sollte man auch das genau für solche Einsatzzwecke passende `defaultdict` verwenden.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Oder mit groupby:

Code: Alles auswählen

import itertools
import operator

data = sorted([line.split() for line in data.splitlines()])
itertools.groupby(data, operator.itemgetter(0))
Etwas unschön ist natürlich die überflüssige Sortierung falls die Daten nicht entsprechend vorliegen.
Das Leben ist wie ein Tennisball.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@mzh:
Wie siehts denn mit der Sortierung in der Zielliste aus? Darf die verloren gehen? Alle hier gezeigten Lösungen ignorieren die nämlich mehr oder weniger.

Falls die Sortierung wichtig ist, wäre ein Generator ganz nützlich (ungetestet):

Code: Alles auswählen

def gen(it):
    row = None
    for line in it:
        nrow = map(int, line.split())
        if row is None:
            row = [nrow[0], [nrow[1:]]]
        elif row[0] == nrow[0]:
            row[1].append(nrow[1:])
        else:
            yield row
            row = [nrow[0], [nrow[1:]]]
    yield row
Der Generator fasst nur gleiche folgende Zeilen zusammen (also solche mit identischem "Indexwert"). Desweiteren hat er den Vorteil, maximal die Daten der gleichen Zeilen zwischen zu speichern, und funktioniert daher auch mit sehr großer Inputdatenmenge.
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

@Leute
Danke für die Hinweise so weit, die groupby Lösung finde ich schon recht ansprechend, das könnte evtl. so dreissig Zeilen Code bei mir ersetzen (das Beispiel ist natürlich nur eine versimplifizierte Version von dem was ich machen sollte).
Die Sortierung ist nicht unbedingt zwingend, ergibt sich aber eigentlich automatisch durch das ordinale erste Element, dh. 3, 4, 5, ... . Dh. diese Zahlen geben die Reihenfolge vor.

In meiner eigenen Lösung habe ich so etwas wie einen externen und internen Zähler implementiert. Der externe Zähler wird genau dann inkrementiert, wenn der Leit-index sich ändert, also von z.B. 3 auf 4. Und in jedem Schritt wird der innere Index, der die Reihen die zu einem Leit-index gehören anschreibt, um eins erhöht. Auch, damit ich die Zeilen zu einem Index zuordnen kann, sammle ich einelementige Listen der Indeces bevor die Bearbeitung beginnt (siehe 'total_indeces'). Zuerst, und das ist bei mir etwas umständlich, muss ich also eine Liste von Leitindeces erstellen. Dann kommt der beschriebene Algorithmus. M.E. besteht hier die Hauptschwierigkeit darin, dass ich nicht im Voraus weiss, wie viele Element zu einem Leitindex gehören, im Beispiel hat '3' drei Elemente, '4' zwei und '5' wiede drei, können aber beliebig viele sein.

Code: Alles auswählen


a=
magic_list_typecast(
3     14 16 18
3     13 17 18
3     12 17 16
4     12 16 16
4     10 16 13
5      8  15 19
5      6  15 19
5      6  16 18)

# a[0] = 14 16 18

total_indeces = [ 3, 3, 3, 4, 4, 5, 5, 5 ]
unique_list = [ [3], [4], [5] ]

# Cluster elements
tot_cnt = 0
uni_cnt = 0
for ti in total_indeces:
    if ti == unique_list[uni_cnt][0]:
        unique_indeces[uni_cnt].append(a[tot_cnt])
        tot_cnt += 1
    else:
        uni_cnt += 1
        unique_indeces[uni_cnt].append(a[tot_cnt])
        tot_cnt += 1
Hat mich schon eine Weile gekostet bis ich darauf kam, das mit zwei Indeces zu machen, ich will aber trotzdem eine Lösung finden die einfacher zu lesen ist, weil ich im Moment selber nicht sicher bin, ob es wirklich immer das machen wird, was ich will.
[url=http://www.proandkon.com]proandkon.com[/url]
Antworten