Gleiche Zahlen zählen und Anzahl ausgeben

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.
fkh
User
Beiträge: 6
Registriert: Sonntag 25. September 2011, 18:15

Hallo an alle!

Ich bin sowohl hier als auch in python Neuling. Und schon gibt's die ersten Probleme:

Ich möchte jeweils die Anzahl aufeinanderfolgender Einsen in einer liste/array mit 20500 Nullen und Einsen in einer weiteren Liste/array ausgeben.

Beispiel:

list=(0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,etc.)

Ausgabe:
list=(2,4,7,1,etc.)

Vielen dank für die Hilfe.

fkh
BlackJack

@fkh: Wie sieht denn Dein bisheriger Ansatz aus? Wie würdest Du das Vorgehen denn im Detail beschreiben wenn Du jemandem erzählen solltest wie man das macht? *Das* müsstest Du dann ja nur noch in Quelltext umsetzen.
fkh
User
Beiträge: 6
Registriert: Sonntag 25. September 2011, 18:15

@BlackJack
Nunja das ist mir selber nicht so ganz klar. Ich habe es schon mit einer while schleife und if bedingung probiert. Sowas wie:

Code: Alles auswählen

p = []
l_max = 0
for n in range(20454)
    if time_series_0[n] !=0:      #time_series_0 ist hier der array mit 0 und 1 einträgen
         l_max = l_max+1
    else:
        l_max = 0
    p.append(l_max)
Allerdings habe ich da das Problem, dass ich z.b. für die Sequenz (..,1,1,1,1,...) nicht (...,4,...) erhalte sondern (...1,2,3,4,...). Um das zu umgehen bin ich bis jetzt soweit:

Code: Alles auswählen

ind_1 = []
lst = list(time_series_0)     # time_series_0 ist mein Vektor mit den 0 und 1 Einträgen, der wandele ich hier in eine Liste um, da ich lieber mit Listen arbeite
n = lst.count(1)
print "Anzahl 1er in Zeitreihe:", n

for i in range(n):         # hier schreibe ich die Indizes der Stelle in der Liste (lst), welche eine 1 enthalten in eine neue liste (ind_1) und lösche den 1 Eintrag in der Liste (lst)
    ind = lst.index(1)    # so wie ich mir das vorstelle, sollten jetzt direkt aufeinanderfolgende 1 Einträge in lst den gleichen Index in ind_1 haben.
    ind_1.append(ind)
    del lst[ind]

p =[]

for t in range(len(ind_1)-1):
    temp = cmp(ind_1[t],ind_1[t+1])   # hier vergleiche ich aufeinanderfolgende Indizes miteinander, falls unterschiedlich schreibe ich in die neue Liste p eine 1
    if temp != 0:
        p.append(1)
    if temp == 0:                               # falls die aufeinanderfolgenden Indizes gleich sind, dann soll hier die Anzahl der gleichen Indizes in die Liste p eingetragen werden
        temp2 = ind_1[t+1]
        temp3 = ind_1.count(temp2)
        print "Anzahl der gleichen indizes:", temp3
        if temp3 < 3:                               # bei Anzahl der gleichen Indizes kleiner als 3 kann ich einfach die Anzahl 2 in die Liste schreiben
            p.append(temp3)
        elif temp3 > 2:                             # wenn die Anzahl 3 oder größer ist, habe ich wieder das Problem der Wiederholung: bei z.b. 5 gleichen aufeinanderfolgenden Indizes habe ich 
            pass                                       # dann (..5, 5, 5, 5..) in meiner Liste p hinzugefügt anstatt nur einer 5, deswegen nur die pass anweisung
print p
Ich hoffe mein Skript ist einigermaßen verständlich und die Problemstellung klar geworden.
Für weitere Hilfe oder eine einfachere (schnellere) Schleife wär ich sehr dankbar.
BlackJack

@fkh: Das ist alles ein wenig kompliziert gemacht. Wie würdest Du das denn von Hand machen? Du kommst ja selbst offensichtlich auf das richtige Ergebnis. Das musst Du beschreiben — in Worten — und diese Beschreibung dann immer detaillierter machen, bis sich die einzelnen Schritte in Quelltext ausdrücken lassen. Wenn Du diese Reihe von 0en und 1en auf einem Blatt Papier hättest und die 1en zählen solltest, dann ist Dir doch wahrscheinlich egal an welchem Index die 0en und 1en stehen. Du fängst doch einfach von vorne an die Ziffern durch zu gehen. Und dann musst Du nur noch umschreiben wann Du jeweils anfängst Ziffern zu zählen und wann Du mit einer Reihe von 1en fertig bist und eine Zahl in der Ergebnisliste notieren musst.

Apropos Index: Das Muster ``for i in xrange(len(sequence)):`` um dann `i` nur zu verwenden um auf Elemente von `sequence` zuzugreifen ist „unpythonisch” weil man stattdessen in der Schleife auch *direkt* über die Elemente von `sequence` iterieren kann.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

fkh hat geschrieben:Ich möchte jeweils die Anzahl aufeinanderfolgender Einsen in einer liste/array mit 20500 Nullen und Einsen in einer weiteren Liste/array ausgeben.

Beispiel:

list=(0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,etc.)
list ist kein guter Name für einen Bezeichner, da du dadurch das eingebaute list überschreibst.

Das Problem als solches würde ich mit itertools.groupby lösen. Man kann den Beispielcode als Basis nehmen und landet dann bei einem Dreizeiler.
BlackJack

Das ist einer der Fälle wo man sich entscheiden kann ob ein Anfänger mehr lernt, wenn er so etwas mal selbst implementiert, also das Vorgehen „von Hand” als Algorithmus formuliert, oder vorgefertigte Bausteine zusammensetzt. Einen Einzeiler mit `groupby()`, Generatorausdruck, und „list comprehension” hatte ich gestern auch schon gebastelt, aber ich sehe nicht wie man da als Anfänger selber drauf kommt. :-)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mach dir den Ablauf klar:
- Gehe Zahl für Zahl durch
- Beginne zu zählen, sobald eine Eins auftaucht
- Zähler wird dabei jeweils um eins erhöht
- Folgt eine Null, kommt der Zählerstand in die Ergebnisliste
- Zähler wird anschließend zurückgesetzt
- Wenn über alle Zahlen iteriert wurde, wird die Ergebnisliste ausgegeben

Vorbedingungen: Eine leere Ergebnisliste und ein Zählstand von Null.

Du kannst hier mit einer simplen Fallunterscheidung für jeden Iterationsschritt arbeiten.
BlackJack

Ergänzung: Auf Spezialfälle und Randbedingungen achten. Zum Beispiel leere Liste als Eingabe und 0 oder 1 als letztes Element in der Eingabe.
fkh
User
Beiträge: 6
Registriert: Sonntag 25. September 2011, 18:15

Hallo allerseits,

Erstmal danke für die vielen Tipps. Nach einer Nacht darüber schlafen, habe ich jetzt auch meinen Denkfehler gefunden. Der Wert des counters darf erst in die Liste, wenn wieder eine 0 vorkommt, also erst in der else Bedingung. Ich würde es jetzt folgendermaßen lösen:

Code: Alles auswählen

p = []
l_max = 0
for n in range(len(time_series_0)):
    if time_series_0[n] != 0:
        l_max = l_max +1
    elif time_series_0[n] == 0:
        p.append(l_max)
        l_max = 0
p = scipy.array(p)
p = filter(None,p)
Danke für die vielen Antworten!
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Japp, meine Lösung von heute morgen war ganz ähnlich:

Code: Alles auswählen

def count_numbers(numbers):
    results = []
    counter = 0
    for number in numbers:
        if number == 1:
            counter += 1
        elif (number == 0) and counter:
            results.append(counter)
            counter = 0
    return results

numbers = [0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0]
print count_numbers(numbers)
Allerdings beachtet dies noch nicht alle durch BlackJack eingeworfenen Sonderfälle/Randbedingungen.

Die `itertools.count()`-Variante wäre übrigens:

Code: Alles auswählen

[len(list(group)) for (key, group) in itertools.groupby(numbers) if key == 1]
lunar

@snafu:

Code: Alles auswählen

[sum(1 for _ in group) for key, group in groupby(numbers) if key == 1]
Vermeidet die überflüssige Liste.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

@lunar:

Code: Alles auswählen

[sum(group) for key, group in groupby(numbers) if key == 1]
vermeidet die überflüssige generator expression. :-P
lunar

@bords0: Da darf aber nicht nach "key == 0" gefragt sein… ;)
BlackJack

@fkh & snafu: Beide Lösungen haben das gleiche Problem wenn die Einabe mit einer 1 endet.
problembär

Code: Alles auswählen

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

numbers = (0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)
counts = []
startvalue = -1
tocheck = startvalue
count = 0
for i in numbers:
    if i == tocheck:
        count +=1
    else:
        if tocheck != startvalue:
            counts.append(count)
        tocheck = i
        count = 1
for i in counts:
    print i
BlackJack

@problembär: Das löst aber nicht die Aufgabe. Es sollen nicht alle Läufe von gleichen Zahlen ermittelt werden, sondern nur die von den 1en. Selbst wenn Du damit alle Läufe zählen möchtest, hat auch diese Lösung das Problem, dass der letzte Wert des Ergebnisses fehlt. Die Ausgabe endet mit einer 1, aber sie müsste ja wegen den zwei letzten 0en mit einer 2 enden.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das meinte ich eigentlich auch mit "(...) beachtet noch nicht die Einwürfe von BlackJack".

Es hat sich also gezeigt, dass mein Ablauf ungenau definiert war. Als Mensch würde ich ein Ergebnis unter Umständen auch dann aufschreiben, wenn keine Zahlen mehr übrig sind. Damit ich es aufschreibe, muss das Ergebnis grundsätzlich größer als Null sein.

Um in Python herauszufinden, ob noch weitere Iterationsschritte nötig sind, fallen mir diverse Möglichkeiten ein:

1. Elemente mittels `.pop()` abfragen und möglichen `IndexError` behandeln. `.pop()` ist allerdings auf Listen beschränkt.

2a. Weiteren Iterator einführen, der quasi einen Schritt voraus ist und mittels `next()` prüft.
2b. Lediglich *einen* Iterator benutzen, aber `for` durch `while` ersetzen und auch hier mit `next()` arbeiten.

3. Die Schleife durchlaufen und am Ende prüfen, ob Zählstand > 0 ist. Dann genau so verfahren, wie nach einem "Zahlen-Switch". Bedeutet sozusagen Copy&Paste im Kleinformat oder die Einführung einer Hilfsfunktion.

4. Die Länge der Zahlenliste merken und in jedem Schleifendurchlauf dekrementieren.

Ich habe mich dann für die letzte Möglichkeit entschieden und die Funktion gleich auch etwas generischer gestaltet:

Code: Alles auswählen

def count_occurrences(iterable, keyname):
    remaining = len(iterable)
    results = []
    counter = 0
    for elem in iterable:
        remaining -= 1
        if elem == keyname:
            counter += 1
        if counter and (elem != keyname or not remaining):
            results.append(counter)
            counter = 0
    return results

numbers = [0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1]
print count_occurrences(numbers, 1)
Beachtet die Einsen am Ende. :)
problembär

BlackJack hat geschrieben:@problembär: Das löst aber nicht die Aufgabe. Es sollen nicht alle Läufe von gleichen Zahlen ermittelt werden, sondern nur die von den 1en.
Na gut, dann prüft man eben noch, ob "tocheck == 1" ist.
BlackJack hat geschrieben:Selbst wenn Du damit alle Läufe zählen möchtest, hat auch diese Lösung das Problem, dass der letzte Wert des Ergebnisses fehlt. Die Ausgabe endet mit einer 1, aber sie müsste ja wegen den zwei letzten 0en mit einer 2 enden.
Stimmt, das soll natürlich nicht sein. Das Problem ist, daß der letzte Wechsel nicht mehr in der Schleife berücksichtigt wird, weil kein weiterer Wechsel mehr folgt (und innerhalb der Schleife nur auf Wechsel der Listenelemente geprüft wird). Dann fügt man eben nach der Schleife noch den letzten Stand von "count" der Ergebnisliste an. Also:

Code: Alles auswählen

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

numbers = (0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)
counts = []
startvalue = -1
tocheck = startvalue
count = 0
for i in numbers:
    if i == tocheck:
        count +=1
    else:
        if tocheck != startvalue and tocheck == 1:
            counts.append(count)
        tocheck = i
        count = 1
if tocheck == 1:
    counts.append(count)
for i in counts:
    print i
Eigentlich keine große Sache.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Code: Alles auswählen

import itertools
numbers = [0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,1]
groups = (list(grp) for key, grp in itertools.groupby(numbers) if key == 1)
print [len(list_len) for list_len in groups]
Gibt bei mir ein korrektes Ergebnis zurück oO
Oder habe ich mal wieder was überlesen?

btw Ergebniss = [2, 4, 7, 1, 3, 1]

€ bzw

Code: Alles auswählen

import itertools
numbers = [0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,1]
print [len(list(grp)) for key, grp in itertools.groupby(numbers) if key == 1]
lunar

@JonasR: Ja und? Diese Lösung würde schon gezeigt, und nie angezweifelt.
Antworten