TL;DR
Depuis le v0.21
il s'agit d'un bogue, et d'un problème ouvert sur GitHub. Voir aussi GH16289 .
Pourquoi est-ce que je reçois cette erreur ?
Il s'agit (selon toute probabilité) pd.eval
qui ne peut pas analyser les séries de plus de 100 lignes. Voici un exemple.
len(s)
300000
pd.eval(s.head(100)) # returns a parsed result
Considérant que
pd.eval(s.head(101))
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
Ce problème persiste, quel que soit l'analyseur ou le moteur.
Que signifie cette erreur ?
Lorsqu'une série de plus de 100 lignes est transmise, pd.eval
opère sur le __repr__
de la série, plutôt que les objets qu'elle contient (ce qui est la cause de ce bogue). La série __repr__
les lignes tronquées, en les remplaçant par un ...
(ellipse). Cette ellipse est interprétée à tort par le moteur comme une Ellipsis
objet -
...
Ellipsis
pd.eval('...')
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
Ce qui est exactement la cause de cette erreur.
Que puis-je faire pour que cela fonctionne ?
Pour l'instant, il n'y a pas de solution (la question est toujours ouverte au 28/12/2017), cependant mais il existe quelques solutions de contournement.
Option 1
ast.literal_eval
Cette option devrait fonctionner d'emblée si vous pouvez garantir que vous n'avez pas de chaînes malformées.
from ast import literal_eval
s.apply(literal_eval)
0 [133, 115, 3, 1]
1 [114, 115, 2, 3]
2 [51, 59, 1, 1]
dtype: object
S'il existe une possibilité de données mal formées, vous devrez écrire un petit code de gestion des erreurs. Vous pouvez le faire avec une fonction -
def safe_parse(x):
try:
return literal_eval(x)
except (SyntaxError, ValueError):
return np.nan # replace with any suitable placeholder value
Transmettre cette fonction à apply
-
s.apply(safe_parse)
0 [133, 115, 3, 1]
1 [114, 115, 2, 3]
2 [51, 59, 1, 1]
dtype: object
ast
fonctionne pour n'importe quel nombre de lignes et est lent, mais fiable. Vous pouvez également utiliser pd.json.loads
pour les données JSON, en appliquant les mêmes idées qu'avec literal_eval
.
Option 2
yaml.load
Une autre option intéressante pour analyser des données simples, I a récupéré ceci de @ayhan il y a quelque temps.
import yaml
s.apply(yaml.load)
0 [133, 115, 3, 1]
1 [114, 115, 2, 3]
2 [51, 59, 1, 1]
dtype: object
Je n'ai pas testé cette méthode sur des structures plus complexes, mais elle devrait fonctionner pour presque toutes les représentations de données sous forme de chaînes de caractères.
Vous pouvez trouver la documentation de PyYAML aquí . Faites défiler la page un peu plus bas et vous trouverez plus de détails sur la load
función.
Note
-
Si vous travaillez avec des données JSON, il peut être judicieux de lire votre fichier en utilisant la fonction pd.read_json
o pd.io.json.json_normalize
pour commencer.
-
Vous pouvez également effectuer l'analyse au fur et à mesure que vous lisez vos données, en utilisant la fonction read_csv
-
s = pd.read_csv(converters=literal_eval, squeeze=True)
Où le converters
appliquera la fonction passée à la colonne au fur et à mesure qu'elle est lue, de sorte que vous n'aurez pas à vous occuper de l'analyse ultérieurement.
-
Dans la continuité de ce qui précède, si vous travaillez avec un dataframe, passez une valeur de dict
-
df = pd.read_csv(converters={'col' : literal_eval})
Où col
est la colonne qui doit être analysée Vous pouvez également passer pd.json.loads
(pour les données json), ou pd.eval
(si vous avez 100 lignes ou moins).
Nous remercions MaxU et Moondra d'avoir découvert ce problème.