python - 被pandas条件和/或布尔索引困扰

时间:2017-04-20 03:58:24

标签: python pandas indexing boolean conditional

我遇到条件/布尔索引问题。我试图用逻辑填充数据帧(dfp),该逻辑以类似形状的数据帧(dfs)加上自己的前一行(dfp)的数据为条件。 这是我最近的失败......

import pandas as pd
dfs = pd.DataFrame({'a':[1,0,-1,0,1,0,0,-1,0,0],'b':[0,1,0,0,-1,0,1,0,-1,0]})

In [171]: dfs
Out[171]: 
       a  b
    0  1  0
    1  0  1
    2 -1  0
    3  0  0
    4  1 -1
    5  0  0
    6  0  1
    7 -1  0
    8  0 -1
    9  0  0

dfp = pd.DataFrame(index=dfs.index,columns=dfs.columns)

dfp[(dfs==1)|((dfp.shift(1)==1)&(dfs!=-1))] = 1

In [166]: dfp.fillna(0)
Out[166]: 
     a    b
0  1.0  0.0
1  0.0  1.0
2  0.0  0.0
3  0.0  0.0
4  1.0  0.0
5  0.0  0.0
6  0.0  1.0
7  0.0  0.0
8  0.0  0.0
9  0.0  0.0

所以如果满足2个条件中的任何一个,我希望dfp在行n中有1:

1) dfs same row = 1 or 2) both dfp previous row = 1 and dfs same row <> -1

我希望我的最终输出看起来像这样:

   a  b
0  1  0
1  1  1
2  0  1
3  0  1
4  1  0
5  1  0
6  1  1
7  0  1
8  0  0
9  0  0

更新/编辑: 有时视觉更有用 - 下面是它在Excel中的映射方式。

enter image description here

提前致谢,非常感谢您的时间。

2 个答案:

答案 0 :(得分:3)

让我们总结一下不变量:

  • 如果dfs值为1,则dfp值为1
  • 如果dfs值为-1,则dfp值为0
  • 如果dfs值为0,则dfp值为1,前一个dfp值为1,否则为{39} ; s 0

或以另一种方式表达:

  • 如果第一个值为dfp,则11开头,否则为0
  • dfp中有0之前,1值为dfs
  • dfp中有1之前,-1值为dfs

在python中很容易制定:

def create_new_column(dfs_col):
    newcol = np.zeros_like(dfs_col)
    if dfs_col[0] == 1:
        last = 1
    else:
        last = 0
    for idx, val in enumerate(dfs_col):
        if last == 1 and val == -1:
            last = 0
        if last == 0 and val == 1:
            last = 1
        newcol[idx] = last

    return newcol

测试:

>>> create_new_column(dfs.a)
array([1, 1, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
>>> create_new_column(dfs.b)
array([0, 1, 1, 1, 0, 0, 1, 1, 0, 0], dtype=int64)

然而,这在Python中非常低效,因为迭代numpy-arrays(和pandas Series / DataFrames)很慢而且python中的for - 循环也是低效的。

但是,如果你有numbaCython,你可以编译它,它可能(可能)比任何NumPy解决方案都快,因为NumPy需要多次滚动和/或累积操作。

例如numba:

>>> import numba
>>> numba_version = numba.njit(create_new_column)  # compilation step

>>> numba_version(np.asarray(dfs.a))  # need cast to np.array
array([1, 1, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64)
>>> numba_version(np.asarray(dfs.b))  # need cast to np.array
array([0, 1, 1, 1, 0, 0, 1, 1, 0, 0], dtype=int64)

即使dfs有数百万行,numba解决方案也只需几毫秒:

>>> dfs = pd.DataFrame({'a':np.random.randint(-1, 2, 1000000),'b':np.random.randint(-1, 2, 1000000)})
>>> %timeit numba_version(np.asarray(dfs.b))
100 loops, best of 3: 9.37 ms per loop

答案 1 :(得分:3)

不是最好的方法,但有效的方法。

    dfs = pd.DataFrame({'a':[1,0,-1,0,1,0,0,-1,0,0],'b':[0,1,0,0,-1,0,1,0,-1,0]})
    dfp = dfs.copy()

定义函数如下。使用&#39;最后&#39;这里有点hacky。

    last = [0]
    def f( x ):
         if x == 1:
             x = 1
         elif x != -1 and last[0] == 1:
             x = 1
         else:
             x = 0
         last[0] = x
         return x

只需在每列上应用func f。

    dfp.a = dfp.a.apply( f )
    dfp

       a  b
    0  1  0
    1  1  1
    2  0  0
    3  0  0
    4  1 -1
    5  1  0
    6  1  1
    7  0  0
    8  0 -1
    9  0  0

类似于col b。不要忘记重新注册&#39;持续#。

    last[0] = 0
    dfp.b = dfp.b.apply( f )
    dfp
       a  b
    0  1  0
    1  1  1
    2  0  1
    3  0  1
    4  1  0
    5  1  0
    6  1  1
    7  0  1
    8  0  0
    9  0  0