如果列值在一定范围内彼此接近,则删除 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 列过滤行:如果 b
和 c
的当前值接近(在 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 次时才保留行