web-dev-qa-db-fra.com

Convertit une colonne Pandas contenant des NaN en dtype `int`

J'ai lu les données d'un fichier .csv dans un cadre de données Pandas comme ci-dessous. Pour l'une des colonnes, à savoir id, je souhaite spécifier le type de colonne en tant que int. Le problème est que la série id a des valeurs manquantes/vides.

Lorsque j'essaie de convertir la colonne id en entier lors de la lecture du fichier .csv, je reçois:

df= pd.read_csv("data.csv", dtype={'id': int}) 
error: Integer column has NA values

Alternativement, j'ai essayé de convertir le type de colonne après avoir lu ce qui suit, mais cette fois je reçois:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

Comment puis-je m'y attaquer?

94
Rhubarb

Le manque de rep NaN dans les colonnes entières est un pandas "gotcha" .

La solution habituelle consiste simplement à utiliser des flotteurs.

112
Andy Hayden

Mon cas d'utilisation est en train de regrouper des données avant de les charger dans une table de base de données:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

Supprimez NaNs, convertissez-les en int, convertissez-les en str, puis réinsérez les NAN.

Ce n'est pas joli mais ça fait le travail! 

13
hibernado

Si vous pouvez modifier vos données stockées, utilisez une valeur sentinel pour id manquant. Un cas d'utilisation courant, déduit du nom de la colonne, étant que id est un entier strictement supérieur à zéro, vous pouvez utiliser 0 comme valeur sentinelle pour pouvoir écrire

if row['id']:
   regular_process(row)
else:
   special_process(row)
3
gboffi

Dans la version 0.24.0 (à venir), les pandas ont acquis la capacité de contenir des types entiers avec des valeurs manquantes.

Type de données Nullable Integer .

Les pandas peuvent représenter des données entières avec éventuellement des valeurs manquantes avec arrays.IntegerArray . C'est un type d'extension implémenté dans les pandas. Ce n'est pas le type par défaut pour les entiers, et ne sera pas déduit; vous devez explicitement passer le type dans array () ou Series:

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64
3
jezrael

Il est maintenant possible de créer une colonne pandas contenant des NaNs en tant que type int, car elle est maintenant ajoutée officiellement sur les pandas 0.24.0.

Notes de publication de pandas 0.24.x Citation: "Pandas est désormais capable de contenir des types entiers avec des valeurs manquantes

2
mork

Vous pouvez utiliser .dropna() s'il est correct de supprimer les lignes contenant les valeurs NaN.

df = df.dropna(subset=['id'])

Sinon, Utilisez .fillna() et .astype() pour remplacer le NaN par des valeurs et les convertir en int.

J'ai rencontré ce problème lors du traitement d'un fichier CSV avec de grands entiers, alors que certains d'entre eux manquaient (NaN). Utiliser float comme type n'était pas une option, car je risquais de perdre la précision.

Ma solution était de utiliser str comme type intermédiaire . Ensuite, vous pouvez convertir la chaîne en int comme vous voudrez plus tard dans le code. J'ai remplacé NaN par 0, mais vous pouvez choisir n'importe quelle valeur.

df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)

Pour l'illustration, voici un exemple de perte de précision des flotteurs:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

Et le résultat est:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
2
elomage

Si vous voulez absolument combiner des entiers et des NaN dans une colonne, vous pouvez utiliser le type de données 'objet':

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

Cela remplacera les NaN par un entier (peu importe), convertira en int, convertira en objet et finalement réinsérera les NaN.

0
jmenglund

La plupart des solutions décrites ici expliquent comment utiliser un entier fictif pour représenter des valeurs nulles. Cette approche n'est pas utile si vous n'êtes pas sûr que l'entier n'apparaisse pas dans vos données source. Ma méthode avec le format volonté flotte sans leurs valeurs décimales et convertit les valeurs NULL en valeurs None. Le résultat est un type de données d'objet qui ressemblera à un champ entier avec des valeurs null lorsqu'il est chargé dans un fichier CSV.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
0
Corbin

J'ai rencontré ce problème en travaillant avec pyspark. S'agissant d'une interface python pour le code s'exécutant sur une machine virtuelle, elle nécessite la sécurité du type et utiliser float au lieu de int n'est pas une option. J'ai résolu le problème en encapsulant les pandas pd.read_csv dans une fonction qui remplira les colonnes définies par l'utilisateur avec des valeurs de remplissage définies par l'utilisateur avant de les convertir au type requis. Voici ce que j'ai fini par utiliser:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = {}, **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df
0
Neuneck

En supposant que votre DateColumn formaté 3312018.0 devrait être converti en 03/31/2018 en tant que chaîne. Et, certains enregistrements sont manquants ou 0.

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
0
Justin Malinchak

Commencez par supprimer les lignes contenant NaN. Effectuez ensuite la conversion d’entiers sur les lignes restantes . Insérez enfin les lignes supprimées à nouveau .

0
kamran kausar