supprimer des valeurs infinies des trames de données dans les pandas?

220

quel est le moyen le plus rapide / le plus simple de supprimer des valeurs nan et inf / -inf d'un DataFrame pandas sans réinitialiser mode.use_inf_as_null? J'aimerais pouvoir utiliser les arguments subsetet howde dropna, sauf avec des infvaleurs considérées comme manquantes, comme:

df.dropna(subset=["col1", "col2"], how="all", with_inf=True)

Est-ce possible? Existe-t-il un moyen de dire dropnad'inclure infdans sa définition des valeurs manquantes?


la source

Réponses:

418

Le moyen le plus simple serait de commencer par replaceinfs vers NaN:

df.replace([np.inf, -np.inf], np.nan)

puis utilisez dropna:

df.replace([np.inf, -np.inf], np.nan).dropna(subset=["col1", "col2"], how="all")

Par exemple:

In [11]: df = pd.DataFrame([1, 2, np.inf, -np.inf])

In [12]: df.replace([np.inf, -np.inf], np.nan)
Out[12]:
    0
0   1
1   2
2 NaN
3 NaN

La même méthode fonctionnerait pour une série.

Andy Hayden
la source
2
Comment peut-on "échanger" les infvaleurs à un prédéfini intcomme 0, dans une certaine colonne?
3kstc le
4
@ 3kstc use .replace(..., 0). Pour faire juste sur les colonnes, vous mettez à jour ces colonnes, c'estdf[cols] = df[cols].replace(..., 0)
Andy Hayden
3
Peut-être que cela vaut la peine de préciser que replacecela ne fonctionne pas sur place, donc une nouvelle DataFrameest retournée
Marco
37

Avec le contexte d'option, cela est possible sans réglage permanent use_inf_as_na. Par exemple:

with pd.option_context('mode.use_inf_as_na', True):
    df = df.dropna(subset=['col1', 'col2'], how='all')

Bien sûr, il peut être configuré pour traiter de infmanière NaNpermanente avec

pd.set_option('use_inf_as_na', True)

Pour les versions plus anciennes, remplacez use_inf_as_napar use_inf_as_null.

ayhan
la source
6
C'est la réponse la plus lisible et par conséquent la meilleure, même si elle viole en lettre (mais pas en esprit) la question d'origine.
ijoseph
2
Pandas à partir de (au moins) 0,24: use_inf_as_nullavait été déprécié et sera supprimé dans une future version. Utilisez use_inf_as_naplutôt. Ajouter à / mettre à jour la réponse?
Håkon T.
1
Celui-ci est un meilleur choix à traiter infcomme des valeurs nulles au niveau des paramètres globaux au lieu du niveau opérationnel. Cela pourrait potentiellement gagner du temps en imputant les valeurs en premier.
TaoPR
15

Voici une autre méthode .locpermettant de remplacer inf par nan sur une série:

s.loc[(~np.isfinite(s)) & s.notnull()] = np.nan

Donc, en réponse à la question initiale:

df = pd.DataFrame(np.ones((3, 3)), columns=list('ABC'))

for i in range(3): 
    df.iat[i, i] = np.inf

df
          A         B         C
0       inf  1.000000  1.000000
1  1.000000       inf  1.000000
2  1.000000  1.000000       inf

df.sum()
A    inf
B    inf
C    inf
dtype: float64

df.apply(lambda s: s[np.isfinite(s)].dropna()).sum()
A    2
B    2
C    2
dtype: float64
Alexandre
la source
11

Utilisation (rapide et simple):

df = df[np.isfinite(df).all(1)]

Cette réponse est basée sur la réponse de DougR dans une autre question. Voici un exemple de code:

import pandas as pd
import numpy as np
df=pd.DataFrame([1,2,3,np.nan,4,np.inf,5,-np.inf,6])
print('Input:\n',df,sep='')
df = df[np.isfinite(df).all(1)]
print('\nDropped:\n',df,sep='')

Résultat:

Input:
    0
0  1.0000
1  2.0000
2  3.0000
3     NaN
4  4.0000
5     inf
6  5.0000
7    -inf
8  6.0000

Dropped:
     0
0  1.0
1  2.0
2  3.0
4  4.0
6  5.0
8  6.0
Markus Dutschke
la source
8

Une autre solution encore consisterait à utiliser la isinméthode. Utilisez-le pour déterminer si chaque valeur est infinie ou manquante, puis enchaînez leall méthode pour déterminer si toutes les valeurs des lignes sont infinies ou manquantes.

Enfin, utilisez la négation de ce résultat pour sélectionner les lignes qui n'ont pas toutes les valeurs infinies ou manquantes via l'indexation booléenne.

all_inf_or_nan = df.isin([np.inf, -np.inf, np.nan]).all(axis='columns')
df[~all_inf_or_nan]
Ted Petrou
la source
7

La solution ci-dessus modifiera les infs qui ne sont pas dans les colonnes cibles. Pour y remédier,

lst = [np.inf, -np.inf]
to_replace = {v: lst for v in ['col1', 'col2']}
df.replace(to_replace, np.nan)
has2k1
la source
3
python 2.7 et versions supérieures de prise en charge des dictées:{v: lst for v in cols}
Aryeh Leib Taurog
4

Vous pouvez utiliser pd.DataFrame.maskavec np.isinf. Vous devez d'abord vous assurer que vos séries de trames de données sont toutes de type float. Utilisez ensuite dropnaavec votre logique existante.

print(df)

       col1      col2
0 -0.441406       inf
1 -0.321105      -inf
2 -0.412857  2.223047
3 -0.356610  2.513048

df = df.mask(np.isinf(df))

print(df)

       col1      col2
0 -0.441406       NaN
1 -0.321105       NaN
2 -0.412857  2.223047
3 -0.356610  2.513048
jpp
la source