2017-12-28 18 views
4

ich eine Reihe von Form haben:Attribute: 'PandasExprVisitor' Objekt hat kein Attribut 'visit_Ellipsis', mit Pandas eval

s 

0 [133, 115, 3, 1] 
1 [114, 115, 2, 3] 
2  [51, 59, 1, 1] 
dtype: object 

Hinweis, dass seine Elemente sind Saiten:

s[0] 
'[133, 115, 3, 1]' 

Ich versuche, pd.eval zu verwenden, um diese Zeichenfolge in einer Spalte von Listen zu analysieren. Dies funktioniert für diese Beispieldaten.

pd.eval(s) 

array([[133, 115, 3, 1], 
     [114, 115, 2, 3], 
     [51, 59, 1, 1]], dtype=object) 

Allerdings, auf viel größere Daten (10K), schlägt dies kläglich!

len(s) 
300000 

pd.eval(s) 
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis' 

Was fehlt mir hier? Stimmt etwas nicht mit der Funktion oder meinen Daten?

Antwort

4

TL; DR
Ab v0.21, ist dies ein Fehler, und eine offene Frage auf GitHub. Siehe GH16289.


Warum erhalte ich diesen Fehler?
Dies (aller Wahrscheinlichkeit nach) ist pd.eval 's Fehler, die Serie mit mehr als 100 Zeilen nicht analysieren kann. Hier ist ein Beispiel.

len(s) 
300000 

pd.eval(s.head(100)) # returns a parsed result 

Ange

pd.eval(s.head(101)) 
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis' 

Dieses Problem weiterhin besteht, unabhängig von der Parser oder den Motor.


Was bedeutet dieser Fehler?
Wenn eine Reihe mit mehr als 100 Zeilen übergeben wird, arbeitet pd.eval auf der __repr__ der Serie, anstatt die darin enthaltenen Objekte (was die Ursache für diesen Fehler ist). Die __repr__ abgeschnittenen Zeilen, ersetzt sie durch eine ... (Ellipse). Das Auslassungszeichen wird durch den Motor als Ellipsis Objekt falsch interpretiert -

... 
Ellipsis 

pd.eval('...') 
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis' 

Welche genau die Ursache für diesen Fehler ist.


Was kann ich tun, dies zu machen zu arbeiten?
Im Moment gibt es keine Lösung (das Problem ist immer noch ab dem 28.12.2017), aber, es gibt ein paar Problemumgehungen.

Option 1
ast.literal_eval
, wenn Sie, dass Sie alle fehlerhaften Strings haben nicht garantieren können, sollte diese Option aus dem Kasten heraus arbeiten.

Wenn fehlerhafte Daten vorliegen, müssen Sie einen kleinen Fehlercode schreiben. Sie können mit einer Funktion das tun -

def safe_parse(x): 
    try: 
     return literal_eval(x) 
    except (SyntaxError, ValueError): 
     return np.nan # replace with any suitable placeholder value 

Pass diese Funktion apply -

s.apply(safe_parse) 

0 [133, 115, 3, 1] 
1 [114, 115, 2, 3] 
2  [51, 59, 1, 1] 
dtype: object 

ast Werke für eine beliebige Anzahl von Zeilen und ist langsam, aber zuverlässig. Sie können auch pd.json.loads für JSON-Daten verwenden und die gleichen Ideen wie mit literal_eval anwenden.

Option 2
yaml.load
Eine weitere große Möglichkeit für einfache Daten Parsen, ich picked this up von @ayhan eine Weile her.

import yaml 
s.apply(yaml.load) 

0 [133, 115, 3, 1] 
1 [114, 115, 2, 3] 
2  [51, 59, 1, 1] 
dtype: object 

Ich habe dies nicht auf komplexere Strukturen getestet, aber das sollte für fast jede grundlegende String-Darstellung von Daten funktionieren.

Sie finden die Dokumentation für PyYAML here. Scrollen Sie ein wenig nach unten und Sie finden weitere Details zur Funktion load.


Hinweis

  • Wenn Sie mit JSON-Daten arbeiten, kann es zweckmäßig sein, die Dateien zu lesen, mit der Verwendung von pd.read_json oder pd.io.json.json_normalize zu beginnen.
  • Sie auch Parsen durchführen können, wie Sie in Ihren Daten lesen, mit read_csv -

    s = pd.read_csv(converters=literal_eval, squeeze=True) 
    

    Wo die converters Argument, dass die Funktion auf die Säule gegeben gelten, wie es gelesen wird, so dass Sie nicht haben beschäftigen Sie sich später mit dem Parsen.

  • Fortsetzung der Punkt oben, wenn Sie mit einem Datenrahmen arbeiten, passieren ein dict -

    df = pd.read_csv(converters={'col' : literal_eval}) 
    

    Wo col die Spalte, die Sie können auch pd.json.loads (für JSON-Daten übergeben werden analysiert muss) oder pd.eval (wenn Sie 100 Zeilen oder weniger haben).


Credits MAXU und Moondra dieses Problem für die Aufdeckung.

+1

ja, ich erinnere mich an diesen Fall ...;) – MaxU

+0

@MaxU Ich stellte sicher, dass Sie und Moondra in der Antwort. Lassen Sie mich wissen, ob ich die Antwort irgendwie verbessern kann! –

+1

Ich mochte auch [@ Ayhans Lösung] (https://Stackoverflow.com/a/45827519/5741205) viel ... – MaxU

2

Ihre Daten sind in Ordnung, und pandas.eval ist fehlerhaft, aber nicht in der Art, wie Sie denken. Es gibt einen Hinweis in the relevant github issue page, der mich drängte, genauer hinzusehen at the documentation.

pandas.eval(expr, parser='pandas', engine=None, truediv=True, local_dict=None, 
      global_dict=None, resolvers=(), level=0, target=None, inplace=False) 

    Evaluate a Python expression as a string using various backends. 

    Parameters: 
     expr: str or unicode 
      The expression to evaluate. This string cannot contain any Python 
      statements, only Python expressions. 
     [...] 

Wie Sie sehen können, ist das dokumentierte Verhalten Strings-pd.eval, in Einklang mit dem allgemeinen (und erwartetem) Verhalten der eval/exec Klasse von Funktionen zu übergeben. Sie übergeben eine Zeichenfolge und enden mit einem beliebigen Objekt.

Wie ich es sehe, ist pandas.eval fehlerhaft, weil es nicht die Series Eingabe expr im Voraus zurückweist, so dass es angesichts der Ambiguität rät. Die Tatsache, dass die Standardkürzung des Series '__repr__, die für schönes Drucken entworfen wurde, drastisch Ihr Ergebnis beeinflussen kann, ist der beste Beweis für diese Situation.

Die Lösung ist dann, von dem XY-Problem zurückzutreten und the right tool to convert your data zu verwenden und vorzugsweise pandas.eval für diesen Zweck vollständig zu verwenden. Selbst in den Arbeitsfällen, in denen die Series klein ist, kann man nicht wirklich sicher sein, dass zukünftige Pandas Versionen dieses "Feature" nicht komplett durchbrechen.

+0

Okay, also du nennst ein XY-Problem. Was schlagen Sie vor? –

+0

@ cᴏʟᴅsᴘᴇᴇᴅ Ihre Antwort listet die Alternativen auf, ich wollte nur betonen, dass anstatt auf 'pd.eval' zu warten," repariert "werden sollte, sollte man einen davon wählen :) –

+0

Ah, okay. Ich habe das zunächst nicht aufgegriffen. Danke für die Klarstellung! Außerdem stimme ich Ihrer Antwort zu, da in den Dokumenten nicht erwähnt wird, was weitergegeben werden soll und was nicht. So machen Leute den allgemeinen Fehler anzunehmen, dass 'pd.eval' an jeder Zeichenkette in der Reihe isoliert arbeitet, anstatt die ganze Reihe' __repr__' als Ganzes, was sie tut. –

Verwandte Themen