[PYTHON] Caractéristiques de pd.NA dans Pandas 1.0.0 (RC0)

update1 25/01/2020: Ajouté ce comportement semblable à un bogue était un bogue

Depuis le 13/01/2020, pandas 1.0.0rc0 a été publié, mais l'une des fonctionnalités majeures est l'introduction de pd.NA comme valeur manquante. Je vais résumer cette propriété et comment l'utiliser.

Avertissement: il a été confirmé qu'il fonctionne avec pandas 1.0.0 rc0, et il y a de fortes chances que cela change à l'avenir.

Enfin, [Environnement de vérification](#Verification Environment).

emballer

--pd.NA apparaît comme la signification de la valeur manquante. --pd.NA peut être utilisé avec IntegerArray, BooleanArray, StringArray --Avec l'introduction de pd.NA, la valeur manquante peut être exprimée dans la classe int (pas de conversion imprudente en float). --pd.NA est un objet singleton et est cohérent avec tous les types de données. --Toutes les valeurs de retour des opérateurs de comparaison pour pd.NA sont pd.NA (même comportement que pd.NA dans Julia, NA dans R).

data type

Une nouvelle classe appelée NAType est introduite dans les pandas. Le but est d'indiquer la valeur comme une valeur manquante.

>>> import pandas as pd
>>> pd.NA
<NA>

>>> type(pd.NA)
<class 'pandas._libs.missing.NAType'>

Dans pd.Series et pd.DataFrame, si vous ne spécifiez pas de type, il est traité comme un type d'objet, et si vous le spécifiez, il est traité comme ce type. ʻInt64Dtype est un entier Nullable (Un ExtensionDtype pour les données entières int64. Tableau de valeurs entières (facultatives manquantes)) introduit à partir de pandas 0.24. Notez que vous devez spécifier dtype en majuscules ʻInt64 au lieu de ʻint64`. Techniquement, l'introduction de «Pandas Extension Arrays» a permis d'utiliser ExtensionDType.

>>> pd.Series([pd.NA]).dtype
dtype('O') # O means Object

#Le dtype peut être spécifié soit comme un alias de chaîne, soit comme le type lui-même. Ce qui suit est spécifié sous forme de chaîne de caractères.
>>> pd.Series([pd.NA], dtype="Int64").dtype
Int64Dtype()

>>> pd.Series([pd.NA], dtype="boolean").dtype
BooleanDtype

>>> pd.Series([pd.NA], dtype="string").dtype
StringDtype

Cliquez ici pour l'implémentation de NAType.

https://github.com/pandas-dev/pandas/blob/493363ef60dd9045888336b5c801b2a3d00e976d/pandas/_libs/missing.pyx#L335-L485

Fait intéressant, la valeur de hachage est définie par «2 ** 61 --1 == 2305843009213693951». Il n'y a pas de problème car il n'y a pas de conflit avec la clé du dictionnaire. Ce n'est pas lié à pd.NA, mais en fait, le hachage des valeurs entières en python va avec 2 ** 61 -1.

>>> hash(pd.NA) == 2 ** 61 -1
True

>>> {pd.NA: "a", 2305843009213693951: "b"}
{<NA>: 'a', 2305843009213693951: 'b'}

>>> (hash(2**61 - 2), hash(2**61 - 1), hash(2**61))
(2305843009213693950, 0, 1)

Types dans pd.Series, pd.DataFrame

La détermination du type doit être spécifiée en majuscules ʻInt64 au lieu de ʻint64.

>>> pd.Series([1, 2]) + pd.Series([pd.NA, pd.NA])
0    <NA>
1    <NA>
dtype: object

>>> pd.Series([1, 2]) + pd.Series([pd.NA, pd.NA], dtype="Int64")
0    <NA>
1    <NA>
dtype: Int64

Spécifier ʻint64` entraînera une erreur.

>>> pd.Series([pd.NA], dtype="int64").dtype
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/pandas/core/series.py", line 304, in __init__
    data = sanitize_array(data, index, dtype, copy, raise_cast_failure=True)
  File "/usr/local/lib/python3.7/site-packages/pandas/core/construction.py", line 438, in sanitize_array
    subarr = _try_cast(data, dtype, copy, raise_cast_failure)
  File "/usr/local/lib/python3.7/site-packages/pandas/core/construction.py", line 535, in _try_cast
    subarr = maybe_cast_to_integer_array(arr, dtype)
  File "/usr/local/lib/python3.7/site-packages/pandas/core/dtypes/cast.py", line 1502, in maybe_cast_to_integer_array
    casted = np.array(arr, dtype=dtype, copy=copy)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NAType'

Le résultat de l'opération avec booléen se comporte de la même manière que Julia's missing et R's NA.

>>> pd.Series([True, False, pd.NA]) & True
0     True
1    False
2       NA
dtype: bool

>>> pd.Series([True, False, pd.NA]) | True
0     True
1     True
2     True
dtype: bool

>>> pd.NA & True
NA

>>> pd.NA & False
False

>>> pd.NA | True
True

>>> pd.NA | False
NA
>>> pd.Series([1, 2, pd.NA], dtype="Int64")
0       1
1       2
2    <NA>
dtype: Int64
>>> pd.Series([True, False, pd.NA], dtype="boolean")
0     True
1    False
2     <NA>
dtype: boolean

Le résultat de l'opération de somme est "NA" propagé (propager), mais "pd.Series.sum ()" sans argument est traité comme "0" et n'est pas propagé. Il est nécessaire de spécifier sum (skipna = False) pour le traiter comme propagate. Cependant, lors de la spécification du type de `` Int64 '', np.nan était affiché à la place de NA. ~~ J'ai cherché un problème pour voir s'il s'agissait du comportement attendu ou d'un bogue, mais ce n'était pas clair. J'ai donc créé imprudemment un ticket d'émission. ~~ ticket d'émission Importé. Cela semble se refléter dans rc1.

>>> sum([1, pd.NA])
<NA>
# pd.Series object
>>> pd.Series([1, pd.NA])
0       1
1    <NA>
dtype: object

>>> pd.Series([1, pd.NA]).sum()
1
>>> pd.Series([1, pd.NA]).sum(skipna=False)
<NA>
# pd.Series Int64
>>> pd.Series([1, pd.NA], dtype='Int64')
0       1
1    <NA>
dtype: Int64

>>> pd.Series([1, pd.NA], dtype='Int64').sum()
1
>>> pd.Series([1, pd.NA], dtype='Int64').sum(skipna=False)
nan

fonction pow

Le traitement des puissances est cohérent avec «NA_integer_» de R. Le comportement de Julia est un mystère.

>>> pd.NA ** 0
1
>>> 1 ** pd.NA
1
>>> -1 ** pd.NA
-1
> R.version.string
[1] "R version 3.6.1 (2019-07-05)"

> NA_integer_ ^ 0L
[1] 1
> 1L ^ NA_integer_
[1] 1
> -1L ^ NA_integer_
[1] -1
julia> VERSION
v"1.3.1"

julia> missing ^ 0
missing

julia> 1 ^ missing
missing

julia> -1 ^ missing
missing

Spécifié par read_csv

Expérimentez avec le fichier csv suivant. (test.csv)

X_int,X_bool,X_string
1,True,"a"
2,False,"b"
NA,NA,"NA"

Le comportement est le même que celui de pandas 0.25.3 sans spécifier dtype.

>>> df1 = pd.read_csv("test.csv")
>>> df1
   X_int X_bool X_string
0    1.0   True        a
1    2.0  False        b
2    NaN    NaN      NaN
>>> df1.dtypes
X_int       float64
X_bool       object
X_string     object
dtype: object

ʻInt64etstring` peuvent être spécifiés pour dtype.

#dtype peut être la classe de type suivante au lieu d'un littéral de caractère.
# df2 = pd.read_csv("test.csv", dtype={'X_int': pd.Int64Dtype(), 'X_string': pd.StringDtype()})
>>> df2 = pd.read_csv("test.csv", dtype={'X_int': 'Int64', 'X_string': 'string'})
>>> df2
   X_int X_bool X_string
0      1   True        a
1      2  False        b
2   <NA>    NaN     <NA>
>>> df2.dtypes
X_int        Int64
X_bool      object
X_string    string
dtype: object

D'un autre côté, même si boolean '' `pd.BooleanDtype ()` est spécifié, la lecture en tant que booléen NA échoue. Bien sûr, spécifier 'bool' 'est également une erreur. problème Lorsque je l'ai signalé, il a été importé avec succès. Cela semble fonctionner correctement avec rc1.

>>> df3 = pd.read_csv("test.csv", dtype={'X_bool': 'boolean'})
Traceback (most recent call last):
  File "pandas/_libs/parsers.pyx", line 1191, in pandas._libs.parsers.TextReader._convert_with_dtype
  File "/usr/local/lib/python3.7/site-packages/pandas/core/arrays/base.py", line 232, in _from_sequence_of_strings
    raise AbstractMethodError(cls)
pandas.errors.AbstractMethodError: This method must be defined in the concrete class type

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 676, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 454, in _read
    data = parser.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 1133, in read
    ret = self._engine.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 2037, in read
    data = self._reader.read(nrows)
  File "pandas/_libs/parsers.pyx", line 859, in pandas._libs.parsers.TextReader.read
  File "pandas/_libs/parsers.pyx", line 874, in pandas._libs.parsers.TextReader._read_low_memory
  File "pandas/_libs/parsers.pyx", line 951, in pandas._libs.parsers.TextReader._read_rows
  File "pandas/_libs/parsers.pyx", line 1083, in pandas._libs.parsers.TextReader._convert_column_data
  File "pandas/_libs/parsers.pyx", line 1114, in pandas._libs.parsers.TextReader._convert_tokens
  File "pandas/_libs/parsers.pyx", line 1194, in pandas._libs.parsers.TextReader._convert_with_dtype
NotImplementedError: Extension Array: <class 'pandas.core.arrays.boolean.BooleanArray'> must implement _from_sequence_of_strings in order to be used in parser methods
>>> df3 = pd.read_csv("test.csv", dtype={'X_bool': pd.BooleanDtype()})
Traceback (most recent call last):
  File "pandas/_libs/parsers.pyx", line 1191, in pandas._libs.parsers.TextReader._convert_with_dtype
  File "/usr/local/lib/python3.7/site-packages/pandas/core/arrays/base.py", line 232, in _from_sequence_of_strings
    raise AbstractMethodError(cls)
pandas.errors.AbstractMethodError: This method must be defined in the concrete class type

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 676, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 454, in _read
    data = parser.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 1133, in read
    ret = self._engine.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 2037, in read
    data = self._reader.read(nrows)
  File "pandas/_libs/parsers.pyx", line 859, in pandas._libs.parsers.TextReader.read
  File "pandas/_libs/parsers.pyx", line 874, in pandas._libs.parsers.TextReader._read_low_memory
  File "pandas/_libs/parsers.pyx", line 951, in pandas._libs.parsers.TextReader._read_rows
  File "pandas/_libs/parsers.pyx", line 1083, in pandas._libs.parsers.TextReader._convert_column_data
  File "pandas/_libs/parsers.pyx", line 1114, in pandas._libs.parsers.TextReader._convert_tokens
  File "pandas/_libs/parsers.pyx", line 1194, in pandas._libs.parsers.TextReader._convert_with_dtype
NotImplementedError: Extension Array: <class 'pandas.core.arrays.boolean.BooleanArray'> must implement _from_sequence_of_strings in order to be used in parser methods
>>> df3 = pd.read_csv("test.csv", dtype={'X_bool': 'bool'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 676, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 454, in _read
    data = parser.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 1133, in read
    ret = self._engine.read(nrows)
  File "/usr/local/lib/python3.7/site-packages/pandas/io/parsers.py", line 2037, in read
    data = self._reader.read(nrows)
  File "pandas/_libs/parsers.pyx", line 859, in pandas._libs.parsers.TextReader.read
  File "pandas/_libs/parsers.pyx", line 874, in pandas._libs.parsers.TextReader._read_low_memory
  File "pandas/_libs/parsers.pyx", line 951, in pandas._libs.parsers.TextReader._read_rows
  File "pandas/_libs/parsers.pyx", line 1083, in pandas._libs.parsers.TextReader._convert_column_data
  File "pandas/_libs/parsers.pyx", line 1114, in pandas._libs.parsers.TextReader._convert_tokens
  File "pandas/_libs/parsers.pyx", line 1231, in pandas._libs.parsers.TextReader._convert_with_dtype
ValueError: Bool column has NA values in column 1

(Impression) Tout le coupable était que la valeur manquante n'existait pas dans numpy. Il y a donc diverses incohérences dans l'introduction au niveau de la couche pandas. Diverses choses sont écrites ici. https://dev.pandas.io/docs/user_guide/gotchas.html#why-not-make-numpy-like-r

La raison pour laquelle j'ai appris en premier lieu

J'ai remarqué par retweet de quelqu'un sur Twitter

https://mobile.twitter.com/jorisvdbossche/status/1208476049690046465 スクリーンショット 2020-01-13 8.49.57.png

emballer

--pd.NA apparaît comme la signification de la valeur manquante. --pd.NA peut être utilisé avec IntegerArray, BooleanArray, StringArray --Avec l'introduction de pd.NA, la valeur manquante peut être exprimée dans la classe int (pas de conversion imprudente en float). --pd.NA est un objet singleton et est cohérent avec tous les types de données. --Toutes les valeurs de retour de l'opérateur de comparaison pour pd.NA sont pd.NA (même comportement que l'objet manquant de Julia, R`` NA`))

finalement

Si vous aimez ce genre d'histoire maniaque, venez nous rendre visite à justInCase. https://www.wantedly.com/companies/justincase


URL de référence

Environnement de vérification

Je l'ai confirmé sur docker.

FROM python:3.7.6
WORKDIR /home
RUN pip install pandas==1.0.0rc0
CMD ["/bin/bash"]
$ docker build -t pdna .
$ docker run -it --rm -v $(pwd):/home/ pdna

Inside Docker

root@286578c2496b:/home# cat /etc/issue
Debian GNU/Linux 10 \n \l
root@286578c2496b:/home# uname -a
Linux 286578c2496b 4.9.184-linuxkit #1 SMP Tue Jul 2 22:58:16 UTC 2019 x86_64 GNU/Linux
root@286578c2496b:/home# python -c "import pandas as pd; pd.show_versions()"

INSTALLED VERSIONS
------------------
commit           : None
python           : 3.7.6.final.0
python-bits      : 64
OS               : Linux
OS-release       : 4.9.184-linuxkit
machine          : x86_64
processor        : 
byteorder        : little
LC_ALL           : None
LANG             : C.UTF-8
LOCALE           : en_US.UTF-8

pandas           : 1.0.0rc0
numpy            : 1.18.1
pytz             : 2019.3
dateutil         : 2.8.1
pip              : 19.3.1
setuptools       : 44.0.0
Cython           : None
pytest           : None
hypothesis       : None
sphinx           : None
blosc            : None
feather          : None
xlsxwriter       : None
lxml.etree       : None
html5lib         : None
pymysql          : None
psycopg2         : None
jinja2           : None
IPython          : None
pandas_datareader: None
bs4              : None
bottleneck       : None
fastparquet      : None
gcsfs            : None
lxml.etree       : None
matplotlib       : None
numexpr          : None
odfpy            : None
openpyxl         : None
pandas_gbq       : None
pyarrow          : None
pytables         : None
pytest           : None
s3fs             : None
scipy            : None
sqlalchemy       : None
tables           : None
tabulate         : None
xarray           : None
xlrd             : None
xlwt             : None
xlsxwriter       : None
numba            : None

Recommended Posts

Caractéristiques de pd.NA dans Pandas 1.0.0 (RC0)
Résumé des méthodes fréquemment utilisées chez les pandas
Résumé de ce qui a été utilisé dans 100 coups de Pandas (# 1 ~ # 32)
L'en-tête est mal aligné avec read_csv () et read_table () de Pandas
Parlez des fonctionnalités dont les pandas et moi étions en charge dans le projet
Apprenez les pandas en 10 minutes
À propos de MultiIndex of Pandas
UnicodeDecodeError dans pandas read_csv
Fonctionnement de base des Pandas
Installation de Python 3.3 rc1
Caractéristiques du langage Go
Principales caractéristiques de ChainMap
"Erreur de type: type de valeur non reconnu: <class'str '>" dans to_datetime des pandas
Comparaison de la gestion des trames de données en Python (pandas), R, Pig
Comment obtenir un aperçu de vos données dans Pandas
Gestion des devis en [bash]
Partiel en cas de problème
Caractéristiques du langage de programmation [Memo]
Liste des nœuds dans les diagrammes
Jugement d'équivalence d'objet en Python
Résumé de l'utilisation de base de Pandas
Comportement de la méthode pandas rolling ()
Implémentation du tri rapide en Python
À propos des fonctionnalités de Python
Index d'utilisation de certains pandas
Permuter les colonnes dans les trames de données pandas
Le pouvoir des pandas: Python
[Enfin une mise à jour majeure] J'ai vérifié diverses mises à jour de Pandas 1.0.0rc