web-dev-qa-db-fra.com

Trouver des lignes non numériques dans la structure de données des pandas?

J'ai une grande base de données dans pandas), mis à part que la colonne utilisée comme index est supposée avoir uniquement des valeurs numériques:

df = pd.DataFrame({'a': [1, 2, 3, 'bad', 5],
                   'b': [0.1, 0.2, 0.3, 0.4, 0.5],
                   'item': ['a', 'b', 'c', 'd', 'e']})
df = df.set_index('item')

Comment trouver la ligne de la dataframe df contenant une valeur non numérique?

Dans cet exemple, il s’agit de la quatrième ligne du cadre de données, qui contient la chaîne 'bad' dans la colonne a. Comment cette ligne peut-elle être trouvée par programme?

47
user248237

Vous pouvez utiliser np.isreal pour vérifier le type de chaque élément ( applymap applique une fonction à chaque élément du DataFrame):

In [11]: df.applymap(np.isreal)
Out[11]:
          a     b
item
a      True  True
b      True  True
c      True  True
d     False  True
e      True  True

Si tous les éléments de la ligne sont vrais, ils sont tous numériques:

In [12]: df.applymap(np.isreal).all(1)
Out[12]:
item
a        True
b        True
c        True
d       False
e        True
dtype: bool

Donc, pour obtenir le sous-cadre DataFrame de rouges, (Remarque: la négation, ~, de ce qui précède trouve ceux qui ont au moins un voyou non numérique):

In [13]: df[~df.applymap(np.isreal).all(1)]
Out[13]:
        a    b
item
d     bad  0.4

Vous pouvez également trouver l'emplacement du premier délinquant que vous pouvez utiliser argmin :

In [14]: np.argmin(df.applymap(np.isreal).all(1))
Out[14]: 'd'

Comme @ CTZh fait remarquer, il peut être légèrement plus rapide de vérifier s'il s'agit d'une instance de int ou float (il y a une surcharge supplémentaire avec np.isreal):

df.applymap(lambda x: isinstance(x, (int, float)))
57
Andy Hayden

Déjà quelques bonnes réponses à cette question, cependant voici un extrait de Nice que j'utilise régulièrement pour supprimer des lignes si elles ont des valeurs non numériques sur certaines colonnes:

# Eliminate invalid data from dataframe (see Example below for more context)

num_df = (df.drop(data_columns, axis=1)
         .join(df[data_columns].apply(pd.to_numeric, errors='coerce')))

num_df = num_df[num_df[data_columns].notnull().all(axis=1)]

La façon dont cela fonctionne est d’abord drop tous les data_columns du df, puis utilisez un join pour les remettre après les avoir passés par pd.to_numeric (avec l'option 'coerce', de sorte que toutes les entrées non numériques soient converties en NaN). Le résultat est enregistré sur num_df.

Sur la deuxième ligne, nous utilisons un filtre qui ne conserve que les lignes où toutes les valeurs ne sont pas nulles.

Notez que pd.to_numeric contraint NaN tout ce qui ne peut pas être converti en valeur numérique, ainsi les chaînes représentant des valeurs numériques ne seront pas supprimées. Par exemple '1.25' sera reconnu comme la valeur numérique 1.25.

Avertissement: pd.to_numeric a été introduit dans pandas version 0.17.0

Exemple:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({"item": ["a", "b", "c", "d", "e"],
   ...:                    "a": [1,2,3,"bad",5],
   ...:                    "b":[0.1,0.2,0.3,0.4,0.5]})

In [3]: df
Out[3]: 
     a    b item
0    1  0.1    a
1    2  0.2    b
2    3  0.3    c
3  bad  0.4    d
4    5  0.5    e

In [4]: data_columns = ['a', 'b']

In [5]: num_df = (df
   ...:           .drop(data_columns, axis=1)
   ...:           .join(df[data_columns].apply(pd.to_numeric, errors='coerce')))

In [6]: num_df
Out[6]: 
  item   a    b
0    a   1  0.1
1    b   2  0.2
2    c   3  0.3
3    d NaN  0.4
4    e   5  0.5

In [7]: num_df[num_df[data_columns].notnull().all(axis=1)]
Out[7]: 
  item  a    b
0    a  1  0.1
1    b  2  0.2
2    c  3  0.3
4    e  5  0.5
14
Pedro M Duarte

Désolé pour la confusion, cela devrait être la bonne approche. Voulez-vous seulement capturer 'bad' seulement, pas des choses comme 'good'; Ou simplement des valeurs non numériques?

In[15]:
np.where(np.any(np.isnan(df.convert_objects(convert_numeric=True)), axis=1))
Out[15]:
(array([3]),)
6
CT Zhu
# Original code
df = pd.DataFrame({'a': [1, 2, 3, 'bad', 5],
                   'b': [0.1, 0.2, 0.3, 0.4, 0.5],
                   'item': ['a', 'b', 'c', 'd', 'e']})
df = df.set_index('item')

Convertir en numérique en utilisant 'coerce' qui remplit les mauvaises valeurs avec 'nan'

a = pd.to_numeric(df.a, errors='coerce')

Utilisez isna pour renvoyer un index booléen:

idx = a.isna()

Appliquez cet index au bloc de données:

df[idx]

sortie

Renvoie la ligne contenant les mauvaises données:

        a    b
item          
d     bad  0.4
5
matiu

Si vous travaillez avec une colonne avec des valeurs de chaîne, vous pouvez utiliser la fonction très utile series.str.isnumeric () comme:

a = pd.Series(['hi','hola','2.31','288','312','1312', '0,21', '0.23'])

Ce que je fais est de copier cette colonne dans une nouvelle colonne et de faire un str.replace ('.', '') Et str.replace (',', ''), puis je sélectionne les valeurs numériques. et:

a = a.str.replace('.','')
a = a.str.replace(',','') 
a.str.isnumeric()

Out [15]: 0 False 1 False 2 Vrai 3 Vrai 4 Vrai 5 Vrai 6 Vrai 7 Vrai dtype: bool

Bonne chance à tous!

2
Borja_042

Je pense à quelque chose comme, juste donner une idée, pour convertir la colonne en chaîne, et travailler avec une chaîne est plus facile. Cependant, cela ne fonctionne pas avec les chaînes contenant des nombres, comme bad123. et ~ prend le complément de sélection.

df['a'] = df['a'].astype(str)
df[~df['a'].str.contains('0|1|2|3|4|5|6|7|8|9')]
df['a'] = df['a'].astype(object)

et en utilisant '|'.join([str(i) for i in range(10)]) pour générer '0|1|...|8|9'

ou en utilisant la fonction np.isreal(), tout comme la réponse la plus votée

df[~df['a'].apply(lambda x: np.isreal(x))]
0
dyang