Diverse Charaktere löschen im Dataframe

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.
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

Hallo zusammen

Ich stehe seit 2 Tagen auf dem Schlauch. Gestern meinte ich zwar, eine Lösung gefunden (gegooglet) zu haben, aber irgendwie klappt's nicht. Hier folgendes Problem:

df = pd.DataFrame({"C1": ["a", "a", "e"], "C2": ['guxt.a-el', 'data inn', 'Der Ul,t.PC']})

Ich möchte nun eine Spalte C3 erstellen, die abhängig ist von C2 und zwar wie folgt:
Kopiere C2, aber lösche folgende Zeichen:
. , -
Ausserdem lösche folgende Buchstaben, wenn sie in folgender Liste erscheinen:
["PC", "inn", "Der", "x"]

der erste Teil funktioniert mit translate. Der zweite Teil funktioniert leider gar nicht; deshalb habe ich ihn als Kommentar gesetzt (#) Hier ist der Code. Wäre dankbar, wenn darauf hingewiesen wird, wo der Fehler ist:

Code: Alles auswählen

import pandas
df = pd.DataFrame({"C1": ["a", "a", "e"], "C2": ['guxt.a-el', 'data inn', 'Der Ul,t.PC']})

df["C3"] = "x"
def neuC3():
    dellist=["PC", "inn", "Der", "x"]
    for k, v in df.iterrows():
        df.ix[k, "C3"] = df.ix[k, "C2"].translate(None, ".,-")
        #for dellist in df.ix[k, "C2"]:
            #df.ix[k, "C3"] = df.ix[k, "C2"].replace(dellist, "")
    return df
Zusätzlich, resp. in einem weiteren Schritt, möchte ich nun 2 Dataframes haben, die aufgesplittet sind anhand der Spalte C1: alle mit "a" in einem Dataframe, die anderen (in diesem Fall 1 Reihe) im zweiten Dataframe.
Wie mache ich das am effektivsten?

Ich danke Euch für die Hilfe. Ich erkenne einen Fortschritt bei mir, jedoch komme ich leider des Öfteren (noch) an meine Grenzen :cry:
Zuletzt geändert von Anonymous am Dienstag 19. Februar 2013, 16:10, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@peterwoodbridge: Das müsstest aber eigentlich durch ein wenig Nachdenken selbst hinbekommen. Du musst über die Elemente in `dellist` iterieren und dann jedes davon ersetzen. Und zwar in der *dritten* Spalte und nicht jedes mal wieder den alten Wert aus der zweiten Spalte nehmen etwas ersetzen und dann in die Dritte schreiben.
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

:idea:

Code: Alles auswählen

import pandas
df = pd.DataFrame({"C1": ["a", "a", "e"], "C2": ['guxt.a-el', 'data inn', 'Der Ul,t.PC']})
def neuC3():
    df["C3"] = df["C2"]
    dellist=["PC", "inn", "Der ", "x", ".", ",", "-"," "]
    for k, v in df.iterrows():
        for y in dellist:
            df.ix[k, "C3"] = df.ix[k, "C3"].replace(y, "")
    return df
Mit dieser Semilösung kann ich leben (habe Leerzeichen auch weggenommen). Die Frage ist, ob dies der optimalste Weg ist, sowas zu lösen?
Auf jeden Fall danke ich für den Hinweis, über die delliste zu iterrieren!
Zuletzt geändert von Anonymous am Dienstag 19. Februar 2013, 17:50, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@peterwoodbridge: ”Einfacher” liesse sich das Ersetzen mit dem `re`-Modul erledigen. In Anführungsstrichen deshalb, weil man sich dafür mit regulären Ausdrücken auseinandersetzen muss, und wenn man die noch nicht kennen sollte, natürlich erst einmal Einarbeitungszeit anfällt.

Ich würde auch nicht innerhalb der inneren Schleife immer auf die Datenstruktur zugreifen sondern den Wert vor der Schleife dort herauslesen und dann nach der Schleife den geänderten Wert zurückschreiben. Ich kenne mich mit `pandas` nicht so gut aus, aber vielleicht kann man das auch komplett ausserhalb lösen, also der neuen Spalte gleich eine Liste (oder vielleicht sogar ein iterierbares Objekt) zuweisen, statt mit dem `k` zu arbeiten. Auf jeden Fall ist es komisch, dass `value` in der Schleife gar nicht benutzt wird. Da muss es einen anderen Weg geben.

Das ``return`` ist überflüssig, denn das Objekt wird ja verändert. Da ist eher die Frage warum eine Funktion ein Objekt verändert das sie gar nicht kennen sollte, weil es nicht als Argument übergeben wurde.

Ungetestet:

Code: Alles auswählen

import re
import pandas


def create_new_column(data_frame, old_name, new_name):
    data_frame[new_name] = ''
    regex = re.compile(r'PC|inn|Der |[-x., ]')
    for index, _ in data_frame.iterrows():
        data_frame.ix[index, new_name] = regex.sub(
            data_frame.ix[index, old_name], ''
        )


def main():
    data_frame = pandas.DataFrame(
        {'C1': ['a', 'a', 'e'], 'C2': ['guxt.a-el', 'data inn', 'Der Ul,t.PC']}
    )
    create_new_column(data_frame, 'C2', 'C3')


if __name__ == '__main__':
    main()
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

Das re-Modul schaue ich bei Gelegenheit an. Danke dafür.

das return hab ich gemacht um direkt zu überprüfen, obs funktioniert....

Welches Objekt sollte die Funktion nicht kennen und wieso nicht?
BlackJack

@peterwoodbridge: Das `df` sollte die Funktion nicht einfach so benutzen. Werte, ausser Konstanten, sollten Funktionen als Argumente betreten und nicht einfach so auf magische Weise aus der ”Umgebung” kommen. Sonst ist die Funktion keine abgeschlossene Einheit mehr, die man einfach (wieder)verwenden und testen kann.
BlackJack

@peterwoodbridge: Ich habe mir das jetzt mal auf einem Rechner angeschaut auf dem `pandas` installiert ist und im Grunde kann man sagen wenn Du eine ``for``-Schleife über die Datenstruktur schreibst, verwendest Du es fast sicher falsch. Denn genau wie `numpy` — auf dem `pandas` aufbaut — ist das darauf ausgelegt, dass Schleifen intern in C abgearbeitet werden, statt von extern mit Python auf die einzelnen Werte zuzugreifen. Sowohl ein Datenrahmen als auch die Spaltenobjekte (`Series`) haben dafür Methoden denen man zum Beispiel Funktionen mitgeben kann, die auf die einzelnen Werte angewendet werden. Dein Problem kann man idiomatisch so lösen:

Code: Alles auswählen

import re
from functools import partial
import pandas


def main():
    data_frame = pandas.DataFrame(
        {'C1': ['a', 'a', 'e'], 'C2': ['guxt.a-el', 'data inn', 'Der Ul,t.PC']}
    )
    print data_frame
    
    replace = partial(re.compile('PC|inn|Der |[-x., ]').sub, '')
    data_frame['C3'] = data_frame['C2'].map(replace)
    print '\n', data_frame


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

Code: Alles auswählen

$ ./forum5.py
  C1           C2
0  a    guxt.a-el
1  a     data inn
2  e  Der Ul,t.PC

  C1           C2      C3
0  a    guxt.a-el  gutael
1  a     data inn    data
2  e  Der Ul,t.PC     Ult
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

BlackJack hat geschrieben:@peterwoodbridge: Ich habe mir das jetzt mal auf einem Rechner angeschaut auf dem `pandas` installiert ist und im Grunde kann man sagen wenn Du eine ``for``-Schleife über die Datenstruktur schreibst, verwendest Du es fast sicher falsch. [/code]
in wiefern falsch verwenden?

was spricht gegen meinen code?

sorry für meine Anfängerfragen... ich sehe zwar dass dein code funktioniert, aber wieso darf ich meinen nicht verwenden?
BlackJack

@peterwoodbridge: Falsch im Sinne von nicht idiomatisch. Es ist nicht dazu gedacht so verwendet zu werden. Gehen tut vieles; man kann auch mit dem Griff von einem Schraubenzieher einen Nagel in die Wand hauen — wenn direkt daneben im Werkzeugkasten aber ein Hammer liegt, werden die Leute einem sagen das man da was falsch macht. ;-)

`numpy` ist dazu da um Operationen mit ganzen Arrays durchzuführen in dem man die Operationen vektorisiert, also nicht etwas in einer verhältnismässig langsamen Pythonschleife mit jedem einzelnen Element anstellt, sondern sagt „mache dieses mit allen Elementen”, was dann innerhalb des Datentyps mit C/C++/Fortran Laufzeit- und Speichereffizient ausgeführt wird. Die `pandas`-Bibliothek setzt auf `numpy` auf und führt diese Idee weiter um den vorher eher anonymen Spalten und Zeilen „Namen” zu geben und um weitere Operationen zu erweitern.

Wenn man da jetzt wieder mit Python-Schleifen drauf operiert, stellt sich die Frage warum man überhaupt `numpy` und `pandas` verwendet.

Bis jetzt würde eine Implementierung von dem Code ohne `pandas` sich übrigens hauptsächlich durch die Darstellung beim ``print`` unterscheiden. Wenn ich `pandas` weg lasse und ein Wörterbuch für die Daten hernehme, sehen Quelltext und Ausgabe so aus:

Code: Alles auswählen

import re
from functools import partial
from pprint import pprint


def main():
    data = {
        'C1': ['a', 'a', 'e'], 'C2': ['guxt.a-el', 'data inn', 'Der Ul,t.PC']
    }
    pprint(data)
    
    replace = partial(re.compile('PC|inn|Der |[-x., ]').sub, '')
    data['C3'] = map(replace, data['C2'])
    print
    pprint(data)


if __name__ == '__main__':
    main()

Code: Alles auswählen

$ ./forum.py
{'C1': ['a', 'a', 'e'], 'C2': ['guxt.a-el', 'data inn', 'Der Ul,t.PC']}

{'C1': ['a', 'a', 'e'],
 'C2': ['guxt.a-el', 'data inn', 'Der Ul,t.PC'],
 'C3': ['gutael', 'data', 'Ult']}
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

ok ich sehe den Sinn von numpy. aber pandas unterstützt ja das iterrieren in einem dataframe mit iterrows() http://pandas.pydata.org/pandas-docs/de ... rrows.html

dennoch werde ich mir deinen code genauer anschauen. danke dafür.

zu guter Letzt habe ich dennoch eine Frage:

Code: Alles auswählen

compile('PC|inn|Der |[-x., ]').sub, '')
kann dieses PC|inn|Der ... etc in einer Liste geschrieben werden? es sind im eigentlichen Dataframe ca. 30 Symbole/Zeichenfolgen, die ersetzt werden müssen
BlackJack

@peterwoodbridge: Man kann aus einer Liste mit Zeichenketten einen regulären Ausdruck zusammensetzen. Man muss die Einzelteile mit `re.escape()` behandeln, damit man keine Probleme mit solchen bekommt, die für reguläre Ausdrücke bedeutsame Zeichen enthalten.
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

Kannst du noch kurz erläutern wann iterrows verwendet werden soll?
BlackJack

@peterwoodbridge: Naja, wenn man über die Zeilen iterieren will um damit irgend etwas zu tun. Etwas was nicht „intern” geregelt werden kann und was nicht dazu dient ein neues `pandas`-Objekt zu erstellen sondern nur einen Seiteneffekt pro Zeile zum Ziel hat. Beispielsweise die einzelnen Zeilen in eine Datenbank schreiben, oder Werte daraus für eine Dantenbank- oder Webanfrage verwenden, oder irgendwelche anderen Objekte/Strukturen aus den Daten aufbauen.
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

Blackjack vielen Dank für deine Mühe. Ich versuche konstant, mich zu verbessern, aber naja, da tauchen halt immer wieder Unklarheiten auf.

Ich versteh leider nach wie vor nicht, ob folgendes Problem auch ohne iterrows zu bewältigen ist:

In einer Spalte ist eine Zeichenfolge, gefolgt von einem "-" und nachher wieder eine Zeichenfolge (zB "guxt.a-el").
Nun möchte ich in einem ersten Schritt diese Zeichenfolge splitten: nimm alles VOR dem "-" und konventiere die Endung anhand einer Übersetzungstabelle, die ich in einem Dictionary eingetragen habe.
Der "-" soll mit einem "." ersetzt werden (das ist einfach; zuerst splitte ich die Zeichenfolge; nehme nur den ersten Teil [0] und füge ein "." an)

In einem nächsten Schritt werden dann die Endungen überprüft anhand des Dictionarys
{"el": txy, "upa": zzz, "hoh": aaa} (insgesamt ca. 60 keys/values).

Mit iterrows funktionierts; nun stelle ich wieder die Frage, ob es anders "besser" ist und falls ja, wie ich das mache (im Gegensatz zum anderen Problem werden ja die Zeichen nicht nur gelöscht, sondern ersetzt).
BlackJack

@peterwoodbridge: Ob man etwas entfernt oder etwas ersetzt macht doch keinen Unterschied. Wenn man die Verarbeitung *eines* Elements in einer Funktion (oder allgemeiner etwas aufrufbarem) ausdrücken kann, dann kann man die `map()`-Methode verwenden.
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

nun ist mir noch was aufgefallen: bisher wollte ich die Zelle immer verändern; nun möchte ich in einem nächsten Schritt, die ganze Reihe löschen, wenn der String in der Spalte 2 in einer zuvor definierten Liste zu finden ist... ich nehm mal an, dass auch hier iterrows der falsche Weg ist :K
BlackJack

@peterwoodbridge: Grundlage von `pandas` sind wie gesagt `numpy`-Arrays. Die kann man mit einem Boolean-Array indexieren:

Code: Alles auswählen

In [39]: df
Out[39]: 
    C1 C2
0    4  b
1    5  a
2    1  b
3    3  a
4    2  c
5    1  b
6    2  b
7    1  b
8    1  c
9    2  d
10   3  a
11   4  b
12   6  a
13   6  d
14   1  d
15   5  b
16   3  b
17   1  a
18   1  b
19   2  a

In [40]: df[df.C2.isin(['a', 'b'])]
Out[40]: 
    C1 C2
0    4  b
1    5  a
2    1  b
3    3  a
5    1  b
6    2  b
7    1  b
10   3  a
11   4  b
12   6  a
15   5  b
16   3  b
17   1  a
18   1  b
19   2  a

In [41]: df[~df.C2.isin(['a', 'b'])]
Out[41]: 
    C1 C2
4    2  c
8    1  c
9    2  d
13   6  d
14   1  d
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

super! danke!!!
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

wie kann ich ein neues Dataframe erstellen, welches mir alle Zeilen des oben genannten Dataframes raus gibt, wo die Endung in der zweiten Spalte auf ".PC" lautet?

die Funktion endswith() geht weder für lists noch für Series.

Mit iterrows will ichs NICHT versuchen zu lösen :(
peterwoodbridge
User
Beiträge: 50
Registriert: Freitag 30. November 2012, 10:26

mit Zufall hab ichs herausgefunden, aber ist das eine sinnvolle Lösung?

Code: Alles auswählen

 df=df[df["C2"].map(lambda x: x.endswith(".PC"))]
ps: ich versteh das mapping eigentlich nicht.
Antworten