[PYTHON] Un petit examen minutieux de Pandas 1.0 et Dask

Contexte

Pandas 1.0.0 est sorti le 29 janvier 2020! Crépitement Depuis le 14/02/2020, il s'agit du 1.0.1.

Personnellement, je pense que les changements suivants sont des points importants. --pandas original NA --Expérimental pour le type String

Bien.

Lors de l'analyse, j'utilise souvent les bibliothèques et pandas suivants ensemble.

En particulier, j'aimerais connaître l'état du support de dask pour pandas 1.0 et d'autres comportements détaillés. La version de dask est la 2.10.1 au 14/02/2020.

Concernant les apports, je pense qu'il n'y a pas de problème si dask le soutient. (Il y a aussi un moment où le temps d'attente de traitement de dask est libre.)

スクリーンショット 2020-02-14 午前11.20.52.png

Ce qui vous intéresse

Tom Augspurger semble soutenir les pandas 1.0 comme un démon, et j'ai de grandes attentes jusqu'à présent. スクリーンショット 2020-02-14 午後2.26.08.png

résultat

Pour ceux qui ne veulent connaître que le résultat.

--Dask peut exécuter quatre règles et opérations sur des chaînes même s'il contient pandas.NA. --dask ne peut pas être set_index avec des types personnalisés tels que ʻInt64, string --Les deux pandas / dask ne peuvent pas indexer le filtre sur les colonnesbooléennes contenant pandas.NA --dask devient le type ʻobject même si ʻapply (meta = 'string') , mais il est réactivé par ʻas type ('string').

Construction de l'environnement d'enquête

Pour le moment, préparez un environnement de vérification propre. Le système d'exploitation est macOS Catalina 10.15.2.

En ce qui concerne la version python, les pandas ne définissent que le minimum, et dask semble être compatible python3.8, donc Si c'est 3.7.4, il n'y aura pas de problème.

Pour les dépendances, incluez versions minimales pour les dépendances. Cependant, fastparquet et pyArrow ne peuvent pas coexister sur mac problème utilisé pour exister, donc pyArrow ne doit pas être inclus juste au cas où. Je ne m'en sers pas.

Le travail de vérification est effectué sur jupyterlab.

pyenv virtualenv 3.7.4 pandas100
pyenv shell pandas100
pip install -r requirements.txt

requirements.txt


pandas==1.0.1
dask[complete]==2.10.1
fastparquet==0.3.3

jupyterlab==1.2.6

numpy==1.18.1
pytz==2019.3
python-dateutil==2.8.1
numexpr==2.7.1
beautifulsoup4==4.8.2
gcsfs==0.6.0
lxml==4.5.0
matplotlib==3.1.3
numba==0.48.0
openpyxl==3.0.3
pymysql==0.9.3
tables==3.6.1
s3fs==0.4.0
scipy==1.4.1
sqlalchemy==1.3.13
xarray==0.15.0
xlrd==1.2.0
xlsxwriter==1.2.7
xlwt==1.3.0

dask vs pandas1.0

Commencez par vérifier pd.NA.

Vérifiez le comportement de pd.NA dans chaque

s=... type(s.loc[3])
pandas.Series([1,2,3,None], dtype='int') TypeError
pandas.Series([1,2,3,pandas.NA], dtype='int') TypeError
pandas.Series([1,2,3,None], dtype='Int64') pandas._libs.missing.NAType
pandas.Series([1,2,3,None], dtype='float') numpy.float64
pandas.Series([1,2,3,pandas.NA], dtype='float') TypeError
pandas.Series([1,2,3,None], dtype='Int64').astype('float') numpy.float64
pandas.Series(['a', 'b', 'c' ,None], dtype='string') pandas._libs.missing.NAType
pandas.Series(['a', 'b', 'c' ,None], dtype='object').astype('string') pandas._libs.missing.NAType
pandas.Series([True, False, True ,None], dtype='boolean') pandas._libs.missing.NAType
pandas.Series([1, 0, 1 ,None], dtype='float').astype('boolean') pandas._libs.missing.NAType
pandas.Series(pandas.to_datetime(['2020-01-01', '2020-01-02', '2020-01-03', None])) pandas._libs.tslibs.nattype.NaTType
pandas.Series(pandas.to_timedelta(['00:00:01', '00:00:02', '00:00:03', None])) pandas._libs.tslibs.nattype.NaTType
pandas.Series([object(), object(), object(), None], dtype='object') NoneType
pandas.Series([object(), object(), object(), pandas.NA], dtype='object') pandas.Series([object(), object(), object(), pandas.NA], dtype='object')

Résumé,

--dtype int ne devient pas pandas.NA (TypeError tel quel) --dtype Int64, chaîne, booléen devient pandas.NA. --dtype float devient numpy.NaN --dtype datetime64, timedelta64 devient NAT L'objet --dtype ne convertit pas automatiquement None en pandas.NA

Examinez ce qui se passe quand il s'agit de dask.dataframe.from_pandas.

>>> import pandas
>>> import dask.dataframe

>>> df = pandas.DataFrame({'i': [1,2,3,4], 
...                        'i64': pandas.Series([1,2,3,None], dtype='Int64'), 
...                        's': pandas.Series(['a', 'b', 'c' ,None], dtype='string'), 
...                        'f': pandas.Series([1,2,3,None], dtype='Int64').astype('float')})

>>> ddf = dask.dataframe.from_pandas(df, npartitions=1)
>>> df
	i	i64	s	f
0	1	1	a	1.0
1	2	2	b	2.0
2	3	3	c	3.0
3	4	<NA>	<NA>	NaN

>>> ddf
Dask DataFrame Structure:
	i	i64	s	f
npartitions=1				
0	int64	Int64	string	float64
3	...	...	...	...

En effet, ʻInt64 est aussi ʻInt64 sur dask. La même chose s'applique à "string".

>>> #(Entier) opération sur Int64
>>> df.i64 * 2
0       2
1       4
2       6
3    <NA>
Name: i64, dtype: Int64

>>> (ddf.i64 * 2).compute()
0       2
1       4
2       6
3    <NA>
Name: i64, dtype: Int64

Le traitement Int64-> Int64 fonctionne correctement.

>>> #(Score minoritaire flottant) calcul pour Int64
>>> df.i64 - df.f
0    0.0
1    0.0
2    0.0
3    NaN
dtype: float64

>>> (ddf.i64 - ddf.f).compute()
0    0.0
1    0.0
2    0.0
3    NaN
dtype: float64

Le traitement Int64-> float64 fonctionne également correctement.

>>> # pandas.Défini dans la colonne Int64 contenant NA_index
>>> df.set_index('i64')
	i	s	f	i64_result	i64-f
i64					
1	1	a	1.0	2	0.0
2	2	b	2.0	4	0.0
3	3	c	3.0	6	0.0
NaN	4	<NA>	NaN	<NA>	NaN

>>> ddf.set_index('i64').compute()
TypeError: data type not understood

>>> # pandas.Que se passerait-il sans NA
>>> ddf['i64_nonnull'] = ddf.i64.fillna(1)
... ddf.set_index('i64_nonnull').compute()
TypeError: data type not understood

Eh! dask ne peut pas set_index dans la colonne ʻInt64`! Bien sûr, les pandas le peuvent.

>>> # pandas.Définir dans la colonne de chaîne contenant NA_index
>>> df.set_index('s')
	i	i64	f
s			
a	1	1	1.0
b	2	2	2.0
c	3	3	3.0
NaN	4	<NA>	NaN

>>> ddf.set_index('s').compute()
TypeError: Cannot perform reduction 'max' with string dtype

>>> # pandas.Que se passerait-il sans NA
>>> ddf['s_nonnull'] = ddf.s.fillna('a')
... ddf.set_index('s_nonnull')
TypeError: Cannot perform reduction 'max' with string dtype

Je ne peux pas non plus faire de string. Cela ne peut pas encore être utilisé (dans mon utilisation).

# .Essayez la fonction str
>>> df.s.str.startswith('a')
0     True
1    False
2    False
3     <NA>
Name: s, dtype: boolean

>>> ddf.s.str.startswith('a').compute()
0     True
1    False
2    False
3     <NA>
Name: s, dtype: boolean

Hmmm, ça marche.

>>> # pandas.Filtrer par colonne booléenne contenant NA
>>> df[df.s.str.startswith('a')]
ValueError: cannot mask with array containing NA / NaN values

>>> # pandas.Est-ce que NA est mauvais?
>>> df['s_nonnull'] = df.s.fillna('a')
... df[df.s_nonnull.str.startswith('a')]
	i	i64	s	f	i64_nonnull	s_nonnull
0	1	1	a	1.0	1	a
3	4	<NA>	<NA>	NaN	1	a

>>> ddf[ddf.s.str.startswith('a')].compute()
ValueError: cannot mask with array containing NA / NaN values

>>> ddf['s_nonnull'] = ddf.s.fillna('a')
... ddf[ddf.s_nonnull.str.startswith('a')].compute()
	i	i64	s	f	i64_nonnull	s_nonnull
0	1	1	a	1.0	1	a
3	4	<NA>	<NA>	NaN	1	a
>>> ddf[ddf.s.str.startswith('a')].compute()

e! !! !! Vous ne pouvez pas filtrer si pandas.NA est inclus? Ce n'est pas bien!

>>> #appliquer à meta='Int64'Essayez de spécifier
>>> ddf['i10'] = ddf.i.apply(lambda v: v * 10, meta='Int64')
>>> ddf
Dask DataFrame Structure:
	i	i64	s	f	i64_nonnull	s_nonnull	i10
npartitions=1							
0	int64	Int64	string	float64	Int64	string	int64
3	...	...	...	...	...	...	...

>>> #appliquer à meta='string'Essayez de spécifier
>>> ddf['s_double'] = ddf.s.apply(lambda v: v+v, meta='string')
Dask DataFrame Structure:
	i	i64	s	f	i64_nonnull	s_nonnull	i10	s_double
npartitions=1								
0	int64	Int64	string	float64	Int64	string	int64	object
3	...	...	...	...	...	...	...	...

>>> # astype('string')Essayer
>>> ddf['s_double'] = ddf['s_double'].astype('string')
>>> ddf
Dask DataFrame Structure:
	i	i64	s	f	i64_nonnull	s_nonnull	i10	s_double
npartitions=1								
0	int64	Int64	string	float64	Int64	string	int64	string
3	...	...	...	...	...	...	...	...

Si vous le spécifiez avec meta =, n'est-il pas reflété? .. .. Il peut être réactivé avec un type, mais c'est un problème. .. ..

résultat

--Le calcul est OK --Dans dask, il ne peut pas être utilisé comme index (car le type qui prend en charge pandas.NA ne peut pas être utilisé en premier lieu)

dask vs pandas.Categorical

Afin d'enquêter sur le catégorique chez les pandas, cette fois, nous utiliserons la méthode utilisant le Dtype catégorique. L'utilisation de base de Dtype catégorique est

  1. Instancier en spécifiant des catégories et en ordonnant
  2. Instanciez pandas.Series en tant qu'instance dtype = CategoricalDtype

Je pense que c'est. Exemple de code ci-dessous

>>> #Tout d'abord, créez un Dtype catégorique
>>> int_category = pandas.CategoricalDtype(categories=[1,2,3,4,5], 
...                                        ordered=True)
>>> int_category
CategoricalDtype(categories=[1, 2, 3, 4, 5], ordered=True)

>>> int_category.categories
Int64Index([1, 2, 3, 4, 5], dtype='int64')

>>> #Comme ces pandas.Faire une série
>>> int_series = pandas.Series([1,2,3], dtype=int_category)
>>> int_series
0    1
1    2
2    3
dtype: category
Categories (5, int64): [1 < 2 < 3 < 4 < 5]

>>> #Au moment de la génération, il convertira les valeurs qui ne sont pas dans la catégorie en NaN
>>> int_series = pandas.Series([1,2,3,6], dtype=int_category)
>>> int_series
0      1
1      2
2      3
3    NaN
dtype: category
Categories (5, int64): [1 < 2 < 3 < 4 < 5]

>>> #Se mettre en colère après génération
>>> int_series.loc[3] = 10
ValueError: Cannot setitem on a Categorical with a new category, set the categories first

Ensuite, essayez d'utiliser Categorical sur dask.

>>> import pandas
>>> import dask.dataframe
>>> # pandas.Générer DataFrame
>>> df = pandas.DataFrame({'a': pandas.Series([1, 2, 3, 1, 2, 3], dtype=int_category), 
...                        'b': pandas.Series([1, 2, 3, 1, 2, 3], dtype='int64')})
>>> df
	a	b
0	1	1
1	2	2
2	3	3
3	1	1
4	2	2
5	3	3

>>> # dask.dataframe.Convertir en DataFrame
>>> ddf = dask.dataframe.from_pandas(df, npartitions=1)
>>> ddf
Dask DataFrame Structure:
	a	b
npartitions=1		
0	category[known]	int64
5	...	...

Pour le moment, j'ai pu le rendre dask car c'est catégorique.

#L'ajout de nouvelles valeurs de catégorie est légal dans les pandas
>>> df.loc[2, 'a'] = 30
ValueError: Cannot setitem on a Categorical with a new category, set the categories first

#Dans dask, il ne peut pas être attribué en premier lieu indépendamment de la catégorie
>>> ddf.loc['a', 3] = 10
TypeError: '_LocIndexer' object does not support item assignment

#Chez les pandas, le calcul des valeurs de catégorie est également légal
>>> df.a * 2
TypeError: unsupported operand type(s) for *: 'Categorical' and 'int'

#Même avec dask, le calcul des valeurs de catégorie est légal
>>> ddf.a * 2
TypeError: unsupported operand type(s) for *: 'Categorical' and 'int'

#Essayez de spécifier comme méta avec appliquer de dask
>>> ddf['c'] = ddf.a.apply(lambda v: v, meta=int_category)
Dont know how to create metadata from category

#dask appliquer, meta='category'Ferez-vous de votre mieux si vous le faites?
>>> ddf['c'] = ddf.a.apply(lambda v: v, meta='category')
>>> ddf.dtypes
a    category
b       int64
c      object
dtype: object

>>> #Vérifiez s'il est cohérent avec le contenu
>>> ddf.compute().dtypes
a    category
b       int64
c    category
dtype: object

>>> #essayez un type
>>> ddf['c'] = ddf.c.astype(int_category)
>>> ddf
Dask DataFrame Structure:
	a	b	c
npartitions=1			
0	category[known]	int64	category[known]
5	...	...	...

Je vois. La partie contrainte de la catégorie est maintenue, mais si vous faites .apply (meta =), la gestion dtype de dask sera boguée. Il est possible de le relancer avec astype, mais c'est un problème. .. .. N'est-il pas possible d'utiliser uniquement des filtres?

#Essayez d'agréger
>>> ddf.groupby('a').b.mean().compute()
a
1    1.0
2    2.0
3    3.0
4    NaN
5    NaN
Name: b, dtype: float64

#Le type n'est-il pas cassé en étant traité comme un index?
Dask DataFrame Structure:
a	b
npartitions=1		
category[known]	float64
...	...
Dask Name: reset_index, 34 tasks

Hmmm, pensez-vous que cela correspond à l'agrégation?

résultat

--Lors de l'utilisation de pandas.Catégorique avec dask, les filtres et les agrégats semblent bien --Utilisez ʻas type` lorsque vous voulez ajouter une nouvelle colonne catégorielle au dask DataFrame.

to_parquet vs pandas1.0

>>> #Tout d'abord, les pandas.Générer DataFrame
>>> df = pandas.DataFrame(
    {
        'i64': pandas.Series([1, 2, 3,None], dtype='Int64'),
        'i64_nonnull': pandas.Series([1, 2, 3, 4], dtype='Int64'),
        's': pandas.Series(['a', 'b', 'c',None], dtype='string'),
        's_nonnull': pandas.Series(['a', 'b', 'c', 'd'], dtype='string'),
    }
)
>>> df
	i64	i64_nonnull	s	s_nonnull
0	1	1	a	a
1	2	2	b	b
2	3	3	c	c
3	<NA>	4	<NA>	d

>>> # dask.dataframe.Convertir en DataFrame
>>> ddf = dask.dataframe.from_pandas(df, npartitions=1)
>>> ddf
Dask DataFrame Structure:
	i64	i64_nonnull	s	s_nonnull
npartitions=1				
0	Int64	Int64	string	string
3	...	...	...	...

Pour le moment, essayez to_parquet.

>>> ddf.to_parquet('test1', engine='fastparquet')
ValueError: Dont know how to convert data type: Int64

sérieusement. .. .. Je m'y attendais. .. .. Même si Int64 n'est pas bon, une chaîne peut être possible. .. ..

>>> ddf.to_parquet('test2', engine='fastparquet')
ValueError: Dont know how to convert data type: string

C'était mauvais.

résultat

--Int64 et string ne peuvent pas être to_parquet.

en conclusion

Comment était-ce? Peut-être que personne n'a lu ce commentaire jusqu'à la fin. Dois-je avoir séparé les messages?

pandas 1.0 J'espère que cela aidera les gens qui y réfléchissent.

À bientôt.

Recommended Posts

Un petit examen minutieux de Pandas 1.0 et Dask
Une petite introduction de fonction de niche de faiss
Une compréhension approximative de python-fire et un mémo
Connectez beaucoup de Python ou et et
[Pandas_flavor] Ajouter une méthode de Pandas DataFrame
Pandas: un exemple très simple de DataFrame.rolling ()
Un mémorandum d'étude et de mise en œuvre du Deep Learning
Calcul des indicateurs techniques par TA-Lib et pandas
L'en-tête est mal aligné avec read_csv () et read_table () de Pandas
[Python] Une compréhension approximative des itérables, des itérateurs et des générateurs
Prise en compte des forces et faiblesses de Python
Fonctionnement de base de Python Pandas Series et Dataframe (1)
Analyse des données financières par pandas et leur visualisation (2)
Analyse des données financières par pandas et leur visualisation (1)
Ajouter une liste de fonctions de bibliothèque numpy petit à petit --a
Une histoire d'essayer pyenv, virtualenv et virtualenvwrapper
[PyTorch] Un peu de compréhension de CrossEntropyLoss avec des formules mathématiques
Prenez note de la liste des utilisations de base de Pandas
Une comparaison rapide des bibliothèques de test Python et node.js
Créez un lot d'images et gonflez avec ImageDataGenerator
[python] [Gracenote Web API] Une petite personnalisation de pygn
(Pour moi) Flask_2 (liste et pour, étend, et un peu plus)
Fonctionnement de base des pandas
À propos de MultiIndex of Pandas
index et réindexation des pandas
Fonctionnement de base des Pandas
pandas rééchantillonner et rouler
Moyenne et liste des pandas
Ajouter une liste de fonctions de la bibliothèque numpy petit à petit --- b
À la suite du montage et du réglage avec POH! Lite
Ajouter une liste de fonctions de bibliothèque numpy petit à petit --c
Transformez le nombre de secondes donné en heures, minutes et secondes
Détecter les objets d'une couleur et d'une taille spécifiques avec Python
Un résumé approximatif des différences entre Windows et Linux
Python: créer un dictionnaire à partir d'une liste de clés et de valeurs
J'ai essayé un peu le comportement de la fonction zip
Principes de base de Pandas pour les débutants ④ Gestion des éléments de date et d'heure
Une collection de méthodes utilisées lors de l'agrégation de données avec des pandas
Une histoire sur l'écriture d'AWS Lambda et de devenir un peu accro aux valeurs par défaut des arguments Python