AttributeError:'PandasExprVisitor'对象没有属性'visit_Ellipsis',使用pandas eval
- 2025-04-15 09:19:00
- admin 原创
- 81
问题描述:
我有一系列以下形式:
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'
我这里漏掉了什么?函数或数据有问题吗?
解决方案 1:
TL;DR
这无疑是 eval 中的一个 bug。请参阅 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'
无论是解析器还是引擎,这个问题仍然存在。
这个错误是什么意思?
pd.eval
操作的是__repr__
Series 的 ,而不是其包含的对象(这是导致此错误的原因)。__repr__
截断的行,用省略号替换它们...
。引擎会将此省略号误解为Ellipsis
对象 -
...
Ellipsis
pd.eval('...')
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'
pd.eval
从技术上讲,它不应该解析一系列字符串(文档中提到它是为了接收字符串)并且(如接受的答案所述)将尝试对结果做出合理的猜测,而不是直接拒绝输入。
这是否是预期的行为或不完整的行为(许多 pandas 方法根据输入以不同的方式运行 - 并且 eval 可以通过将自身映射到每一行来对一系列进行操作,这也是我最初假设它是如何工作的)有待讨论,因为有一个未解决的问题在跟踪这一点。
我该怎么做才能让它正常工作?
目前,还没有解决方案(截至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.loads
JSON 数据,其思路与 相同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_json
或pd.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 发现此问题。
解决方案 2:
你的数据没问题,虽然pandas.eval
有 bug,但跟你想的不一样。相关的 GitHub issue 页面里有个提示,促使我仔细查看了文档。
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.
[...]
如你所见,文档中的行为是将字符串传递给,这与/类函数pd.eval
的常规(和预期)行为一致。你传递一个字符串,最终会得到一个任意对象。eval
`exec`
在我看来,它pandas.eval
存在 bug,因为它没有预先拒绝Series
输入expr
,导致它在出现歧义时进行猜测。事实上,为美观打印而设计的Series
'的默认缩写__repr__
可能会极大地影响结果,这就是这种情况的最好证明。
解决方案是暂时放下 XY 问题,使用合适的工具来转换数据,最好pandas.eval
完全停止使用 。即使在 较小的实际情况下Series
,你也无法确定未来的 Pandas 版本是否会完全破坏这一“特性”。
扫码咨询,免费领取项目管理大礼包!