我有一系列的表格:
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'我在这里错过了什么?这个函数或我的数据有什么问题吗?
发布于 2017-12-29 10:39:15
您的数据是好的,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.
[...]如您所见,所记录的行为是将字符串传递给pd.eval,这与eval/exec函数类的一般(和预期)行为一致。您传递一个字符串,并以任意对象结束。
在我看来,pandas.eval是错误的,因为它不预先拒绝Series输入expr,这导致它在含糊不清的情况下猜测。为漂亮打印而设计的Series‘__repr__的默认缩短会极大地影响您的结果,这是这种情况的最好证明。
然后,解决方案是从XY问题中退一步,使用转换数据的正确工具,最好完全停止使用pandas.eval。即使在Series很小的工作情况下,你也不能确定未来的熊猫版本不会完全破坏这个“功能”。
发布于 2017-12-28 13:14:30
TL;博士
这无疑是个早期的错误。请参阅打开的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__上,而不是包含在其中的对象(这就是这个错误的原因)。__repr__截断行,将它们替换为... (省略号)。这个省略号被引擎误解为Ellipsis对象-
...
Ellipsis
pd.eval('...')
AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'从技术上讲,pd.eval不应该解析一系列字符串(所提到的文档是用来接收字符串的),并且(正如可接受的答案所描述的),它将尝试对结果进行合理的猜测,而不是直接拒绝输入。
不管这是有意的行为还是不完全的行为(许多熊猫方法根据输入操作不同--而eval可以通过将自己映射到每一行来处理一个系列,这也是我最初认为这是如何工作的)是值得讨论的,因为有一个跟踪这一问题的未决问题。
,我能做些什么才能让它起作用呢?
目前,还没有一个解决办法(截至2017年12月28日,这一问题仍未解决),然而,有几个解决办法。
选项1
如果您可以保证没有任何格式错误的字符串,则此选项应立即生效。
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: objectast适用于任意数量的行,速度慢,但可靠。您还可以对JSON数据使用pd.json.loads,应用与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函数的更多细节。
Note
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发现这个问题的功劳。
https://stackoverflow.com/questions/48008191
复制相似问题