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?
Le manque de rep NaN dans les colonnes entières est un pandas "gotcha" .
La solution habituelle consiste simplement à utiliser des flotteurs.
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!
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)
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
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
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
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.
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)))
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
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'))
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 .