这是我要回答的问题。
假设我有一只熊猫

      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是第一个,同样的情况也会发生。
这个automaticotypes通常会在用户需要浮点结果时对其执行位操作,但第一个值返回一个整数,如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的威力或复杂性。

09-11 19:53