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

Posted

技术标签:

【中文标题】如果列值在一定范围内彼此接近,则删除 pandas 行【英文标题】:Delete pandas rows if column values are close to each other within a certain range 【发布时间】:2019-01-23 07:19:54 【问题描述】:

我想删除在某个预定义范围内两列中的值彼此接近的所有 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 列中迭代地应用此条件并将此条件应用于两个不同的列?

旁注: 我的熊猫数据框中有两百万行。因此,我想知道最有效的方法。

【问题讨论】:

b 列和 c 列中的下一个元素应在同一列中前一个值的 99% 以内。即(19.96 应该在 20.02 的 99% 范围内,&& 9.89 应该是 10.12 的 99%)。之后,对第 3 行和第 1 行重复此操作。 (因为第二行被删除了) 很抱歉 column_b 和 column_c 变量令人困惑。我的意思是同一列中的下一个值应该与同一列中的前一个值接近 99%。 b 列和 c 列都应在其先前值的 99% 以内,这就是不包括第 4 行的原因。 是的,这就是为什么删除第 4 行的原因与第 2 行完全相同。我正在尝试过滤掉值彼此接近的行。 该死的,我真是太愚蠢了。这是我的错误。 9.89 只是一个虚构的数字,它应该类似于 10.10。我想过滤 both 列 b 和 c 非常接近其先前值的值。 【参考方案1】:

很大程度上基于 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

如果有更快的方法可以达到相同的结果,请告诉我。

【讨论】:

【参考方案2】:

鉴于被比较的行可能会根据每次比较的结果而改变,我不确定如果不使用与 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

【讨论】:

这几乎就是我想要的。我希望它通过比较到目前为止 all 接受的行来过滤,而不是像您显示的那样仅与最后一个接受的行进行比较。例如:df = pd.DataFrame('a':[1,2,3,4,5], \ 'b':[20,20,20,20,20],\ 'c':[100,100.9,101.1,101.8,100.0]) 输出应该是:a b c 1 20 100.0 3 20 101.1 row5 应该被排除,因为它类似于之前接受的 row1。 如果输出格式不清晰。对于前面的示例,输出应仅包含 row1 和 row3。 (必须忽略 2、4 和 5。)

以上是关于如果列值在一定范围内彼此接近,则删除 pandas 行的主要内容,如果未能解决你的问题,请参考以下文章

Pandas HTML 输出条件格式 - 如果值在范围内,则突出显示单元格

Pandas:仅当某个列值在过去 N 个月内出现 N 次时才保留行

如果相同的列值在多行中重复,则 UPDATE - SQL / MYSQL

如果值在列表中,则 Pandas 数据框中的重复行

计算由另一列值分组的列值在 pandas 数据框中的共现

在pandas df中,对列的值在范围内的行进行分组。