numpy 如果列值在一定范围内彼此接近,则删除pandas行

hvvq6cgz  于 2023-04-12  发布在  其他
关注(0)|答案(3)|浏览(113)

我想删除所有pandas行,其中两列中的值在某个预定义范围内彼此接近。

例如:

df = pd.DataFrame({'a':[1,2,3,4,5,6], \
'b':[20.02,19.96,19.98,20.10,26.75,56.12],\
'c':[10.12,10.10,123.54,124.12,245.12,895.21]})

a      b         c
1    20.02   10.12
2    19.96   10.10
3    19.98   123.54
4    20.10   124.12
5    26.75   245.12
6    56.12   895.21

基于列b和c筛选行:如果bc的当前值接近(在1%以内)前一个接受行的值:

(0.99*previous_b < b < 1.01*previous_b) && (0.99*previous_c < c < 1.01*previous_c)

则它们被排除。

结果

a      b         c
1    20.02   10.12
3    19.98   123.54
5    26.75   245.12
6    56.12   895.21

我可以用numpy.isclose来表示一个数字:

df['b'].apply(np.isclose, b=20.02, atol=0.01 * 20.02)

我如何推广它,以便我迭代地应用它,遍历所有pandas列,并将此条件应用于两个不同的列?

**旁注:**我的pandas dataframe中有200万行。因此,我想知道最有效的方法。

c8ib6hqw

c8ib6hqw1#

考虑到被比较的行可能会根据每次比较的结果而改变,我不确定你是否可以在不使用一些等同于for循环的逻辑的情况下实现这一点:

#Taking initial comparison values from first row
b,c = df.iloc[0][['b','c']]
#Including first row in result
filters = [True]

#Skipping first row in comparisons
for index, row in df.iloc[1:].iterrows():
    if 0.99*b <= row['b'] <= 1.01*b and 0.99*c <= row['c'] <= 1.01*c:
        filters.append(False)
    else:
        filters.append(True)
        # Updating values to compare based on latest accepted row
        b = row['b']
        c = row['c']

df2 = df.loc[filters]
print(df2)

   a      b       c
0  1  20.02   10.12
2  3  19.98  123.54
4  5  26.75  245.12
5  6  56.12  895.21

检查边缘情况,其中行(n+1)接近行(n)(并且被排除),但是行(n+2)接近行(n+1)但不接近行(n)(并且因此应该被包括):

df = pd.DataFrame({'a':[1,2,3], \
                   'b':[20,20,20],\
                   'c':[100,100.9,101.1]})
a   b      c
0  1  20  100.0
2  3  20  101.1
7cjasjjr

7cjasjjr2#

很大程度上基于ukemi之前的回答。在这个例子中,每个列的值都与之前接受的所有行进行比较,而不仅仅是最后接受的行。

df = pd.DataFrame({'a':[1,2,3,4,5,6,7,8,9],'b':[20.02,19.96,19.98,20.10,26.75,56.12, 20.04,56.24, 56.15],\
               'c':[10.12,10.10,123.54,124.12,245.12,6.00,10.11,6.50,128.67]})

    a     b       c
0   1   20.02   10.12
1   2   19.96   10.10
2   3   19.98   123.54
3   4   20.10   124.12
4   5   26.75   245.12
5   6   56.12   6.00
6   7   20.04   10.11
7   8   56.24   6.50
8   9   56.15   128.67

b = []
c = []

#Taking initial comparison values from first row
b.append(df.iloc[0]['b'])
c.append(df.iloc[0]['c'])

#Including first row in result
filters = [True]

#Skipping first row in comparisons
for index, row in df.iloc[1:].iterrows():
    tag = 0
    for i in range(len(b)):
        #Thresholds have been changed to 5% and 10% respectively in this case.
        if 0.95*b[i] <= row['b'] <= 1.05*b[i] and 0.90*c[i] <= row['c'] <= 1.10*c[i]:
            filters.append(False)
            tag = 1
            break

    if tag == 0:
        filters.append(True)
        # Updating values to compare based on latest accepted row
        b.append(row['b'])
        c.append(row['c'])

df2 = df.loc[filters]

print(df2)

    a    b       c
0   1   20.02   10.12
2   3   19.98   123.54
4   5   26.75   245.12
5   6   56.12   6.00
8   9   56.15   128.67

请告诉我是否有更快的方法来达到同样的效果。

vxbzzdmp

vxbzzdmp3#

我使用itertools.pairwise,它在python〉=3.10中可用。
1.对行进行排序;
1.按相反顺序成对比较;
1.如果在标准内,则丢弃较高的一个。
假设列名为key,删除条件为EPISION

from itertools import pairwise
from tqdm import tqdm

df = ...
key = "key"
EPISION = 1e-4

df = df.sort_values(by=[key])
for (i, iser), (j, jser) in tqdm(df[::-1].iterrows(), total=len(df) - 1):
    if iser[key] - jser[key] < EPISION:
        df.drop(index=i, inplace=True)

print(df)

相关问题