Python >> Tutoriel Python >  >> Python Tag >> Pandas

Pandas NaN — Travailler avec des données manquantes

Pandas est Excel sur les stéroïdes :la puissante bibliothèque Python vous permet d'analyser des données structurées et tabulaires avec une efficacité et une facilité surprenantes. Pandas est l'une des raisons pour lesquelles les codeurs maîtres atteignent 100 fois l'efficacité des codeurs moyens. Dans l'article d'aujourd'hui, vous apprendrez à travailler avec des données manquantes, en particulier comment gérer les valeurs NaN dans Pandas DataFrames.

Vous découvrirez toutes les différentes raisons pour lesquelles les NaN apparaissent dans vos DataFrames et comment les gérer. Commençons !

Vérification des séries pour les valeurs NaN

Problème :Comment vérifier une série pour les valeurs NaN ?

Jetez un oeil au code suivant :

import pandas as pd
import numpy as np

data = pd.Series([0, np.NaN, 2])
result = data.hasnans

print(result)
# True

La série peut contenir NaN -values :une abréviation pour Not-A-Number —qui décrivent des valeurs indéfinies.

Pour vérifier si une Série contient un ou plusieurs NaN valeur, utilisez l'attribut hasnans . L'attribut renvoie True s'il y a au moins un NaN valeur et False Par ailleurs.

Il y a un NaN valeur dans la série, donc la sortie est True .

Le filtrage des séries génère NaN

Problème  :Lors du filtrage d'une série avec where() et aucun élément ne passe la condition de filtrage, quel est le résultat ?

import pandas as pd

xs = pd.Series([5, 1, 4, 2, 3])
xs.where(xs > 2, inplace=True)
result = xs.hasnans

print(result)
# True

La méthode where() filtre une série par une condition. Seuls les éléments qui satisfont la condition restent dans la série résultante. Et que se passe-t-il si une valeur ne satisfait pas la condition ? Par défaut, toutes les lignes ne satisfaisant pas la condition sont remplies avec NaN -valeurs.

C'est pourquoi notre série contient NaN -values ​​après filtrage avec la méthode where() .

Travailler avec plusieurs séries de longueurs différentes

Problème  :Si vous ajoutez élément par élément deux objets Series avec un nombre différent d'éléments, que se passe-t-il avec les éléments restants ?

import pandas as pd

s = pd.Series(range(0, 10))
t = pd.Series(range(0, 20))
result = (s + t)[1]

print(result)
# 2

Pour ajouter deux éléments de série, utilisez l'opérateur d'addition par défaut + . La série n'a pas besoin d'avoir la même taille car une fois la première série terminée, les résultats suivants par élément sont NaN valeurs.

A l'indice 1 dans la série résultante, vous obtenez le résultat de 1 + 1 = 2 .

Créer un DataFrame à partir d'une liste de dictionnaires avec des clés inégales

Problème :Comment créer un DataFrame à partir d'une liste de dictionnaires si les dictionnaires ont des clés inégales ? Un DataFrame s'attend à ce que les mêmes colonnes soient disponibles pour chaque ligne !

import pandas as pd

data = [{'Car':'Mercedes', 'Driver':'Hamilton, Lewis'},
        {'Car':'Ferrari', 'Driver':'Schumacher, Michael'},
        {'Car':'Lamborghini'}]

df = pd.DataFrame(data, index=['Rank 2', 'Rank 1', 'Rank 3'])
df.sort_index(inplace=True)
result = df['Car'].iloc[0]

print(result)
# Ferrari

Vous pouvez créer un DataFrame à partir d'une liste de dictionnaires. Les clés des dictionnaires définissent les étiquettes des colonnes et les valeurs définissent les entrées des colonnes. Tous les dictionnaires ne doivent pas contenir les mêmes clés. Si un dictionnaire ne contient pas une clé particulière, cela sera interprété comme un NaN -valeur.

Cet extrait de code utilise des étiquettes de chaîne comme valeurs d'index pour trier le DataFrame. Après avoir trié le DataFrame, la ligne avec l'étiquette d'index Rank 1 est à l'emplacement 0 dans le DataFrame et la valeur dans la colonne Car est Ferrari .

Trier un DataFrame par colonne avec des valeurs NaN

Problème :Que se passe-t-il si vous triez un DataFrame par colonne si la colonne contient un NaN valeur ?

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

selection = df.sort_values(by="engine-size")
result = selection.index.to_list()[0]
print(result)
# 1

Dans cet extrait de code, vous triez les lignes du DataFrame par les valeurs de la colonne engine-size .

Le point principal est que NaN les valeurs sont toujours déplacées à la fin dans le tri Pandas. Ainsi, la première valeur est 1.8 , qui appartient à la ligne avec la valeur d'index 1 .

Compter les valeurs non-NaN

Problème :Comment compter le nombre d'éléments dans une colonne de dataframe qui ne sont pas Nan ?

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

df.count()[5]
print(result)
# 4

La méthode count() renvoie le nombre de non-NaN valeurs pour chaque colonne. Le DataFrame df a cinq rangées. La cinquième colonne
contient un NaN évaluer. Par conséquent, le nombre de la cinquième colonne est 4 .

Supprimer les valeurs NaN

Problème :comment supprimer toutes les lignes contenant un NaN valeur dans l'une de ses colonnes - et comment limiter cela à certaines colonnes ?

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

selection1 = df.dropna(subset=["price"])
selection2 = df.dropna()
print(len(selection1), len(selection2))
# 5 4

Le dropna() du DataFrame la méthode supprime toutes les lignes contenant un NaN valeur dans l'une de ses colonnes. Mais comment restreindre les colonnes à scanner pour NaN valeurs ?

En passant une liste d'étiquettes de colonne au paramètre facultatif subset , vous pouvez définir les colonnes que vous souhaitez prendre en compte.

L'appel du dropna() sans restriction, abandonne la ligne 2 à cause du NaN valeur dans la colonne engine-size . Lorsque vous limitez les colonnes uniquement à price , aucune ligne ne sera supprimée, car aucun NaN la valeur est présente.

Supprimer Nan et réinitialiser l'index

Problème  :Qu'arrive-t-il aux index après avoir supprimé certaines lignes ?

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

df.drop([0, 1, 2], inplace=True)
df.reset_index(inplace=True)
result = df.index.to_list()
print(result)
# [0, 1]

La méthode drop() sur un DataFrame supprime les lignes ou les colonnes par index. Vous pouvez transmettre une valeur unique ou une liste de valeurs.

Par défaut le inplace le paramètre est défini sur False , afin que les modifications n'affectent pas l'objet DataFrame initial. Au lieu de cela, la méthode renvoie une copie modifiée du DataFrame. Dans le puzzle, vous définissez inplace à True , donc les suppressions sont effectuées directement sur le DataFrame.

Après avoir supprimé les trois premières lignes, les deux premières étiquettes d'index sont 3 et 4. Vous pouvez réinitialiser l'indexation par défaut en appelant la méthode reset_index() sur le DataFrame, de sorte que l'index recommence à 0. Comme il ne reste que deux lignes dans le DataFrame, le résultat est [0, 1] .

Concaténation de DataFrames dissemblables remplis de NaN

Problème :Comment concaténer deux DataFrames si elles ont des colonnes différentes ?

import pandas as pd

df = pd.read_csv("Cars.csv")
df2 = pd.read_csv("Cars2.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

# Additional Dataframe "df2"
# – --------
#      make   origin
# 0   skoda  Czechia
# 1  toyota    Japan
# 2    ford      USA
# – --------

try:
    result = pd.concat([df, df2], axis=0, ignore_index=True)
    print("Y")
except Exception:
    print ("N")

# Y

Même si les DataFrames ont des colonnes différentes, vous pouvez les concaténer.

Si DataFrame 1 a les colonnes A et B et DataFrame 2 a les colonnes C et D, le résultat de la concaténation des DataFrames 1 et 2 est un DataFrame avec les colonnes A, B, C et D. Les valeurs manquantes dans les lignes sont remplies avec NaN .

Fusion externe

Problème  :Lors de la fusion (=assemblage) de deux DataFrames, que se passe-t-il s'il manque des valeurs ?

import pandas as pd

df = pd.read_csv("Cars.csv")
df2 = pd.read_csv("Cars2.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

# Additional dataframe "df2"
# – --------
#     make   origin
# 0  skoda  Czechia
# 1  mazda    Japan
# 2   ford      USA
# – --------

result = pd.merge(df, df2, how="outer", left_on="make", right_on="make")
print(len(result["fuel"]))
print(result["fuel"].count())
# 7
# 5

Avec la fonction Panda merge() et le paramètre how défini sur outer , vous pouvez effectuer une jointure externe .

Le DataFrame résultant d'une jointure externe contient toutes les valeurs des deux DataFrames d'entrée ; les valeurs manquantes sont remplies avec NaN .

De plus, ce puzzle montre comment NaN les valeurs sont comptées par le len() alors que la méthode count() n'inclut pas NaN valeurs.

Remplacement de NaN

Problème :Comment remplacer tous les NaN valeurs dans un DataFrame avec une valeur donnée ?

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

df.fillna(2.0, inplace=True)
result = df["engine-size"].sum()
print(result)
# 13.8

La méthode fillna() remplace NaN valeurs avec une nouvelle valeur. Ainsi, la somme de toutes les valeurs de la colonne engine-size est de 13,8.

Différence entre la longueur et le nombre :c'est NaN !

Problème :Quelle est la différence entre le len() et le count() fonctions ?

import pandas as pd

df = pd.read_csv("Cars.csv")
df2 = pd.read_csv("Cars2.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

# Additional dataframe "df2"
# – --------
#     make   origin
# 0  skoda  Czechia
# 1  mazda    Japan
# 2   ford      USA
# – --------

result = pd.merge(df2, df, how="left", left_on="make", right_on="make")
print(len(result["fuel"]))
print(result["fuel"].count())
# 3
# 1

Dans une jointure gauche, le DataFrame gauche est le maître et toutes ses valeurs sont incluses dans le DataFrame résultant.

Par conséquent, le résultat DataFrame contient trois lignes, mais depuis skoda et ford n'apparaissent pas dans DataFrame df , une seule ligne pour mazda contient une valeur.

Encore une fois, nous voyons la différence entre l'utilisation de la fonction len() qui comprend également NaN valeurs et la méthode count() qui ne compte pas NaN valeurs.

Equals() vs. ==lors de la comparaison de NaN

Problème :

import pandas as pd

df = pd.read_csv("Cars.csv")

# Dataframe "df"
# – --------
#       make    fuel aspiration   body-style   price  engine-size
# 0     audi     gas      turbo        sedan   30000          2.0
# 1    dodge     gas        std        sedan   17000          1.8
# 2    mazda  diesel        std        sedan   17000          NaN
# 3  porsche     gas      turbo  convertible  120000          6.0
# 4    volvo  diesel        std        sedan   25000          2.0
# – --------

df["engine-size_copy"] = df["engine-size"]
check1 = (df["engine-size_copy"] == df["engine-size"]).all()
check2 = df["engine-size_copy"].equals(df["engine-size"])
print(check1 == check2)
# False

Cet extrait de code montre comment comparer des colonnes ou des DataFrames entiers concernant la forme et les éléments.

La comparaison utilisant l'opérateur == renvoie False pour notre DataFrame car la comparaison NaN -valeurs avec == donne toujours False .

Par contre, df.equals() permet de comparer deux Series ou DataFrames. Dans ce cas, NaN -les valeurs au même endroit sont considérées comme égales.

Les en-têtes de colonne n'ont pas besoin d'avoir le même type, mais les éléments dans les colonnes doivent être du même dtype .

Depuis le résultat de check1 est False et le résultat de check2 donne True , la sortie finale est False .