Python:如何使用apply广播操作而不编写辅助函数
我有一列包含字符串的数据,我想创建一个新列,只从相应的数据字符串中获取前两个字符 为此使用Python:如何使用apply广播操作而不编写辅助函数,python,numpy,pandas,apply,Python,Numpy,Pandas,Apply,我有一列包含字符串的数据,我想创建一个新列,只从相应的数据字符串中获取前两个字符 为此使用apply函数似乎是合乎逻辑的,但它的工作方式与预期不同。它甚至似乎与apply的其他用法不一致。见下文 In [205]: dfrm_test = pandas.DataFrame({"A":np.repeat("the", 10)}) In [206]: dfrm_test Out[206]: A 0 the 1 the 2 the 3 the 4 the 5 the 6 th
apply
函数似乎是合乎逻辑的,但它的工作方式与预期不同。它甚至似乎与apply
的其他用法不一致。见下文
In [205]: dfrm_test = pandas.DataFrame({"A":np.repeat("the", 10)})
In [206]: dfrm_test
Out[206]:
A
0 the
1 the
2 the
3 the
4 the
5 the
6 the
7 the
8 the
9 the
In [207]: dfrm_test["A"].apply(lambda x: x+" cat")
Out[207]:
0 the cat
1 the cat
2 the cat
3 the cat
4 the cat
5 the cat
6 the cat
7 the cat
8 the cat
9 the cat
Name: A
In [208]: dfrm_test["A"].apply(lambda x: x[0:2])
Out[208]:
0 the
1 the
Name: A
基于此,apply
似乎什么都不做,只执行与内部调用的NumPy等价的操作。也就是说,apply
似乎执行与第一个示例中的arr+“cat”
相同的操作。如果NumPy碰巧播出了这个节目,那么它就会起作用。如果不是,那就不会了
但这似乎违背了文件中的承诺。以下是pandas.Series.apply的预期报价:
对系列的值调用函数。可以是只需要单个值的ufunc或Python函数()
它明确表示它可以接受只需要单个值的Python函数。不起作用的函数(lambda x:x[0:2]
)肯定满足这一点。它并没有说单个参数必须是数组。考虑到像numpy.sqrt
这样的东西通常用于单输入(因此不完全是数组),因此期望Pandas使用任何这样的函数似乎是很自然的
是否有我在这里缺少的使用apply
的方法
注意:我确实在下面编写了自己的额外函数:
def ix2(arr):
return np.asarray([x[0:2] for x in arr])
我验证了这个版本是否可以与Pandasapply
一起使用。但这与问题无关。编写外部操作在Series对象之上的东西要比不断编写使用列表理解的包装器来有效循环Series内容容易得多。这不正是apply
应该从用户那里抽象出来的吗
我使用的是Pandas版本0.7.3,它位于工作场所共享网络上,因此无法升级到最新版本
添加:
我能够确认此行为从版本0.7.3更改为版本0.8.1。在0.8.1中,它在没有NumPy ufunc包装的情况下按预期工作
我的猜测是,在代码中,有人试图在try-except语句中使用numpy.vectorize
或numpy.fromfunc
。也许它不能正确地与我正在使用的特定lambda函数一起工作,因此在代码的部分,它默认只依赖于通用NumPy广播
如果可能的话,从Pandas开发者那里得到一些确认将是非常棒的。但与此同时,ufunc解决方案应该足够了。我能想到的一个解决方案是将Python函数转换为numpy.ufunc
:
并在应用中使用此选项:
In [50]: dfrm_test
Out[50]:
A
0 the
1 the
2 the
3 the
4 the
5 the
6 the
7 the
8 the
9 the
In [51]: dfrm_test["A"].apply(np.frompyfunc((lambda x: x[0:2]), 1, 1))
Out[51]:
0 th
1 th
2 th
3 th
4 th
5 th
6 th
7 th
8 th
9 th
Name: A
In [52]: pandas.version.version
Out[52]: '0.7.3'
In [53]: dfrm_test["A"].apply(lambda x: x[0:2])
Out[53]:
0 the
1 the
Name: A
尝试使用dfrm_test.A.map(lambda x:x[0:2])
从0.8.1开始使用:
In [47]: dfrm_test.A.str[:2]
Out[47]:
0 th
1 th
2 th
3 th
4 th
5 th
6 th
7 th
8 th
9 th
Name: A
熊猫0.8返回进行dfrm_测试[“A”]。应用(lambda x:x[0:2])
10次th
。您确认这只是版本7.2-1中的一个错误吗??请注意,我在问题的底部提到,我无法摆脱使用这个版本。我不知道,我现在无法检查它。如果我在0.8上遇到同样的问题,我可以尝试找到解决方案,但如果没有7.2,我就无法解决。更正:我有0.7.3版。这是我早些时候报道的热情洋溢的分布。同样的bug仍在发生。这是可行的,但是我认为这也是一个解决办法,因为它没有解决“代码>应用< /COD>不符合承诺的事实。您是否可以验证map
是否在apply
将起作用的所有相同情况下都起作用?我也不喜欢从序列的map
到数据帧的applymap
之间的不一致性。我不确定这是否是Series的“变通方法”。map
是元素操作的预期方法Series.apply
将首先尝试将整个系列传递到输入函数中,并且只有在引发异常时才会返回元素操作。这与apply
的文档及其0.8.1行为相矛盾,在该行为中,它成功地执行了上面示例的元素版本,而版本0.7.3似乎使用了您描述的逻辑。因为apply
应该在0.7.3中工作,就像在0.8.1中一样(根据文档),所以我认为这是一种变通方法<代码>映射
很好,但应用应该可以。我在github master上,它不工作;它可能是偶然在0.8.1中工作的<代码>应用的设计目的是,您可以应用ufunc并获取索引完整的序列。看一下源代码,它尝试调用func(self)并将其包装在try/except块中,然后在except中调用map\u expert。在您的示例中,您提供的函数可以获取一个系列并返回一个系列,但不执行元素操作,因此代码无法知道如何触发元素大小写。为了明确说明您希望按元素应用输入函数,您必须使用Series.map
。尽管我同意您的看法,但apply的docstring在这方面还不清楚。我们可以为应用改进文档。
In [47]: dfrm_test.A.str[:2]
Out[47]:
0 th
1 th
2 th
3 th
4 th
5 th
6 th
7 th
8 th
9 th
Name: A