numpy Python DataFrame:根据条件标记(1-0))相邻行

bvuwiixz  于 2022-11-10  发布在  Python
关注(0)|答案(2)|浏览(117)

我有一列数字,其中包含数字和NaN。我想添加一列标签,用1和0来标识我们有编号的“区域”:该区域包括相邻的(上方和下方)行。
结果应该如下所示:

Number   Label
Nan      0
Nan      1
4        1
Nan      1
Nan      0
Nan      0
Nan      1
8.9      1
Nan      1
Nan      0
Nan      0
Nan      1
47       1

我想出了以下解决方案。但它很难看,如果我想标记更多相邻的单元格(在下面的+2以上),它就不会缩放。

import numpy as np
import pandas as pd
pd.set_option('display.max_rows', 100)

# Generating our DataFrame and ensuring there are some NaN

df = pd.DataFrame(np.random.randn(100), columns=['number'])
df.loc[df.number<1] = np.nan

# diffusing the values on adjacent cells and summing

df['label'] = df.number.fillna(0) 
            + df.number.shift(1).fillna(0)
            + df.number.shift(-1).fillna(0)

# Replace values by 1

df.loc[df.label>0, 'label'] = 1
print(df)

有人能帮我找到一个更优雅的解决方案吗?也许用一个很好的Df.应用程序,我用起来有很多困难?

3z6pesqy

3z6pesqy1#

基于移位法

(剧透提醒:这是我的第一个答案,但不是我最好的。这不是最快的。有关更快的解决方案,请参阅开机自检结束)
只要您的条件仍然是“前一行、当前行或下一行的数字”(我的意思是,如果您不想将其扩展到“k个前一行或k个下一行”),shift方法似乎是最快的方法。尽管如此,我对fillna的想法并不确定。
我会用一种更直接的方法

df['label'] = 1*(~df.number.isna() | ~df.number.shift(1).isna() | ~df.number.shift(-1).isna())

滑动窗口-view

(其他扰乱:我认为这种方法只适用于一般情况,而不适用于3行大窗口(当前为+1之前为+1之后)的特定情况)。但事实上,即使是在这种情况下,它也更快)
使用变量窗口,您可以使用np.lib.stride_tricks.sliding_window_view快速查看相邻值

def fillLabel(df):
    df['label']=0
    v = np.lib.stride_tricks.sliding_window_view(~np.isnan(df.number.values), (PADDING_VALUE*2+1,))
    # Note: PADDING_VALUE is the same as in Steinn Hauser Magnusson's answer
    label=np.any(v,axis=1)
    df.label.values[1:-1]=label

请注意,顾名思义,sliding_window_view是一个视图。而不是数据的副本。因此,即使您有100,000行并且PADDING_VALUE是10000,它也不会用100亿个2D数组填满您的内存。这只是检查单行中相邻值的一种方便方法。
我最近在另一个答案中使用了它,我对此做了更多的解释。
这4种方法的结果(到目前为止):你的,Steinn Hauser Magnusson的,和我的2
方法|计时(Ms)

你的|1.58
My Shift-One-liner|1.30
Steinn卷积|0.58
滑动窗口|0.28
所以,我必须承认,我没有想到卷积和我的第二种方法会比简单的“3行”情况下的简单一行程序更快。但是这个滑动窗口视图函数非常快(因为,同样,它只是一个视图),即使在这种情况下,它也是最快的。它在两个标准上都是赢家:它是最快的,但你可以选择窗口大小。

iqjalb3h

iqjalb3h2#

我建议对此使用卷积运算。当您想要“屏蔽”某个特定空间上的数组时,它真的很棒。在本例中,您希望屏蔽数组[...,1,1,1,...]在每个label == 1的顶部。以下是我的做法:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "Number": [pd.NA,pd.NA,4,pd.NA,pd.NA,pd.NA,pd.NA,8.9,pd.NA,pd.NA,pd.NA,pd.NA,47,]})

PADDING_VALUE = 1 # set padding

def change_neighbors(np_array):
    len_of_neighbors = 2*PADDING_VALUE+1 # imagine this is the length going from [1] -> [..., 1, 1, 1, ...]
    conv_arr = np.convolve(np_array, [1]*len_of_neighbors, "same") # "same" value makes it extrapolate zeros at boundaries.

    # need to account for overlaps when convolving. 
    # Some values might be "2" depending on closeness of non-nan chars
    conv_arr[conv_arr>1]=1 

    return conv_arr

df["Label"] = df["Number"].notnull()
df["Label"] = change_neighbors(df["Label"].values)

print(df)

# >>>    Number  Label

# >>> 0    <NA>      0

# >>> 1    <NA>      1

# >>> 2       4      1

# >>> 3    <NA>      1

# >>> 4    <NA>      0

# >>> 5    <NA>      0

# >>> 6    <NA>      1

# >>> 7     8.9      1

# >>> 8    <NA>      1

# >>> 9    <NA>      0

# >>> 10   <NA>      0

# >>> 11   <NA>      1

# >>> 12     47      1

请注意,当您应用卷积时,一些值加起来可能大于1(如果填充特别大,或者“1元素”非常接近)。出于这个原因,我将所有大于1的值都设置为等于1,但是您可能有另一个用例。希望这个能帮上忙!

相关问题