AttributeError:'PandasExprVisitor'对象没有属性'visit_Ellipsis',使用pandas eval

cs9*_*s95 7 python eval apply pandas

我有一系列的形式:

s

0    [133, 115, 3, 1]
1    [114, 115, 2, 3]
2      [51, 59, 1, 1]
dtype: object
Run Code Online (Sandbox Code Playgroud)

请注意,其元素是字符串:

s[0]
'[133, 115, 3, 1]'
Run Code Online (Sandbox Code Playgroud)

我正在尝试pd.eval将此字符串解析为一列列表.这适用于此示例数据.

pd.eval(s)

array([[133, 115, 3, 1],
       [114, 115, 2, 3],
       [51, 59, 1, 1]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

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

len(s)
300000

pd.eval(s)
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?功能或我的数据有问题吗?

cs9*_*s95 8

TL; DR
截至v0.21,这是一个bug,也是GitHub上的一个未解决的问题.见GH16289.


为什么我收到此错误?
这(很可能)是pd.eval错误,不能解析超过100行的系列.这是一个例子.

len(s)
300000

pd.eval(s.head(100))  # returns a parsed result
Run Code Online (Sandbox Code Playgroud)

然而,

pd.eval(s.head(101))
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
Run Code Online (Sandbox Code Playgroud)

无论解析器或引擎如何,此问题都会持续存在.


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

...
Ellipsis

pd.eval('...')
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
Run Code Online (Sandbox Code Playgroud)

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


我该怎么做才能让它发挥作用?
目前,还没有解决方案(截至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 
Run Code Online (Sandbox Code Playgroud)

如果数据格式错误,您需要编写一些错误处理代码.你可以用一个功能 -

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

将此功能传递给apply-

s.apply(safe_parse)

0    [133, 115, 3, 1]
1    [114, 115, 2, 3]
2      [51, 59, 1, 1]
dtype: object
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我没有在更复杂的结构上测试它,但这应该适用于几乎任何基本的数据字符串表示.

你可以在这里找到PyYAML的文档.向下滚动一下,您将找到有关该load功能的更多详细信息.


注意

  • 如果您正在使用JSON数据,则可能适合使用pd.read_jsonpd.io.json.json_normalize开始阅读您的文件.
  • 您还可以在读取数据时执行解析,使用read_csv-

    s = pd.read_csv(converters=literal_eval, squeeze=True)
    
    Run Code Online (Sandbox Code Playgroud)

    converters论点将应用功能上的柱子,因为它是阅读,因此,您不必处理后解析.

  • 继续上述观点,如果您正在处理数据帧,请传递dict-

    df =  pd.read_csv(converters={'col' : literal_eval})
    
    Run Code Online (Sandbox Code Playgroud)

    col需要解析的列在哪里您也可以传递pd.json.loads(对于json数据),或者pd.eval(如果您有100行或更少行).


致MaxU和Moondra发现此问题.


And*_*eak 3

你的数据很好,但也pandas.eval有问题,但不是你想象的那样。相关的github问题页面中有一个提示,促使我仔细查看文档

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.
        [...]
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,记录的行为是将字符串传递给,与/函数类pd.eval的一般(和预期)行为一致。您传递一个字符串,最终得到一个任意对象。evalexec

在我看来,这pandas.eval是有问题的,因为它不会预先拒绝Series输入expr,导致它在面对歧义时进行猜测。事实上,为漂亮打印而设计的Series'的默认缩短__repr__会极大地影响您的结果,这一事实就是这种情况的最好证明。

然后,解决方案是从 XY 问题退一步,使用正确的工具来转换数据,最好pandas.eval完全停止用于此目的。即使在很小的工作情况下Series,你也不能真正确定未来的 pandas 版本不会完全破坏这个“功能”。

  • @cᴏʟᴅsᴘᴇᴇᴅ我不同意:文档非常明确地将“expr”作为“str”/“unicode”实例。有一条注释说“*`Series` 和 `DataFrame` 对象受支持,并且其行为与普通的 Python 评估一样*”,但它指的是传递的字符串的_contents_。其他任何想法都是一厢情愿的想法 :P 但相关 SO 帖子中的评论表明,这确实偶尔会给用户带来麻烦,因此提供解决方案/替代方案具有价值。 (2认同)