Wie 3D Liste initialiseren (Pendant zu Matlab 2D Cells)

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
Nras
User
Beiträge: 24
Registriert: Dienstag 25. März 2014, 10:38

Hallo zusammen,

ich komme von einem Matlab-Hintergrund und bin nun dabei, einige geschriebene Programme in Python umzuschreiben. Dazu habe ich schon einige Tutorials durchgearbeitet. Auf der Suche nach einem Pendant zu Matlabs cell-arrays habe ich gefunden, dass man Listen benutzen soll. Für eine Cell mit 3 Zeilen und 2 Spalten kann ich das so initialisieren und dann nach belieben füllen, beispielsweise mit .append():

Code: Alles auswählen

M = [
    [[], []],
    [[], []],
    [[], []]
]

print M[1][0]
M[1][0].append(17)
print M[1][0]
Meine Frage lautet, wie kann ich das für beliebige Größen initialisieren, also für m Zeilen und n Spalten?

Viele Grüße,
Nras.
BlackJack

@Nras: Das was Du da zeigst ist in Python ungewöhnlich. Man erstellt normalerweise keine leeren verschachtelten Datenstrukturen um die dann zu füllen, sondern erstellt die Strukturen gleich mit den Daten. Und das dann mit Schleifen und bei Listen in dem man mit einer leeren Liste anfängt und die dann füllt, oder falls es sich anbietet erstellt man die Liste gleich mit Werten mit der „list comprehension”-Syntax.

Ansonsten kann man die Frage so beantworten:

Code: Alles auswählen

In [6]: M = [[list() for _ in xrange(2)] for _ in xrange(3)]

In [7]: M
Out[7]: [[[], []], [[], []], [[], []]]
Aber wie gesagt, es kann passieren das da am Ende kein typischer Python-Code bei heraus kommt.
Nras
User
Beiträge: 24
Registriert: Dienstag 25. März 2014, 10:38

Hallo,

danke für den Code, das liefert genau das von mir Gewünschte. Dass kein üblicher Python Code enstehen könnte, kann wirklich sein, da ich in Python noch sehr unerfahren bin. Vielleicht gibt es ja auch eine viel elegantere Methode für mein Vorhaben. Ich möchte ein zweidimensionales Histogramm von gebenen Listen x und y machen, ich brauche aber nicht bloß die Häufigkeiten, sondern am Ende für jedes der Bins in der 2d-Ebene die Liste mit den Indizes der Datenpunkte, die in dieses Bin gefallen sind. Pseudomäßig also so (x und y gleich lang gegeben):

Code: Alles auswählen

    for index in range(len(x)):
        row = find_correct_row(x[index])
        col = find_correct_col(y[index])
        m[row][col].append(index)
Da fällt es mir schwer bis unmöglich, direkt mit list comprehensions oder ähnlichem zu arbeiten.

Viele Grüße,
Nras.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Was man hier sehr wahrscheinlich machen wuerde, waere ein Dictionary statt einer komplexen Listen Struktur zu nutzen.

Fuer dein kleines Snippet also:

Code: Alles auswählen

from collections import defaultdict

distribution = defaultdict(list)

xs = [...]
ys = [...]

for index, (x, y) in enumerate(zip(xs, ys)):
    row = find_correct_row(x)
    col = find_correct_col(y)
    distribution[(row, col)].append(index)
Damit muss man nicht manuell mit den Indizes hantieren und spart sich das aufbauen und verwalten der Listenstruktur.
Nras
User
Beiträge: 24
Registriert: Dienstag 25. März 2014, 10:38

Hallo,

dass Tupel als key für ein dictionary funktionieren, habe jetzt erst gelernt. Die Zeile 8 gefällt mir auch sehr gut und ist wohl viel mehr "python-like" als meine Schleife nur über den Index. Dafür auch vielen Dank.
Ich bin auf jeden Fall interessiert, auch die Vorteile von Python zu nutzen und nicht nur Sachen 1zu1 zu übersetzen.

Viele Grüße,
Nras.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Nras hat geschrieben:Hallo zusammen,

ich komme von einem Matlab-Hintergrund und bin nun dabei, einige geschriebene Programme in Python umzuschreiben. Dazu habe ich schon einige Tutorials durchgearbeitet. Auf der Suche nach einem Pendant zu Matlabs cell-arrays habe ich gefunden, dass man Listen benutzen soll.
Nein, nimm numpy.

Code: Alles auswählen

import numpy
print numpy.empty((3, 2))
print numpy.zeros((3, 2))
BlackJack

@Darii: Ich hätte ja auch `numpy` vorgeschlagen, wenn es denn zu der Aufgabenstellung gepasst hätte.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

BlackJack hat geschrieben:@Darii: Ich hätte ja auch `numpy` vorgeschlagen, wenn es denn zu der Aufgabenstellung gepasst hätte.

Code: Alles auswählen

In [4]: array = numpy.empty((2,2), dtype=object)
In [9]: array[1,1] = "test"
In [12]: array[0,1] = 2
In [13]: array
Out[13]:
array([[None, 2],
       [None, 'test']], dtype=object)
Verhält sich wie IMHO wie ein Matlab cell Objekt.

Grüße,
anogayales
BlackJack

@anogayales: Das mag ja sein, aber wie löst man damit jetzt die Aufgabenstellung und welchen Vorteil hätte das gegenüber dem `defaultdict()`? Oder verschachtelten Listen?
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

BlackJack hat geschrieben:@anogayales: Das mag ja sein, aber wie löst man damit jetzt die Aufgabenstellung und welchen Vorteil hätte das gegenüber dem `defaultdict()`? Oder verschachtelten Listen?
Das wollte doch OP wissen:
Nras hat geschrieben: Meine Frage lautet, wie kann ich das für beliebige Größen initialisieren, also für m Zeilen und n Spalten?
und das löst doch meine Codesnippet.
Der Vorteil liegt darin, dass es wesentlich lesbarer als das hier ist

Code: Alles auswählen

M = [[list() for _ in xrange(2)] for _ in xrange(3)]
Ich weiß, es ist einfach den von dir vorgestellten Codesnippet in einer Funktion zu packen. Wenn man vom Matlab Hintergrund kommt ist numpy einfacher zu verstehen als Python Listen oder Defaultdicts.

Grüße,
anogayales
Nras
User
Beiträge: 24
Registriert: Dienstag 25. März 2014, 10:38

Hallo anogayales,

deine Variante funktionert bei mir nicht einfach so. Ich will Listen unbekannter Länge speichern, nicht nur eine Zahl. Das heißt, wenn array[i,j] noch nicht gefüllt ist, müsste ich array[i,j] = [17] schreiben, und ansonsten array[i,j].append(17):
Ebenso verhält es sich, wenn ich nicht das Defaultdict benutzen würde, was ich bis gestern auch noch nicht kannte, sondern ein leeres dict und dann mit has_key abfrage:

Code: Alles auswählen

import numpy as np

array = np.empty((10,10), dtype=object)
mydict = {}
for i in range(1000):
    # zufaellige Position
    row = np.random.randint(0,10,1)[0]
    col = np.random.randint(0,10,1)[0]

    # array-Variante
    if array[row,col] == None:
        array[row,col] = [i]
    else:
        array[row,col].append(i)

    # dictionary-Variante
    if mydict.has_key((row,col)):
        mydict[(row,col)].append(i)
    else:
        mydict[(row,col)] = [i]

print array[3,7]
print array[2,3]
print "-----"
print mydict[(3,7)]
print mydict[(2,3)]
Da es bestimmt noch mehr Varianten gibt (neben meiner Idee der Liste in der Liste in der Liste), frage ich mich, ob eine der Methoden den anderen vorzuziehen ist.
Ein Gedanke, der mir gefällt, ist, dass ich mit dem dictionary freier bin, was meine keys betrifft. Das müssen dann ja nicht Integer (Zeile, Spalte einer Matrix) sein, sondern könnten direkt Werte enthalten für das Zentrum meiner Bins. In dieser Anwendung habe ich beispielsweise 2 mal n Zufallszahlen zwischen 0 und 1. Beispielsweise 10 bins in jede Richtung, dann wären die Zentren bei 0.05, 0.15, ..., 0.95 und das könnte ich direkt als key nehmen. array[1,0] = ... wäre dann direkt mydict[(0.15, 0.05)].

Wie gesagt, ich bin noch recht neu in Python (numpy benutze ich natürlich schon) und weiß eben noch nicht, was "gut" ist oder welche Vorteile zum Beispiel ein numpy.array gegenüber einem dictionary hat. Wenn da noch jemand etwas zu sagen hätte, würde ich das sicher aufmerksam lesen.

Viele Grüße,
Nras.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hallo Nras,

es wäre vielleicht auch gut zu wissen was du *eigentlich* machen willst. Willst du ein Histogramm berechnen? Wozu brauchst du pro Bin eine Liste?

Wenn du eine Liste variabler Länge pro Bin brauchst, dann ist ein Defaultdict der richtige Weg. Eine Abbildung von Bin-Index auf Bin-Zentrum musst du dir dann noch selbst schreiben, das ist aber schnell gemacht.

Grüße,
anogayales
BlackJack

@anogayales: Ja das war die Frage im ersten Beitrag, das ist aber (IMHO) nicht die beste Datenstruktur für das was Nras dann danach damit machen möchte.

@Nras: Die `dict.has_key()`-Methode ist veraltet, da würde man eher den ``in``-Operator verwenden, also statt ``if haystack.has_key(needle):`` ein ``if needle in haystack:``.
Nras
User
Beiträge: 24
Registriert: Dienstag 25. März 2014, 10:38

Hallo,

zunächst mal Danke für die Antworten. Das mit den verschachtelten Listen war nur so eine Idee, die ich inzwischen aber schon zugunsten von arrays oder dictionaries verworfen habe. Vermutlich wäre es besser, weitere Fragen in einem anderen Beitrag zu erstellen.
anogayales hat geschrieben:... es wäre vielleicht auch gut zu wissen was du *eigentlich* machen willst. Willst du ein Histogramm berechnen? Wozu brauchst du pro Bin eine Liste?
Zudem möchte ich euch nicht mit dem INhalt des ganzen Projekt belasten, aber vielleicht reicht es, kurz folgendes zu schreiben:
ich will später für jedes Bin (i,j) in einer bestimmten Weise "benachbarte Bins" einsammeln, bis ich eine bestimmte Anzahl an Elementen, beispielsweise 100, beisammen habe. Für dieses Zählen habe ich das Histogramm, das einfach aus obigen dict (oder array) erstellt wird, in dem ich ein mal die Länge der Einträge auslese und abspeichere.

Code: Alles auswählen

freq_dict = {}
for key in mydict:
    hist_dict[key] = len(mydict[key])
Mit den Daten, die in dem Bin (i,j) und den Nachbar-Bins liegen, werden dann weitere Berechnungen durchgeführt (beispielsweise Quantile bestimmt). Genau dazu brauche ich die Information, welche Datenpunkte (also den Index) in welches Bin gefallen sind. Das Ergebnis davon wird dann wiederum in einem dict (oder array) abgelegt und auch damit wird noch weitergerechnet, usw. Das Endergebnis ist dann ein numpy array, das in eine Datenbank exportiert wird.

Den Code für das gesamte Projekt habe ich in Matlab schon fertig und dieser soll auch prinzipiell nicht mehr geändert, eben nur in Python (auch numpy) übersetzt werden.
Wenns denn hilft (-:
anogayales hat geschrieben:Eine Abbildung von Bin-Index auf Bin-Zentrum musst du dir dann noch selbst schreiben, das ist aber schnell gemacht
Klar, so ist es ja bisher auch realisiert, aber man könnte sich ja den Umweg über den Bin-Index direkt sparen und das jeweilige Bin-Zentrum direkt als key für das dictionary nehmen.
BlackJack hat geschrieben:@Nras: Die `dict.has_key()`-Methode ist veraltet, da würde man eher den ``in``-Operator verwenden, also statt ``if haystack.has_key(needle):`` ein ``if needle in haystack:``.
Danke, soetwas sind für mich sehr wertvolle Kommentare, die mir beim Einstieg in Python helfen, damit ich mir direkt die Konventionen aneigne (-:

Viele Grüße,
Nras.
BlackJack

@Nras: Etwas kompaktere Art die Erstellung des `freq_dict` zu schreiben (ungetestet):

Code: Alles auswählen

freq_dict = dict((key, len(value)) for key, value in mydict.viewitems())
Antworten