Gruppieren innerhalb einer Liste

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
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Dienstag 27. März 2012, 12:02

Hallo,

ich habe eine Liste, die wie folgt aussieht:
liste =['SNAP_20120105_101306.bz2', 'mod.prot_SNAP_20120106_101306.bz2', 'mod.prot42.SNAP_20100109_101311.bz2', 'SNAP_20100305_103306.bz2', 'mod.prot_SNAP_20110705_121301.bz2', 'folio_20100112_102206.bz2', 'SNAP_20100112_102206.bz2', 'mod.prot42.SNAP_20120134_101809.bz2', 'SNAP_20120912_101306.bz2', 'mod.prot_SNAP_20120312_091306.bz2', 'folio_20100109_102206.bz2']
Diese Liste möchte ich nun sortieren/gruppieren, so dass ich dann als Ausgabe folgendes erhalte:
liste_2010 = [['folio_20100109_102206.bz2', 'folio_20100112_102206.bz2'], ['mod.prot42.SNAP_20100109_101311.bz2'], ['SNAP_20100112_102206.bz2', 'SNAP_20100305_103306.bz2']]
Es soll also eine neue Liste generiert werden, die alle Daten aus dem Jahr 2010 enthält, gruppiert nach dem String, der sich vor der Jahreszahl befindet.

Wie stelle ich das am geschicktesten an?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7477
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dienstag 27. März 2012, 12:25

Ich würde das so angehen:

Code: Alles auswählen

from collections import defaultdict

d = defaultdict(list)

for filename in filenames:                          
    group, year = re.search(r"(.*)_(\d{4})\d{4}_", filename).groups()
    if int(year) == 2010:
        d[group].append(filename)
		
print d.values()
Das ist natürlich nocht nicht "optimiert"; man sollte den RegExp kompilieren.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 5936
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 27. März 2012, 12:25

Ich kopiere einfach mal die Ausgaben aus der IPython-Shell:

Code: Alles auswählen

In [1]: from collections import defaultdict

In [2]: ergebnisse = defaultdict(list)

In [3]: liste =['SNAP_20120105_101306.bz2', 'mod.prot_SNAP_20120106_101306.bz2', 'mod.prot42.SNAP_20100109_101311.bz2', 'SNAP_20100305_103306.bz2', 'mod.prot_SNAP_20110705_121301.bz2', 'folio_20100112_102206.bz2', 'SNAP_20100112_102206.bz2', 'mod.prot42.SNAP_20120134_101809.bz2', 'SNAP_20120912_101306.bz2', 'mod.prot_SNAP_20120312_091306.bz2', 'folio_20100109_102206.bz2']

In [4]: for element in liste:
   ...:     name, datum = element.split('_', 1)
   ...:     if datum.startswith('2010'):
   ...:         ergebnisse[name].append(element)
   ...:         
   ...:         

In [5]: ergebnisse
Out[5]: defaultdict(<type 'list'>, {'SNAP': ['SNAP_20100305_103306.bz2', 'SNAP_20100112_102206.bz2'], 'folio': ['folio_20100112_102206.bz2', 'folio_20100109_102206.bz2'], 'mod.prot42.SNAP': ['mod.prot42.SNAP_20100109_101311.bz2']})

In [6]: ergebnisse.values()
Out[6]: 
[['SNAP_20100305_103306.bz2', 'SNAP_20100112_102206.bz2'],
 ['folio_20100112_102206.bz2', 'folio_20100109_102206.bz2'],
 ['mod.prot42.SNAP_20100109_101311.bz2']]
Benutzeravatar
snafu
User
Beiträge: 5936
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 27. März 2012, 12:29

Hyperion hat geschrieben:Das ist natürlich nocht nicht "optimiert"; man sollte den RegExp kompilieren.
Braucht man nicht. Eine gewisse Anzahl an zuletzt kompilierten regulären Ausdrücken wird automatisch gecached (s. Modulquelltext).

Im übrigens fehlt bei uns beiden noch das `sorted(d.values())`. ;)
Zuletzt geändert von snafu am Dienstag 27. März 2012, 12:31, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7477
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dienstag 27. März 2012, 12:30

@snafu: Auf `split` wollte ich auch erst setzen; aber dann fiel mir u.a. der zweite Eintrag der Liste auf. Da kommt noch ein "_" vor dem Datum... somit funzt Deine Lösung hier "zufällig". Robuster ist es hier über RegExps zum Ziel zu gelangen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 5936
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 27. März 2012, 12:33

Hyperion hat geschrieben:@snafu: Auf `split` wollte ich auch erst setzen; aber dann fiel mir u.a. der zweite Eintrag der Liste auf. Da kommt noch ein "_" vor dem Datum... somit funzt Deine Lösung hier "zufällig". Robuster ist es hier über RegExps zum Ziel zu gelangen.
Hast auf jeden Fall recht. So gründlich hatte ich mir das Schema der Vorgabe garnicht angeguckt. :oops:

Ohne Zweifel bringt es für einen möglichen Verwendungszweck als eine Funktion, die das gewünschte Jahr als Argument nimmt, natürlich auch mehr, das geparste Jahr in eine "echte" Zahl zu wandeln. Meins war eher Quick&Dirty. :)
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Dienstag 27. März 2012, 14:07

@snafu, Hyperion:

Ja - super !!

Wenn ich mir eure Lösungen so anschaue, dann sieht das so einfach aus. :D

Tja, was soll ich sagen. Besten Dank!!! :!:
Benutzeravatar
snafu
User
Beiträge: 5936
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 27. März 2012, 14:56

@Hyperion:

Code: Alles auswählen

re.split('(?<=\D)_(?=\d)', 'mod.prot_SNAP_20120106_101306.bz2')
...scheint ähnlich robust zu sein und ist für jemanden, der ein bißchen Ahnung von regulären Ausdrücken hat (oder notfalls weiß, wo er nachgucken muss), IMHO etwas naheliegender.

Vielleicht kurz für diejenigen, die es nicht auf Anhieb "entziffern" können:

Suche nach "Nicht-Zahl" (also hier: Buchstabe oder Unterstrich), gefolgt von Unterstrich, gefolgt von Zahl. Es wird Gebrauch von Lookbehind (`(?<=...)`) und Lookahead (`(?=...)`) gemacht. Das kleine `\d` matcht alle Ziffern, das große `\D` ist das genaue Gegenteil. Der Unterstrich selbst fungiert als Trennzeichen.

EDIT: Ok, man hätte bei einem festen Datumsformat nicht direkt die Jahreszahl isoliert. Daran hatte ich jetzt nicht gedacht.
Antworten