NumPy ou Pandas: conserver le type de tableau sous forme d'entier tout en ayant une valeur NaN

160

Existe-t-il un moyen préféré de conserver le type de données d'un numpytableau fixe comme int(ou int64ou autre), tout en ayant un élément à l'intérieur répertorié comme numpy.NaN?

En particulier, je convertis une structure de données interne en un Pandas DataFrame. Dans notre structure, nous avons des colonnes de type entier qui ont toujours des NaN (mais le dtype de la colonne est int). Il semble tout refondre en flottant si nous en faisons un DataFrame, mais nous aimerions vraiment l'être int.

Pensées?

Choses essayées:

J'ai essayé d'utiliser la from_records()fonction sous pandas.DataFrame, avec coerce_float=Falseet cela n'a pas aidé. J'ai également essayé d'utiliser des tableaux masqués NumPy, avec NaN fill_value, qui ne fonctionnaient pas non plus. Tous ces éléments ont fait du type de données de colonne un flottant.

ely
la source
Pourriez-vous utiliser un tableau masqué numpy?
mgilson
Je vais essayer. J'ai également essayé la from_recordsfonction sous pandas.DataFrame, avec coerce_float=False, mais pas de chance ... cela donne toujours du type aux nouvelles données float64.
ely
1
Ouais, pas de chance. Même avec un tableau masqué, il se convertit toujours en float. On dirait que Pandas dit ceci: "Y a-t-il un NaN quelque part? ... Alors tout est un flotteur." Espérons qu'il existe un moyen de contourner ce problème.
ely
1
Le support facultatif des entiers nulles est maintenant officiellement ajouté sur pandas 0.24.0 - enfin :) - veuillez trouver une réponse mise à jour ci-dessous. Notes de version pandas 0.24.x
mork

Réponses:

70

Cette fonctionnalité a été ajoutée aux pandas (à partir de la version 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

À ce stade, il nécessite l'utilisation de l'extension dtype Int64 (en majuscule), plutôt que du dtype par défaut int64 (en minuscules).

techvslife
la source
1
Pour l'instant, vous devez spécifier un dtype spécial comme 'Int64'pour le faire fonctionner. Ce sera encore mieux quand il sera activé par défaut.
Jean Paul
C'est bien! Il y a un petit problème cependant que PyCharm ne parvient pas à afficher le dataframe dans la fenêtre de débogage s'il est utilisé de cette façon. Vous pouvez voir ma réponse à une autre question pour savoir comment forcer son affichage: stackoverflow.com/questions/38956660/... (le problème d'origine est différent, mais la solution pour afficher le dataframe fonctionne)
Alaa M.
Dois-je utiliser 'Int64'ou y a-t-il quelque chose comme 'Int8'? Il utilise une quantité insensée de mémoire par rapport à np.float.
Superdooperhero
'Int8'semble fonctionner, mais np.floatsemble toujours se charger beaucoup plus rapidement. Le problème semble être qu'il ne libère pas de mémoire entre les deux. Supposons que le ramasse-miettes finira par s'exécuter.
Superdooperhero
103

NaNne peut pas être stocké dans un tableau d'entiers. Il s'agit d'une limitation connue des pandas pour le moment; J'attendais que des progrès soient réalisés avec les valeurs NA dans NumPy (similaires aux NA dans R), mais il faudra au moins 6 mois à un an avant que NumPy n'obtienne ces fonctionnalités, il semble:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(Cette fonctionnalité a été ajoutée à partir de la version 0.24 de pandas, mais notez qu'elle nécessite l'utilisation de l'extension dtype Int64 (en majuscule), plutôt que le dtype par défaut int64 (en minuscules): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # optional-integer-na-support )

Wes McKinney
la source
7
Salut Wes, y a-t-il une mise à jour à ce sujet? Nous rencontrons des problèmes qui joignent les colonnes sont converties en entiers ou en flottants, en fonction de l'existence d'une valeur NA dans la liste d'origine. (Création de problèmes plus tard lors de la tentative de fusion de ces dataframes)
Carst
8

Si les performances ne sont pas le problème principal, vous pouvez stocker des chaînes à la place.

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

Ensuite, vous pouvez mélanger NaNautant que vous le souhaitez. Si vous voulez vraiment avoir des entiers, en fonction de votre application, vous pouvez utiliser -1, ou 0, ou 1234567890, ou une autre valeur dédiée à représenter NaN.

Vous pouvez également dupliquer temporairement les colonnes: une comme vous l'avez, avec des flottants; l'autre expérimental, avec des entiers ou des chaînes. Insère ensuite assertsà chaque endroit raisonnable en vérifiant que les deux sont synchronisés. Après suffisamment de tests, vous pouvez lâcher les flotteurs.

osa
la source
5

Ce n'est pas une solution pour tous les cas, mais le mien (coordonnées génomiques) j'ai eu recours à 0 comme NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

Cela permet au moins d'utiliser le type de colonne `` natif '' approprié, des opérations telles que la soustraction, la comparaison, etc. fonctionnent comme prévu

poisson-globe
la source
5

Pandas v0.24 +

Les fonctionnalités à prendre NaNen charge dans les séries d'entiers seront disponibles dans la version v0.24 à la hausse. Il y a des informations à ce sujet dans la section v0.24 "Quoi de neuf", et plus de détails sous Type de données entier nul .

Pandas v0.23 et versions antérieures

En général, il est préférable de travailler avec des floatséries lorsque cela est possible, même lorsque la série est remontée de intà en floatraison de l'inclusion de NaNvaleurs. Cela permet des calculs vectorisés basés sur NumPy où, sinon, les boucles de niveau Python seraient traitées.

La documentation suggère : "Une possibilité est d'utiliser des dtype=objecttableaux à la place." Par exemple:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

Pour des raisons esthétiques, par exemple la sortie dans un fichier, cela peut être préférable.

Pandas v0.23 et versions antérieures: contexte

NaNest considéré comme unfloat . Les documents actuellement (à partir de la v0.23) spécifient la raison pour laquelle les séries d'entiers sont remontées vers float:

En l'absence de prise en charge de NA haute performance intégrée à NumPy à partir de zéro, la principale victime est la capacité de représenter les NA dans des tableaux d'entiers.

Ce compromis est fait en grande partie pour des raisons de mémoire et de performances, et aussi pour que la série résultante continue à être «numérique».

Les documents fournissent également des règles pour la remontée en raison de l' NaNinclusion:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object
jpp
la source
1

C'est désormais possible, puisque pandas v 0.24.0

notes de publication de pandas 0.24.x Citation: " Pandas a acquis la capacité de contenir des dtypes entiers avec des valeurs manquantes.

mork
la source
1

Je voulais juste ajouter que dans le cas où vous essayez de convertir un vecteur float (1.143) en entier (1) dont NA convertit en le nouveau dtype 'Int64', vous obtiendrez une erreur. Pour résoudre ce problème, vous devez arrondir les nombres puis faire ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

Mon cas d'utilisation est que j'ai une série flottante que je veux arrondir à int, mais lorsque vous faites .round () un '* .0' à la fin du nombre reste, vous pouvez donc supprimer ce 0 de la fin en conversion en int.

Pedro Moisés Camacho Ureña
la source
0

S'il y a des blancs dans les données texte, les colonnes qui seraient normalement des entiers seront converties en flottants en dtype float64 car int64 dtype ne peut pas gérer les valeurs NULL. Cela peut entraîner un schéma incohérent si vous chargez plusieurs fichiers certains avec des espaces (qui finiront par float64 et d'autres sans lesquels finiront par int64

Ce code tentera de convertir toutes les colonnes de type numérique en Int64 (par opposition à int64) car Int64 peut gérer les valeurs NULL

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
Kynrek
la source