TL;DR
这可能是eval中的一个错误。请参见开放的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
操作的是 Series 的
__repr__
,而不是其中包含的对象(这是此错误的原因)。
__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
将此函数传递给
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
函数的更多详细信息。
注意
If you're working with JSON data, it might be suitable to read your file using pd.read_json
or pd.io.json.json_normalize
to begin with.
You can also perform parsing as you read in your data, using read_csv
-
s = pd.read_csv(converters=literal_eval, squeeze=True)
Where the converters
argument will apply that function passed on the column as it is read, so you don't have to deal with parsing later.
Continuing the point above, if you're working with a dataframe, pass a dict
-
df = pd.read_csv(converters={'col' : literal_eval})
Where col
is the column that needs to be parsed
You can also pass pd.json.loads
(for json data), or pd.eval
(if you have 100 rows or less).
感谢MaxU和Moondra发现了这个问题。