AttributeError:使用熊猫eval,“ PandasExprVisitor”对象没有属性“ visit_Ellipsis”


问题内容

我有一系列的表格:

s

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

注意,它的元素是 字符串

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

我试图用来pd.eval将此字符串解析为列表的一列。这适用于此样本数据。

pd.eval(s)

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

但是,在更大的数据(10K的数量级)上,这很惨!

len(s)
300000

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

我在这里想念什么?函数或数据有问题吗?


问题答案:

TL; DR
截至v0.21,这是一个错误,并且是GitHub上的一个未解决问题。参见GH16289


为什么会出现此错误?
(很可能)这是pd.eval的错,它不能解析具有100多行的序列。这是一个例子。

len(s)
300000

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

鉴于,

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

无论解析器还是引擎,此问题仍然存在。


这个错误是什么意思?
当传递的行超过100行时,对其进行pd.eval操作__repr__,而不是对其中包含的对象进行操作(这是导致此错误的原因)。所述__repr__截短的行,具有替换它们...(省略号)。该省略号被引擎误解为Ellipsis对象-

...
Ellipsis

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

这正是导致此错误的原因。


我该怎么做才能使它正常工作?
目前,尚无解决方案(该问题截至2017年12月28日仍未解决), 但是 ,有一些解决方法。

选项1 如果可以保证没有任何格式错误的字符串,则此选项应立即可用。
ast.literal_eval

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

如果可能出现数据格式错误,则需要编写一些错误处理代码。您可以使用函数来做到这一点-

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

将此功能传递给apply-

s.apply(safe_parse)

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

ast适用于任意数量的行,速度很慢,但可靠。您也可以将pd.json.loadsJSON数据用于,方法与相同literal_eval

选项2
yaml.load
解析简单数据的另一个不错的选择,我前一阵子是从@ayhan那里选出来的

import yaml
s.apply(yaml.load)

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

我尚未在更复杂的结构上对此进行过测试,但这对于几乎所有基本的数据字符串表示形式均适用。

您可以在此处找到PyYAML的文档。向下滚动一点,您将找到有关该load功能的更多详细信息。


注意

  • 如果您使用的是JSON数据,则可能适合使用pd.read_jsonpd.io.json.json_normalize从头开始读取文件。
  • 您还可以在读取数据时执行解析,方法是read_csv-
    s = pd.read_csv(converters=literal_eval, squeeze=True)
    

converters参数将在读取的列上应用传递给该列的函数,因此您以后不必处理解析。

  • 继续上述要点,如果您正在使用数据框,请传递dict-
    df =  pd.read_csv(converters={'col' : literal_eval})
    

col您还可以传递pd.json.loads(对于json数据)或pd.eval(如果您有100行或更少的行),则将其解析在哪里。


感谢MaxU和Moondra发现此问题。