python - 被熊猫条件和/或布尔索引难倒

Posted

技术标签:

【中文标题】python - 被熊猫条件和/或布尔索引难倒【英文标题】:python - stumped by pandas conditionals and/or boolean indexing 【发布时间】:2017-09-16 12:32:48 【问题描述】:

我在使用条件/布尔索引时遇到问题。我正在尝试使用以来自类似形状的数据框 (dfs) 的数据加上其自身的前一行 (dfp) 的数据为条件的逻辑填充数据框 (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

因此,如果满足以下两个条件之一,我希望 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 中的映射方式。

提前致谢,非常感谢您抽出宝贵时间。

【问题讨论】:

也许你解释了逻辑。我认为-1意味着向上移动正确吗?但是,我不理解索引 5 的 A 列中的 1。 注意到,斯科特。已修复 - 很抱歉如此含糊。 [a][6] 来自哪里? (在您想要的输出中) 奥斯汀 - dfp.ix[6,a] 应该 = 1 因为 (dfp.ix[5,a]==1) & (dfs.ix[6,a]!=-1) - 见上文,更新更清晰。 @MJS 在我看来,第 5 行似乎是 0,0。您是否认为行的处理是相互依赖的? 【参考方案1】:

不是最好的方法,但可行。

    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()

如下定义函数。 'last' 在这里的用法有点老套。

    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 也是如此。不要忘记重新初始化“last”。

    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

【讨论】:

非常感谢vara - 我对您的解决方案投了赞成票,该解决方案运行良好,产生了准确的结果。结果比上面未编译的稍慢。【参考方案2】:

让我们总结一下不变量:

如果dfs 的值为1,则dfp 的值为1。 如果dfs 的值为-1,则dfp 的值为0。 如果dfs 的值为0,则dfp 的值为1,如果之前的dfp 值为1,否则为0

或者换一种说法:

如果第一个值为1,则dfp1 开头,否则为0 dfp 的值是 0,直到在 dfs 中有一个 1dfp 的值是 1,直到在 dfs 中有一个 -1

这在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-loops 也是低效的。

但是,如果您有 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

【讨论】:

感谢 MSeifert(和 jezrael)。我现在正在尝试应用您的逻辑 - 以前没有使用过 numba。 MSeifert - 我已经复制了结果,您的解决方案是迄今为止我见过的最快的。谢谢。 @MJS 不客气。它对 numba 有效吗?如果没有,我可能还可以找到另一个(比 numba 慢但比 python 快)解决方案。 它确实适用于 numba,非常酷。再次感谢您的宝贵时间,我会接受您的回答。 @MSeifert - 非常好的解决方案 ;) +100 ;)

以上是关于python - 被熊猫条件和/或布尔索引难倒的主要内容,如果未能解决你的问题,请参考以下文章

python运算学习之Numpy ------ 数组的切片索引与循环遍历条件和布尔数组

Python从菜鸟到高手:条件和条件语句

SparkPandasNotImplementedError:.iloc 需要数字切片或条件布尔索引

Python 只有整数、切片...和整数或布尔数组是有效的索引

熊猫布尔系列不会绘图

具有多个条件的布尔索引[重复]