值与一组值的矢量化比较

时间:2016-11-28 10:18:06

标签: python pandas numpy

我有一个pd.Dataframe,其中的列包含一个值,例如

df.iloc[:10]['occ']
Out[18]: 
0    4220
1     205
2    7630
3    8965
4     430
5    3930
6    4230
7    5620
8    4040
9    4130

然后,我为另外的组创建了另一个包含startend值的数据框。我想根据occ值将组分配到第一个数据框。

       start   end
group             
10        10   950
11      1000  3540
12      3600  3655
13      3700  3955
14      4000  4160

由于这些组不相交,我们有一个简单的双射。我计划为每个occ值,取最后一行的group-index小于上述occ值。

testAgainst = np.repeat(dfGroups['start'].values[np.newaxis, :], repeats=10, axis=0)

array([[  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000],
       [  10, 1000, 3600, 3700, 4000, 4200, 4300, 4700, 5000, 6000, 6200,
        7000, 7700, 9000]])

现在,由于尺寸为(10,)(10, 14),因此应该会发生自动广播。我期待能够做到

df.iloc[:10]['occ'] < testAgainst

得到结果

0  False False False False False False True  True  True  True  True  True  True  True 
1  False True  True  True  True  True  True  True  True  True  True  True  True  True 

表示前两行,因为4220大于4200(以及之后的所有数字),205大于10

然而,我得到了

Traceback (most recent call last):
  File "/home/foo/.conda/envs/myenv3/lib/python3.5/site-packages/IPython/core/interactiveshell.py", line 2881, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-28-1bce7761846c>", line 1, in <module>
    df.iloc[:10]['occ'] < testAgainst
  File "/home/foo/.conda/envs/myenv3/lib/python3.5/site-packages/pandas/core/ops.py", line 832, in wrapper
    return self._constructor(na_op(self.values, np.asarray(other)),
  File "/home/foo/.conda/envs/myenv3/lib/python3.5/site-packages/pandas/core/ops.py", line 792, in na_op
    result = getattr(x, name)(y)
ValueError: operands could not be broadcast together with shapes (10,) (10,14) 
  1. 为什么广播在这里不起作用?
  2. 鉴于此操作失败,将组分配给我的数据帧的最有效方法是什么(实际案例:10-15,组,df中的2500万行)。

1 个答案:

答案 0 :(得分:1)

1)广播失败的原因是因为Series个对象构成了一维标记的数组 [shape = (10,)] ,与2进行比较-D array [shape = (1, 14)]

让我们考虑一下:ser = df.iloc[:10]['occ']

如果你做了:

>>> ser.iloc[0] < testAgainst
array([[False, False, False, False, False, False,  True,  True,  True,
     True,  True,  True,  True,  True]], dtype=bool)

这意味着如果您可以将相同的比较应用于系列的所有行,它将为您提供正确的结果。

>>> ser.apply(lambda x: x < testAgainst.ravel())

但是,由于它没有被矢量化,因此它非常慢,因此将其应用于更多行是不可行的。

你现在可以做的是重新塑造系列,以便在其中插入额外的尺寸。

这允许NumPy分别匹配系列(10, 1)和数组(1, 14)的两个形状,以便可以通过各个维度的配对进行比较。

2)更优化的解决方案可能是:

>>> pd.Series((ser.values[:, None] < testAgainst).tolist())   # same as ser.values.reshape(-1,1)

结果输出:

0    [False, False, False, False, False, False, Tru...
1    [False, True, True, True, True, True, True, Tr...
2    [False, False, False, False, False, False, Fal...
3    [False, False, False, False, False, False, Fal...
4    [False, True, True, True, True, True, True, Tr...
5    [False, False, False, False, True, True, True,...
6    [False, False, False, False, False, False, Tru...
7    [False, False, False, False, False, False, Fal...
8    [False, False, False, False, False, True, True...
9    [False, False, False, False, False, True, True...
dtype: object

注意: 只需测试数组的一个样本即可,您无需重复此数组以匹配系列对象的形状。