这是我要回答的问题。
假设我有一只熊猫
col_name
1 [16, 4, 30]
2 [5, 1, 2]
3 [4, 5, 52, 888]
4 [1, 2, 4]
5 [5, 99, 4, 75, 1, 2]
我想删除整个列中的所有元素
出现少于x次,例如,假设x=3
这意味着我希望得到如下结果:
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
为了方便起见,这是数据。
d = {'col_name': {1: [16, 4, 30],
2: [5, 1, 2],
3: [4, 5, 52, 888],
4: [1, 2, 4],
5: [5, 99, 4, 75, 1, 2]}}
df = pd.DataFrame(d)
当前方法:
from collections import Counter
c = Counter(pd.Series(np.concatenate(df.col_name.tolist())))
def foo(array):
return [x for x in array if c[x] >= 3]
df.col_name = df.col_name.apply(foo)
df
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
这很有效,但速度很慢。所以,我想用
np.vectorize
来加速:v = np.vectorize(foo)
df.col_name = v(df.col_name) # <---- error thrown here
并获取此错误:
/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/numpy/lib/function_base.py in _vectorize_call(self, func, args)
2811
2812 if ufunc.nout == 1:
-> 2813 res = array(outputs, copy=False, subok=True, dtype=otypes[0])
2814 else:
2815 res = tuple([array(x, copy=False, subok=True, dtype=t)
ValueError: setting an array element with a sequence.
似乎我对
np.vectorize
的工作原理有误解。我做错了什么,如果有什么问题的话,我如何才能让这个解决方案与np.vectorize
一起工作?为了澄清这一点,我并不是在寻找解决方法,只是想帮助理解我为什么会犯这个错误。
最佳答案
使用数据帧和函数:
In [70]: df
Out[70]:
col_name
1 [16, 4, 30]
2 [5, 1, 2]
3 [4, 5, 52, 888]
4 [1, 2, 4]
5 [5, 99, 4, 75, 1, 2]
In [71]: df.values # values is an object array
Out[71]:
array([[list([16, 4, 30])],
[list([5, 1, 2])],
[list([4, 5, 52, 888])],
[list([1, 2, 4])],
[list([5, 99, 4, 75, 1, 2])]], dtype=object)
使用
apply
,但返回序列,而不是修改df
:In [73]: df.col_name.apply(foo)
Out[73]:
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
Name: col_name, dtype: object
In [74]: timeit df.col_name.apply(foo)
214 µs ± 912 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
若要进行比较,请将
foo
应用于原始词典,d
:In [76]: {i:foo(d['col_name'][i]) for i in range(1,6)}
Out[76]: {1: [4], 2: [5, 1, 2], 3: [4, 5], 4: [1, 2, 4], 5: [5, 4, 1, 2]}
In [77]: timeit {i:foo(d['col_name'][i]) for i in range(1,6)}
18.3 µs ± 39.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
注意,这比从数据帧中提取列表要快。
In [84]: timeit df.col_name.tolist()
25.3 µs ± 92 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
foo
应用于列表而不是字典的方法大致相同:In [85]: dlist=df.col_name.tolist()
In [86]: timeit [foo(x) for x in dlist]
16.6 µs ± 27.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
定义向量化函数:
In [87]: f = np.vectorize(foo, otypes=[object])
In [88]: f(dlist)
Out[88]:
array([list([4]), list([5, 1, 2]), list([4, 5]), list([1, 2, 4]),
list([5, 4, 1, 2])], dtype=object)
In [89]: timeit f(dlist)
36.7 µs ± 173 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这比直接迭代慢将列表预转换为对象数组(
object
)只会节省一两微秒。因为我们返回的是一个对象数组,所以我们最好使用
darr=np.array(dlist)
(它frompyfunc
使用):In [94]: ff = np.frompyfunc(foo, 1,1)
In [95]: ff(darr)
Out[95]:
array([list([4]), list([5, 1, 2]), list([4, 5]), list([1, 2, 4]),
list([5, 4, 1, 2])], dtype=object)
In [96]: timeit ff(darr)
18 µs ± 6.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
我测试过
vectorize
比直接迭代快2倍的情况。这可能是一个更大的测试数组的情况。在
frompyfunc
用户中,numpy
以速度慢而著称,而且通常很难使用(特别是如果省略了np.vectorize
)。它在这里的表观速度与otypes
有关,与阵列应用程序相比,它似乎有很多开销。考虑到
pandas apply
使用对象dtype数组的倾向,pandas
可能是比frompyfunc
更好的工具。至于为什么普通的
np.vectorize
会引起错误,我怀疑这与它如何选择隐含的vectorize
有关。In [106]: f1 = np.vectorize(foo)
In [107]: f(darr[[0,0,0]])
Out[107]: array([list([4]), list([4]), list([4])], dtype=object)
In [108]: f1(darr[[0,0,0]])
...
ValueError: setting an array element with a sequence.
我们必须深入研究
otypes
代码,但我怀疑它从第一个vectorize
结果推断返回类型应该是整数。但是实际的调用返回一个列表即使是1元素列表也无法放入整数槽。测试用于确定
[4]
的vectorize
方法:In [126]: f1._get_ufunc_and_otypes(foo,[darr])
Out[126]: (<ufunc '? (vectorized)'>, 'l')
otypes
从输入数组的第一个元素计算_get_ufunc_and_otypes
,然后 if isinstance(outputs, tuple):
nout = len(outputs)
else:
nout = 1
outputs = (outputs,)
otypes = ''.join([asarray(outputs[_k]).dtype.char
for _k in range(nout)])
在您的例子中,
outputs
是一个列表,因此它将outputs
设置为1,并从第一个结果推断出[4]
如果nout
是第一个,同样的情况也会发生。这个automatic
otypes
通常会在用户需要浮点结果时对其执行位操作,但第一个值返回一个整数,如0然后它们会被意外截断。该方法对
[5,1,2]
类型进行了测试让我们来测试一下:返回元组而不是列表的
otypes
的第一个版本:In [162]: foot = lambda x: tuple(foo(x))
In [163]: [foot(x) for x in darr]
Out[163]: [(4,), (5, 1, 2), (4, 5), (1, 2, 4), (5, 4, 1, 2)]
In [164]: ft = np.vectorize(foot)
应用于整个
outputs
时出现相同错误:In [165]: ft(darr)
...
ValueError: setting an array element with a sequence.
但是当应用所有返回3个元素的
foo
的子集时,我得到一个数组元组:In [167]: ft(darr[[1,3,1,3]])
Out[167]: (array([5, 1, 5, 1]), array([1, 2, 1, 2]), array([2, 4, 2, 4]))
这无助于解决最初的问题,但确实说明了使用
darr
的威力或复杂性。