Seite 1 von 1

Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 14:43
von frosch
Hallo,

ich bräuchte mal wieder euer Hilfe. Ich habe als Input ein File welches 3 Spalten enthält:
VON , BIS , ID
('04.07.2013', '31.07.2013', '292'),
('01.08.2013', '11.08.2013', '292'),
('04.03.2013', '31.03.2013', '293'),
('01.04.2013', '05.04.2013', '293'),
('06.04.2013', '02.06.2013', '293'),
('03.06.2013', '07.06.2013', '293'),
('09.09.2013', '20.09.2013', '293'),
('22.05.2013', '24.05.2013', '303')


Mein Programm soll nun die Datensätze mit gleicher ID und durchgehenden Zeitraum zusammenfügen, dass Ergebis sollten dann sein:
('04.07.2013', '11.08.2013', '292'),
('04.03.2013', '07.06.2013', '293'),
('09.09.2013', '20.09.2013', '293'),
('22.05.2013', '24.05.2013', '303')

Bis jetzt kann mein Programm nur 2 Datensätze zusammenfügen, jedoch nicht mehrere:

Code: Alles auswählen

while i < len(idlist)-1:
	if idlist[i][2] != idlist[i+1][2]:
		idlist2.append(idlist[i])
	if idlist[i][2] == idlist[i+1][2]:
		print "i", idlist[i]
		# Wenn Zeile1 Tag+1 und Zeile2 Tag gleich sind und auch der Monat gleich sind
		if ((int((idlist[i][1])[:2])+1 == int((idlist[i+1][0])[:2])) and (((idlist[i][1])[3:])[:2]) == (((idlist[i+1][0])[3:])[:2])):
			idlist2.append((idlist[i][0],idlist[i+1][1],idlist[i][2]))
			i = i+1
		# Wenn Zeile 1 Tag gleich 31 und Zeile 2 Tag 01, dann muss Zeile 1 den Monat "01" or "03" or "05" or "07" or "08" or "10" or "12" haben
		elif (idlist[i][1])[:2] == "31" and (((idlist[i][1])[3:])[:2]) == "01" or "03" or "05" or "07" or "08" or "10" or "12":
			if (idlist[i+1][0])[:2] == '01':
				idlist2.append((idlist[i][0],idlist[i+1][1],idlist[i][2]))
				i = i+1
		# Wenn Zeile 1 Tag gleich 30 und Zeile 2 Tag 01, dann muss Zeile 1 den Monat "04" or "06" or "09" or "11"
		elif (idlist[i][1])[:2] == "30" and (((idlist[i][1])[3:])[:2]) == "04" or "06" or "09" or "11":
			if (idlist[i+1][0])[:2] == '01':
				idlist2.append((idlist[i][0],idlist[i+1][1],idlist[i][2]))
				i = i+1
		# Wenn Zeile 1 Tag 28 oder 29 und Zeile 2 Tag 01, dann muss Zeile 1 den Monat "02"
		elif (((idlist[i][1])[:2]) == "28" or "29") and ((((idlist[i][1])[3:])[:2]) == "02") and ((idlist[i+1][0])[:2]) == '01':
			idlist2.append((idlist[i][0],idlist[i+1][1],idlist[i][2]))
			i = i+1
		else:
			print "else", idlist[i]
			idlist2.append(idlist[i])
	i = i+1
Wie kann ich mehrere Datensätze zusamenfügen??

Dank vorab und Quack
DER FROSCH

Re: Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 14:55
von BlackJack
@frosch: Das sieht ja gruselig aus. Als allererstes solltest Du mal die Eingabedaten von Zeichenketten in vernünftige Datentypen/Objekte umwandeln. Also mindestens die Datumsangaben in `datetime.date`-Objekte, mit denen kann man nämlich rechnen, also zum Beispiel ermitteln wie viele Tage dazwischen liegen. Dannn braucht man diesen ganzen Zeichenkettenverarbeitungswust nicht, den Du da veranstaltest.

Und dann geht man die Datensätze durch, merkt sich immer den vorhergehenden, und fügt entsprechend solange Datensätze zusammen bis sich die ID ändert, oder mehr als ein Tag zwischen den Zeiträumen liegt. Dann kommt der aktuelle Datensatz ins Ergebnis und der neue wird zum aktuellen.

Re: Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 14:56
von Sirius3
@frosch: als erstes solltest Du mal das datetime-Modul verwenden:

Code: Alles auswählen

>>> date1 = datetime.datetime.strptime('31.07.2013','%d.%m.%Y')
>>> date2 = datetime.datetime.strptime('01.08.2013','%d.%m.%Y')
>>> delta = date2-date1
>>> delta.days
1

Re: Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 15:01
von /me
Ich würde etwa so beginnen:

Code: Alles auswählen

from datetime import datetime
import itertools
import operator

data = [('04.07.2013', '31.07.2013', '292'),
        ('01.08.2013', '11.08.2013', '292'),
        ('04.03.2013', '31.03.2013', '293'),
        ('01.04.2013', '05.04.2013', '293'),
        ('06.04.2013', '02.06.2013', '293'),
        ('03.06.2013', '07.06.2013', '293'),
        ('09.09.2013', '20.09.2013', '293'),
        ('22.05.2013', '24.05.2013', '303')]
date_format = '%d.%m.%Y'

# create real dates
data = [(datetime.strptime(date_start, date_format),
         datetime.strptime(date_end, date_format),
         key) for date_start, date_end, key in data]

# sort dates
data.sort(key=lambda x: (x[2], x[0]))

# group by key
groups = itertools.groupby(data, key=operator.itemgetter(2))
Die letzten beiden Schritte kann man auch noch zusammenfassen wenn man mag.

Code: Alles auswählen

groups = itertools.groupby(sorted(data, key=lambda x: (x[2], x[0])), key=operator.itemgetter(2))

Re: Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 15:12
von BlackJack
Lösungsvorschlag:

Code: Alles auswählen

from datetime import datetime as DateTime, timedelta as TimeDelta
from pprint import pprint

ONE_DAY = TimeDelta(days=1)


def parse_date(string):
    return DateTime.strptime(string, '%d.%m.%Y').date()


def aggregate(rows):
    rows = iter(rows)
    try:
        start, end, id_ = next(rows)
    except StopIteration:
        pass  # Empty iterable.  It's okay to do nothing.
    else:    
        for next_start, next_end, next_id in rows:
            if id_ == next_id and next_start - end == ONE_DAY:
                end = next_end
            else:
                yield start, end, id_
                start, end, id_ = next_start, next_end, next_id
        yield start, end, id_


def main():
    data = [
        ('04.07.2013', '31.07.2013', '292'),
        ('01.08.2013', '11.08.2013', '292'),
        ('04.03.2013', '31.03.2013', '293'),
        ('01.04.2013', '05.04.2013', '293'),
        ('06.04.2013', '02.06.2013', '293'),
        ('03.06.2013', '07.06.2013', '293'),
        ('09.09.2013', '20.09.2013', '293'),
        ('22.05.2013', '24.05.2013', '303'),
    ]
    aggregated = list(
        aggregate((parse_date(s), parse_date(e), i) for s, e, i in data)
    )
    pprint(aggregated)


if __name__ == '__main__':
    main()
Ausgabe:

Code: Alles auswählen

[(datetime.date(2013, 7, 4), datetime.date(2013, 8, 11), '292'),
 (datetime.date(2013, 3, 4), datetime.date(2013, 6, 7), '293'),
 (datetime.date(2013, 9, 9), datetime.date(2013, 9, 20), '293'),
 (datetime.date(2013, 5, 22), datetime.date(2013, 5, 24), '303')]

Re: Zusammenfassen von Datensätzen

Verfasst: Montag 6. Januar 2014, 17:33
von diesch
Alternative:

Code: Alles auswählen

#!/usr/bin/python
# coding: utf-8

import datetime
from pprint import pprint

VON, BIS, ID = 0, 1, 2

MAX_DELTA = datetime.timedelta(days=1)

def parse_date(string):
    return datetime.datetime.strptime(string, '%d.%m.%Y').date()


idlist = [[parse_date(i[VON]), parse_date(i[BIS]), i[ID]]
          for i in 
          ('04.07.2013', '31.07.2013', '292'),
          ('01.08.2013', '11.08.2013', '292'),
          ('04.03.2013', '31.03.2013', '293'),
          ('01.04.2013', '05.04.2013', '293'),
          ('06.04.2013', '02.06.2013', '293'),
          ('03.06.2013', '07.06.2013', '293'),
          ('09.09.2013', '20.09.2013', '293'),
          ('22.05.2013', '24.05.2013', '303')
          ]

result = []


for item in sorted(idlist, key=lambda x: (x[ID], x[VON])):
    if not result:  # erster Eintrag
        result.append(item)
    elif result[-1][ID] == item[ID] and item[VON] - result[-1][BIS] <= MAX_DELTA:
        result[-1][BIS] = item[BIS]  # "verlängern"
    else:
        result.append(item)

    
pprint(result)
Ausgabe:

Code: Alles auswählen

[[datetime.date(2013, 7, 4), datetime.date(2013, 8, 11), '292'],
 [datetime.date(2013, 3, 4), datetime.date(2013, 6, 7), '293'],
 [datetime.date(2013, 9, 9), datetime.date(2013, 9, 20), '293'],
 [datetime.date(2013, 5, 22), datetime.date(2013, 5, 24), '303']]

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 7. Januar 2014, 07:45
von mutetella
Ich hätte auch noch was:

Code: Alles auswählen

In [18]: data = [
   ....: (datetime.date(2013, 7, 4), datetime.date(2013, 7, 31), '292'), 
   ....: (datetime.date(2013, 8, 1), datetime.date(2013, 8, 11), '292'), 
   ....: (datetime.date(2013, 3, 4), datetime.date(2013, 3, 31), '293'), 
   ....: (datetime.date(2013, 4, 1), datetime.date(2013, 4, 5), '293'), 
   ....: (datetime.date(2013, 4, 6), datetime.date(2013, 6, 2), '293'), 
   ....: (datetime.date(2013, 6, 3), datetime.date(2013, 6, 7), '293'), 
   ....: (datetime.date(2013, 9, 9), datetime.date(2013, 9, 20), '293'), 
   ....: (datetime.date(2013, 5, 22), datetime.date(2013, 5, 24), '303')
   ....: ]

In [19]: for bundle in data:
    grouped_data[bundle[-1]].extend(bundle[:-1])
   ....:     

In [20]: sorted(
   ....: ((min(value), max(value), key) for key, value in grouped_data.iteritems()), 
   ....: key=lambda b: b[-1]
   ....: )
Out[20]: 
[(datetime.date(2013, 7, 4), datetime.date(2013, 8, 11), '292'),
 (datetime.date(2013, 3, 4), datetime.date(2013, 9, 20), '293'),
 (datetime.date(2013, 5, 22), datetime.date(2013, 5, 24), '303')]
mutetella


EDIT: Ok, ich sehe gerade: Themaverfehlung! Zeiträume sollten ja zusammenhängend sein...

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 7. Januar 2014, 13:41
von p90
Hier noch was nicht ganz so hüpsches aber wollte doch auch mal eine Lösung geben und nicht immer nru BlackJack mit Fragen Löchern:

Code: Alles auswählen

from datetime import datetime
data = [('04.07.2013', '31.07.2013', '292'),
        ('01.08.2013', '11.08.2013', '292'),
        ('04.03.2013', '31.03.2013', '293'),
        ('01.04.2013', '05.04.2013', '293'),
        ('06.04.2013', '02.06.2013', '293'),
        ('03.06.2013', '07.06.2013', '293'),
        ('09.09.2013', '20.09.2013', '293'),
        ('22.05.2013', '24.05.2013', '303')]
date_format = '%d.%m.%Y'

# as we only are looking for similarities in datasets with
# same id, sort by ids first, then look at dates

id_to_dates_list = dict()
for entry in data:
	date_start = datetime.strptime(entry[0], date_format)
	date_end = datetime.strptime(entry[1], date_format)
	id = entry[2]
	try:
		id_to_dates_list[id].append([date_start, date_end])
	except KeyError:
		id_to_dates_list[id] = [[date_start, date_end]]

solutions = dict()
for id in id_to_dates_list:
	tmp = []
	while (len(id_to_dates_list[id])>1):
		check_event = id_to_dates_list[id][0]
		changed = False
		for event in id_to_dates_list[id][1:]:
			if check_event[0] > event[1] and (check_event[0] - event[1]).days == 1:
				changed = True
				check_event[0] = event[0]
				id_to_dates_list[id].remove(event)
			if check_event[1] < event[0] and (event[0] - check_event[1]).days == 1:
				changed = True
				check_event[1] = event[1]
				id_to_dates_list[id].remove(event)
		if not changed:
			tmp.append(id_to_dates_list[id].pop(0))
	else:
		tmp.append(id_to_dates_list[id][0])
	solutions[id] = tmp

for id in solutions:
	for solution_group in solutions[id]:
		start = solution_group[0]
		end = solution_group[1]
		print(start, end, id)

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 7. Januar 2014, 14:11
von Hyperion
@p90: Bitte verwende doch vier Spaces als Einrückung - und keine Tabs ;-)

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 7. Januar 2014, 14:26
von EyDu
@p90: Da geht aber noch ein wenig was an deinem Code:

Zeilen 20 bis 23 könnten gut durch ``collections.defaultdict`` ersetzt werden, dann sparst du dir den händischen Test. Dafür hast du aber ein schönes Beispiel gebracht, warum in Namen keine Datentypen verwendet werden sollen. "id_to_dates_list" hört sich zwar nach Liste an, enthält aber ein Dictionary ;-)

Zeilen 32 bis 39 können sicher eleganter formuliert werden und der else-Teil der while-Schleife hängt da auch nur so rum. Da du kein break in Schleifenkörper hast, wird das else so oder so immer ausgeführt. Dann kannst du es auch weglassen und die enthaltenen Anweisungen direkt hinschreiben. Unschön sind natürlich auch die Klammern bei der Bedingung des while.

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 7. Januar 2014, 15:17
von Sirius3
@p90: zusätzlich zu dem was EyDu schreibt:

Zeile 32 und 36: der Vergleich ob größer oder kleiner ist überflüssig, da wenn die Differenz 1 Tag ist, ist implizit die erste Bedingung immer erfüllt.

Du merkst selbst, wie umständlich es ist, eine Liste solange zu verändern, bis kein Element mehr in dieser Liste ist. Mit diesem Vorgehen muß man einen ziemlich großen Codebereich komplett überblicken, um zu verstehen, was dort passiert. Python macht es sehr einfach (mit Generatoren) Listen Schritt für Schritt von einer Menge Eingangsdaten in eine Menge Ausgangsdaten zu transformieren, wobei aber die Listen nie geändert, sondern nur neue Listen neu erzeugt werden. Solch ein lineare Kette von Operationen viel einfacher zu verstehen, als Dein while-Schleife.

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 11:05
von frosch
Hallo,

danke für die vielen Antworten in der raschen Zeit. Leider mußte ich auf Grund einer Grippe eine Woche im Bett verbringen :-(
Hab mal wieder viel Neues gelernt!!

@diesch: Ich habe mich für dein Programm entschieden, da ich es ab besten verstanden habe. Doch leider bekomme ich folgende Fehlermeldung:
result[-1][BIS] = item[BIS] # "verlängern"
TypeError: 'tuple' object does not support item assignment
Wie liegt der Fehler?

Hier mein Programm:

Code: Alles auswählen

 for line in in_f1.readlines():
    linelist = line.split("|")
    if len(linelist) == 16: 
		if (linelist[5]).find('2013') != -1:
			lolist.append((linelist[1],linelist[2],linelist[3]))
			
  
  
 VON, BIS, ID = 0, 1, 2
     
  MAX_DELTA = datetime.timedelta(days=1)
     
  date_format = '%d.%m.%Y'
     
     
  idlist = [(datetime.datetime.strptime(date_start, date_format), datetime.datetime.strptime(date_end, date_format), key) for date_start, date_end, key in lolist]
 
     
  result = []
     
     
  for item in sorted(idlist, key=lambda x: (x[ID], x[VON])):
    if not result:  # erster Eintrag
        result.append(item)
    elif result[-1][ID] == item[ID] and item[VON] - result[-1][BIS] <= MAX_DELTA:
        result[-1][BIS] = item[BIS]  # "verlängern"
    else:
        result.append(item)
     
       
  pprint(result)

Dank vorab und Quack
DER FROSCH

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 11:31
von Sirius3
@frosch: Du erzeugst ja auch Tuple in Zeile 16, und keine Listen wie diesch, und Tuple sind unveränderlich.

In Zeile 1 ist »readlines« überflüssig, ebenso die Klammern um »linelist« in Zeile 4, obwohl man das besser als »'2013' in linelist[5]« schreibt.
Du hast viel zu viele Leerzeilen, die das Lesen sehr erschweren.

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 11:41
von frosch
und wie erzeuge ich dann eine Liste? :K

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 12:10
von BlackJack
@frosch: In der Python-Dokumentation gibt es ein Tutorial wo die Grunddatentypen vorkommen.

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 13:37
von diesch
Ändere Zeile 16 in

Code: Alles auswählen

idlist = [[datetime.datetime.strptime(date_start, date_format), datetime.datetime.strptime(date_end, date_format), key] for date_start, date_end, key in lolist]
Beachte die [] statt ()

Re: Zusammenfassen von Datensätzen

Verfasst: Dienstag 14. Januar 2014, 13:45
von frosch
@diesch: Danke für die Antwort. Die unterschiedliche Klammerung habe ich total übersehen! :oops: