4 votes

Supprimer les lignes pandas si les valeurs de la colonne sont proches les unes des autres dans une certaine plage

Je voudrais supprimer toutes les lignes pandas où les valeurs dans deux colonnes sont proches les unes des autres dans une certaine plage prédéfinie.

Par exemple:

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

Filtrer les lignes en fonction des colonnes b et c: si les valeurs actuelles de b et c sont proches (dans les 1% de) des valeurs de la ligne précédemment acceptée:

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

alors elles sont exclues.

Résultat

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

Je peux utiliser numpy.isclose pour un nombre:

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

Comment puis-je généraliser cela pour appliquer cela de manière itérative en parcourant toutes les colonnes pandas et appliquer cette condition sur deux colonnes différentes?

Note latérale: J'ai deux millions de lignes dans mon dataframe pandas. Par conséquent, je voudrais connaître la manière la plus efficace de le faire.

2voto

ukemi Points 3499

Étant donné que les lignes comparées peuvent changer en fonction du résultat de chaque comparaison, je ne suis pas sûr que vous puissiez y parvenir sans utiliser une logique équivalente à une boucle 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

Vérification du cas limite où la ligne(n+1) est proche de la ligne(n) (et exclue), mais la ligne(n+2) est proche de la ligne(n+1) mais n'est pas proche de la ligne(n) (et donc devrait être incluse) :

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

0voto

Vishnu Points 112

Largement basé sur la réponse antérieure de ukemi. Dans cet exemple, chaque valeur de colonne est comparée à toutes les lignes précédemment acceptées au lieu de seulement la dernière ligne acceptée.

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.14],\
               '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 = []

# Prendre les valeurs de comparaison initiales à partir de la première ligne
b.append(df.iloc[0]['b'])
c.append(df.iloc[0]['c'])

# Inclure la première ligne dans le résultat
filters = [True]

# Ignorer la première ligne dans les comparaisons
for index, row in df.iloc[1:].iterrows():
    tag = 0
    for i in range(len(b)):
        # Dans ce cas, les seuils ont été modifiés respectivement à 5% et 10%
        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)
        # Mise à jour des valeurs à comparer en fonction de la dernière ligne acceptée
        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

Veuillez me faire savoir s'il existe un moyen plus rapide d'obtenir le même résultat.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X